From e0c454eeeade2797a32c6e1c6971a91c993a8796 Mon Sep 17 00:00:00 2001 From: sjlongland Date: Thu, 24 Sep 2015 08:11:15 +0000 Subject: [PATCH] Convert MS-DOS text files to Unix git-svn-id: https://svn.code.sf.net/p/freetel/code@2352 01035d8c-6547-0410-b346-abe4f91aad63 --- codec2-dev/octave/cml.patch | 24 +- codec2-dev/octave/ldpc.m | 208 +- codec2-dev/octave/ldpcdec.m | 600 +++--- codec2-dev/octave/ldpcenc.m | 250 +-- codec2-dev/octave/ldpcut.m | 318 +-- codec2-dev/octave/lpcauto.m | 232 +- codec2-dev/octave/test_dqpsk.m | 788 +++---- codec2-dev/octave/test_dqpsk2.m | 930 ++++---- codec2-dev/octave/test_foff.m | 804 +++---- codec2-dev/octave/test_qpsk.m | 1032 ++++----- codec2-dev/octave/test_qpsk2.m | 1282 +++++------ codec2-dev/octave/test_qpsk3.m | 1914 ++++++++--------- codec2-dev/stm32/inc/stm32f4xx_conf.h | 188 +- codec2-dev/stm32/src/adc_rec.c | 152 +- codec2-dev/stm32/src/adc_sd.c | 150 +- codec2-dev/stm32/src/adc_sfdr_ut.c | 180 +- codec2-dev/stm32/src/adcdac_ut.c | 140 +- codec2-dev/stm32/src/codec2_profile.c | 364 ++-- codec2-dev/stm32/src/dac_it.c | 410 ++-- codec2-dev/stm32/src/dac_play.c | 126 +- codec2-dev/stm32/src/dac_ut.c | 118 +- codec2-dev/stm32/src/debugblinky.c | 90 +- codec2-dev/stm32/src/fast_dac_ut.c | 230 +- codec2-dev/stm32/src/fdmdv_dump_rt.c | 308 +-- codec2-dev/stm32/src/fdmdv_profile.c | 298 +-- codec2-dev/stm32/src/fft_test.c | 352 +-- codec2-dev/stm32/src/freedv_rx_profile.c | 272 +-- codec2-dev/stm32/src/freedv_tx_profile.c | 180 +- codec2-dev/stm32/src/power_ut.c | 270 +-- .../stm32/src/sm1000_leds_switches_ut.c | 82 +- codec2-dev/stm32/src/startup_stm32f4xx.s | 1024 ++++----- codec2-dev/stm32/src/stm32f4_dac.c | 812 +++---- codec2-dev/stm32/src/stm32f4_dacduc.c | 832 +++---- codec2-dev/stm32/src/system_stm32f4xx.c | 1168 +++++----- codec2-dev/stm32/src/tuner_ut.c | 244 +-- codec2-dev/stm32/stlink/stlink.patch | 2 +- 36 files changed, 8187 insertions(+), 8187 deletions(-) diff --git a/codec2-dev/octave/cml.patch b/codec2-dev/octave/cml.patch index c7cadd0c..d5ba3205 100644 --- a/codec2-dev/octave/cml.patch +++ b/codec2-dev/octave/cml.patch @@ -2,21 +2,21 @@ diff -ruN -x '*.o' -x '*.dll' -x '*.mex' -x '*.mat' cml-orig/CmlStartup.m cml/Cm --- cml-orig/CmlStartup.m 2007-09-08 23:12:26.000000000 +0930 +++ cml/CmlStartup.m 2015-09-04 07:21:14.218455223 +0930 @@ -41,7 +41,7 @@ - addpath( strcat( cml_home, '/mex'), ... - strcat( cml_home, '/mat'), ... - strcat( cml_home, '/matalt' ), ... -- strcat( cml_home, '/mexhelp'), ... -+ %strcat( cml_home, '/mexhelp'), ... - strcat( cml_home, '/demos' ), ... - strcat( cml_home, '/scenarios'), ... - strcat( cml_home, '/localscenarios'),... + addpath( strcat( cml_home, '/mex'), ... + strcat( cml_home, '/mat'), ... + strcat( cml_home, '/matalt' ), ... +- strcat( cml_home, '/mexhelp'), ... ++ %strcat( cml_home, '/mexhelp'), ... + strcat( cml_home, '/demos' ), ... + strcat( cml_home, '/scenarios'), ... + strcat( cml_home, '/localscenarios'),... @@ -59,4 +59,4 @@ - save_directory = strcat( cml_home, '/scenarios/CmlHome.mat' ); - end - + save_directory = strcat( cml_home, '/scenarios/CmlHome.mat' ); + end + -save( save_directory, save_flag, 'cml_home' ); \ No newline at end of file -+save( save_directory, save_flag, 'cml_home' ); ++save( save_directory, save_flag, 'cml_home' ); diff -ruN -x '*.o' -x '*.dll' -x '*.mex' -x '*.mat' cml-orig/source/matrix.h cml/source/matrix.h --- cml-orig/source/matrix.h 1970-01-01 09:30:00.000000000 +0930 +++ cml/source/matrix.h 2015-09-04 07:06:46.907248420 +0930 diff --git a/codec2-dev/octave/ldpc.m b/codec2-dev/octave/ldpc.m index 00499625..38713031 100644 --- a/codec2-dev/octave/ldpc.m +++ b/codec2-dev/octave/ldpc.m @@ -1,104 +1,104 @@ -% ldpc.m -% -% David Rowe 2013 -% Octave functions to help us use the CML LDPC code. -% -% Installing CML library -% ---------------------- -% -% $ sudo apt-get install liboctave-dev -% $ wget http://www.iterativesolutions.com/user/image/cml.1.10.zip -% $ unzip cml.1.10.zip -% $ patch < ~/codec2-dev/octave/cml.patch -% $ cd source -% $ octave -% octave:> make - -1; - - -function code_param = ldpc_init(rate, framesize, modulation, mod_order, mapping) - [code_param.H_rows, code_param.H_cols, code_param.P_matrix] = InitializeWiMaxLDPC( rate, framesize, 0 ); - code_param.data_bits_per_frame = length(code_param.H_cols) - length( code_param.P_matrix ); - code_param.S_matrix = CreateConstellation( modulation, mod_order, mapping ); - code_param.bits_per_symbol = log2(mod_order); - code_param.code_bits_per_frame = framesize; - code_param.symbols_per_frame = framesize/2; -endfunction - - - -function [codeword s] = ldpc_enc(data, code_param) - codeword = LdpcEncode( data, code_param.H_rows, code_param.P_matrix ); - s = Modulate( codeword, code_param.S_matrix ); -endfunction - - -function detected_data = ldpc_dec(code_param, max_iterations, demod_type, decoder_type, r, EsNo, fading) - if nargin == 6 - fading = ones(1, length(r)); - end - - symbol_likelihood = Demod2D( r, code_param.S_matrix, EsNo, fading); - - % initialize the extrinsic decoder input - - input_somap_c = zeros(1, code_param.code_bits_per_frame ); - bit_likelihood = Somap( symbol_likelihood, demod_type, input_somap_c ); - - input_decoder_c = bit_likelihood(1:code_param.code_bits_per_frame); - - x_hat= MpDecode( -input_decoder_c, code_param.H_rows, code_param.H_cols, ... - max_iterations, decoder_type, 1, 1); - detected_data = x_hat(max_iterations,:); -endfunction - - -% Packs a binary array into an array of 8 bit bytes, MSB first - -function packed = packmsb(unpacked) - packed = zeros(1,floor(length(unpacked)+7)/8); - bit = 7; byte = 1; - for i=1:length(unpacked) - packed(byte) = bitor(packed(byte), bitshift(unpacked(i),bit)); - bit--; - if (bit < 0) - bit = 7; - byte++; - end - end -endfunction - - -% unpacks an array of 8 bit bytes into a binary array of unpacked bits, MSB first - -function unpacked = unpackmsb(packed) - bit = 7; byte = 1; - for i=1:length(packed)*8 - unpacked(i) = bitand(bitshift(packed(byte), -bit), 1); - bit--; - if (bit < 0) - bit = 7; - byte++; - end - end -endfunction - - -% symbol interleaver that acts on bits 2 at a time - -function y = interleave_bits(interleaver, x) - y = zeros(1,length(x)); - for i = 1:length(interleaver) - dst = interleaver(i); - y(2*(dst-1)+1:2*dst) = x(2*(i-1)+1:2*(i)); - end -endfunction - -% symbol de-interleaver - -function x = deinterleave_symbols(interleaver, y) - for i = 1:length(interleaver) - x(i) = y(interleaver(i)); - end -endfunction +% ldpc.m +% +% David Rowe 2013 +% Octave functions to help us use the CML LDPC code. +% +% Installing CML library +% ---------------------- +% +% $ sudo apt-get install liboctave-dev +% $ wget http://www.iterativesolutions.com/user/image/cml.1.10.zip +% $ unzip cml.1.10.zip +% $ patch < ~/codec2-dev/octave/cml.patch +% $ cd source +% $ octave +% octave:> make + +1; + + +function code_param = ldpc_init(rate, framesize, modulation, mod_order, mapping) + [code_param.H_rows, code_param.H_cols, code_param.P_matrix] = InitializeWiMaxLDPC( rate, framesize, 0 ); + code_param.data_bits_per_frame = length(code_param.H_cols) - length( code_param.P_matrix ); + code_param.S_matrix = CreateConstellation( modulation, mod_order, mapping ); + code_param.bits_per_symbol = log2(mod_order); + code_param.code_bits_per_frame = framesize; + code_param.symbols_per_frame = framesize/2; +endfunction + + + +function [codeword s] = ldpc_enc(data, code_param) + codeword = LdpcEncode( data, code_param.H_rows, code_param.P_matrix ); + s = Modulate( codeword, code_param.S_matrix ); +endfunction + + +function detected_data = ldpc_dec(code_param, max_iterations, demod_type, decoder_type, r, EsNo, fading) + if nargin == 6 + fading = ones(1, length(r)); + end + + symbol_likelihood = Demod2D( r, code_param.S_matrix, EsNo, fading); + + % initialize the extrinsic decoder input + + input_somap_c = zeros(1, code_param.code_bits_per_frame ); + bit_likelihood = Somap( symbol_likelihood, demod_type, input_somap_c ); + + input_decoder_c = bit_likelihood(1:code_param.code_bits_per_frame); + + x_hat= MpDecode( -input_decoder_c, code_param.H_rows, code_param.H_cols, ... + max_iterations, decoder_type, 1, 1); + detected_data = x_hat(max_iterations,:); +endfunction + + +% Packs a binary array into an array of 8 bit bytes, MSB first + +function packed = packmsb(unpacked) + packed = zeros(1,floor(length(unpacked)+7)/8); + bit = 7; byte = 1; + for i=1:length(unpacked) + packed(byte) = bitor(packed(byte), bitshift(unpacked(i),bit)); + bit--; + if (bit < 0) + bit = 7; + byte++; + end + end +endfunction + + +% unpacks an array of 8 bit bytes into a binary array of unpacked bits, MSB first + +function unpacked = unpackmsb(packed) + bit = 7; byte = 1; + for i=1:length(packed)*8 + unpacked(i) = bitand(bitshift(packed(byte), -bit), 1); + bit--; + if (bit < 0) + bit = 7; + byte++; + end + end +endfunction + + +% symbol interleaver that acts on bits 2 at a time + +function y = interleave_bits(interleaver, x) + y = zeros(1,length(x)); + for i = 1:length(interleaver) + dst = interleaver(i); + y(2*(dst-1)+1:2*dst) = x(2*(i-1)+1:2*(i)); + end +endfunction + +% symbol de-interleaver + +function x = deinterleave_symbols(interleaver, y) + for i = 1:length(interleaver) + x(i) = y(interleaver(i)); + end +endfunction diff --git a/codec2-dev/octave/ldpcdec.m b/codec2-dev/octave/ldpcdec.m index 6f71f832..2607766d 100644 --- a/codec2-dev/octave/ldpcdec.m +++ b/codec2-dev/octave/ldpcdec.m @@ -1,300 +1,300 @@ -% ldpcdec.m -% David Rowe 31 Dec 2013 -% -% LDPC decoder test program, given a file of QPSK symbols (IQIQ floats), -% performs frame sync, decodes, and measures BER. - -function ldpcdec(filename, Eprob) - - % Start CML library - - currentdir = pwd; - addpath '/home/david/tmp/cml/mat' % assume the source files stored here - cd /home/david/tmp/cml - CmlStartup % note that this is not in the cml path! - cd(currentdir) - - % Our LDPC library - - ldpc; - - % Start simulation - - rand('state',1); - - rate = 3/4; - framesize = 576; - - mod_order = 4; - modulation = 'QPSK'; - mapping = 'gray'; - - demod_type = 0; - decoder_type = 0; - max_iterations = 100; - EsNo = 4; - if (nargin == 1) - Eprob = 0.0; - end - - nbitspervocoderframe = 52; - nvocoderframes = 8; - nbitspermodemframe = 72; - - code_param = ldpc_init(rate, framesize, modulation, mod_order, mapping); - code_param.code_bits_per_frame = 576; - - data = []; - r = []; - load interleaver.txt - interleaver = interleaver + 1; - - Nframes = 100; - uw = [1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0]; - - Nc = 18; - - % repeat same simulated vocoder data to ease testing - - vd = round( rand( 1, nbitspervocoderframe*nvocoderframes) ); - - % Decoder: Sync with LDPC frames, de-interleave, LDPC decode, strip off UW, measure BER ------- - - mcwfilename = strcat(filename,"_modcodeword.bin"); - fm=fopen(mcwfilename,"rb"); - etfilename = strcat(filename,"_modcodeword_et.bin"); - fet=fopen(etfilename ,"rb"); - epfilename = strcat(filename,".err"); - fep=fopen(epfilename ,"wb"); - printf("Input QPSK symbols: %s\n", mcwfilename); - if (fet == -1) - printf("Input entered track file: none\n"); - else - printf("Input entered track file: %s\n", etfilename); - end - printf("Output error pattern file: %s\n", epfilename); - - mod_uw = build_mod_uw(uw, 2*length(vd)/length(uw)); - - mod_codeword = zeros(1, code_param.code_bits_per_frame/2); - lmod_codeword = code_param.code_bits_per_frame/2; - - Terrs = 0; Trawerrs = 0; Ferrs = 0; Tbits = 0; Tframes = 0; nerr = []; nrawerr = []; - corr = []; n = 0; - sync_state = 0; sync_count = 0; - mod_unpackedmodem_log = []; - sync_state_log = []; - entered_track_log = []; - sig_pwr_log = []; - - symbols = erasures = 0; - - [mod_unpackedmodem_float32, count] = fread(fm,nbitspermodemframe, "float32"); - if (fet == -1) - entered_track = 0; - else - entered_track = fread(fet, 1, "int"); - end - - while (count == nbitspermodemframe) - n++; - - mod_unpackedmodem = mod_unpackedmodem_float32(1:2:nbitspermodemframe) + j*mod_unpackedmodem_float32(2:2:nbitspermodemframe); - mod_unpackedmodem_log = [mod_unpackedmodem_log mod_unpackedmodem]; - %erasures = rand(1,length(mod_unpackedmodem)) < Eprob; - %mod_unpackedmodem(erasures) = 0; - - % keep buffer of one entire codeword - - mod_codeword(1:lmod_codeword-length(mod_unpackedmodem)) = mod_codeword(length(mod_unpackedmodem)+1:lmod_codeword); - mod_codeword(lmod_codeword-length(mod_unpackedmodem)+1:lmod_codeword) = mod_unpackedmodem; - - [uw_sync corr(n)] = look_for_uw(mod_codeword(1:length(mod_uw)), mod_uw); - - next_sync_state = sync_state; - if ((sync_state == 0) && (uw_sync == 1)) - next_sync_state = 1; - sync_count = 0; - end - if ((sync_state == 1) && (entered_track != 0)) - next_sync_state = 0; - end - sync_state = next_sync_state; - sync_state_log = [sync_state_log sync_state]; - entered_track_log = [entered_track_log entered_track]; - - if (sync_state && (sync_count == 0)) - Tframes++; - - % remove UW symbols - - mod_codeword_no_uw = remove_uw_symbols(mod_codeword, code_param.data_bits_per_frame/2 - length(uw)/2, length(uw)/2); - mod_codeword_no_uw = [mod_codeword_no_uw mod_codeword((code_param.data_bits_per_frame/2+1):code_param.code_bits_per_frame/2)]; - - % de-interleave - - tmp = deinterleave_symbols(interleaver, mod_codeword_no_uw); - - % insert known symbols at end of data - - mod_codeword_deinter = [ tmp(1:(code_param.data_bits_per_frame/2 - length(uw)/2)) ... - ones(1,length(uw)/2) * qpsk_mod([0 0]) ... - tmp((code_param.data_bits_per_frame/2 - length(uw)/2+1):length(tmp)) ]; - - % determine BER stats of raw data before decoding - - raw_bits = zeros(1, code_param.data_bits_per_frame - length(uw)); - for i=1:(code_param.data_bits_per_frame - length(uw))/2 - raw_bits(2*(i-1)+1:2*i) = qpsk_demod(mod_codeword_deinter(i)); - end - error_positions = xor(vd, raw_bits); - Nerrs = sum(error_positions); - Trawerrs += Nerrs; - nrawerr(Tframes) = Nerrs; - - % Determine Es/N for each carrier. For this codeword we assume - % across codeword (currently 320ms) signal is stationary. - % So for each carrier signal level is constant, so we can - % average across all symols of that carrier to get a better - % estimate of the carrier power. The spectral noise density - % No will be the same for the bandwidth of each carrier. So - % we can use noise samples from all symbols together to get - % a better estimate of the noise power. - - sig_pwr(Tframes,:) = zeros(1,Nc); - noise_samples = []; - for n=1:Nc - - % extract a vector of one carrier's symbols for this codeword - % rotate so that decision boundaries are now real and imag axis - - r = mod_codeword(n:Nc:length(mod_codeword)) .* exp(j*pi/4); - - sig_est = mean(abs(r)); - - % The noise is the variance of symbols (samples) about the actual symbol position - % we reflect all symbols into the first quadrant to simplify things, as the actual - % received symbol isn't matter, just the noise around it. We model the received - % symbol based on the estimated signal level. - - refl_symbols = abs(real(r)) + j*abs(imag(r)); - est_symbols = exp(j*pi/4)*sig_est*ones(1,length(r)); - noise_samples = [ noise_samples (est_symbols - refl_symbols)]; - - sig_pwr(Tframes,n) = sig_est .^ 2; - end - noise_pwr(Tframes) = var(noise_samples); - %plot(real(refl_symbols), imag(refl_symbols), '+'); - %hold on; - %plot(real(exp(j*pi/4)*sig_est*ones(1,length(r))), imag(exp(j*pi/4)*sig_est*ones(1,length(r))), 'r+'); - %hold off; - %printf("SNR: %f\n", 10*log10(sig_est*sig_est/noise_pwr(Tframes))); - - % Set erasures for carrier beneath a certain Es/N - - for n=1:Nc - symbols++; - EsN(n) = 10*log10(sig_pwr(Tframes,n)/noise_pwr(Tframes)); - if (EsN(n) < 1) - %mod_codeword(n:Nc:length(mod_codeword)) = 0; - %printf("Tframes: %d n: %d EsN = %3.2fdB\n", Tframes, n, EsN(n)); - erasures++; - end - end - - % De-interleave again with erasures set ---------------------- - - % remove UW symbols - - mod_codeword_no_uw = remove_uw_symbols(mod_codeword, code_param.data_bits_per_frame/2 - length(uw)/2, length(uw)/2); - mod_codeword_no_uw = [mod_codeword_no_uw mod_codeword((code_param.data_bits_per_frame/2+1):code_param.code_bits_per_frame/2)]; - - tmp = deinterleave_symbols(interleaver, mod_codeword_no_uw); - - % insert known symbols at end of data - - mod_codeword_deinter = [ tmp(1:(code_param.data_bits_per_frame/2 - length(uw)/2)) ... - ones(1,length(uw)/2) * qpsk_mod([0 0]) ... - tmp((code_param.data_bits_per_frame/2 - length(uw)/2+1):length(tmp)) ]; - - % LDPC decode ------------------------------------------------ - - detected_data = ldpc_dec(code_param, max_iterations, demod_type, decoder_type, mod_codeword_deinter, EsNo); - - % unpack payload data, removing UW - - vd_rx = detected_data(1:(code_param.data_bits_per_frame - length(uw))); - - % measure coded BER - - error_positions = xor(vd, vd_rx); - Nerrs = sum(error_positions); - if Nerrs>0, fprintf(1,'x'); Ferrs++; , else fprintf(1,'.'), end - Tbits += length(vd); - Terrs += Nerrs; - nerr(Tframes) = Nerrs; - - % save error patterns is simulated vocoder data to disk - - fwrite(fep, error_positions, "short"); - - end - - if (sync_state) - sync_count++; - if (sync_count == 8) - sync_count = 0; - end - end - - % read in one modulated modem frame at a time - - [mod_unpackedmodem_float32, count] = fread(fm, nbitspermodemframe, "float32"); - if (fet == -1) - entered_track = 0; - else - entered_track = fread(fet, 1, "int"); - end - end - - fclose(fep); - - printf("\nFrames: %d bits: %d errors: %d Raw BER = %f Coded BER = %f FER = %f\n", Tframes, Tbits, Terrs, Trawerrs/Tbits, Terrs/Tbits, Ferrs/Tframes); - printf("Symbols: %d Erasures: %d %f\n", symbols, erasures, erasures/symbols); - figure(8) - clf; - [n m] = size(mod_unpackedmodem_log); - plot( real(mod_unpackedmodem_log), imag(mod_unpackedmodem_log), '+') - axis([-2 2 -2 2]); - title('Scatter Diagram'); - - figure(9) - subplot(311) - plot(sync_state_log); - subplot(312) - plot(nrawerr); - subplot(313) - plot(nerr); - - figure(10); - plot(10*log10(sig_pwr(:,3)./noise_pwr(:)),'b'); - hold on; - plot(10+10*log10(noise_pwr(:))); - plot(10+10*log10(sig_pwr(:,3)),'r'); -% for n=2:Nc -% plot(n*10+10*log10(sig_pwr(:,n)./noise_pwr(:,n))); -% plot(n*10+10*log10(sig_pwr(:,n)),'r'); -% end - hold off; - - y = 1:Tframes; - x = 1:Nc; - z = 10*log10(sig_pwr(:,:)./((noise_pwr(:)*ones(1, Nc)))); - %printf("mean SNR = %3.2fdB\n", mean(z)); - figure(11); - imagesc(x,y,z); - figure(12); - mesh(x,y,z); - axis([1 Nc 1 Tframes 5 15]); - -endfunction +% ldpcdec.m +% David Rowe 31 Dec 2013 +% +% LDPC decoder test program, given a file of QPSK symbols (IQIQ floats), +% performs frame sync, decodes, and measures BER. + +function ldpcdec(filename, Eprob) + + % Start CML library + + currentdir = pwd; + addpath '/home/david/tmp/cml/mat' % assume the source files stored here + cd /home/david/tmp/cml + CmlStartup % note that this is not in the cml path! + cd(currentdir) + + % Our LDPC library + + ldpc; + + % Start simulation + + rand('state',1); + + rate = 3/4; + framesize = 576; + + mod_order = 4; + modulation = 'QPSK'; + mapping = 'gray'; + + demod_type = 0; + decoder_type = 0; + max_iterations = 100; + EsNo = 4; + if (nargin == 1) + Eprob = 0.0; + end + + nbitspervocoderframe = 52; + nvocoderframes = 8; + nbitspermodemframe = 72; + + code_param = ldpc_init(rate, framesize, modulation, mod_order, mapping); + code_param.code_bits_per_frame = 576; + + data = []; + r = []; + load interleaver.txt + interleaver = interleaver + 1; + + Nframes = 100; + uw = [1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0]; + + Nc = 18; + + % repeat same simulated vocoder data to ease testing + + vd = round( rand( 1, nbitspervocoderframe*nvocoderframes) ); + + % Decoder: Sync with LDPC frames, de-interleave, LDPC decode, strip off UW, measure BER ------- + + mcwfilename = strcat(filename,"_modcodeword.bin"); + fm=fopen(mcwfilename,"rb"); + etfilename = strcat(filename,"_modcodeword_et.bin"); + fet=fopen(etfilename ,"rb"); + epfilename = strcat(filename,".err"); + fep=fopen(epfilename ,"wb"); + printf("Input QPSK symbols: %s\n", mcwfilename); + if (fet == -1) + printf("Input entered track file: none\n"); + else + printf("Input entered track file: %s\n", etfilename); + end + printf("Output error pattern file: %s\n", epfilename); + + mod_uw = build_mod_uw(uw, 2*length(vd)/length(uw)); + + mod_codeword = zeros(1, code_param.code_bits_per_frame/2); + lmod_codeword = code_param.code_bits_per_frame/2; + + Terrs = 0; Trawerrs = 0; Ferrs = 0; Tbits = 0; Tframes = 0; nerr = []; nrawerr = []; + corr = []; n = 0; + sync_state = 0; sync_count = 0; + mod_unpackedmodem_log = []; + sync_state_log = []; + entered_track_log = []; + sig_pwr_log = []; + + symbols = erasures = 0; + + [mod_unpackedmodem_float32, count] = fread(fm,nbitspermodemframe, "float32"); + if (fet == -1) + entered_track = 0; + else + entered_track = fread(fet, 1, "int"); + end + + while (count == nbitspermodemframe) + n++; + + mod_unpackedmodem = mod_unpackedmodem_float32(1:2:nbitspermodemframe) + j*mod_unpackedmodem_float32(2:2:nbitspermodemframe); + mod_unpackedmodem_log = [mod_unpackedmodem_log mod_unpackedmodem]; + %erasures = rand(1,length(mod_unpackedmodem)) < Eprob; + %mod_unpackedmodem(erasures) = 0; + + % keep buffer of one entire codeword + + mod_codeword(1:lmod_codeword-length(mod_unpackedmodem)) = mod_codeword(length(mod_unpackedmodem)+1:lmod_codeword); + mod_codeword(lmod_codeword-length(mod_unpackedmodem)+1:lmod_codeword) = mod_unpackedmodem; + + [uw_sync corr(n)] = look_for_uw(mod_codeword(1:length(mod_uw)), mod_uw); + + next_sync_state = sync_state; + if ((sync_state == 0) && (uw_sync == 1)) + next_sync_state = 1; + sync_count = 0; + end + if ((sync_state == 1) && (entered_track != 0)) + next_sync_state = 0; + end + sync_state = next_sync_state; + sync_state_log = [sync_state_log sync_state]; + entered_track_log = [entered_track_log entered_track]; + + if (sync_state && (sync_count == 0)) + Tframes++; + + % remove UW symbols + + mod_codeword_no_uw = remove_uw_symbols(mod_codeword, code_param.data_bits_per_frame/2 - length(uw)/2, length(uw)/2); + mod_codeword_no_uw = [mod_codeword_no_uw mod_codeword((code_param.data_bits_per_frame/2+1):code_param.code_bits_per_frame/2)]; + + % de-interleave + + tmp = deinterleave_symbols(interleaver, mod_codeword_no_uw); + + % insert known symbols at end of data + + mod_codeword_deinter = [ tmp(1:(code_param.data_bits_per_frame/2 - length(uw)/2)) ... + ones(1,length(uw)/2) * qpsk_mod([0 0]) ... + tmp((code_param.data_bits_per_frame/2 - length(uw)/2+1):length(tmp)) ]; + + % determine BER stats of raw data before decoding + + raw_bits = zeros(1, code_param.data_bits_per_frame - length(uw)); + for i=1:(code_param.data_bits_per_frame - length(uw))/2 + raw_bits(2*(i-1)+1:2*i) = qpsk_demod(mod_codeword_deinter(i)); + end + error_positions = xor(vd, raw_bits); + Nerrs = sum(error_positions); + Trawerrs += Nerrs; + nrawerr(Tframes) = Nerrs; + + % Determine Es/N for each carrier. For this codeword we assume + % across codeword (currently 320ms) signal is stationary. + % So for each carrier signal level is constant, so we can + % average across all symols of that carrier to get a better + % estimate of the carrier power. The spectral noise density + % No will be the same for the bandwidth of each carrier. So + % we can use noise samples from all symbols together to get + % a better estimate of the noise power. + + sig_pwr(Tframes,:) = zeros(1,Nc); + noise_samples = []; + for n=1:Nc + + % extract a vector of one carrier's symbols for this codeword + % rotate so that decision boundaries are now real and imag axis + + r = mod_codeword(n:Nc:length(mod_codeword)) .* exp(j*pi/4); + + sig_est = mean(abs(r)); + + % The noise is the variance of symbols (samples) about the actual symbol position + % we reflect all symbols into the first quadrant to simplify things, as the actual + % received symbol isn't matter, just the noise around it. We model the received + % symbol based on the estimated signal level. + + refl_symbols = abs(real(r)) + j*abs(imag(r)); + est_symbols = exp(j*pi/4)*sig_est*ones(1,length(r)); + noise_samples = [ noise_samples (est_symbols - refl_symbols)]; + + sig_pwr(Tframes,n) = sig_est .^ 2; + end + noise_pwr(Tframes) = var(noise_samples); + %plot(real(refl_symbols), imag(refl_symbols), '+'); + %hold on; + %plot(real(exp(j*pi/4)*sig_est*ones(1,length(r))), imag(exp(j*pi/4)*sig_est*ones(1,length(r))), 'r+'); + %hold off; + %printf("SNR: %f\n", 10*log10(sig_est*sig_est/noise_pwr(Tframes))); + + % Set erasures for carrier beneath a certain Es/N + + for n=1:Nc + symbols++; + EsN(n) = 10*log10(sig_pwr(Tframes,n)/noise_pwr(Tframes)); + if (EsN(n) < 1) + %mod_codeword(n:Nc:length(mod_codeword)) = 0; + %printf("Tframes: %d n: %d EsN = %3.2fdB\n", Tframes, n, EsN(n)); + erasures++; + end + end + + % De-interleave again with erasures set ---------------------- + + % remove UW symbols + + mod_codeword_no_uw = remove_uw_symbols(mod_codeword, code_param.data_bits_per_frame/2 - length(uw)/2, length(uw)/2); + mod_codeword_no_uw = [mod_codeword_no_uw mod_codeword((code_param.data_bits_per_frame/2+1):code_param.code_bits_per_frame/2)]; + + tmp = deinterleave_symbols(interleaver, mod_codeword_no_uw); + + % insert known symbols at end of data + + mod_codeword_deinter = [ tmp(1:(code_param.data_bits_per_frame/2 - length(uw)/2)) ... + ones(1,length(uw)/2) * qpsk_mod([0 0]) ... + tmp((code_param.data_bits_per_frame/2 - length(uw)/2+1):length(tmp)) ]; + + % LDPC decode ------------------------------------------------ + + detected_data = ldpc_dec(code_param, max_iterations, demod_type, decoder_type, mod_codeword_deinter, EsNo); + + % unpack payload data, removing UW + + vd_rx = detected_data(1:(code_param.data_bits_per_frame - length(uw))); + + % measure coded BER + + error_positions = xor(vd, vd_rx); + Nerrs = sum(error_positions); + if Nerrs>0, fprintf(1,'x'); Ferrs++; , else fprintf(1,'.'), end + Tbits += length(vd); + Terrs += Nerrs; + nerr(Tframes) = Nerrs; + + % save error patterns is simulated vocoder data to disk + + fwrite(fep, error_positions, "short"); + + end + + if (sync_state) + sync_count++; + if (sync_count == 8) + sync_count = 0; + end + end + + % read in one modulated modem frame at a time + + [mod_unpackedmodem_float32, count] = fread(fm, nbitspermodemframe, "float32"); + if (fet == -1) + entered_track = 0; + else + entered_track = fread(fet, 1, "int"); + end + end + + fclose(fep); + + printf("\nFrames: %d bits: %d errors: %d Raw BER = %f Coded BER = %f FER = %f\n", Tframes, Tbits, Terrs, Trawerrs/Tbits, Terrs/Tbits, Ferrs/Tframes); + printf("Symbols: %d Erasures: %d %f\n", symbols, erasures, erasures/symbols); + figure(8) + clf; + [n m] = size(mod_unpackedmodem_log); + plot( real(mod_unpackedmodem_log), imag(mod_unpackedmodem_log), '+') + axis([-2 2 -2 2]); + title('Scatter Diagram'); + + figure(9) + subplot(311) + plot(sync_state_log); + subplot(312) + plot(nrawerr); + subplot(313) + plot(nerr); + + figure(10); + plot(10*log10(sig_pwr(:,3)./noise_pwr(:)),'b'); + hold on; + plot(10+10*log10(noise_pwr(:))); + plot(10+10*log10(sig_pwr(:,3)),'r'); +% for n=2:Nc +% plot(n*10+10*log10(sig_pwr(:,n)./noise_pwr(:,n))); +% plot(n*10+10*log10(sig_pwr(:,n)),'r'); +% end + hold off; + + y = 1:Tframes; + x = 1:Nc; + z = 10*log10(sig_pwr(:,:)./((noise_pwr(:)*ones(1, Nc)))); + %printf("mean SNR = %3.2fdB\n", mean(z)); + figure(11); + imagesc(x,y,z); + figure(12); + mesh(x,y,z); + axis([1 Nc 1 Tframes 5 15]); + +endfunction diff --git a/codec2-dev/octave/ldpcenc.m b/codec2-dev/octave/ldpcenc.m index 023c5172..87df284a 100644 --- a/codec2-dev/octave/ldpcenc.m +++ b/codec2-dev/octave/ldpcenc.m @@ -1,125 +1,125 @@ -% ldpcenc.m -% David Rowe 20 Dec 2013 -% -% LDPC encoder function. Takes a random data pattern, LDPC Encodes and -% inserts Unique Word (UW) sync bits and ouputs this as a packed -% binary file suitable for the Nc=18 carrier FDMDV modulator, -% fdmdv_mod. Also produces a "modulated" output file of QPSK -% symbols, suitable for feeding into ldpcdec for testing. - -function ldpcenc(filename) - - % Start CML library - - currentdir = pwd; - addpath '/home/david/tmp/cml/mat' % assume the source files stored here - cd /home/david/tmp/cml - CmlStartup % note that this is not in the cml path! - cd(currentdir) - - % Our LDPC library - - ldpc; - - % Start simulation - - rand('state',1); - - rate = 3/4; - framesize = 576; - - mod_order = 4; - modulation = 'QPSK'; - mapping = 'gray'; - - demod_type = 0; - decoder_type = 0; - max_iterations = 100; - - nbitspervocoderframe = 52; - nvocoderframes = 8; - nbitspermodemframe = 72; - - code_param = ldpc_init(rate, framesize, modulation, mod_order, mapping); - - data = []; - r = []; - load interleaver.txt - interleaver = interleaver + 1; - - % Encoder: Generate simulated vocoder data - % LPDC encode - % interleave - % insert UW bits - - Nframes = 100; - uw = [1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0]; - - % repeat same simulated vocoder data to ease testing - - vd = round( rand( 1, nbitspervocoderframe*nvocoderframes) ); - - % pad data with zeros the size of UW - - vdpad = [vd zeros(1, length(uw))]; - - % LDPC encode - - [codewordpad, s] = ldpc_enc(vdpad, code_param); - code_param.code_bits_per_frame = length(codewordpad); - code_param.symbols_per_frame = length(s); - - % remove padded zeros after encoding to leave room for UW bits (code - % is systematic) - - codeword = [ codewordpad(1:length(vd)) codewordpad((length(vd)+length(uw)+1):length(codewordpad)) ]; - - % interleave, insert UW bits, and pack bits (as C modulator likes packed bits) - - codeword_interleaved = interleave_bits(interleaver, codeword); - codeword_interleaved_uw = [insert_uw(codeword_interleaved(1:length(vd)), uw) codeword_interleaved(length(vd)+1:length(codeword_interleaved)) ]; - packedcodeword = packmsb(codeword_interleaved_uw); - - cwfilename = strcat(filename,"_codeword.bin"); - fc=fopen(cwfilename,"wb"); - for nn = 1: Nframes - fwrite(fc,packedcodeword,"uchar"); - end - fclose(fc); - - %printf("framesize: %d data_bits_per_frame: %d code_bits_per_frame: %d\n", ... - % framesize, code_param.data_bits_per_frame, code_param.code_bits_per_frame); - - printf("Encoded %d LDPC codewords, saved in packed file: %s\n", Nframes, cwfilename); - - % Modulator: Modulate to QPSK symbols ------------------------------------------ - - nbytespackedcodeword=length(packedcodeword); - fc=fopen(cwfilename,"rb"); - mcwfilename = strcat(filename,"_modcodeword.bin"); - fm=fopen(mcwfilename,"wb"); - nbytespackedmodemframe = nbitspermodemframe/8; - n = 0; - - [packedmodem, count] = fread(fc,nbytespackedmodemframe,"uchar"); - while (count == nbytespackedmodemframe) - n++; - unpackedmodem = unpackmsb(packedmodem); - - ii = 1; - for i=1:2:length(unpackedmodem) - mod_unpackedmodem(ii) = qpsk_mod(unpackedmodem(i:i+1)); - mod_unpackedmodem_float32(i) = real(mod_unpackedmodem(ii)); - mod_unpackedmodem_float32(i+1) = imag(mod_unpackedmodem(ii)); - ii += 1; - end - - fwrite(fm, mod_unpackedmodem_float32, "float32"); - [packedmodem, count] = fread(fc,nbytespackedmodemframe,"uchar"); - end - fclose(fc); - fclose(fm); - printf("Modulated %d modem frames to file: %s\n", n, mcwfilename); -endfunction - - +% ldpcenc.m +% David Rowe 20 Dec 2013 +% +% LDPC encoder function. Takes a random data pattern, LDPC Encodes and +% inserts Unique Word (UW) sync bits and ouputs this as a packed +% binary file suitable for the Nc=18 carrier FDMDV modulator, +% fdmdv_mod. Also produces a "modulated" output file of QPSK +% symbols, suitable for feeding into ldpcdec for testing. + +function ldpcenc(filename) + + % Start CML library + + currentdir = pwd; + addpath '/home/david/tmp/cml/mat' % assume the source files stored here + cd /home/david/tmp/cml + CmlStartup % note that this is not in the cml path! + cd(currentdir) + + % Our LDPC library + + ldpc; + + % Start simulation + + rand('state',1); + + rate = 3/4; + framesize = 576; + + mod_order = 4; + modulation = 'QPSK'; + mapping = 'gray'; + + demod_type = 0; + decoder_type = 0; + max_iterations = 100; + + nbitspervocoderframe = 52; + nvocoderframes = 8; + nbitspermodemframe = 72; + + code_param = ldpc_init(rate, framesize, modulation, mod_order, mapping); + + data = []; + r = []; + load interleaver.txt + interleaver = interleaver + 1; + + % Encoder: Generate simulated vocoder data + % LPDC encode + % interleave + % insert UW bits + + Nframes = 100; + uw = [1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0]; + + % repeat same simulated vocoder data to ease testing + + vd = round( rand( 1, nbitspervocoderframe*nvocoderframes) ); + + % pad data with zeros the size of UW + + vdpad = [vd zeros(1, length(uw))]; + + % LDPC encode + + [codewordpad, s] = ldpc_enc(vdpad, code_param); + code_param.code_bits_per_frame = length(codewordpad); + code_param.symbols_per_frame = length(s); + + % remove padded zeros after encoding to leave room for UW bits (code + % is systematic) + + codeword = [ codewordpad(1:length(vd)) codewordpad((length(vd)+length(uw)+1):length(codewordpad)) ]; + + % interleave, insert UW bits, and pack bits (as C modulator likes packed bits) + + codeword_interleaved = interleave_bits(interleaver, codeword); + codeword_interleaved_uw = [insert_uw(codeword_interleaved(1:length(vd)), uw) codeword_interleaved(length(vd)+1:length(codeword_interleaved)) ]; + packedcodeword = packmsb(codeword_interleaved_uw); + + cwfilename = strcat(filename,"_codeword.bin"); + fc=fopen(cwfilename,"wb"); + for nn = 1: Nframes + fwrite(fc,packedcodeword,"uchar"); + end + fclose(fc); + + %printf("framesize: %d data_bits_per_frame: %d code_bits_per_frame: %d\n", ... + % framesize, code_param.data_bits_per_frame, code_param.code_bits_per_frame); + + printf("Encoded %d LDPC codewords, saved in packed file: %s\n", Nframes, cwfilename); + + % Modulator: Modulate to QPSK symbols ------------------------------------------ + + nbytespackedcodeword=length(packedcodeword); + fc=fopen(cwfilename,"rb"); + mcwfilename = strcat(filename,"_modcodeword.bin"); + fm=fopen(mcwfilename,"wb"); + nbytespackedmodemframe = nbitspermodemframe/8; + n = 0; + + [packedmodem, count] = fread(fc,nbytespackedmodemframe,"uchar"); + while (count == nbytespackedmodemframe) + n++; + unpackedmodem = unpackmsb(packedmodem); + + ii = 1; + for i=1:2:length(unpackedmodem) + mod_unpackedmodem(ii) = qpsk_mod(unpackedmodem(i:i+1)); + mod_unpackedmodem_float32(i) = real(mod_unpackedmodem(ii)); + mod_unpackedmodem_float32(i+1) = imag(mod_unpackedmodem(ii)); + ii += 1; + end + + fwrite(fm, mod_unpackedmodem_float32, "float32"); + [packedmodem, count] = fread(fc,nbytespackedmodemframe,"uchar"); + end + fclose(fc); + fclose(fm); + printf("Modulated %d modem frames to file: %s\n", n, mcwfilename); +endfunction + + diff --git a/codec2-dev/octave/ldpcut.m b/codec2-dev/octave/ldpcut.m index 0b45ff79..caa5bf2a 100644 --- a/codec2-dev/octave/ldpcut.m +++ b/codec2-dev/octave/ldpcut.m @@ -1,159 +1,159 @@ -% ldpcut.m -% -% David Rowe 18 Dec 2013 -% -% Octave LDPC unit test script, based on simulation by Bill Cowley VK5DSP -% - -% Start CML library (see CML set up instructions in ldpc.m) - -currentdir = pwd; -addpath '/home/david/tmp/cml/mat' % assume the source files stored here -cd /home/david/tmp/cml -CmlStartup % note that this is not in the cml path! -cd(currentdir) - -% Our LDPC library - -ldpc; -qpsk_; - -function sim_out = run_simulation(sim_in) - - rate = sim_in.rate; - framesize = sim_in.framesize; - Ntrials = sim_in.Ntrials; - EsNodBvec = sim_in.EsNodBvec; - verbose = sim_in.verbose; - - % Start simulation - - mod_order = 4; - modulation = 'QPSK'; - mapping = 'gray'; - - demod_type = 0; - decoder_type = 0; - max_iterations = 100; - - code_param = ldpc_init(rate, framesize, modulation, mod_order, mapping); - - for ne = 1:length(EsNodBvec) - EsNodB = EsNodBvec(ne); - EsNo = 10^(EsNodB/10); - variance = 1/EsNo; - - Tbits = Terrs = Ferrs = 0; - - tx_bits = []; - tx_symbols = []; - rx_symbols = []; - - % Encode a bunch of frames - - for nn = 1: Ntrials - atx_bits = round(rand( 1, code_param.data_bits_per_frame)); - tx_bits = [tx_bits atx_bits]; - [tx_codeword atx_symbols] = ldpc_enc(atx_bits, code_param); - tx_symbols = [tx_symbols atx_symbols]; - end - - % Add AWGN noise, 0.5 factor ensures var(noise) == variance , i.e. splits power between Re & Im - - noise = sqrt(variance*0.5)*(randn(1,length(tx_symbols)) + j*randn(1,length(tx_symbols))); - rx_symbols = tx_symbols + noise; - - % Decode a bunch of frames - - for nn = 1: Ntrials - st = (nn-1)*code_param.symbols_per_frame + 1; - en = (nn)*code_param.symbols_per_frame; - arx_codeword = ldpc_dec(code_param, max_iterations, demod_type, decoder_type, rx_symbols(st:en), EsNo, ones(1,code_param.symbols_per_frame)); - st = (nn-1)*code_param.data_bits_per_frame + 1; - en = (nn)*code_param.data_bits_per_frame; - error_positions = xor(arx_codeword(1:code_param.data_bits_per_frame), tx_bits(st:en)); - Nerrs = sum( error_positions); - - if verbose == 2 - % print "." if frame decoded without errors, 'x' if we can't decode - - if Nerrs > 0, printf('x'), else printf('.'), end - if (rem(nn, 50)==0), printf('\n'), end - end - - if Nerrs > 0, Ferrs = Ferrs + 1; end - Terrs = Terrs + Nerrs; - Tbits = Tbits + code_param.data_bits_per_frame; - end - - if verbose - printf("\nEsNodB: %3.2f BER: %f Tbits: %d Terrs: %d Ferrs: %d\n", EsNodB, Terrs/Tbits, Tbits, Terrs, Ferrs) - end - - sim_out.Tbits(ne) = Tbits; - sim_out.Terrs(ne) = Terrs; - sim_out.Ferrs(ne) = Ferrs; - sim_out.BER(ne) = Terrs/Tbits; - sim_out.FER(ne) = Ferrs/Ntrials; - end - -endfunction - -% START SIMULATIONS --------------------------------------------------------------- - -more off; - -% 1/ Simplest possible one frame simulation --------------------------------------- - -printf("Test 1\n------\n"); - -mod_order = 4; -modulation = 'QPSK'; -mapping = 'gray'; -framesize = 576; % CML library has a bunch of different framesizes available -rate = 1/2; -demod_type = 0; -decoder_type = 0; -max_iterations = 100; -EsNo = 10; % decoder needs an estimated channel EsNo (linear ratio, not dB) - -code_param = ldpc_init(rate, framesize, modulation, mod_order, mapping); -tx_bits = round(rand(1, code_param.data_bits_per_frame)); -[tx_codeword, qpsk_symbols] = ldpc_enc(tx_bits, code_param); -rx_codeword = ldpc_dec(code_param, max_iterations, demod_type, decoder_type, qpsk_symbols, EsNo); - -errors_positions = xor(tx_bits, rx_codeword(1:framesize*rate)); -Nerr = sum(errors_positions); -printf("Nerr: %d\n\n", Nerr); - -% 2/ Run a bunch of trials at just one EsNo point -------------------------------------- - -printf("Test 2\n------\n"); - -sim_in.rate = 0.5; -sim_in.framesize = 2*576; -sim_in.verbose = 2; -sim_in.Ntrials = 100; -sim_in.EsNodBvec = 5; -run_simulation(sim_in); - -% 3/ Lets draw a Eb/No versus BER curve ------------------------------------------------- - -printf("\n\nTest 3\n------\n"); - -sim_in.EsNodBvec = -2:10; -sim_out = run_simulation(sim_in); - -EbNodB = sim_in.EsNodBvec - 10*log10(2); % QPSK has two bits/symbol -uncoded_BER_theory = 0.5*erfc(sqrt(10.^(EbNodB/10))); - -figure(1) -clf -semilogy(EbNodB, uncoded_BER_theory,'r;uncoded QPSK theory;') -hold on; -semilogy(EbNodB-10*log10(sim_in.rate), sim_out.BER+1E-10,'g;LDPC coded QPSK simulation;'); -hold off; -grid('minor') -xlabel('Eb/No (dB)') -ylabel('BER') -axis([min(EbNodB) max(EbNodB) min(uncoded_BER_theory) 1]) +% ldpcut.m +% +% David Rowe 18 Dec 2013 +% +% Octave LDPC unit test script, based on simulation by Bill Cowley VK5DSP +% + +% Start CML library (see CML set up instructions in ldpc.m) + +currentdir = pwd; +addpath '/home/david/tmp/cml/mat' % assume the source files stored here +cd /home/david/tmp/cml +CmlStartup % note that this is not in the cml path! +cd(currentdir) + +% Our LDPC library + +ldpc; +qpsk_; + +function sim_out = run_simulation(sim_in) + + rate = sim_in.rate; + framesize = sim_in.framesize; + Ntrials = sim_in.Ntrials; + EsNodBvec = sim_in.EsNodBvec; + verbose = sim_in.verbose; + + % Start simulation + + mod_order = 4; + modulation = 'QPSK'; + mapping = 'gray'; + + demod_type = 0; + decoder_type = 0; + max_iterations = 100; + + code_param = ldpc_init(rate, framesize, modulation, mod_order, mapping); + + for ne = 1:length(EsNodBvec) + EsNodB = EsNodBvec(ne); + EsNo = 10^(EsNodB/10); + variance = 1/EsNo; + + Tbits = Terrs = Ferrs = 0; + + tx_bits = []; + tx_symbols = []; + rx_symbols = []; + + % Encode a bunch of frames + + for nn = 1: Ntrials + atx_bits = round(rand( 1, code_param.data_bits_per_frame)); + tx_bits = [tx_bits atx_bits]; + [tx_codeword atx_symbols] = ldpc_enc(atx_bits, code_param); + tx_symbols = [tx_symbols atx_symbols]; + end + + % Add AWGN noise, 0.5 factor ensures var(noise) == variance , i.e. splits power between Re & Im + + noise = sqrt(variance*0.5)*(randn(1,length(tx_symbols)) + j*randn(1,length(tx_symbols))); + rx_symbols = tx_symbols + noise; + + % Decode a bunch of frames + + for nn = 1: Ntrials + st = (nn-1)*code_param.symbols_per_frame + 1; + en = (nn)*code_param.symbols_per_frame; + arx_codeword = ldpc_dec(code_param, max_iterations, demod_type, decoder_type, rx_symbols(st:en), EsNo, ones(1,code_param.symbols_per_frame)); + st = (nn-1)*code_param.data_bits_per_frame + 1; + en = (nn)*code_param.data_bits_per_frame; + error_positions = xor(arx_codeword(1:code_param.data_bits_per_frame), tx_bits(st:en)); + Nerrs = sum( error_positions); + + if verbose == 2 + % print "." if frame decoded without errors, 'x' if we can't decode + + if Nerrs > 0, printf('x'), else printf('.'), end + if (rem(nn, 50)==0), printf('\n'), end + end + + if Nerrs > 0, Ferrs = Ferrs + 1; end + Terrs = Terrs + Nerrs; + Tbits = Tbits + code_param.data_bits_per_frame; + end + + if verbose + printf("\nEsNodB: %3.2f BER: %f Tbits: %d Terrs: %d Ferrs: %d\n", EsNodB, Terrs/Tbits, Tbits, Terrs, Ferrs) + end + + sim_out.Tbits(ne) = Tbits; + sim_out.Terrs(ne) = Terrs; + sim_out.Ferrs(ne) = Ferrs; + sim_out.BER(ne) = Terrs/Tbits; + sim_out.FER(ne) = Ferrs/Ntrials; + end + +endfunction + +% START SIMULATIONS --------------------------------------------------------------- + +more off; + +% 1/ Simplest possible one frame simulation --------------------------------------- + +printf("Test 1\n------\n"); + +mod_order = 4; +modulation = 'QPSK'; +mapping = 'gray'; +framesize = 576; % CML library has a bunch of different framesizes available +rate = 1/2; +demod_type = 0; +decoder_type = 0; +max_iterations = 100; +EsNo = 10; % decoder needs an estimated channel EsNo (linear ratio, not dB) + +code_param = ldpc_init(rate, framesize, modulation, mod_order, mapping); +tx_bits = round(rand(1, code_param.data_bits_per_frame)); +[tx_codeword, qpsk_symbols] = ldpc_enc(tx_bits, code_param); +rx_codeword = ldpc_dec(code_param, max_iterations, demod_type, decoder_type, qpsk_symbols, EsNo); + +errors_positions = xor(tx_bits, rx_codeword(1:framesize*rate)); +Nerr = sum(errors_positions); +printf("Nerr: %d\n\n", Nerr); + +% 2/ Run a bunch of trials at just one EsNo point -------------------------------------- + +printf("Test 2\n------\n"); + +sim_in.rate = 0.5; +sim_in.framesize = 2*576; +sim_in.verbose = 2; +sim_in.Ntrials = 100; +sim_in.EsNodBvec = 5; +run_simulation(sim_in); + +% 3/ Lets draw a Eb/No versus BER curve ------------------------------------------------- + +printf("\n\nTest 3\n------\n"); + +sim_in.EsNodBvec = -2:10; +sim_out = run_simulation(sim_in); + +EbNodB = sim_in.EsNodBvec - 10*log10(2); % QPSK has two bits/symbol +uncoded_BER_theory = 0.5*erfc(sqrt(10.^(EbNodB/10))); + +figure(1) +clf +semilogy(EbNodB, uncoded_BER_theory,'r;uncoded QPSK theory;') +hold on; +semilogy(EbNodB-10*log10(sim_in.rate), sim_out.BER+1E-10,'g;LDPC coded QPSK simulation;'); +hold off; +grid('minor') +xlabel('Eb/No (dB)') +ylabel('BER') +axis([min(EbNodB) max(EbNodB) min(uncoded_BER_theory) 1]) diff --git a/codec2-dev/octave/lpcauto.m b/codec2-dev/octave/lpcauto.m index 6895ca23..7a5e89e1 100644 --- a/codec2-dev/octave/lpcauto.m +++ b/codec2-dev/octave/lpcauto.m @@ -1,116 +1,116 @@ -function [ar,e,k]=lpcauto(s,p,t) -%LPCAUTO performs autocorrelation LPC analysis [AR,E,K]=(S,P,T) -% Inputs: -% s(ns) is the input signal -% p is the order (default: 12) -% t(nf,3) specifies the frames size details: each row specifies -% up to three values per frame: [len anal skip] where: -% len is the length of the frame (default: length(s)) -% anal is the analysis length (default: len) -% skip is the number of samples to skip at the beginning (default: 0) -% If t contains only one row, it will be used repeatedly -% until there are no more samples left in s. -% -% Outputs: -% ar(nf,p+1) are the AR coefficients with ar(1) = 1 -% e(nf) is the energy in the residual. -% sqrt(e) is often called the 'gain' of the filter. -% k(nf,2) gives the first and last sample of the analysis interval - -% Notes: -% -% (1) The first frame always starts at sample s(1) and the analysis window starts at s(t(1,3)+1). -% (2) The elements of t need not be integers. -% (3) The analysis interval is always multiplied by a hamming window -% (4) As an example, if p=3 and t=[10 5 2], then the illustration below shows -% successive frames labelled a, b, c, ... with capitals for the -% analysis regions. Note that the first frame starts at s(1) -% -% a a A A A A A a a a b b B B B B B b b b c c C C C C C c c c d ... -% -% For speech processing, it can be advantageous to restrict the analysis regions -% to time intervals when the glottis is closed. -% -% (5) Frames can overlap: e.g. t=[10 20] will use analysis frames of -% length 20 overlapped by 10 samples. -% (6) For speech processing p should be at least 2*f*l/c where f is the sampling -% frequency, l the vocal tract length and c the speed of sound. For a typical -% male (l=17 cm) this gives f/1000. - -% Copyright (C) Mike Brookes 1997 -% Version: $Id: lpcauto.m 713 2011-10-16 14:45:43Z dmb $ -% -% VOICEBOX is a MATLAB toolbox for speech processing. -% Home page: http://www.ee.ic.ac.uk/hp/staff/dmb/voicebox/voicebox.html -% -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% This program is free software; you can redistribute it and/or modify -% it under the terms of the GNU General Public License as published by -% the Free Software Foundation; either version 2 of the License, or -% (at your option) any later version. -% -% This program is distributed in the hope that it will be useful, -% but WITHOUT ANY WARRANTY; without even the implied warranty of -% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -% GNU General Public License for more details. -% -% You can obtain a copy of the GNU General Public License from -% http://www.gnu.org/copyleft/gpl.html or by writing to -% Free Software Foundation, Inc.,675 Mass Ave, Cambridge, MA 02139, USA. -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -s = s(:); % make it a column vector -if nargin < 2 p=12; end; -if nargin < 3 t=length(s); end; -%if nargin < 4 w='ham'; end; -[nf,ng]=size(t); -if ng<2 t=[t t]; end; -if ng<3 t=[t zeros(nf,1)]; end; -if nf==1 - nf=floor(1+(length(s)-t(2)-t(3))/t(1)); - tr=0; -else - tr=1; -end; - -ar=zeros(nf,p+1); -ar(:,1)=1; -e=zeros(nf,1); - -t1=1; -it=1; -nw=-1; -zp=zeros(1,p); -r=(0:p); -for jf=1:nf - k(jf,1) = ceil(t1+t(it,3)); - k(jf,2) = ceil(t1+t(it,3)+t(it,2)-1); - cs = (k(jf,1):k(jf,2)).'; - nc = length(cs); - pp=min(p,nc); - dd=s(cs); - if nc~=nw - % possibly we should have a window whose square integral equals unity - ww=hamming(nc); nw=nc; - y=zeros(1,nc+p); - c=(1:nc)'; - end - wd=dd(:).*ww; % windowed data vector - y(1:nc)=wd; % data vector with p appended zeros - z=zeros(nc,pp+1); % data matrix - % was previously z(:)=y(c(:,ones(1,pp+1))+r(ones(nc,1),1:pp+1)); - z(:)=y(repmat(c,1,pp+1)+repmat(r,nc,1)); - rr=wd'*z; - rm=toeplitz(rr(1:pp)); - rk=rank(rm); - if rk - if rk 1 - printf("EsNo (dB): %f EsNo: %f variance: %f\n", EsNodB, EsNo, variance); - end - - Terrs = 0; Tbits = 0; - - tx_symb_log = []; - rx_symb_log = []; - noise_log = []; - sim_out.errors_log = []; - - % init HF channel - - hf_n = 1; - hf_angle_log = []; - hf_fading = ones(1,Nsymb); % default input for ldpc dec - hf_model = ones(Ntrials*Nsymb/Nc, Nc); % defaults for plotting surface - - sim_out.errors_log = []; - sim_out.Nerrs = []; - sim_out.snr_log = []; - sim_out.hf_model_pwr = []; - - symbol_amp_index = 1; - - for nn = 1: Ntrials - - tx_bits = round( rand( 1, framesize*rate ) ); - - % modulate -------------------------------------------- - - s = zeros(1, Nsymb); - for i=1:Nc:Nsymb - for k=1:Nc - tx_symb = qpsk_mod(tx_bits(2*(i-1+k-1)+1:2*(i+k-1))); - tx_symb *= prev_sym_tx(k); - prev_sym_tx(k) = tx_symb; - s(i+k-1) = symbol_amp(symbol_amp_index)*tx_symb; - end - end - s_ch = s; - symbol_amp_index++; - - % HF channel simulation ------------------------------------ - - if hf_sim - - % separation between carriers. Note this is - % effectively under samples at Rs, I dont think this - % matters. Equivalent to doing freq shift at Fs, then - % decimating to Rs. - - wsep = 2*pi*(1+0.5); % e.g. 75Hz spacing at Rs=50Hz, alpha=0.5 filters - - if Nsymb/Nc != floor(Nsymb/Nc) - printf("Error: Nsymb/Nc must be an integrer\n") - return; - end - - % arrange symbols in Nsymb/Nc by Nc matrix - - for i=1:Nc:Nsymb - - % Determine HF channel at each carrier for this symbol - - for k=1:Nc - hf_model(hf_n, k) = hf_gain*(spread(hf_n) + exp(-j*k*wsep*nhfdelay)*spread_2ms(hf_n)); - hf_fading(i+k-1) = abs(hf_model(hf_n, k)); - if hf_mag_only - s_ch(i+k-1) *= abs(hf_model(hf_n, k)); - else - s_ch(i+k-1) *= hf_model(hf_n, k); - end - end - hf_n++; - end - end - - tx_symb_log = [tx_symb_log s_ch]; - - % "genie" SNR estimate - - snr = (s_ch*s_ch')/(Nsymb*variance); - sim_out.snr_log = [sim_out.snr_log snr]; - sim_out.hf_model_pwr = [sim_out.hf_model_pwr mean(hf_fading.^2)]; - - % AWGN noise and phase/freq offset channel simulation - % 0.5 factor ensures var(noise) == variance , i.e. splits power between Re & Im - - noise = sqrt(variance*0.5)*(randn(1,Nsymb) + j*randn(1,Nsymb)); - noise_log = [noise_log noise]; - - % organise into carriers to apply frequency and phase offset - - for i=1:Nc:Nsymb - for k=1:Nc - s_ch(i+k-1) = s_ch(i+k-1)*exp(j*phase_offset) + noise(i+k-1); - end - phase_offset += w_offset; - end - - % de-modulate - - rx_bits = zeros(1, framesize); - for i=1:Nc:Nsymb - for k=1:Nc - rx_symb = s_ch(i+k-1); - tmp = rx_symb; - rx_symb *= conj(prev_sym_rx(k)/abs(prev_sym_rx(k))); - prev_sym_rx(k) = tmp; - rx_bits((2*(i-1+k-1)+1):(2*(i+k-1))) = qpsk_demod(rx_symb); - rx_symb_log = [rx_symb_log rx_symb]; - end - end - - error_positions = xor(rx_bits, tx_bits); - Nerrs = sum(error_positions); - sim_out.Nerrs = [sim_out.Nerrs Nerrs]; - Terrs += Nerrs; - Tbits += length(tx_bits); - - sim_out.errors_log = [sim_out.errors_log error_positions]; - end - - TERvec(ne) = Terrs; - BERvec(ne) = Terrs/Tbits; - - if verbose - printf("EsNo (dB): %f Terrs: %d BER %f ", EsNodB, Terrs, Terrs/Tbits); - printf("\n"); - end - if verbose > 1 - printf("Terrs: %d BER %f C %f N %f Es %f No %f Es/No %f\n\n", Terrs, - Terrs/Tbits, var(tx_symb_log), var(noise_log), - var(tx_symb_log), var(noise_log), var(tx_symb_log)/var(noise_log)); - end - end - - Ebvec = Esvec - 10*log10(bps); - - sim_out.BERvec = BERvec; - sim_out.Ebvec = Ebvec; - sim_out.TERvec = TERvec; - - if plot_scatter - figure(2); - clf; - scat = rx_symb_log .* exp(j*pi/4); - plot(real(scat), imag(scat),'+'); - title('Scatter plot'); - - figure(3); - clf; - y = 1:Rs*2; - x = 1:Nc; - EsNodBSurface = 20*log10(abs(hf_model(y,:))) - 10*log10(variance); - mesh(x,y,EsNodBSurface); - grid - title('HF Channel Es/No'); - - if 0 - figure(4); - clf; - subplot(211) - plot(y,abs(hf_model(y,1))) - title('HF Channel Carrier 1 Mag'); - subplot(212) - plot(y,angle(hf_model(y,1))) - title('HF Channel Carrier 1 Phase'); - end - end - -endfunction - -% Gray coded QPSK modulation function - -function symbol = qpsk_mod(two_bits) - two_bits_decimal = sum(two_bits .* [2 1]); - switch(two_bits_decimal) - case (0) symbol = 1; - case (1) symbol = j; - case (2) symbol = -j; - case (3) symbol = -1; - endswitch -endfunction - -% Gray coded QPSK demodulation function - -function two_bits = qpsk_demod(symbol) - if isscalar(symbol) == 0 - printf("only works with scalars\n"); - return; - end - bit0 = real(symbol*exp(j*pi/4)) < 0; - bit1 = imag(symbol*exp(j*pi/4)) < 0; - two_bits = [bit1 bit0]; -endfunction - -function sim_in = standard_init - sim_in.verbose = 1; - sim_in.plot_scatter = 0; - - sim_in.Esvec = 5:15; - sim_in.Ntrials = 100; - sim_in.framesize = 64; - sim_in.Rs = 100; - sim_in.Nc = 8; - - sim_in.phase_offset = 0; - sim_in.w_offset = 0; - sim_in.phase_noise_amp = 0; - - sim_in.hf_delay_ms = 2; - sim_in.hf_sim = 0; - sim_in.hf_phase_only = 0; - sim_in.hf_mag_only = 0; -endfunction - -function awgn_hf_ber_curves() - sim_in = standard_init(); - - Ebvec = sim_in.Esvec - 10*log10(2); - BER_theory = 0.5*erfc(sqrt(10.^(Ebvec/10))); - - dpsk_awgn = ber_test(sim_in); - sim_in.hf_sim = 1; - dpsk_hf = ber_test(sim_in); - - figure(1); - clf; - semilogy(Ebvec, BER_theory,'r;QPSK theory;') - hold on; - semilogy(dpsk_awgn.Ebvec, dpsk_awgn.BERvec,'g;DQPSK;') - semilogy(dpsk_hf.Ebvec, dpsk_hf.BERvec,'g;DQPSK HF;') - hold off; - xlabel('Eb/N0') - ylabel('BER') - grid("minor") - axis([min(Ebvec) max(Ebvec) 1E-3 1]) -end - -sim_in = standard_init(); - -% energy file sampled every 10ms - -load ../src/ve9qrp.txt -pdB=10*log10(ve9qrp); -for i=1:length(pdB) - if pdB(i) < 0 - pdB(i) = 0; - end -end - -% Down sample to 40ms rate used for 1300 bit/s codec, every 4th sample is transmitted - -pdB = pdB(4:4:length(pdB)); - -% Use linear mapping function in dB domain to map to symbol power - -power_map_x = [ 0 20 24 40 50 ]; -power_map_y = [-6 -6 0 6 6]; -mapped_pdB = interp1(power_map_x, power_map_y, pdB); - -%sim_in.symbol_amp = 10 .^ (mapped_pdB/20); -sim_in.symbol_amp = ones(1,length(pdB)); -sim_in.plot_scatter = 1; -sim_in.verbose = 2; -sim_in.hf_sim = 1; -sim_in.Esvec = 10; -sim_in.Ntrials = 400; - -dqpsk_pwr_hf = ber_test(sim_in); - -% note: need way to test that power is aligned with speech - -figure(4) -clf; -plot((1:sim_in.Ntrials)*80*4, pdB(1:sim_in.Ntrials)); -hold on; -plot((1:sim_in.Ntrials)*80*4, mapped_pdB(1:sim_in.Ntrials),'r'); -hold off; - -figure(5) -clf; - -s = load_raw("../raw/ve9qrp.raw"); -M=320; M_on_2 = M/2; % processing delay between input speech and centre of analysis window -plot(M_on_2:(M_on_2-1+sim_in.Ntrials*M),s(1:sim_in.Ntrials*M)) -hold on; -plot((1:sim_in.Ntrials)*M, 5000*sim_in.symbol_amp(1:sim_in.Ntrials),'r'); -hold off; -axis([1 sim_in.Ntrials*M -3E4 3E4]); - -figure(6) -clf; -plot((1:sim_in.Ntrials)*M, 20*log10(sim_in.symbol_amp(1:sim_in.Ntrials)),'b;Es (dB);'); -hold on; -plot((1:sim_in.Ntrials)*M, 10*log10(dqpsk_pwr_hf.hf_model_pwr),'g;Fading (dB);'); -plot((1:sim_in.Ntrials)*M, 10*log10(dqpsk_pwr_hf.snr_log),'r;Es/No (dB);'); - -ber = dqpsk_pwr_hf.Nerrs/sim_in.framesize; -ber_clip = ber; -ber_clip(find(ber > 0.2)) = 0.2; -plot((1:sim_in.Ntrials)*M, -20+100*ber_clip,'k;BER (0-20%);'); -hold off; -axis([1 sim_in.Ntrials*M -20 20]) - -fep=fopen("dqpsk_errors_pwr.bin","wb"); fwrite(fep, dqpsk_pwr_hf.errors_log, "short"); fclose(fep); -fber=fopen("ber.bin","wb"); fwrite(fber, ber, "float"); fclose(fber); +% test_dqpsk.m +% David Rowe March 2014 +% +% Single sample/symbol DQPSK modem simulation to test modulating modem +% tx power based on speech energy. + +1; + +% main test function + +function sim_out = ber_test(sim_in) + Fs = 8000; + + verbose = sim_in.verbose; + framesize = sim_in.framesize; + Ntrials = sim_in.Ntrials; + Esvec = sim_in.Esvec; + phase_offset = sim_in.phase_offset; + w_offset = sim_in.w_offset; + plot_scatter = sim_in.plot_scatter; + Rs = sim_in.Rs; + hf_sim = sim_in.hf_sim; + nhfdelay = sim_in.hf_delay_ms*Rs/1000; + hf_phase_only = sim_in.hf_phase_only; + hf_mag_only = sim_in.hf_mag_only; + Nc = sim_in.Nc; + symbol_amp = sim_in.symbol_amp; + + bps = 2; + Nsymb = framesize/bps; + for k=1:Nc + prev_sym_tx(k) = qpsk_mod([0 0]); + prev_sym_rx(k) = qpsk_mod([0 0]); + end + + rate = 1; + + % Init HF channel model from stored sample files of spreading signal ---------------------------------- + + % convert "spreading" samples from 1kHz carrier at Fs to complex + % baseband, generated by passing a 1kHz sine wave through PathSim + % with the ccir-poor model, enabling one path at a time. + + Fc = 1000; M = Fs/Rs; + fspread = fopen("../raw/sine1k_2Hz_spread.raw","rb"); + spread1k = fread(fspread, "int16")/10000; + fclose(fspread); + fspread = fopen("../raw/sine1k_2ms_delay_2Hz_spread.raw","rb"); + spread1k_2ms = fread(fspread, "int16")/10000; + fclose(fspread); + + % down convert to complex baseband + spreadbb = spread1k.*exp(-j*(2*pi*Fc/Fs)*(1:length(spread1k))'); + spreadbb_2ms = spread1k_2ms.*exp(-j*(2*pi*Fc/Fs)*(1:length(spread1k_2ms))'); + + % remove -2000 Hz image + b = fir1(50, 5/Fs); + spread = filter(b,1,spreadbb); + spread_2ms = filter(b,1,spreadbb_2ms); + + % discard first 1000 samples as these were near 0, probably as + % PathSim states were ramping up + + spread = spread(1000:length(spread)); + spread_2ms = spread_2ms(1000:length(spread_2ms)); + + % decimate down to Rs + + spread = spread(1:M:length(spread)); + spread_2ms = spread_2ms(1:M:length(spread_2ms)); + + % Determine "gain" of HF channel model, so we can normalise + % carrier power during HF channel sim to calibrate SNR. I imagine + % different implementations of ccir-poor would do this in + % different ways, leading to different BER results. Oh Well! + + hf_gain = 1.0/sqrt(var(spread)+var(spread_2ms)); + + % Start Simulation ---------------------------------------------------------------- + + for ne = 1:length(Esvec) + EsNodB = Esvec(ne); + EsNo = 10^(EsNodB/10); + + variance = 1/EsNo; + if verbose > 1 + printf("EsNo (dB): %f EsNo: %f variance: %f\n", EsNodB, EsNo, variance); + end + + Terrs = 0; Tbits = 0; + + tx_symb_log = []; + rx_symb_log = []; + noise_log = []; + sim_out.errors_log = []; + + % init HF channel + + hf_n = 1; + hf_angle_log = []; + hf_fading = ones(1,Nsymb); % default input for ldpc dec + hf_model = ones(Ntrials*Nsymb/Nc, Nc); % defaults for plotting surface + + sim_out.errors_log = []; + sim_out.Nerrs = []; + sim_out.snr_log = []; + sim_out.hf_model_pwr = []; + + symbol_amp_index = 1; + + for nn = 1: Ntrials + + tx_bits = round( rand( 1, framesize*rate ) ); + + % modulate -------------------------------------------- + + s = zeros(1, Nsymb); + for i=1:Nc:Nsymb + for k=1:Nc + tx_symb = qpsk_mod(tx_bits(2*(i-1+k-1)+1:2*(i+k-1))); + tx_symb *= prev_sym_tx(k); + prev_sym_tx(k) = tx_symb; + s(i+k-1) = symbol_amp(symbol_amp_index)*tx_symb; + end + end + s_ch = s; + symbol_amp_index++; + + % HF channel simulation ------------------------------------ + + if hf_sim + + % separation between carriers. Note this is + % effectively under samples at Rs, I dont think this + % matters. Equivalent to doing freq shift at Fs, then + % decimating to Rs. + + wsep = 2*pi*(1+0.5); % e.g. 75Hz spacing at Rs=50Hz, alpha=0.5 filters + + if Nsymb/Nc != floor(Nsymb/Nc) + printf("Error: Nsymb/Nc must be an integrer\n") + return; + end + + % arrange symbols in Nsymb/Nc by Nc matrix + + for i=1:Nc:Nsymb + + % Determine HF channel at each carrier for this symbol + + for k=1:Nc + hf_model(hf_n, k) = hf_gain*(spread(hf_n) + exp(-j*k*wsep*nhfdelay)*spread_2ms(hf_n)); + hf_fading(i+k-1) = abs(hf_model(hf_n, k)); + if hf_mag_only + s_ch(i+k-1) *= abs(hf_model(hf_n, k)); + else + s_ch(i+k-1) *= hf_model(hf_n, k); + end + end + hf_n++; + end + end + + tx_symb_log = [tx_symb_log s_ch]; + + % "genie" SNR estimate + + snr = (s_ch*s_ch')/(Nsymb*variance); + sim_out.snr_log = [sim_out.snr_log snr]; + sim_out.hf_model_pwr = [sim_out.hf_model_pwr mean(hf_fading.^2)]; + + % AWGN noise and phase/freq offset channel simulation + % 0.5 factor ensures var(noise) == variance , i.e. splits power between Re & Im + + noise = sqrt(variance*0.5)*(randn(1,Nsymb) + j*randn(1,Nsymb)); + noise_log = [noise_log noise]; + + % organise into carriers to apply frequency and phase offset + + for i=1:Nc:Nsymb + for k=1:Nc + s_ch(i+k-1) = s_ch(i+k-1)*exp(j*phase_offset) + noise(i+k-1); + end + phase_offset += w_offset; + end + + % de-modulate + + rx_bits = zeros(1, framesize); + for i=1:Nc:Nsymb + for k=1:Nc + rx_symb = s_ch(i+k-1); + tmp = rx_symb; + rx_symb *= conj(prev_sym_rx(k)/abs(prev_sym_rx(k))); + prev_sym_rx(k) = tmp; + rx_bits((2*(i-1+k-1)+1):(2*(i+k-1))) = qpsk_demod(rx_symb); + rx_symb_log = [rx_symb_log rx_symb]; + end + end + + error_positions = xor(rx_bits, tx_bits); + Nerrs = sum(error_positions); + sim_out.Nerrs = [sim_out.Nerrs Nerrs]; + Terrs += Nerrs; + Tbits += length(tx_bits); + + sim_out.errors_log = [sim_out.errors_log error_positions]; + end + + TERvec(ne) = Terrs; + BERvec(ne) = Terrs/Tbits; + + if verbose + printf("EsNo (dB): %f Terrs: %d BER %f ", EsNodB, Terrs, Terrs/Tbits); + printf("\n"); + end + if verbose > 1 + printf("Terrs: %d BER %f C %f N %f Es %f No %f Es/No %f\n\n", Terrs, + Terrs/Tbits, var(tx_symb_log), var(noise_log), + var(tx_symb_log), var(noise_log), var(tx_symb_log)/var(noise_log)); + end + end + + Ebvec = Esvec - 10*log10(bps); + + sim_out.BERvec = BERvec; + sim_out.Ebvec = Ebvec; + sim_out.TERvec = TERvec; + + if plot_scatter + figure(2); + clf; + scat = rx_symb_log .* exp(j*pi/4); + plot(real(scat), imag(scat),'+'); + title('Scatter plot'); + + figure(3); + clf; + y = 1:Rs*2; + x = 1:Nc; + EsNodBSurface = 20*log10(abs(hf_model(y,:))) - 10*log10(variance); + mesh(x,y,EsNodBSurface); + grid + title('HF Channel Es/No'); + + if 0 + figure(4); + clf; + subplot(211) + plot(y,abs(hf_model(y,1))) + title('HF Channel Carrier 1 Mag'); + subplot(212) + plot(y,angle(hf_model(y,1))) + title('HF Channel Carrier 1 Phase'); + end + end + +endfunction + +% Gray coded QPSK modulation function + +function symbol = qpsk_mod(two_bits) + two_bits_decimal = sum(two_bits .* [2 1]); + switch(two_bits_decimal) + case (0) symbol = 1; + case (1) symbol = j; + case (2) symbol = -j; + case (3) symbol = -1; + endswitch +endfunction + +% Gray coded QPSK demodulation function + +function two_bits = qpsk_demod(symbol) + if isscalar(symbol) == 0 + printf("only works with scalars\n"); + return; + end + bit0 = real(symbol*exp(j*pi/4)) < 0; + bit1 = imag(symbol*exp(j*pi/4)) < 0; + two_bits = [bit1 bit0]; +endfunction + +function sim_in = standard_init + sim_in.verbose = 1; + sim_in.plot_scatter = 0; + + sim_in.Esvec = 5:15; + sim_in.Ntrials = 100; + sim_in.framesize = 64; + sim_in.Rs = 100; + sim_in.Nc = 8; + + sim_in.phase_offset = 0; + sim_in.w_offset = 0; + sim_in.phase_noise_amp = 0; + + sim_in.hf_delay_ms = 2; + sim_in.hf_sim = 0; + sim_in.hf_phase_only = 0; + sim_in.hf_mag_only = 0; +endfunction + +function awgn_hf_ber_curves() + sim_in = standard_init(); + + Ebvec = sim_in.Esvec - 10*log10(2); + BER_theory = 0.5*erfc(sqrt(10.^(Ebvec/10))); + + dpsk_awgn = ber_test(sim_in); + sim_in.hf_sim = 1; + dpsk_hf = ber_test(sim_in); + + figure(1); + clf; + semilogy(Ebvec, BER_theory,'r;QPSK theory;') + hold on; + semilogy(dpsk_awgn.Ebvec, dpsk_awgn.BERvec,'g;DQPSK;') + semilogy(dpsk_hf.Ebvec, dpsk_hf.BERvec,'g;DQPSK HF;') + hold off; + xlabel('Eb/N0') + ylabel('BER') + grid("minor") + axis([min(Ebvec) max(Ebvec) 1E-3 1]) +end + +sim_in = standard_init(); + +% energy file sampled every 10ms + +load ../src/ve9qrp.txt +pdB=10*log10(ve9qrp); +for i=1:length(pdB) + if pdB(i) < 0 + pdB(i) = 0; + end +end + +% Down sample to 40ms rate used for 1300 bit/s codec, every 4th sample is transmitted + +pdB = pdB(4:4:length(pdB)); + +% Use linear mapping function in dB domain to map to symbol power + +power_map_x = [ 0 20 24 40 50 ]; +power_map_y = [-6 -6 0 6 6]; +mapped_pdB = interp1(power_map_x, power_map_y, pdB); + +%sim_in.symbol_amp = 10 .^ (mapped_pdB/20); +sim_in.symbol_amp = ones(1,length(pdB)); +sim_in.plot_scatter = 1; +sim_in.verbose = 2; +sim_in.hf_sim = 1; +sim_in.Esvec = 10; +sim_in.Ntrials = 400; + +dqpsk_pwr_hf = ber_test(sim_in); + +% note: need way to test that power is aligned with speech + +figure(4) +clf; +plot((1:sim_in.Ntrials)*80*4, pdB(1:sim_in.Ntrials)); +hold on; +plot((1:sim_in.Ntrials)*80*4, mapped_pdB(1:sim_in.Ntrials),'r'); +hold off; + +figure(5) +clf; + +s = load_raw("../raw/ve9qrp.raw"); +M=320; M_on_2 = M/2; % processing delay between input speech and centre of analysis window +plot(M_on_2:(M_on_2-1+sim_in.Ntrials*M),s(1:sim_in.Ntrials*M)) +hold on; +plot((1:sim_in.Ntrials)*M, 5000*sim_in.symbol_amp(1:sim_in.Ntrials),'r'); +hold off; +axis([1 sim_in.Ntrials*M -3E4 3E4]); + +figure(6) +clf; +plot((1:sim_in.Ntrials)*M, 20*log10(sim_in.symbol_amp(1:sim_in.Ntrials)),'b;Es (dB);'); +hold on; +plot((1:sim_in.Ntrials)*M, 10*log10(dqpsk_pwr_hf.hf_model_pwr),'g;Fading (dB);'); +plot((1:sim_in.Ntrials)*M, 10*log10(dqpsk_pwr_hf.snr_log),'r;Es/No (dB);'); + +ber = dqpsk_pwr_hf.Nerrs/sim_in.framesize; +ber_clip = ber; +ber_clip(find(ber > 0.2)) = 0.2; +plot((1:sim_in.Ntrials)*M, -20+100*ber_clip,'k;BER (0-20%);'); +hold off; +axis([1 sim_in.Ntrials*M -20 20]) + +fep=fopen("dqpsk_errors_pwr.bin","wb"); fwrite(fep, dqpsk_pwr_hf.errors_log, "short"); fclose(fep); +fber=fopen("ber.bin","wb"); fwrite(fber, ber, "float"); fclose(fber); diff --git a/codec2-dev/octave/test_dqpsk2.m b/codec2-dev/octave/test_dqpsk2.m index 08787510..d421d624 100644 --- a/codec2-dev/octave/test_dqpsk2.m +++ b/codec2-dev/octave/test_dqpsk2.m @@ -1,465 +1,465 @@ -% test_dqpsk2.m -% David Rowe April 2014 -% -% DQPSK modem simulation inclduing filtering to test modulating modem -% tx power based on speech energy. Unlike test_dpsk runs at sample -% rate Fs. - -1; - -% main test function - -function sim_out = ber_test(sim_in) - Fs = 8000; - - verbose = sim_in.verbose; - framesize = sim_in.framesize; - Ntrials = sim_in.Ntrials; - Esvec = sim_in.Esvec; - phase_offset = sim_in.phase_offset; - w_offset = sim_in.w_offset; - plot_scatter = sim_in.plot_scatter; - Rs = sim_in.Rs; - hf_sim = sim_in.hf_sim; - Nhfdelay = floor(sim_in.hf_delay_ms*Fs/1000); - Nc = sim_in.Nc; - symbol_amp = sim_in.symbol_amp; - - bps = 2; - Nsymb = framesize/bps; - for k=1:Nc - prev_sym_tx(k) = qpsk_mod([0 0]); - prev_sym_rx(k) = qpsk_mod([0 0]); - end - - % design root nyquist (root raised cosine) filter and init tx and rx filter states - - alpha = 0.5; T=1/Fs; Nfiltsym=7; M=Fs/Rs; - if floor(Fs/Rs) != Fs/Rs - printf("oversampling ratio must be an integer\n"); - return; - end - hrn = gen_rn_coeffs(alpha, T, Rs, Nfiltsym, M); - Nfilter = length(hrn); - - % convert "spreading" samples from 1kHz carrier at Fs to complex - % baseband, generated by passing a 1kHz sine wave through PathSim - % with the ccir-poor model, enabling one path at a time. - - Fc = 1000; - fspread = fopen("../raw/sine1k_2Hz_spread.raw","rb"); - spread1k = fread(fspread, "int16")/10000; - fclose(fspread); - fspread = fopen("../raw/sine1k_2ms_delay_2Hz_spread.raw","rb"); - spread1k_2ms = fread(fspread, "int16")/10000; - fclose(fspread); - - % down convert to complex baseband - spreadbb = spread1k.*exp(-j*(2*pi*Fc/Fs)*(1:length(spread1k))'); - spreadbb_2ms = spread1k_2ms.*exp(-j*(2*pi*Fc/Fs)*(1:length(spread1k_2ms))'); - - % remove -2000 Hz image - b = fir1(50, 5/Fs); - spread = filter(b,1,spreadbb); - spread_2ms = filter(b,1,spreadbb_2ms); - - % discard first 1000 samples as these were near 0, probably as - % PathSim states were ramping up. Transpose for convenience - - spread = transpose(spread(1000:length(spread))); - spread_2ms = transpose(spread_2ms(1000:length(spread_2ms))); - - % Determine "gain" of HF channel model, so we can normalise - % carrier power during HF channel sim to calibrate SNR. I imagine - % different implementations of ccir-poor would do this in - % different ways, leading to different BER results. Oh Well! - - hf_gain = 1.0/sqrt(var(spread)+var(spread_2ms)); - - % Start Simulation ---------------------------------------------------------------- - - for ne = 1:length(Esvec) - EsNodB = Esvec(ne); - EsNo = 10^(EsNodB/10); - - variance = Fs/(Rs*EsNo); - if verbose > 1 - printf("EsNo (dB): %f EsNo: %f variance: %f\n", EsNodB, EsNo, variance); - end - - Terrs = 0; Tbits = 0; - - tx_symb_log = []; - rx_symb_log = []; - noise_log = []; - sim_out.errors_log = []; - sim_out.tx_baseband_log = []; - sim_out.rx_filt_log = []; - symbol_amp_index = 1; - - % init filter memories and LOs - - tx_filter_memory = zeros(Nc, Nfilter); - rx_filter_memory = zeros(Nc, Nfilter); - s_delay_line_filt = zeros(Nc, Nfiltsym); - phase_tx = ones(1,Nc); - phase_rx = ones(1,Nc); - Fcentre = 1500; Fsep = (1+alpha)*Rs; - freq = Fcentre + Fsep*((-Nc/2+0.5):(Nc/2-0.5)); - freq = exp(j*freq*2*pi/Fs); - - % init HF channel - - sc = 1; hf_n = 1; - hf_sim_delay_line = zeros(1,M+Nhfdelay); - freq_sample_hz = Fcentre + ((Fsep*(-Nc/2)):50:(Fsep*(Nc/2))); - freq_sample_rads = (2*pi/Fs)*freq_sample_hz; - hf_model = ones(Ntrials*Nsymb/Nc, length(freq_sample_rads)); % defaults for plotting surface - - % bunch of outputs we log for graphing - - sim_out.errors_log = []; - sim_out.Nerrs = []; - sim_out.snr_log = []; - sim_out.hf_model_pwr = []; - sim_out.tx_fdm_log = []; - C_log = []; - - for nn = 1: Ntrials - - tx_bits = round( rand( 1, framesize ) ); - - % modulate -------------------------------------------- - - s = zeros(1, Nsymb); - for i=1:Nc:Nsymb - for k=1:Nc - tx_symb = qpsk_mod(tx_bits(2*(i-1+k-1)+1:2*(i+k-1))); - s_qpsk(i+k-1) = tx_symb; - tx_symb *= prev_sym_tx(k); - prev_sym_tx(k) = tx_symb; - s(i+k-1) = symbol_amp(symbol_amp_index)*tx_symb; - end - end - symbol_amp_index++; - s_ch = s; - - % Now we start processing frame Nc symbols at a time to model parallel carriers - - tx_fdm_sym_log = []; - for i=1:Nc:Nsymb - - % Delay tx symbols to match delay due to filters. qpsk - % (rather than dqpsk) symbols used for convenience as - % it's easy to shift symbols than pairs of bits - - s_delay_line_filt(:,1:Nfiltsym-1) = s_delay_line_filt(:,2:Nfiltsym); - s_delay_line_filt(:,Nfiltsym) = s_qpsk(i:i+Nc-1); - s_qpsk(i:i+Nc-1) = s_delay_line_filt(:,1); - for k=1:Nc - tx_bits(2*(i-1+k-1)+1:2*(i+k-1)) = qpsk_demod(s_qpsk(i+k-1)); - end - - % tx filter - - tx_baseband = zeros(Nc,M); - - % tx filter each symbol, generate M filtered output samples for each symbol. - % Efficient polyphase filter techniques used as tx_filter_memory is sparse - - tx_filter_memory(:,Nfilter) = s(i:i+Nc-1); - - for k=1:M - tx_baseband(:,k) = M*tx_filter_memory(:,M:M:Nfilter) * hrn(M-k+1:M:Nfilter)'; - end - tx_filter_memory(:,1:Nfilter-M) = tx_filter_memory(:,M+1:Nfilter); - tx_filter_memory(:,Nfilter-M+1:Nfilter) = zeros(Nc,M); - - sim_out.tx_baseband_log = [sim_out.tx_baseband_log tx_baseband]; - - % upconvert - - tx_fdm = zeros(1,M); - - for c=1:Nc - for k=1:M - phase_tx(c) = phase_tx(c) * freq(c); - tx_fdm(k) = tx_fdm(k) + tx_baseband(c,k)*phase_tx(c); - end - end - - sim_out.tx_fdm_log = [sim_out.tx_fdm_log tx_fdm]; - - % HF channel - - if hf_sim - hf_sim_delay_line(1:Nhfdelay) = hf_sim_delay_line(M+1:M+Nhfdelay); - hf_sim_delay_line(Nhfdelay+1:M+Nhfdelay) = tx_fdm; - - tx_fdm = tx_fdm.*spread(sc:sc+M-1) + hf_sim_delay_line(1:M).*spread_2ms(sc:sc+M-1); - tx_fdm *= hf_gain; - - % sample HF channel spectrum in middle of this symbol for plotting - - hf_model(hf_n,:) = hf_gain*(spread(sc+M/2) + exp(-j*freq_sample_rads*Nhfdelay)*spread_2ms(sc+M/2)); - - sc += M; - hf_n++; - end - - tx_fdm_sym_log = [tx_fdm_sym_log tx_fdm ]; - - % AWGN noise and phase/freq offset channel simulation - % 0.5 factor ensures var(noise) == variance , i.e. splits power between Re & Im - - noise = sqrt(variance*0.5)*(randn(1,M) + j*randn(1,M)); - noise_log = [noise_log noise]; - - % apply frequency and phase offset and noise - - for k=1:M - rx_fdm(k) = tx_fdm(k)*exp(j*phase_offset) + noise(k); - phase_offset += w_offset; - end - - % downconvert - - rx_baseband = zeros(Nc,M); - for c=1:Nc - for k=1:M - phase_rx(c) = phase_rx(c) * freq(c); - rx_baseband(c,k) = rx_fdm(k)*phase_rx(c)'; - end - end - - % rx filter - - rx_filter_memory(:,Nfilter-M+1:Nfilter) = rx_baseband; - rx_filt = rx_filter_memory * hrn'; - rx_filter_memory(:,1:Nfilter-M) = rx_filter_memory(:,1+M:Nfilter); - sim_out.rx_filt_log = [sim_out.rx_filt_log rx_filt]; - - s_ch(i:i+Nc-1) = rx_filt; - end - - % est HF model power for entire code frame (which could be several symbols) - - if hf_sim - frame_hf_model = reshape(hf_model(hf_n-Nsymb/Nc:hf_n-1,:),1,(Nsymb/Nc)*length(freq_sample_hz)); - sim_out.hf_model_pwr = [sim_out.hf_model_pwr mean(abs(frame_hf_model).^2)]; - else - sim_out.hf_model_pwr = [sim_out.hf_model_pwr 1]; - end - - % "genie" SNR estimate - - snr = (tx_fdm_sym_log*tx_fdm_sym_log')/(M*variance); - sim_out.snr_log = [sim_out.snr_log snr]; - - % de-modulate - - rx_bits = zeros(1, framesize); - for i=1:Nc:Nsymb - for k=1:Nc - rx_symb = s_ch(i+k-1); - tmp = rx_symb; - rx_symb *= conj(prev_sym_rx(k)/abs(prev_sym_rx(k))); - prev_sym_rx(k) = tmp; - rx_bits((2*(i-1+k-1)+1):(2*(i+k-1))) = qpsk_demod(rx_symb); - rx_symb_log = [rx_symb_log rx_symb]; - end - end - - % ignore data until we have enough frames to fill filter memory - % then count errors - - if nn > ceil(Nfiltsym/(Nsymb/Nc)) - error_positions = xor(rx_bits, tx_bits); - sim_out.errors_log = [sim_out.errors_log error_positions]; - Nerrs = sum(error_positions); - sim_out.Nerrs = [sim_out.Nerrs Nerrs]; - Terrs += Nerrs; - Tbits += length(tx_bits); - end - - end - - TERvec(ne) = Terrs; - BERvec(ne) = Terrs/Tbits; - - if verbose - printf("EsNo (dB): %f Terrs: %d BER %f ", EsNodB, Terrs, Terrs/Tbits); - printf("\n"); - end - if verbose > 1 - printf("Terrs: %d BER %f C %f N %f Es %f No %f Es/No %f\n\n", Terrs, - Terrs/Tbits, var(sim_out.tx_fdm_log), var(noise_log), - var(sim_out.tx_fdm_log)/(Nc*Rs), var(noise_log)/Fs, (var(sim_out.tx_fdm_log)/(Nc*Rs))/(var(noise_log)/Fs)); - end - end - - Ebvec = Esvec - 10*log10(bps); - - sim_out.BERvec = BERvec; - sim_out.Ebvec = Ebvec; - sim_out.TERvec = TERvec; - - if plot_scatter - figure(2); - clf; - scat = rx_symb_log(Nfiltsym*Nc:length(rx_symb_log)) .* exp(j*pi/4); - plot(real(scat), imag(scat),'+'); - title('Scatter plot'); - - figure(3); - clf; - y = 1:Rs*2; - EsNodBSurface = 20*log10(abs(hf_model(y,:))) + EsNodB; - mesh(1:length(freq_sample_hz),y,EsNodBSurface); - grid - title('HF Channel Es/No'); - end - -endfunction - -% Gray coded QPSK modulation function - -function symbol = qpsk_mod(two_bits) - two_bits_decimal = sum(two_bits .* [2 1]); - switch(two_bits_decimal) - case (0) symbol = 1; - case (1) symbol = j; - case (2) symbol = -j; - case (3) symbol = -1; - endswitch -endfunction - -% Gray coded QPSK demodulation function - -function two_bits = qpsk_demod(symbol) - if isscalar(symbol) == 0 - printf("only works with scalars\n"); - return; - end - bit0 = real(symbol*exp(j*pi/4)) < 0; - bit1 = imag(symbol*exp(j*pi/4)) < 0; - two_bits = [bit1 bit0]; -endfunction - -function sim_in = standard_init - sim_in.verbose = 1; - sim_in.plot_scatter = 0; - - sim_in.Esvec = 5:15; - sim_in.Ntrials = 100; - sim_in.framesize = 64; - sim_in.Rs = 100; - sim_in.Nc = 8; - - sim_in.phase_offset = 0; - sim_in.w_offset = 0; - sim_in.phase_noise_amp = 0; - - sim_in.hf_delay_ms = 2; - sim_in.hf_sim = 0; - sim_in.hf_phase_only = 0; - sim_in.hf_mag_only = 0; -endfunction - -function awgn_hf_ber_curves() - sim_in = standard_init(); - - Ebvec = sim_in.Esvec - 10*log10(2); - BER_theory = 0.5*erfc(sqrt(10.^(Ebvec/10))); - - dpsk_awgn = ber_test(sim_in); - sim_in.hf_sim = 1; - dpsk_hf = ber_test(sim_in); - - figure(1); - clf; - semilogy(Ebvec, BER_theory,'r;QPSK theory;') - hold on; - semilogy(dpsk_awgn.Ebvec, dpsk_awgn.BERvec,'g;DQPSK;') - semilogy(dpsk_hf.Ebvec, dpsk_hf.BERvec,'g;DQPSK HF;') - hold off; - xlabel('Eb/N0') - ylabel('BER') - grid("minor") - axis([min(Ebvec) max(Ebvec) 1E-3 1]) -end - -sim_in = standard_init(); - -% energy file sampled every 10ms - -load ../src/ve9qrp.txt -pdB=10*log10(ve9qrp); -for i=1:length(pdB) - if pdB(i) < 0 - pdB(i) = 0; - end -end - -% Down sample to 40ms rate used for 1300 bit/s codec, every 4th sample is transmitted - -pdB = pdB(4:4:length(pdB)); - -% Use linear mapping function in dB domain to map to symbol power - -%power_map_x = [ 0 20 24 40 50 ]; -%power_map_y = [--6 -6 0 6 6]; -power_map_x = [ 0 50 ]; -power_map_y = [ -15 12]; -mapped_pdB = interp1(power_map_x, power_map_y, pdB); - -sim_in.symbol_amp = 10 .^ (mapped_pdB/20); -%sim_in.symbol_amp = ones(1,length(pdB)); -sim_in.plot_scatter = 1; -sim_in.verbose = 2; -sim_in.hf_delay_ms = 2; -sim_in.hf_sim = 1; -sim_in.Esvec = 10; -sim_in.Ntrials = 400; - -dqpsk_pwr_hf = ber_test(sim_in); - -% note: need way to test that power is aligned with speech - -figure(4) -clf; -plot((1:sim_in.Ntrials)*80*4, pdB(1:sim_in.Ntrials)); -hold on; -plot((1:sim_in.Ntrials)*80*4, mapped_pdB(1:sim_in.Ntrials),'r'); -hold off; - -figure(5) -clf; -s = load_raw("../raw/ve9qrp.raw"); -M=320; M_on_2 = M/2; % processing delay between input speech and centre of analysis window -subplot(211) -plot(M_on_2:(M_on_2-1+sim_in.Ntrials*M),s(1:sim_in.Ntrials*M)) -hold on; -plot((1:sim_in.Ntrials)*M, 5000*sim_in.symbol_amp(1:sim_in.Ntrials),'r'); -hold off; -axis([1 sim_in.Ntrials*M -3E4 3E4]); -subplot(212) -plot(real(dqpsk_pwr_hf.tx_fdm_log)); - - -figure(6) -clf; -plot((1:sim_in.Ntrials)*M, 20*log10(sim_in.symbol_amp(1:sim_in.Ntrials)),'b;Es (dB);'); -hold on; -plot((1:sim_in.Ntrials)*M, 10*log10(dqpsk_pwr_hf.hf_model_pwr),'g;Fading (dB);'); -plot((1:sim_in.Ntrials)*M, 10*log10(dqpsk_pwr_hf.snr_log),'r;Es/No (dB);'); - -ber = dqpsk_pwr_hf.Nerrs/sim_in.framesize; -ber_clip = ber; -ber_clip(find(ber > 0.2)) = 0.2; -plot((1:length(ber_clip))*M, -20+100*ber_clip,'k;BER (0-20%);'); -hold off; -axis([1 sim_in.Ntrials*M -20 20]) - -fep=fopen("dqpsk_errors_pwr.bin","wb"); fwrite(fep, dqpsk_pwr_hf.errors_log, "short"); fclose(fep); -fber=fopen("ber.bin","wb"); fwrite(fber, ber, "float"); fclose(fber); +% test_dqpsk2.m +% David Rowe April 2014 +% +% DQPSK modem simulation inclduing filtering to test modulating modem +% tx power based on speech energy. Unlike test_dpsk runs at sample +% rate Fs. + +1; + +% main test function + +function sim_out = ber_test(sim_in) + Fs = 8000; + + verbose = sim_in.verbose; + framesize = sim_in.framesize; + Ntrials = sim_in.Ntrials; + Esvec = sim_in.Esvec; + phase_offset = sim_in.phase_offset; + w_offset = sim_in.w_offset; + plot_scatter = sim_in.plot_scatter; + Rs = sim_in.Rs; + hf_sim = sim_in.hf_sim; + Nhfdelay = floor(sim_in.hf_delay_ms*Fs/1000); + Nc = sim_in.Nc; + symbol_amp = sim_in.symbol_amp; + + bps = 2; + Nsymb = framesize/bps; + for k=1:Nc + prev_sym_tx(k) = qpsk_mod([0 0]); + prev_sym_rx(k) = qpsk_mod([0 0]); + end + + % design root nyquist (root raised cosine) filter and init tx and rx filter states + + alpha = 0.5; T=1/Fs; Nfiltsym=7; M=Fs/Rs; + if floor(Fs/Rs) != Fs/Rs + printf("oversampling ratio must be an integer\n"); + return; + end + hrn = gen_rn_coeffs(alpha, T, Rs, Nfiltsym, M); + Nfilter = length(hrn); + + % convert "spreading" samples from 1kHz carrier at Fs to complex + % baseband, generated by passing a 1kHz sine wave through PathSim + % with the ccir-poor model, enabling one path at a time. + + Fc = 1000; + fspread = fopen("../raw/sine1k_2Hz_spread.raw","rb"); + spread1k = fread(fspread, "int16")/10000; + fclose(fspread); + fspread = fopen("../raw/sine1k_2ms_delay_2Hz_spread.raw","rb"); + spread1k_2ms = fread(fspread, "int16")/10000; + fclose(fspread); + + % down convert to complex baseband + spreadbb = spread1k.*exp(-j*(2*pi*Fc/Fs)*(1:length(spread1k))'); + spreadbb_2ms = spread1k_2ms.*exp(-j*(2*pi*Fc/Fs)*(1:length(spread1k_2ms))'); + + % remove -2000 Hz image + b = fir1(50, 5/Fs); + spread = filter(b,1,spreadbb); + spread_2ms = filter(b,1,spreadbb_2ms); + + % discard first 1000 samples as these were near 0, probably as + % PathSim states were ramping up. Transpose for convenience + + spread = transpose(spread(1000:length(spread))); + spread_2ms = transpose(spread_2ms(1000:length(spread_2ms))); + + % Determine "gain" of HF channel model, so we can normalise + % carrier power during HF channel sim to calibrate SNR. I imagine + % different implementations of ccir-poor would do this in + % different ways, leading to different BER results. Oh Well! + + hf_gain = 1.0/sqrt(var(spread)+var(spread_2ms)); + + % Start Simulation ---------------------------------------------------------------- + + for ne = 1:length(Esvec) + EsNodB = Esvec(ne); + EsNo = 10^(EsNodB/10); + + variance = Fs/(Rs*EsNo); + if verbose > 1 + printf("EsNo (dB): %f EsNo: %f variance: %f\n", EsNodB, EsNo, variance); + end + + Terrs = 0; Tbits = 0; + + tx_symb_log = []; + rx_symb_log = []; + noise_log = []; + sim_out.errors_log = []; + sim_out.tx_baseband_log = []; + sim_out.rx_filt_log = []; + symbol_amp_index = 1; + + % init filter memories and LOs + + tx_filter_memory = zeros(Nc, Nfilter); + rx_filter_memory = zeros(Nc, Nfilter); + s_delay_line_filt = zeros(Nc, Nfiltsym); + phase_tx = ones(1,Nc); + phase_rx = ones(1,Nc); + Fcentre = 1500; Fsep = (1+alpha)*Rs; + freq = Fcentre + Fsep*((-Nc/2+0.5):(Nc/2-0.5)); + freq = exp(j*freq*2*pi/Fs); + + % init HF channel + + sc = 1; hf_n = 1; + hf_sim_delay_line = zeros(1,M+Nhfdelay); + freq_sample_hz = Fcentre + ((Fsep*(-Nc/2)):50:(Fsep*(Nc/2))); + freq_sample_rads = (2*pi/Fs)*freq_sample_hz; + hf_model = ones(Ntrials*Nsymb/Nc, length(freq_sample_rads)); % defaults for plotting surface + + % bunch of outputs we log for graphing + + sim_out.errors_log = []; + sim_out.Nerrs = []; + sim_out.snr_log = []; + sim_out.hf_model_pwr = []; + sim_out.tx_fdm_log = []; + C_log = []; + + for nn = 1: Ntrials + + tx_bits = round( rand( 1, framesize ) ); + + % modulate -------------------------------------------- + + s = zeros(1, Nsymb); + for i=1:Nc:Nsymb + for k=1:Nc + tx_symb = qpsk_mod(tx_bits(2*(i-1+k-1)+1:2*(i+k-1))); + s_qpsk(i+k-1) = tx_symb; + tx_symb *= prev_sym_tx(k); + prev_sym_tx(k) = tx_symb; + s(i+k-1) = symbol_amp(symbol_amp_index)*tx_symb; + end + end + symbol_amp_index++; + s_ch = s; + + % Now we start processing frame Nc symbols at a time to model parallel carriers + + tx_fdm_sym_log = []; + for i=1:Nc:Nsymb + + % Delay tx symbols to match delay due to filters. qpsk + % (rather than dqpsk) symbols used for convenience as + % it's easy to shift symbols than pairs of bits + + s_delay_line_filt(:,1:Nfiltsym-1) = s_delay_line_filt(:,2:Nfiltsym); + s_delay_line_filt(:,Nfiltsym) = s_qpsk(i:i+Nc-1); + s_qpsk(i:i+Nc-1) = s_delay_line_filt(:,1); + for k=1:Nc + tx_bits(2*(i-1+k-1)+1:2*(i+k-1)) = qpsk_demod(s_qpsk(i+k-1)); + end + + % tx filter + + tx_baseband = zeros(Nc,M); + + % tx filter each symbol, generate M filtered output samples for each symbol. + % Efficient polyphase filter techniques used as tx_filter_memory is sparse + + tx_filter_memory(:,Nfilter) = s(i:i+Nc-1); + + for k=1:M + tx_baseband(:,k) = M*tx_filter_memory(:,M:M:Nfilter) * hrn(M-k+1:M:Nfilter)'; + end + tx_filter_memory(:,1:Nfilter-M) = tx_filter_memory(:,M+1:Nfilter); + tx_filter_memory(:,Nfilter-M+1:Nfilter) = zeros(Nc,M); + + sim_out.tx_baseband_log = [sim_out.tx_baseband_log tx_baseband]; + + % upconvert + + tx_fdm = zeros(1,M); + + for c=1:Nc + for k=1:M + phase_tx(c) = phase_tx(c) * freq(c); + tx_fdm(k) = tx_fdm(k) + tx_baseband(c,k)*phase_tx(c); + end + end + + sim_out.tx_fdm_log = [sim_out.tx_fdm_log tx_fdm]; + + % HF channel + + if hf_sim + hf_sim_delay_line(1:Nhfdelay) = hf_sim_delay_line(M+1:M+Nhfdelay); + hf_sim_delay_line(Nhfdelay+1:M+Nhfdelay) = tx_fdm; + + tx_fdm = tx_fdm.*spread(sc:sc+M-1) + hf_sim_delay_line(1:M).*spread_2ms(sc:sc+M-1); + tx_fdm *= hf_gain; + + % sample HF channel spectrum in middle of this symbol for plotting + + hf_model(hf_n,:) = hf_gain*(spread(sc+M/2) + exp(-j*freq_sample_rads*Nhfdelay)*spread_2ms(sc+M/2)); + + sc += M; + hf_n++; + end + + tx_fdm_sym_log = [tx_fdm_sym_log tx_fdm ]; + + % AWGN noise and phase/freq offset channel simulation + % 0.5 factor ensures var(noise) == variance , i.e. splits power between Re & Im + + noise = sqrt(variance*0.5)*(randn(1,M) + j*randn(1,M)); + noise_log = [noise_log noise]; + + % apply frequency and phase offset and noise + + for k=1:M + rx_fdm(k) = tx_fdm(k)*exp(j*phase_offset) + noise(k); + phase_offset += w_offset; + end + + % downconvert + + rx_baseband = zeros(Nc,M); + for c=1:Nc + for k=1:M + phase_rx(c) = phase_rx(c) * freq(c); + rx_baseband(c,k) = rx_fdm(k)*phase_rx(c)'; + end + end + + % rx filter + + rx_filter_memory(:,Nfilter-M+1:Nfilter) = rx_baseband; + rx_filt = rx_filter_memory * hrn'; + rx_filter_memory(:,1:Nfilter-M) = rx_filter_memory(:,1+M:Nfilter); + sim_out.rx_filt_log = [sim_out.rx_filt_log rx_filt]; + + s_ch(i:i+Nc-1) = rx_filt; + end + + % est HF model power for entire code frame (which could be several symbols) + + if hf_sim + frame_hf_model = reshape(hf_model(hf_n-Nsymb/Nc:hf_n-1,:),1,(Nsymb/Nc)*length(freq_sample_hz)); + sim_out.hf_model_pwr = [sim_out.hf_model_pwr mean(abs(frame_hf_model).^2)]; + else + sim_out.hf_model_pwr = [sim_out.hf_model_pwr 1]; + end + + % "genie" SNR estimate + + snr = (tx_fdm_sym_log*tx_fdm_sym_log')/(M*variance); + sim_out.snr_log = [sim_out.snr_log snr]; + + % de-modulate + + rx_bits = zeros(1, framesize); + for i=1:Nc:Nsymb + for k=1:Nc + rx_symb = s_ch(i+k-1); + tmp = rx_symb; + rx_symb *= conj(prev_sym_rx(k)/abs(prev_sym_rx(k))); + prev_sym_rx(k) = tmp; + rx_bits((2*(i-1+k-1)+1):(2*(i+k-1))) = qpsk_demod(rx_symb); + rx_symb_log = [rx_symb_log rx_symb]; + end + end + + % ignore data until we have enough frames to fill filter memory + % then count errors + + if nn > ceil(Nfiltsym/(Nsymb/Nc)) + error_positions = xor(rx_bits, tx_bits); + sim_out.errors_log = [sim_out.errors_log error_positions]; + Nerrs = sum(error_positions); + sim_out.Nerrs = [sim_out.Nerrs Nerrs]; + Terrs += Nerrs; + Tbits += length(tx_bits); + end + + end + + TERvec(ne) = Terrs; + BERvec(ne) = Terrs/Tbits; + + if verbose + printf("EsNo (dB): %f Terrs: %d BER %f ", EsNodB, Terrs, Terrs/Tbits); + printf("\n"); + end + if verbose > 1 + printf("Terrs: %d BER %f C %f N %f Es %f No %f Es/No %f\n\n", Terrs, + Terrs/Tbits, var(sim_out.tx_fdm_log), var(noise_log), + var(sim_out.tx_fdm_log)/(Nc*Rs), var(noise_log)/Fs, (var(sim_out.tx_fdm_log)/(Nc*Rs))/(var(noise_log)/Fs)); + end + end + + Ebvec = Esvec - 10*log10(bps); + + sim_out.BERvec = BERvec; + sim_out.Ebvec = Ebvec; + sim_out.TERvec = TERvec; + + if plot_scatter + figure(2); + clf; + scat = rx_symb_log(Nfiltsym*Nc:length(rx_symb_log)) .* exp(j*pi/4); + plot(real(scat), imag(scat),'+'); + title('Scatter plot'); + + figure(3); + clf; + y = 1:Rs*2; + EsNodBSurface = 20*log10(abs(hf_model(y,:))) + EsNodB; + mesh(1:length(freq_sample_hz),y,EsNodBSurface); + grid + title('HF Channel Es/No'); + end + +endfunction + +% Gray coded QPSK modulation function + +function symbol = qpsk_mod(two_bits) + two_bits_decimal = sum(two_bits .* [2 1]); + switch(two_bits_decimal) + case (0) symbol = 1; + case (1) symbol = j; + case (2) symbol = -j; + case (3) symbol = -1; + endswitch +endfunction + +% Gray coded QPSK demodulation function + +function two_bits = qpsk_demod(symbol) + if isscalar(symbol) == 0 + printf("only works with scalars\n"); + return; + end + bit0 = real(symbol*exp(j*pi/4)) < 0; + bit1 = imag(symbol*exp(j*pi/4)) < 0; + two_bits = [bit1 bit0]; +endfunction + +function sim_in = standard_init + sim_in.verbose = 1; + sim_in.plot_scatter = 0; + + sim_in.Esvec = 5:15; + sim_in.Ntrials = 100; + sim_in.framesize = 64; + sim_in.Rs = 100; + sim_in.Nc = 8; + + sim_in.phase_offset = 0; + sim_in.w_offset = 0; + sim_in.phase_noise_amp = 0; + + sim_in.hf_delay_ms = 2; + sim_in.hf_sim = 0; + sim_in.hf_phase_only = 0; + sim_in.hf_mag_only = 0; +endfunction + +function awgn_hf_ber_curves() + sim_in = standard_init(); + + Ebvec = sim_in.Esvec - 10*log10(2); + BER_theory = 0.5*erfc(sqrt(10.^(Ebvec/10))); + + dpsk_awgn = ber_test(sim_in); + sim_in.hf_sim = 1; + dpsk_hf = ber_test(sim_in); + + figure(1); + clf; + semilogy(Ebvec, BER_theory,'r;QPSK theory;') + hold on; + semilogy(dpsk_awgn.Ebvec, dpsk_awgn.BERvec,'g;DQPSK;') + semilogy(dpsk_hf.Ebvec, dpsk_hf.BERvec,'g;DQPSK HF;') + hold off; + xlabel('Eb/N0') + ylabel('BER') + grid("minor") + axis([min(Ebvec) max(Ebvec) 1E-3 1]) +end + +sim_in = standard_init(); + +% energy file sampled every 10ms + +load ../src/ve9qrp.txt +pdB=10*log10(ve9qrp); +for i=1:length(pdB) + if pdB(i) < 0 + pdB(i) = 0; + end +end + +% Down sample to 40ms rate used for 1300 bit/s codec, every 4th sample is transmitted + +pdB = pdB(4:4:length(pdB)); + +% Use linear mapping function in dB domain to map to symbol power + +%power_map_x = [ 0 20 24 40 50 ]; +%power_map_y = [--6 -6 0 6 6]; +power_map_x = [ 0 50 ]; +power_map_y = [ -15 12]; +mapped_pdB = interp1(power_map_x, power_map_y, pdB); + +sim_in.symbol_amp = 10 .^ (mapped_pdB/20); +%sim_in.symbol_amp = ones(1,length(pdB)); +sim_in.plot_scatter = 1; +sim_in.verbose = 2; +sim_in.hf_delay_ms = 2; +sim_in.hf_sim = 1; +sim_in.Esvec = 10; +sim_in.Ntrials = 400; + +dqpsk_pwr_hf = ber_test(sim_in); + +% note: need way to test that power is aligned with speech + +figure(4) +clf; +plot((1:sim_in.Ntrials)*80*4, pdB(1:sim_in.Ntrials)); +hold on; +plot((1:sim_in.Ntrials)*80*4, mapped_pdB(1:sim_in.Ntrials),'r'); +hold off; + +figure(5) +clf; +s = load_raw("../raw/ve9qrp.raw"); +M=320; M_on_2 = M/2; % processing delay between input speech and centre of analysis window +subplot(211) +plot(M_on_2:(M_on_2-1+sim_in.Ntrials*M),s(1:sim_in.Ntrials*M)) +hold on; +plot((1:sim_in.Ntrials)*M, 5000*sim_in.symbol_amp(1:sim_in.Ntrials),'r'); +hold off; +axis([1 sim_in.Ntrials*M -3E4 3E4]); +subplot(212) +plot(real(dqpsk_pwr_hf.tx_fdm_log)); + + +figure(6) +clf; +plot((1:sim_in.Ntrials)*M, 20*log10(sim_in.symbol_amp(1:sim_in.Ntrials)),'b;Es (dB);'); +hold on; +plot((1:sim_in.Ntrials)*M, 10*log10(dqpsk_pwr_hf.hf_model_pwr),'g;Fading (dB);'); +plot((1:sim_in.Ntrials)*M, 10*log10(dqpsk_pwr_hf.snr_log),'r;Es/No (dB);'); + +ber = dqpsk_pwr_hf.Nerrs/sim_in.framesize; +ber_clip = ber; +ber_clip(find(ber > 0.2)) = 0.2; +plot((1:length(ber_clip))*M, -20+100*ber_clip,'k;BER (0-20%);'); +hold off; +axis([1 sim_in.Ntrials*M -20 20]) + +fep=fopen("dqpsk_errors_pwr.bin","wb"); fwrite(fep, dqpsk_pwr_hf.errors_log, "short"); fclose(fep); +fber=fopen("ber.bin","wb"); fwrite(fber, ber, "float"); fclose(fber); diff --git a/codec2-dev/octave/test_foff.m b/codec2-dev/octave/test_foff.m index 3d26e89e..34afed3d 100644 --- a/codec2-dev/octave/test_foff.m +++ b/codec2-dev/octave/test_foff.m @@ -1,402 +1,402 @@ -% test_foff.m -% David Rowe April 2015 -% -% Octave script for testing the cohpsk freq offset estimator - -graphics_toolkit ("gnuplot"); -more off; - -cohpsk; -fdmdv; -autotest; - -rand('state',1); -randn('state',1); - - -% Core function for testing frequency offset estimator. Performs one test - -function sim_out = freq_off_est_test(sim_in) - global Nfilter; - global M; - - Rs = 100; - Nc = 4; - Nd = 2; - framesize = 32; - Fs = 8000; - Fcentre = 1500; - - Nsw = 3; % numbers of sync window frames we process over. Set based - % on Nsym to flush filter memory by final frame in windw - - afdmdv.Nsym = 5; - afdmdv.Nt = 5; - - afdmdv.Fs = 8000; - afdmdv.Nc = Nd*Nc-1; - afdmdv.Rs = Rs; - M = afdmdv.M = afdmdv.Fs/afdmdv.Rs; - afdmdv.Nfilter = afdmdv.Nsym*M; - afdmdv.tx_filter_memory = zeros(afdmdv.Nc+1, afdmdv.Nfilter); - excess_bw = 0.5; - afdmdv.gt_alpha5_root = gen_rn_coeffs(excess_bw, 1/Fs, Rs, afdmdv.Nsym, afdmdv.M); - afdmdv.Fsep = afdmdv.Rs*(1+excess_bw); - afdmdv.phase_tx = ones(afdmdv.Nc+1,1); - freq_hz = afdmdv.Fsep*( -Nc*Nd/2 - 0.5 + (1:Nc*Nd).^1.1 ); - afdmdv.freq_pol = 2*pi*freq_hz/Fs; - afdmdv.freq = exp(j*afdmdv.freq_pol); - afdmdv.Fcentre = 1500; - - afdmdv.fbb_rect = exp(j*2*pi*Fcentre/Fs); - afdmdv.fbb_phase_tx = 1; - afdmdv.fbb_phase_rx = 1; - %afdmdv.phase_rx = ones(afdmdv.Nc+1,1); - afdmdv.phase_rx = 1 - 2*(mod(1:Nc*Nd,2)); - nin = M; - - P = afdmdv.P = 4; - afdmdv.Nfilter = afdmdv.Nsym*afdmdv.M; - afdmdv.rx_filter_mem_timing = zeros(afdmdv.Nc+1, afdmdv.Nt*afdmdv.P); - afdmdv.Nfiltertiming = afdmdv.M + afdmdv.Nfilter + afdmdv.M; - afdmdv.rx_filter_memory = zeros(afdmdv.Nc+1, afdmdv.Nfilter); - - acohpsk = standard_init(); - acohpsk.framesize = framesize; - acohpsk.ldpc_code = 0; - acohpsk.ldpc_code_rate = 1; - acohpsk.Nc = Nc; - acohpsk.Rs = Rs; - acohpsk.Ns = 4; - acohpsk.Nd = Nd; - acohpsk.modulation = 'qpsk'; - acohpsk.do_write_pilot_file = 0; - acohpsk = symbol_rate_init(acohpsk); - acohpsk.Ncm = 10*acohpsk.Nsymbrowpilot*M; - acohpsk.coarse_mem = zeros(1,acohpsk.Ncm); - acohpsk.Ndft = 2^(ceil(log2(acohpsk.Ncm))); - - ch_fdm_frame_buf = zeros(1, Nsw*acohpsk.Nsymbrowpilot*M); - - frames = sim_in.frames; - EsNodB = sim_in.EsNodB; - foff = sim_in.foff; - dfoff = sim_in.dfoff; - fading_en = sim_in.fading_en; - - EsNo = 10^(EsNodB/10); - hf_delay_ms = 2; - phase_ch = 1; - - rand('state',1); - tx_bits_coh = round(rand(1,framesize*10)); - ptx_bits_coh = 1; - [spread spread_2ms hf_gain] = init_hf_model(Fs, frames*acohpsk.Nsymbrowpilot*afdmdv.M); - - hf_n = 1; - nhfdelay = floor(hf_delay_ms*Fs/1000); - ch_fdm_delay = zeros(1, acohpsk.Nsymbrowpilot*M + nhfdelay); - - sync = 0; next_sync = 1; - sync_start = 1; - freq_offset_log = []; - sync_time_log = []; - ch_fdm_frame_log = []; - ch_symb_log = []; - tx_fdm_frame_log = []; - ratio_log = []; - - for f=1:frames - - acohpsk.frame = f; - - % - % Mod -------------------------------------------------------------------- - % - - tx_bits = tx_bits_coh(ptx_bits_coh:ptx_bits_coh+framesize-1); - ptx_bits_coh += framesize; - if ptx_bits_coh > length(tx_bits_coh) - ptx_bits_coh = 1; - end - - [tx_symb tx_bits] = bits_to_qpsk_symbols(acohpsk, tx_bits, [], []); - - tx_fdm_frame = []; - for r=1:acohpsk.Nsymbrowpilot - tx_onesymb = tx_symb(r,:); - [tx_baseband afdmdv] = tx_filter(afdmdv, tx_onesymb); - [tx_fdm afdmdv] = fdm_upconvert(afdmdv, tx_baseband); - tx_fdm_frame = [tx_fdm_frame tx_fdm]; - end - tx_fdm_frame_log = [tx_fdm_frame_log tx_fdm_frame]; - - % - % Channel -------------------------------------------------------------------- - % - - ch_fdm_frame = zeros(1,acohpsk.Nsymbrowpilot*M); - for i=1:acohpsk.Nsymbrowpilot*M - foff_rect = exp(j*2*pi*foff/Fs); - foff += dfoff; - phase_ch *= foff_rect; - ch_fdm_frame(i) = tx_fdm_frame(i) * phase_ch; - end - phase_ch /= abs(phase_ch); - - if fading_en - ch_fdm_delay(1:nhfdelay) = ch_fdm_delay(acohpsk.Nsymbrowpilot*M+1:nhfdelay+acohpsk.Nsymbrowpilot*M); - ch_fdm_delay(nhfdelay+1:nhfdelay+acohpsk.Nsymbrowpilot*M) = ch_fdm_frame; - - for i=1:acohpsk.Nsymbrowpilot*M - ahf_model = hf_gain*(spread(hf_n)*ch_fdm_frame(i) + spread_2ms(hf_n)*ch_fdm_delay(i)); - ch_fdm_frame(i) = ahf_model; - hf_n++; - end - end - - % each carrier has power = 2, total power 2Nc, total symbol rate NcRs, noise BW B=Fs - % Es/No = (C/Rs)/(N/B), N = var = 2NcFs/NcRs(Es/No) = 2Fs/Rs(Es/No) - - variance = 2*Fs/(acohpsk.Rs*EsNo); - uvnoise = sqrt(0.5)*(randn(1,acohpsk.Nsymbrowpilot*M) + j*randn(1,acohpsk.Nsymbrowpilot*M)); - noise = sqrt(variance)*uvnoise; - - ch_fdm_frame += noise; - ch_fdm_frame_log = [ch_fdm_frame_log ch_fdm_frame]; - - % - % Try to achieve sync -------------------------------------------------------------------- - % - - % store Nsw frames so we can rewind if we get a good candidate - - ch_fdm_frame_buf(1:(Nsw-1)*acohpsk.Nsymbrowpilot*M) = ch_fdm_frame_buf(acohpsk.Nsymbrowpilot*M+1:Nsw*acohpsk.Nsymbrowpilot*M); - ch_fdm_frame_buf((Nsw-1)*acohpsk.Nsymbrowpilot*M+1:Nsw*acohpsk.Nsymbrowpilot*M) = ch_fdm_frame; - - next_sync = sync; - sync = 0; - - if sync == 0 - - % we can test +/- 20Hz, so we break this up into 3 tests to cover +/- 60Hz - - max_ratio = 0; - for acohpsk.f_est = Fcentre-40:40:Fcentre+40 - %for acohpsk.f_est = Fcentre - - printf(" [%d] acohpsk.f_est: %f +/- 20\n", f, acohpsk.f_est); - [ch_symb rx_timing rx_filt rx_baseband afdmdv acohpsk.f_est ] = rate_Fs_rx_processing(afdmdv, ch_fdm_frame_buf, acohpsk.f_est, Nsw*acohpsk.Nsymbrowpilot, nin, 0); - - % coarse timing (frame sync) and initial fine freq est --------------------------------------------- - - for i=1:Nsw-1 - acohpsk.ct_symb_buf = update_ct_symb_buf(acohpsk.ct_symb_buf, ch_symb((i-1)*acohpsk.Nsymbrowpilot+1:i*acohpsk.Nsymbrowpilot,:), acohpsk.Nct_sym_buf, acohpsk.Nsymbrowpilot); - end - [anext_sync acohpsk] = frame_sync_fine_freq_est(acohpsk, ch_symb((Nsw-1)*acohpsk.Nsymbrowpilot+1:Nsw*acohpsk.Nsymbrowpilot,:), sync, next_sync); - - if anext_sync == 1 - printf(" [%d] acohpsk.ratio: %f\n", f, acohpsk.ratio); - if acohpsk.ratio > max_ratio - max_ratio = acohpsk.ratio; - f_est = acohpsk.f_est - acohpsk.f_fine_est; - next_sync = anext_sync; - end - end - end - end - - % we've found a sync candidate - - if (next_sync == 1) - - % rewind and re-process last few frames with f_est - - acohpsk.f_est = f_est; - printf(" [%d] trying sync and f_est: %f\n", f, acohpsk.f_est); - [ch_symb rx_timing rx_filt rx_baseband afdmdv acohpsk.f_est] = rate_Fs_rx_processing(afdmdv, ch_fdm_frame_buf, acohpsk.f_est, Nsw*acohpsk.Nsymbrowpilot, nin, 0); - ch_symb_log = ch_symb; - for i=1:Nsw-1 - acohpsk.ct_symb_buf = update_ct_symb_buf(acohpsk.ct_symb_buf, ch_symb((i-1)*acohpsk.Nsymbrowpilot+1:i*acohpsk.Nsymbrowpilot,:), acohpsk.Nct_sym_buf, acohpsk.Nsymbrowpilot); - end - [next_sync acohpsk] = frame_sync_fine_freq_est(acohpsk, ch_symb((Nsw-1)*acohpsk.Nsymbrowpilot+1:Nsw*acohpsk.Nsymbrowpilot,:), sync, next_sync); - if abs(acohpsk.f_fine_est) > 2 - printf(" [%d] Hmm %f is a bit big so back to coarse est ...\n", f, acohpsk.f_fine_est); - next_sync = 0; - end - - % candidate checks out so log stats - - if (next_sync == 1) - printf(" [%d] in sync!\n", f); - freq_offset_log = [freq_offset_log Fcentre+foff-acohpsk.f_est]; - sync_time_log = [sync_time_log f-sync_start]; - ratio_log = [ratio_log max_ratio]; - next_sync = 0; sync_start = f; - end - end - - %printf("f: %d sync: %d next_sync: %d\n", f, sync, next_sync); - [sync acohpsk] = sync_state_machine(acohpsk, sync, next_sync); - - end - - % ftx=fopen("coarse_tx.raw","wb"); fwrite(ftx, 1000*ch_fdm_frame_log, "short"); fclose(ftx); - - sim_out.freq_offset_log = freq_offset_log; - sim_out.sync_time_log = sync_time_log; - sim_out.ch_fdm_frame_log = ch_fdm_frame_log; - sim_out.ch_symb_log = ch_symb_log; - sim_out.tx_fdm_frame_log = tx_fdm_frame_log; - sim_out.ratio_log = ratio_log; -endfunction - - -function freq_off_est_test_single - sim_in.frames = 5; - sim_in.EsNodB = 120; - sim_in.foff = 0; - sim_in.dfoff = 0; - sim_in.fading_en = 0; - - sim_out = freq_off_est_test(sim_in); - - figure(1) - subplot(211) - plot(sim_out.freq_offset_log) - ylabel('f est error') - xlabel('time') - - subplot(212) - if length(sim_out.freq_offset_log) > 0 - hist(sim_out.freq_offset_log) - xlabel('f est error') - ylabel('histogram'); - end - - figure(2) - subplot(211) - plot(sim_out.sync_time_log) - ylabel('time to sync (frames)') - subplot(212) - if length(sim_out.sync_time_log) > 0 - hist(sim_out.sync_time_log) - xlabel('time to sync (frames)') - ylabel('histogram') - end - - figure(3) - subplot(211) - plot(real(sim_out.tx_fdm_frame_log(1:2*960))) - grid; - subplot(212) - plot(real(sim_out.ch_symb_log),'+') - grid; - - figure(4) - clf; - plot(sim_out.ch_symb_log,'+') - - figure(5) - clf; - plot(sim_out.ratio_log) - xlabel('time') - ylabel('ratio for sync') -endfunction - - -function [freq_off_log EsNodBSet] = freq_off_est_test_curves - EsNodBSet = [20 12 8]; - - sim_in.frames = 100; - sim_in.foff = -40; - sim_in.dfoff = 0; - sim_in.fading_en = 1; - freq_off_log = 1E6*ones(sim_in.frames, length(EsNodBSet) ); - sync_time_log = 1E6*ones(sim_in.frames, length(EsNodBSet) ); - - for i=1:length(EsNodBSet) - sim_in.EsNodB = EsNodBSet(i); - printf("%f\n", sim_in.EsNodB); - - sim_out = freq_off_est_test(sim_in); - freq_off_log(1:length(sim_out.freq_offset_log),i) = sim_out.freq_offset_log; - sync_time_log(1:length(sim_out.sync_time_log),i) = sim_out.sync_time_log; - end - - figure(1) - clf - hold on; - for i=1:length(EsNodBSet) - data = freq_off_log(find(freq_off_log(:,i) < 1E6),i); - s = std(data); - m = mean(data); - stdbar = [m-s; m+s]; - plot(EsNodBSet(i), data, '+') - plot([EsNodBSet(i) EsNodBSet(i)]+0.5, stdbar,'+-') - end - hold off - - axis([6 22 -25 25]) - if sim_in.fading_en - title_str = sprintf('foff = %d Hz Fading', sim_in.foff); - else - title_str = sprintf('foff = %d Hz AWGN', sim_in.foff); - end - title(title_str); - xlabel('Es/No (dB)') - ylabel('freq offset error (Hz)'); - - figure(2) - clf - hold on; - for i=1:length(EsNodBSet) - leg = sprintf("%d;%d dB;", i, EsNodBSet(i)); - plot(freq_off_log(find(freq_off_log(:,i) < 1E6),i),leg) - end - hold off; - title(title_str); - xlabel('test'); - ylabel('freq offset error (Hz)'); - legend('boxoff'); - - figure(3) - clf - hold on; - for i=1:length(EsNodBSet) - data = sync_time_log(find(sync_time_log(:,i) < 1E6),i); - if length(data) - s = std(data); - m = mean(data); - stdbar = [m-s; m+s]; - plot(EsNodBSet(i), data, '+') - plot([EsNodBSet(i) EsNodBSet(i)]+0.5, stdbar,'+-') - end - end - hold off; - axis([6 22 0 10]) - ylabel('sync time (frames)') - xlabel('Es/No (dB)'); - title(title_str); - - figure(4) - clf - hold on; - for i=1:length(EsNodBSet) - leg = sprintf("%d;%d dB;", i, EsNodBSet(i)); - plot(sync_time_log(find(sync_time_log(:,i) < 1E6),i),leg) - end - hold off; - title(title_str); - xlabel('test'); - ylabel('sync time (frames)'); - legend('boxoff'); - -endfunction - - -% select on of these to run: - -freq_off_est_test_single; -%freq_off_est_test_curves; - +% test_foff.m +% David Rowe April 2015 +% +% Octave script for testing the cohpsk freq offset estimator + +graphics_toolkit ("gnuplot"); +more off; + +cohpsk; +fdmdv; +autotest; + +rand('state',1); +randn('state',1); + + +% Core function for testing frequency offset estimator. Performs one test + +function sim_out = freq_off_est_test(sim_in) + global Nfilter; + global M; + + Rs = 100; + Nc = 4; + Nd = 2; + framesize = 32; + Fs = 8000; + Fcentre = 1500; + + Nsw = 3; % numbers of sync window frames we process over. Set based + % on Nsym to flush filter memory by final frame in windw + + afdmdv.Nsym = 5; + afdmdv.Nt = 5; + + afdmdv.Fs = 8000; + afdmdv.Nc = Nd*Nc-1; + afdmdv.Rs = Rs; + M = afdmdv.M = afdmdv.Fs/afdmdv.Rs; + afdmdv.Nfilter = afdmdv.Nsym*M; + afdmdv.tx_filter_memory = zeros(afdmdv.Nc+1, afdmdv.Nfilter); + excess_bw = 0.5; + afdmdv.gt_alpha5_root = gen_rn_coeffs(excess_bw, 1/Fs, Rs, afdmdv.Nsym, afdmdv.M); + afdmdv.Fsep = afdmdv.Rs*(1+excess_bw); + afdmdv.phase_tx = ones(afdmdv.Nc+1,1); + freq_hz = afdmdv.Fsep*( -Nc*Nd/2 - 0.5 + (1:Nc*Nd).^1.1 ); + afdmdv.freq_pol = 2*pi*freq_hz/Fs; + afdmdv.freq = exp(j*afdmdv.freq_pol); + afdmdv.Fcentre = 1500; + + afdmdv.fbb_rect = exp(j*2*pi*Fcentre/Fs); + afdmdv.fbb_phase_tx = 1; + afdmdv.fbb_phase_rx = 1; + %afdmdv.phase_rx = ones(afdmdv.Nc+1,1); + afdmdv.phase_rx = 1 - 2*(mod(1:Nc*Nd,2)); + nin = M; + + P = afdmdv.P = 4; + afdmdv.Nfilter = afdmdv.Nsym*afdmdv.M; + afdmdv.rx_filter_mem_timing = zeros(afdmdv.Nc+1, afdmdv.Nt*afdmdv.P); + afdmdv.Nfiltertiming = afdmdv.M + afdmdv.Nfilter + afdmdv.M; + afdmdv.rx_filter_memory = zeros(afdmdv.Nc+1, afdmdv.Nfilter); + + acohpsk = standard_init(); + acohpsk.framesize = framesize; + acohpsk.ldpc_code = 0; + acohpsk.ldpc_code_rate = 1; + acohpsk.Nc = Nc; + acohpsk.Rs = Rs; + acohpsk.Ns = 4; + acohpsk.Nd = Nd; + acohpsk.modulation = 'qpsk'; + acohpsk.do_write_pilot_file = 0; + acohpsk = symbol_rate_init(acohpsk); + acohpsk.Ncm = 10*acohpsk.Nsymbrowpilot*M; + acohpsk.coarse_mem = zeros(1,acohpsk.Ncm); + acohpsk.Ndft = 2^(ceil(log2(acohpsk.Ncm))); + + ch_fdm_frame_buf = zeros(1, Nsw*acohpsk.Nsymbrowpilot*M); + + frames = sim_in.frames; + EsNodB = sim_in.EsNodB; + foff = sim_in.foff; + dfoff = sim_in.dfoff; + fading_en = sim_in.fading_en; + + EsNo = 10^(EsNodB/10); + hf_delay_ms = 2; + phase_ch = 1; + + rand('state',1); + tx_bits_coh = round(rand(1,framesize*10)); + ptx_bits_coh = 1; + [spread spread_2ms hf_gain] = init_hf_model(Fs, frames*acohpsk.Nsymbrowpilot*afdmdv.M); + + hf_n = 1; + nhfdelay = floor(hf_delay_ms*Fs/1000); + ch_fdm_delay = zeros(1, acohpsk.Nsymbrowpilot*M + nhfdelay); + + sync = 0; next_sync = 1; + sync_start = 1; + freq_offset_log = []; + sync_time_log = []; + ch_fdm_frame_log = []; + ch_symb_log = []; + tx_fdm_frame_log = []; + ratio_log = []; + + for f=1:frames + + acohpsk.frame = f; + + % + % Mod -------------------------------------------------------------------- + % + + tx_bits = tx_bits_coh(ptx_bits_coh:ptx_bits_coh+framesize-1); + ptx_bits_coh += framesize; + if ptx_bits_coh > length(tx_bits_coh) + ptx_bits_coh = 1; + end + + [tx_symb tx_bits] = bits_to_qpsk_symbols(acohpsk, tx_bits, [], []); + + tx_fdm_frame = []; + for r=1:acohpsk.Nsymbrowpilot + tx_onesymb = tx_symb(r,:); + [tx_baseband afdmdv] = tx_filter(afdmdv, tx_onesymb); + [tx_fdm afdmdv] = fdm_upconvert(afdmdv, tx_baseband); + tx_fdm_frame = [tx_fdm_frame tx_fdm]; + end + tx_fdm_frame_log = [tx_fdm_frame_log tx_fdm_frame]; + + % + % Channel -------------------------------------------------------------------- + % + + ch_fdm_frame = zeros(1,acohpsk.Nsymbrowpilot*M); + for i=1:acohpsk.Nsymbrowpilot*M + foff_rect = exp(j*2*pi*foff/Fs); + foff += dfoff; + phase_ch *= foff_rect; + ch_fdm_frame(i) = tx_fdm_frame(i) * phase_ch; + end + phase_ch /= abs(phase_ch); + + if fading_en + ch_fdm_delay(1:nhfdelay) = ch_fdm_delay(acohpsk.Nsymbrowpilot*M+1:nhfdelay+acohpsk.Nsymbrowpilot*M); + ch_fdm_delay(nhfdelay+1:nhfdelay+acohpsk.Nsymbrowpilot*M) = ch_fdm_frame; + + for i=1:acohpsk.Nsymbrowpilot*M + ahf_model = hf_gain*(spread(hf_n)*ch_fdm_frame(i) + spread_2ms(hf_n)*ch_fdm_delay(i)); + ch_fdm_frame(i) = ahf_model; + hf_n++; + end + end + + % each carrier has power = 2, total power 2Nc, total symbol rate NcRs, noise BW B=Fs + % Es/No = (C/Rs)/(N/B), N = var = 2NcFs/NcRs(Es/No) = 2Fs/Rs(Es/No) + + variance = 2*Fs/(acohpsk.Rs*EsNo); + uvnoise = sqrt(0.5)*(randn(1,acohpsk.Nsymbrowpilot*M) + j*randn(1,acohpsk.Nsymbrowpilot*M)); + noise = sqrt(variance)*uvnoise; + + ch_fdm_frame += noise; + ch_fdm_frame_log = [ch_fdm_frame_log ch_fdm_frame]; + + % + % Try to achieve sync -------------------------------------------------------------------- + % + + % store Nsw frames so we can rewind if we get a good candidate + + ch_fdm_frame_buf(1:(Nsw-1)*acohpsk.Nsymbrowpilot*M) = ch_fdm_frame_buf(acohpsk.Nsymbrowpilot*M+1:Nsw*acohpsk.Nsymbrowpilot*M); + ch_fdm_frame_buf((Nsw-1)*acohpsk.Nsymbrowpilot*M+1:Nsw*acohpsk.Nsymbrowpilot*M) = ch_fdm_frame; + + next_sync = sync; + sync = 0; + + if sync == 0 + + % we can test +/- 20Hz, so we break this up into 3 tests to cover +/- 60Hz + + max_ratio = 0; + for acohpsk.f_est = Fcentre-40:40:Fcentre+40 + %for acohpsk.f_est = Fcentre + + printf(" [%d] acohpsk.f_est: %f +/- 20\n", f, acohpsk.f_est); + [ch_symb rx_timing rx_filt rx_baseband afdmdv acohpsk.f_est ] = rate_Fs_rx_processing(afdmdv, ch_fdm_frame_buf, acohpsk.f_est, Nsw*acohpsk.Nsymbrowpilot, nin, 0); + + % coarse timing (frame sync) and initial fine freq est --------------------------------------------- + + for i=1:Nsw-1 + acohpsk.ct_symb_buf = update_ct_symb_buf(acohpsk.ct_symb_buf, ch_symb((i-1)*acohpsk.Nsymbrowpilot+1:i*acohpsk.Nsymbrowpilot,:), acohpsk.Nct_sym_buf, acohpsk.Nsymbrowpilot); + end + [anext_sync acohpsk] = frame_sync_fine_freq_est(acohpsk, ch_symb((Nsw-1)*acohpsk.Nsymbrowpilot+1:Nsw*acohpsk.Nsymbrowpilot,:), sync, next_sync); + + if anext_sync == 1 + printf(" [%d] acohpsk.ratio: %f\n", f, acohpsk.ratio); + if acohpsk.ratio > max_ratio + max_ratio = acohpsk.ratio; + f_est = acohpsk.f_est - acohpsk.f_fine_est; + next_sync = anext_sync; + end + end + end + end + + % we've found a sync candidate + + if (next_sync == 1) + + % rewind and re-process last few frames with f_est + + acohpsk.f_est = f_est; + printf(" [%d] trying sync and f_est: %f\n", f, acohpsk.f_est); + [ch_symb rx_timing rx_filt rx_baseband afdmdv acohpsk.f_est] = rate_Fs_rx_processing(afdmdv, ch_fdm_frame_buf, acohpsk.f_est, Nsw*acohpsk.Nsymbrowpilot, nin, 0); + ch_symb_log = ch_symb; + for i=1:Nsw-1 + acohpsk.ct_symb_buf = update_ct_symb_buf(acohpsk.ct_symb_buf, ch_symb((i-1)*acohpsk.Nsymbrowpilot+1:i*acohpsk.Nsymbrowpilot,:), acohpsk.Nct_sym_buf, acohpsk.Nsymbrowpilot); + end + [next_sync acohpsk] = frame_sync_fine_freq_est(acohpsk, ch_symb((Nsw-1)*acohpsk.Nsymbrowpilot+1:Nsw*acohpsk.Nsymbrowpilot,:), sync, next_sync); + if abs(acohpsk.f_fine_est) > 2 + printf(" [%d] Hmm %f is a bit big so back to coarse est ...\n", f, acohpsk.f_fine_est); + next_sync = 0; + end + + % candidate checks out so log stats + + if (next_sync == 1) + printf(" [%d] in sync!\n", f); + freq_offset_log = [freq_offset_log Fcentre+foff-acohpsk.f_est]; + sync_time_log = [sync_time_log f-sync_start]; + ratio_log = [ratio_log max_ratio]; + next_sync = 0; sync_start = f; + end + end + + %printf("f: %d sync: %d next_sync: %d\n", f, sync, next_sync); + [sync acohpsk] = sync_state_machine(acohpsk, sync, next_sync); + + end + + % ftx=fopen("coarse_tx.raw","wb"); fwrite(ftx, 1000*ch_fdm_frame_log, "short"); fclose(ftx); + + sim_out.freq_offset_log = freq_offset_log; + sim_out.sync_time_log = sync_time_log; + sim_out.ch_fdm_frame_log = ch_fdm_frame_log; + sim_out.ch_symb_log = ch_symb_log; + sim_out.tx_fdm_frame_log = tx_fdm_frame_log; + sim_out.ratio_log = ratio_log; +endfunction + + +function freq_off_est_test_single + sim_in.frames = 5; + sim_in.EsNodB = 120; + sim_in.foff = 0; + sim_in.dfoff = 0; + sim_in.fading_en = 0; + + sim_out = freq_off_est_test(sim_in); + + figure(1) + subplot(211) + plot(sim_out.freq_offset_log) + ylabel('f est error') + xlabel('time') + + subplot(212) + if length(sim_out.freq_offset_log) > 0 + hist(sim_out.freq_offset_log) + xlabel('f est error') + ylabel('histogram'); + end + + figure(2) + subplot(211) + plot(sim_out.sync_time_log) + ylabel('time to sync (frames)') + subplot(212) + if length(sim_out.sync_time_log) > 0 + hist(sim_out.sync_time_log) + xlabel('time to sync (frames)') + ylabel('histogram') + end + + figure(3) + subplot(211) + plot(real(sim_out.tx_fdm_frame_log(1:2*960))) + grid; + subplot(212) + plot(real(sim_out.ch_symb_log),'+') + grid; + + figure(4) + clf; + plot(sim_out.ch_symb_log,'+') + + figure(5) + clf; + plot(sim_out.ratio_log) + xlabel('time') + ylabel('ratio for sync') +endfunction + + +function [freq_off_log EsNodBSet] = freq_off_est_test_curves + EsNodBSet = [20 12 8]; + + sim_in.frames = 100; + sim_in.foff = -40; + sim_in.dfoff = 0; + sim_in.fading_en = 1; + freq_off_log = 1E6*ones(sim_in.frames, length(EsNodBSet) ); + sync_time_log = 1E6*ones(sim_in.frames, length(EsNodBSet) ); + + for i=1:length(EsNodBSet) + sim_in.EsNodB = EsNodBSet(i); + printf("%f\n", sim_in.EsNodB); + + sim_out = freq_off_est_test(sim_in); + freq_off_log(1:length(sim_out.freq_offset_log),i) = sim_out.freq_offset_log; + sync_time_log(1:length(sim_out.sync_time_log),i) = sim_out.sync_time_log; + end + + figure(1) + clf + hold on; + for i=1:length(EsNodBSet) + data = freq_off_log(find(freq_off_log(:,i) < 1E6),i); + s = std(data); + m = mean(data); + stdbar = [m-s; m+s]; + plot(EsNodBSet(i), data, '+') + plot([EsNodBSet(i) EsNodBSet(i)]+0.5, stdbar,'+-') + end + hold off + + axis([6 22 -25 25]) + if sim_in.fading_en + title_str = sprintf('foff = %d Hz Fading', sim_in.foff); + else + title_str = sprintf('foff = %d Hz AWGN', sim_in.foff); + end + title(title_str); + xlabel('Es/No (dB)') + ylabel('freq offset error (Hz)'); + + figure(2) + clf + hold on; + for i=1:length(EsNodBSet) + leg = sprintf("%d;%d dB;", i, EsNodBSet(i)); + plot(freq_off_log(find(freq_off_log(:,i) < 1E6),i),leg) + end + hold off; + title(title_str); + xlabel('test'); + ylabel('freq offset error (Hz)'); + legend('boxoff'); + + figure(3) + clf + hold on; + for i=1:length(EsNodBSet) + data = sync_time_log(find(sync_time_log(:,i) < 1E6),i); + if length(data) + s = std(data); + m = mean(data); + stdbar = [m-s; m+s]; + plot(EsNodBSet(i), data, '+') + plot([EsNodBSet(i) EsNodBSet(i)]+0.5, stdbar,'+-') + end + end + hold off; + axis([6 22 0 10]) + ylabel('sync time (frames)') + xlabel('Es/No (dB)'); + title(title_str); + + figure(4) + clf + hold on; + for i=1:length(EsNodBSet) + leg = sprintf("%d;%d dB;", i, EsNodBSet(i)); + plot(sync_time_log(find(sync_time_log(:,i) < 1E6),i),leg) + end + hold off; + title(title_str); + xlabel('test'); + ylabel('sync time (frames)'); + legend('boxoff'); + +endfunction + + +% select on of these to run: + +freq_off_est_test_single; +%freq_off_est_test_curves; + diff --git a/codec2-dev/octave/test_qpsk.m b/codec2-dev/octave/test_qpsk.m index 6d8796e0..453302b9 100644 --- a/codec2-dev/octave/test_qpsk.m +++ b/codec2-dev/octave/test_qpsk.m @@ -1,516 +1,516 @@ -% test_qpsk.m -% David Rowe Feb 2014 -% -% QPSK modem simulation, initially based on code by Bill Cowley -% Generates curves BER versus E/No curves for different modems. -% Design to test coherent demodulation ideas on HF channels without -% building a full blown modem. Uses 'genie provided' estimates for -% timing estimation, frame sync. -% - -1; - -% main test function - -function sim_out = ber_test(sim_in, modulation) - Fs = 8000; - - verbose = sim_in.verbose; - framesize = sim_in.framesize; - Ntrials = sim_in.Ntrials; - Esvec = sim_in.Esvec; - phase_offset = sim_in.phase_offset; - phase_est = sim_in.phase_est; - w_offset = sim_in.w_offset; - plot_scatter = sim_in.plot_scatter; - Rs = sim_in.Rs; - hf_sim = sim_in.hf_sim; - Nhfdelay = floor(sim_in.hf_delay_ms*1000/Fs); - hf_phase_only = sim_in.hf_phase_only; - hf_mag_only = sim_in.hf_mag_only; - - bps = 2; - Nsymb = framesize/bps; - prev_sym_tx = qpsk_mod([0 0]); - prev_sym_rx = qpsk_mod([0 0]); - rx_symb_log = []; - - Np = sim_in.Np; % number of pilot symbols to use in phase est - Ns = sim_in.Ns; % spacing of pilot symbols, so (Nps-1) data symbols between every pilot - Nps = Np*Ns; - r_delay_line = zeros(1,Nps+1); - s_delay_line = zeros(1,Nps+1); - spread_main_phi = 0; - spread_delay_phi = 0; - spread_main_phi_log = []; - - ldpc_code = sim_in.ldpc_code; - - if ldpc_code - % Start CML library - - currentdir = pwd; - addpath '/home/david/tmp/cml/mat' % assume the source files stored here - cd /home/david/tmp/cml - CmlStartup % note that this is not in the cml path! - cd(currentdir) - - % Our LDPC library - - ldpc; - - rate = 3/4; - mod_order = 4; - modulation = 'QPSK'; - mapping = 'gray'; - - demod_type = 0; - decoder_type = 0; - max_iterations = 100; - - code_param = ldpc_init(rate, framesize, modulation, mod_order, mapping); - code_param.code_bits_per_frame = framesize; - code_param.symbols_per_frame = framesize/bps; - else - rate = 1; - end - - % convert "spreading" samples from 1kHz carrier at Fs to complex - % baseband, generated by passing a 1kHz sine wave through PathSim - % with the ccir-poor model, enabling one path at a time. - - Fc = 1000; - fspread = fopen("../raw/sine1k_2Hz_spread.raw","rb"); - spread1k = fread(fspread, "int16")/10000; - fclose(fspread); - fspread = fopen("../raw/sine1k_2ms_delay_2Hz_spread.raw","rb"); - spread1k_2ms = fread(fspread, "int16")/10000; - fclose(fspread); - - % down convert to complex baseband - spreadbb = spread1k.*exp(-j*(2*pi*Fc/Fs)*(1:length(spread1k))'); - spreadbb_2ms = spread1k_2ms.*exp(-j*(2*pi*Fc/Fs)*(1:length(spread1k_2ms))'); - - % remove -2000 Hz image - b = fir1(50, 5/Fs); - spread = filter(b,1,spreadbb); - spread_2ms = filter(b,1,spreadbb_2ms); - - % discard first 1000 samples as these were near 0, probably as - % PathSim states were ramping up - - spread = spread(1000:length(spread)); - spread_2ms = spread_2ms(1000:length(spread_2ms)); - - % Determine "gain" of HF channel model, so we can normalise - % carrier power during HF channel sim to calibrate SNR. I imagine - % different implementations of ccir-poor would do this in - % different ways, leading to different BER results. Oh Well! - - hf_gain = 1.0/sqrt(var(spread)+var(spread_2ms)); - - % design root nyquist (root raised cosine) filter and init tx and rx filter states - - alpha = 0.5; T=1/Fs; Nfiltsym=7; M=Fs/Rs; - if floor(Fs/Rs) != Fs/Rs - printf("oversampling ratio must be an integer\n"); - return; - end - hrn = gen_rn_coeffs(alpha, T, Rs, Nfiltsym, M); - Nfilter = length(hrn); - tx_filter_memory = zeros(1, Nfilter); - rx_filter_memory = zeros(1, Nfilter); - s_delay_line_filt = zeros(1,Nfiltsym); - tx_bits_delay_line_filt = zeros(1,Nfiltsym*bps); - hf_sim_delay_line = zeros(1,M+Nhfdelay); - - for ne = 1:length(Esvec) - Es = Esvec(ne); - EsNo = 10^(Es/10); - - % Given Es/No, determine the variance of a normal noise source: - % - % Es = C/Rs where C is carrier power (energy per unit time) and Rs is the symbole rate - % N = NoB where N is the total noise power, No is the Noise spectral density is W/Hz - % and B is the bandwidth of the noise which is Fs - % No = N/Fs - % - % equating Es/No we get: - % - % Es/No = (C/Rs)/(No/Fs) - % No = CFs/(Rs(Es/No)) - - variance = Fs/(Rs*EsNo); - Terrs = 0; Tbits = 0; Terrsldpc = 0; Tbitsldpc = 0; Ferrsldpc = 0; - if verbose > 1 - printf("EsNo (dB): %f EsNo: %f variance: %f\n", Es, EsNo, variance); - end - - % init HF channel - sc = 1; - - tx_filt_log = []; - rx_filt_log = []; - rx_baseband_log = []; - tx_baseband_log = []; - noise_log = []; - hf_angle_log = []; - tx_phase = rx_phase = 0; - tx_data_buffer = zeros(1,2*framesize); - s_data_buffer = zeros(1,2*Nsymb); - C_log = []; - - for nn = 1: Ntrials - - tx_bits = round( rand( 1, framesize*rate ) ); - %tx_bits = [1 0 zeros(1,framesize*rate-2)]; - - % modulate - - if ldpc_code - [tx_bits, s] = ldpc_enc(tx_bits, code_param); - t2 = tx_bits; - s2 = s; - else - s = zeros(1, Nsymb); - for i=1:Nsymb - tx_symb = qpsk_mod(tx_bits(2*(i-1)+1:2*i)); - %printf("shift: %f prev_sym: %f ", tx_symb, prev_sym_tx); - if strcmp(modulation,'dqpsk') - tx_symb *= prev_sym_tx; - %printf("tx_symb: %f\n", tx_symb); - prev_sym_tx = tx_symb; - end - s(i) = tx_symb; - end - end - s_ch = s; - - % root nyquist filter symbols - - for k=1:Nsymb - - % tx filter symbols - - tx_filt = zeros(1,M); - - % tx filter each symbol, generate M filtered output samples for each symbol. - % Efficient polyphase filter techniques used as tx_filter_memory is sparse - - tx_filter_memory(Nfilter) = s_ch(k); - - for i=1:M - tx_filt(i) = M*tx_filter_memory(M:M:Nfilter) * hrn(M-i+1:M:Nfilter)'; - end - tx_filter_memory(1:Nfilter-M) = tx_filter_memory(M+1:Nfilter); - tx_filter_memory(Nfilter-M+1:Nfilter) = zeros(1,M); - - % HF channel simulation - - if hf_sim - - hf_sim_delay_line(1:Nhfdelay) = hf_sim_delay_line(M+1:M+Nhfdelay); - hf_sim_delay_line(Nhfdelay+1:M+Nhfdelay) = tx_filt; - - % disable as a wrap around will cause a nasty phase jump. Best to generate - % longer files - %if ((sc+M) > length(spread)) || ((sc+M) > length(spread_2ms)) - % sc =1 ; - %end - comb = conj(spread(sc:sc+M-1))' + conj(spread_2ms(sc:sc+M-1))'; - if hf_phase_only - tx_filt = tx_filt.*exp(j*angle(comb)); - else - if hf_mag_only - comb = conj(spread(sc:sc+M-1))' + conj(spread_2ms(sc:sc+M-1))'; - tx_filt = tx_filt.*abs(comb); - else - % regular HF channel sim - tx_filt = tx_filt.*conj(spread(sc:sc+M-1))' + hf_sim_delay_line(1:M).*conj(spread_2ms(sc:sc+M-1))'; - end - end - sc += M; - - % normalise so average HF power C=1 - - if hf_phase_only == 0 % C already 1 if we are just tweaking phase - tx_filt *= hf_gain; - end - C_log = [C_log abs(comb)*hf_gain]; - end - tx_filt_log = [tx_filt_log tx_filt]; - hf_angle_log = [hf_angle_log angle(comb)]; - - % AWGN noise and phase/freq offset channel simulation - % 0.5 factor ensures var(noise) == variance , i.e. splits power between Re & Im - - noise = sqrt(variance*0.5)*( randn(1,M) + j*randn(1,M) ); - noise_log = [noise_log noise]; - rx_baseband = tx_filt.*exp(j*phase_offset) + noise; - phase_offset += w_offset; - - % rx filter symbol - - rx_filter_memory(Nfilter-M+1:Nfilter) = rx_baseband; - rx_filt = rx_filter_memory * hrn'; - rx_filter_memory(1:Nfilter-M) = rx_filter_memory(1+M:Nfilter); - rx_filt_log = [rx_filt_log rx_filt]; - - % delay in tx symbols to compensate for filtering - % delay, as tx symbols are used as pilot symbols input - % to phase est - - s_delay_line_filt(1:Nfiltsym-1) = s_delay_line_filt(2:Nfiltsym); - s_delay_line_filt(Nfiltsym) = s(k); - s(k) = s_delay_line_filt(1); - - % delay in tx bits to compensate for filtering delay - - tx_bits_delay_line_filt(1:(Nfiltsym-1)*bps) = tx_bits_delay_line_filt(bps+1:Nfiltsym*bps); - tx_bits_delay_line_filt((Nfiltsym-1)*bps+1:Nfiltsym*bps) = tx_bits((k-1)*bps+1:k*bps); - tx_bits((k-1)*bps+1:k*bps) = tx_bits_delay_line_filt(1:bps); - - s_ch(k) = rx_filt; - end - - % coherent demod phase estimation and correction using pilot symbols - % cheating a bit here, we use fact that all tx-ed symbols are known - - if phase_est - for i=1:Nsymb - - % delay line for phase est window - - r_delay_line(1:Nps-1) = r_delay_line(2:Nps); - r_delay_line(Nps) = s_ch(i); - - % delay in tx data to compensate data for phase est window - - s_delay_line(1:Nps-1) = s_delay_line(2:Nps); - s_delay_line(Nps) = s(i); - tx_bits(2*(i-1)+1:2*i) = qpsk_demod(s_delay_line(floor(Nps/2)+1)); - - % estimate phase from surrounding known pilot symbols and correct - - corr = 0; centre = floor(Nps/2)+1; - for k=1:Ns:(Nps+1) - if (k != centre) - corr += s_delay_line(k) * r_delay_line(k)'; - end - end - s_ch(i) = r_delay_line(centre).*exp(j*angle(corr)); - end - %printf("corr: %f angle: %f\n", corr, angle(corr)); - end - - % de-modulate - - rx_bits = zeros(1, framesize); - for i=1:Nsymb - rx_symb = s_ch(i); - if strcmp(modulation,'dqpsk') - tmp = rx_symb; - rx_symb *= conj(prev_sym_rx/abs(prev_sym_rx)); - prev_sym_rx = tmp; - end - rx_bits((2*(i-1)+1):(2*i)) = qpsk_demod(rx_symb); - rx_symb_log = [rx_symb_log rx_symb]; - end - - % Measure BER - - % discard bits from first 2*Nfiltsym+Nps+1 symbols as tx - % and rx filter and phase est memories not full - - skip = bps*(2*Nfiltsym+1+Nps+1); - if nn == 1 - tx_bits_tmp = tx_bits(skip:length(tx_bits)); - rx_bits_tmp = rx_bits(skip:length(rx_bits)); - else - tx_bits_tmp = tx_bits; - rx_bits_tmp = rx_bits; - end - - error_positions = xor( rx_bits_tmp, tx_bits_tmp ); - Nerrs = sum(error_positions); - Terrs += Nerrs; - Tbits += length(tx_bits_tmp); - - % Optionally LDPC decode - - if ldpc_code - % filter memories etc screw up frame alignment so we need to buffer a frame - - tx_data_buffer(1:framesize) = tx_data_buffer(framesize+1:2*framesize); - s_data_buffer(1:Nsymb) = s_data_buffer(Nsymb+1:2*Nsymb); - tx_data_buffer(framesize+1:2*framesize) = tx_bits; - s_data_buffer(Nsymb+1:2*Nsymb) = s_ch; - - offset = Nfiltsym-1; - if (phase_est) - offset += floor(Nps/2); - end - st_tx = offset*bps+1; - st_s = offset; - - detected_data = ldpc_dec(code_param, max_iterations, demod_type, decoder_type, ... - s_data_buffer(st_s+1:st_s+Nsymb), min(100,EsNo)); - - % ignore first frame as filter, phase est memories filling up - if nn != 1 - error_positions = xor( detected_data(1:framesize*rate), ... - tx_data_buffer(st_tx:st_tx+framesize*rate-1) ); - Nerrs = sum(error_positions); - if Nerrs - Ferrsldpc++; - end - Terrsldpc += Nerrs; - Tbitsldpc += framesize*rate; - end - end - - end - - TERvec(ne) = Terrs; - BERvec(ne) = Terrs/Tbits; - if ldpc_code - TERldpcvec(ne) = Terrsldpc; - FERldpcvec(ne) = Ferrsldpc; - BERldpcvec(ne) = Terrsldpc/Tbitsldpc; - end - - if verbose - printf("EsNo (dB): %f Terrs: %d BER %f BER theory %f", Es, Terrs, - Terrs/Tbits, 0.5*erfc(sqrt(EsNo/2))); - if ldpc_code - printf(" LDPC: Terrs: %d BER: %f Ferrs: %d FER: %f", - Terrsldpc, Terrsldpc/Tbitsldpc, Ferrsldpc, Ferrsldpc/(Ntrials-1)); - end - printf("\n"); - end - if verbose > 1 - printf("Terrs: %d BER %f BER theory %f C %f N %f Es %f No %f Es/No %f\n\n", Terrs, - Terrs/Tbits, 0.5*erfc(sqrt(EsNo/2)), var(tx_filt_log), var(noise_log), - var(tx_filt_log)/Rs, var(noise_log)/Fs, (var(tx_filt_log)/Rs)/(var(noise_log)/Fs)); - end - end - - Ebvec = Esvec - 10*log10(bps); - sim_out.BERvec = BERvec; - sim_out.Ebvec = Ebvec; - sim_out.TERvec = TERvec; - if ldpc_code - sim_out.BERldpcvec = BERldpcvec; - sim_out.TERldpcvec = TERldpcvec; - sim_out.FERldpcvec = FERldpcvec; - end - - if plot_scatter - figure(2); - clf; - scat = rx_symb_log(2*Nfiltsym:length(rx_symb_log)) .* exp(j*pi/4); - plot(real(scat), imag(scat),'+'); - - figure(3); - clf; - - subplot(211) - plot(C_log); - subplot(212) - plot(hf_angle_log); - axis([1 10000 min(hf_angle_log) max(hf_angle_log)]) - end -endfunction - -% Gray coded QPSK modulation function - -function symbol = qpsk_mod(two_bits) - two_bits_decimal = sum(two_bits .* [2 1]); - switch(two_bits_decimal) - case (0) symbol = 1; - case (1) symbol = j; - case (2) symbol = -j; - case (3) symbol = -1; - endswitch -endfunction - -% Gray coded QPSK demodulation function - -function two_bits = qpsk_demod(symbol) - if isscalar(symbol) == 0 - printf("only works with scalars\n"); - return; - end - bit0 = real(symbol*exp(j*pi/4)) < 0; - bit1 = imag(symbol*exp(j*pi/4)) < 0; - two_bits = [bit1 bit0]; -endfunction - -% Start simulations --------------------------------------- - -more off; -sim_in.verbose = 2; - -sim_in.Esvec = 5; -sim_in.Ntrials = 100; -sim_in.framesize = 100; -sim_in.Rs = 400; -sim_in.phase_offset = 0; -sim_in.phase_est = 0; -sim_in.w_offset = 0; -sim_in.plot_scatter = 1; -sim_in.hf_delay_ms = 2; -sim_in.hf_sim = 1; -sim_in.Np = 6; -sim_in.Ns = 5; -sim_in.hf_phase_only = 0; -sim_in.hf_mag_only = 1; -sim_in.ldpc_code = 0; - -Ebvec = sim_in.Esvec - 10*log10(2); -BER_theory = 0.5*erfc(sqrt(10.^(Ebvec/10))); - -sim_qpsk = ber_test(sim_in, 'qpsk'); - -figure(1); -clf; -semilogy(Ebvec, BER_theory,'r;QPSK theory;') -hold on; -semilogy(sim_qpsk.Ebvec, sim_qpsk.BERvec,'g;QPSK;') -hold off; -xlabel('Eb/N0') -ylabel('BER') -grid("minor") - - -if 0 -sim_in.hf_mag_only = 1; -sim_qpsk_mag = ber_test(sim_in, 'qpsk'); - -sim_in.hf_mag_only = 0; -sim_in.hf_phase_only = 1; -sim_in.phase_est = 1; -sim_qpsk_phase = ber_test(sim_in, 'qpsk'); - -sim_in.hf_phase_only = 0; -sim_qpsk_coh_6_5 = ber_test(sim_in, 'qpsk'); - -sim_in.phase_est = 0; -sim_dqpsk = ber_test(sim_in, 'dqpsk'); - -figure(1); -clf; -semilogy(Ebvec, BER_theory,'r;QPSK theory;') -hold on; -semilogy(sim_qpsk_mag.Ebvec, sim_qpsk_mag.BERvec,'g;QPSK CCIR poor mag;') -semilogy(sim_qpsk_phase.Ebvec, sim_qpsk_phase.BERvec,'k;QPSK CCIR poor phase;') -semilogy(sim_qpsk_coh_6_5.Ebvec, sim_qpsk_coh_6_5.BERvec,'c;QPSK CCIR poor Np=6 Ns=5;') -semilogy(sim_dqpsk.Ebvec, sim_dqpsk.BERvec,'b;DQPSK CCIR poor;') -%semilogy(sim_qpsk_coh_5_24.Ebvec, sim_qpsk_coh_5_24.BERvec,'k;QPSK Ns=5 Np=24;') -%semilogy(sim_qpsk_coh_2_12.Ebvec, sim_qpsk_coh_2_12.BERvec,'c;QPSK Ns=2 Np=12;') -hold off; -xlabel('Eb/N0') -ylabel('BER') -grid("minor") -axis([min(Ebvec)-1 max(Ebvec)+1 1E-2 1]) -end +% test_qpsk.m +% David Rowe Feb 2014 +% +% QPSK modem simulation, initially based on code by Bill Cowley +% Generates curves BER versus E/No curves for different modems. +% Design to test coherent demodulation ideas on HF channels without +% building a full blown modem. Uses 'genie provided' estimates for +% timing estimation, frame sync. +% + +1; + +% main test function + +function sim_out = ber_test(sim_in, modulation) + Fs = 8000; + + verbose = sim_in.verbose; + framesize = sim_in.framesize; + Ntrials = sim_in.Ntrials; + Esvec = sim_in.Esvec; + phase_offset = sim_in.phase_offset; + phase_est = sim_in.phase_est; + w_offset = sim_in.w_offset; + plot_scatter = sim_in.plot_scatter; + Rs = sim_in.Rs; + hf_sim = sim_in.hf_sim; + Nhfdelay = floor(sim_in.hf_delay_ms*1000/Fs); + hf_phase_only = sim_in.hf_phase_only; + hf_mag_only = sim_in.hf_mag_only; + + bps = 2; + Nsymb = framesize/bps; + prev_sym_tx = qpsk_mod([0 0]); + prev_sym_rx = qpsk_mod([0 0]); + rx_symb_log = []; + + Np = sim_in.Np; % number of pilot symbols to use in phase est + Ns = sim_in.Ns; % spacing of pilot symbols, so (Nps-1) data symbols between every pilot + Nps = Np*Ns; + r_delay_line = zeros(1,Nps+1); + s_delay_line = zeros(1,Nps+1); + spread_main_phi = 0; + spread_delay_phi = 0; + spread_main_phi_log = []; + + ldpc_code = sim_in.ldpc_code; + + if ldpc_code + % Start CML library + + currentdir = pwd; + addpath '/home/david/tmp/cml/mat' % assume the source files stored here + cd /home/david/tmp/cml + CmlStartup % note that this is not in the cml path! + cd(currentdir) + + % Our LDPC library + + ldpc; + + rate = 3/4; + mod_order = 4; + modulation = 'QPSK'; + mapping = 'gray'; + + demod_type = 0; + decoder_type = 0; + max_iterations = 100; + + code_param = ldpc_init(rate, framesize, modulation, mod_order, mapping); + code_param.code_bits_per_frame = framesize; + code_param.symbols_per_frame = framesize/bps; + else + rate = 1; + end + + % convert "spreading" samples from 1kHz carrier at Fs to complex + % baseband, generated by passing a 1kHz sine wave through PathSim + % with the ccir-poor model, enabling one path at a time. + + Fc = 1000; + fspread = fopen("../raw/sine1k_2Hz_spread.raw","rb"); + spread1k = fread(fspread, "int16")/10000; + fclose(fspread); + fspread = fopen("../raw/sine1k_2ms_delay_2Hz_spread.raw","rb"); + spread1k_2ms = fread(fspread, "int16")/10000; + fclose(fspread); + + % down convert to complex baseband + spreadbb = spread1k.*exp(-j*(2*pi*Fc/Fs)*(1:length(spread1k))'); + spreadbb_2ms = spread1k_2ms.*exp(-j*(2*pi*Fc/Fs)*(1:length(spread1k_2ms))'); + + % remove -2000 Hz image + b = fir1(50, 5/Fs); + spread = filter(b,1,spreadbb); + spread_2ms = filter(b,1,spreadbb_2ms); + + % discard first 1000 samples as these were near 0, probably as + % PathSim states were ramping up + + spread = spread(1000:length(spread)); + spread_2ms = spread_2ms(1000:length(spread_2ms)); + + % Determine "gain" of HF channel model, so we can normalise + % carrier power during HF channel sim to calibrate SNR. I imagine + % different implementations of ccir-poor would do this in + % different ways, leading to different BER results. Oh Well! + + hf_gain = 1.0/sqrt(var(spread)+var(spread_2ms)); + + % design root nyquist (root raised cosine) filter and init tx and rx filter states + + alpha = 0.5; T=1/Fs; Nfiltsym=7; M=Fs/Rs; + if floor(Fs/Rs) != Fs/Rs + printf("oversampling ratio must be an integer\n"); + return; + end + hrn = gen_rn_coeffs(alpha, T, Rs, Nfiltsym, M); + Nfilter = length(hrn); + tx_filter_memory = zeros(1, Nfilter); + rx_filter_memory = zeros(1, Nfilter); + s_delay_line_filt = zeros(1,Nfiltsym); + tx_bits_delay_line_filt = zeros(1,Nfiltsym*bps); + hf_sim_delay_line = zeros(1,M+Nhfdelay); + + for ne = 1:length(Esvec) + Es = Esvec(ne); + EsNo = 10^(Es/10); + + % Given Es/No, determine the variance of a normal noise source: + % + % Es = C/Rs where C is carrier power (energy per unit time) and Rs is the symbole rate + % N = NoB where N is the total noise power, No is the Noise spectral density is W/Hz + % and B is the bandwidth of the noise which is Fs + % No = N/Fs + % + % equating Es/No we get: + % + % Es/No = (C/Rs)/(No/Fs) + % No = CFs/(Rs(Es/No)) + + variance = Fs/(Rs*EsNo); + Terrs = 0; Tbits = 0; Terrsldpc = 0; Tbitsldpc = 0; Ferrsldpc = 0; + if verbose > 1 + printf("EsNo (dB): %f EsNo: %f variance: %f\n", Es, EsNo, variance); + end + + % init HF channel + sc = 1; + + tx_filt_log = []; + rx_filt_log = []; + rx_baseband_log = []; + tx_baseband_log = []; + noise_log = []; + hf_angle_log = []; + tx_phase = rx_phase = 0; + tx_data_buffer = zeros(1,2*framesize); + s_data_buffer = zeros(1,2*Nsymb); + C_log = []; + + for nn = 1: Ntrials + + tx_bits = round( rand( 1, framesize*rate ) ); + %tx_bits = [1 0 zeros(1,framesize*rate-2)]; + + % modulate + + if ldpc_code + [tx_bits, s] = ldpc_enc(tx_bits, code_param); + t2 = tx_bits; + s2 = s; + else + s = zeros(1, Nsymb); + for i=1:Nsymb + tx_symb = qpsk_mod(tx_bits(2*(i-1)+1:2*i)); + %printf("shift: %f prev_sym: %f ", tx_symb, prev_sym_tx); + if strcmp(modulation,'dqpsk') + tx_symb *= prev_sym_tx; + %printf("tx_symb: %f\n", tx_symb); + prev_sym_tx = tx_symb; + end + s(i) = tx_symb; + end + end + s_ch = s; + + % root nyquist filter symbols + + for k=1:Nsymb + + % tx filter symbols + + tx_filt = zeros(1,M); + + % tx filter each symbol, generate M filtered output samples for each symbol. + % Efficient polyphase filter techniques used as tx_filter_memory is sparse + + tx_filter_memory(Nfilter) = s_ch(k); + + for i=1:M + tx_filt(i) = M*tx_filter_memory(M:M:Nfilter) * hrn(M-i+1:M:Nfilter)'; + end + tx_filter_memory(1:Nfilter-M) = tx_filter_memory(M+1:Nfilter); + tx_filter_memory(Nfilter-M+1:Nfilter) = zeros(1,M); + + % HF channel simulation + + if hf_sim + + hf_sim_delay_line(1:Nhfdelay) = hf_sim_delay_line(M+1:M+Nhfdelay); + hf_sim_delay_line(Nhfdelay+1:M+Nhfdelay) = tx_filt; + + % disable as a wrap around will cause a nasty phase jump. Best to generate + % longer files + %if ((sc+M) > length(spread)) || ((sc+M) > length(spread_2ms)) + % sc =1 ; + %end + comb = conj(spread(sc:sc+M-1))' + conj(spread_2ms(sc:sc+M-1))'; + if hf_phase_only + tx_filt = tx_filt.*exp(j*angle(comb)); + else + if hf_mag_only + comb = conj(spread(sc:sc+M-1))' + conj(spread_2ms(sc:sc+M-1))'; + tx_filt = tx_filt.*abs(comb); + else + % regular HF channel sim + tx_filt = tx_filt.*conj(spread(sc:sc+M-1))' + hf_sim_delay_line(1:M).*conj(spread_2ms(sc:sc+M-1))'; + end + end + sc += M; + + % normalise so average HF power C=1 + + if hf_phase_only == 0 % C already 1 if we are just tweaking phase + tx_filt *= hf_gain; + end + C_log = [C_log abs(comb)*hf_gain]; + end + tx_filt_log = [tx_filt_log tx_filt]; + hf_angle_log = [hf_angle_log angle(comb)]; + + % AWGN noise and phase/freq offset channel simulation + % 0.5 factor ensures var(noise) == variance , i.e. splits power between Re & Im + + noise = sqrt(variance*0.5)*( randn(1,M) + j*randn(1,M) ); + noise_log = [noise_log noise]; + rx_baseband = tx_filt.*exp(j*phase_offset) + noise; + phase_offset += w_offset; + + % rx filter symbol + + rx_filter_memory(Nfilter-M+1:Nfilter) = rx_baseband; + rx_filt = rx_filter_memory * hrn'; + rx_filter_memory(1:Nfilter-M) = rx_filter_memory(1+M:Nfilter); + rx_filt_log = [rx_filt_log rx_filt]; + + % delay in tx symbols to compensate for filtering + % delay, as tx symbols are used as pilot symbols input + % to phase est + + s_delay_line_filt(1:Nfiltsym-1) = s_delay_line_filt(2:Nfiltsym); + s_delay_line_filt(Nfiltsym) = s(k); + s(k) = s_delay_line_filt(1); + + % delay in tx bits to compensate for filtering delay + + tx_bits_delay_line_filt(1:(Nfiltsym-1)*bps) = tx_bits_delay_line_filt(bps+1:Nfiltsym*bps); + tx_bits_delay_line_filt((Nfiltsym-1)*bps+1:Nfiltsym*bps) = tx_bits((k-1)*bps+1:k*bps); + tx_bits((k-1)*bps+1:k*bps) = tx_bits_delay_line_filt(1:bps); + + s_ch(k) = rx_filt; + end + + % coherent demod phase estimation and correction using pilot symbols + % cheating a bit here, we use fact that all tx-ed symbols are known + + if phase_est + for i=1:Nsymb + + % delay line for phase est window + + r_delay_line(1:Nps-1) = r_delay_line(2:Nps); + r_delay_line(Nps) = s_ch(i); + + % delay in tx data to compensate data for phase est window + + s_delay_line(1:Nps-1) = s_delay_line(2:Nps); + s_delay_line(Nps) = s(i); + tx_bits(2*(i-1)+1:2*i) = qpsk_demod(s_delay_line(floor(Nps/2)+1)); + + % estimate phase from surrounding known pilot symbols and correct + + corr = 0; centre = floor(Nps/2)+1; + for k=1:Ns:(Nps+1) + if (k != centre) + corr += s_delay_line(k) * r_delay_line(k)'; + end + end + s_ch(i) = r_delay_line(centre).*exp(j*angle(corr)); + end + %printf("corr: %f angle: %f\n", corr, angle(corr)); + end + + % de-modulate + + rx_bits = zeros(1, framesize); + for i=1:Nsymb + rx_symb = s_ch(i); + if strcmp(modulation,'dqpsk') + tmp = rx_symb; + rx_symb *= conj(prev_sym_rx/abs(prev_sym_rx)); + prev_sym_rx = tmp; + end + rx_bits((2*(i-1)+1):(2*i)) = qpsk_demod(rx_symb); + rx_symb_log = [rx_symb_log rx_symb]; + end + + % Measure BER + + % discard bits from first 2*Nfiltsym+Nps+1 symbols as tx + % and rx filter and phase est memories not full + + skip = bps*(2*Nfiltsym+1+Nps+1); + if nn == 1 + tx_bits_tmp = tx_bits(skip:length(tx_bits)); + rx_bits_tmp = rx_bits(skip:length(rx_bits)); + else + tx_bits_tmp = tx_bits; + rx_bits_tmp = rx_bits; + end + + error_positions = xor( rx_bits_tmp, tx_bits_tmp ); + Nerrs = sum(error_positions); + Terrs += Nerrs; + Tbits += length(tx_bits_tmp); + + % Optionally LDPC decode + + if ldpc_code + % filter memories etc screw up frame alignment so we need to buffer a frame + + tx_data_buffer(1:framesize) = tx_data_buffer(framesize+1:2*framesize); + s_data_buffer(1:Nsymb) = s_data_buffer(Nsymb+1:2*Nsymb); + tx_data_buffer(framesize+1:2*framesize) = tx_bits; + s_data_buffer(Nsymb+1:2*Nsymb) = s_ch; + + offset = Nfiltsym-1; + if (phase_est) + offset += floor(Nps/2); + end + st_tx = offset*bps+1; + st_s = offset; + + detected_data = ldpc_dec(code_param, max_iterations, demod_type, decoder_type, ... + s_data_buffer(st_s+1:st_s+Nsymb), min(100,EsNo)); + + % ignore first frame as filter, phase est memories filling up + if nn != 1 + error_positions = xor( detected_data(1:framesize*rate), ... + tx_data_buffer(st_tx:st_tx+framesize*rate-1) ); + Nerrs = sum(error_positions); + if Nerrs + Ferrsldpc++; + end + Terrsldpc += Nerrs; + Tbitsldpc += framesize*rate; + end + end + + end + + TERvec(ne) = Terrs; + BERvec(ne) = Terrs/Tbits; + if ldpc_code + TERldpcvec(ne) = Terrsldpc; + FERldpcvec(ne) = Ferrsldpc; + BERldpcvec(ne) = Terrsldpc/Tbitsldpc; + end + + if verbose + printf("EsNo (dB): %f Terrs: %d BER %f BER theory %f", Es, Terrs, + Terrs/Tbits, 0.5*erfc(sqrt(EsNo/2))); + if ldpc_code + printf(" LDPC: Terrs: %d BER: %f Ferrs: %d FER: %f", + Terrsldpc, Terrsldpc/Tbitsldpc, Ferrsldpc, Ferrsldpc/(Ntrials-1)); + end + printf("\n"); + end + if verbose > 1 + printf("Terrs: %d BER %f BER theory %f C %f N %f Es %f No %f Es/No %f\n\n", Terrs, + Terrs/Tbits, 0.5*erfc(sqrt(EsNo/2)), var(tx_filt_log), var(noise_log), + var(tx_filt_log)/Rs, var(noise_log)/Fs, (var(tx_filt_log)/Rs)/(var(noise_log)/Fs)); + end + end + + Ebvec = Esvec - 10*log10(bps); + sim_out.BERvec = BERvec; + sim_out.Ebvec = Ebvec; + sim_out.TERvec = TERvec; + if ldpc_code + sim_out.BERldpcvec = BERldpcvec; + sim_out.TERldpcvec = TERldpcvec; + sim_out.FERldpcvec = FERldpcvec; + end + + if plot_scatter + figure(2); + clf; + scat = rx_symb_log(2*Nfiltsym:length(rx_symb_log)) .* exp(j*pi/4); + plot(real(scat), imag(scat),'+'); + + figure(3); + clf; + + subplot(211) + plot(C_log); + subplot(212) + plot(hf_angle_log); + axis([1 10000 min(hf_angle_log) max(hf_angle_log)]) + end +endfunction + +% Gray coded QPSK modulation function + +function symbol = qpsk_mod(two_bits) + two_bits_decimal = sum(two_bits .* [2 1]); + switch(two_bits_decimal) + case (0) symbol = 1; + case (1) symbol = j; + case (2) symbol = -j; + case (3) symbol = -1; + endswitch +endfunction + +% Gray coded QPSK demodulation function + +function two_bits = qpsk_demod(symbol) + if isscalar(symbol) == 0 + printf("only works with scalars\n"); + return; + end + bit0 = real(symbol*exp(j*pi/4)) < 0; + bit1 = imag(symbol*exp(j*pi/4)) < 0; + two_bits = [bit1 bit0]; +endfunction + +% Start simulations --------------------------------------- + +more off; +sim_in.verbose = 2; + +sim_in.Esvec = 5; +sim_in.Ntrials = 100; +sim_in.framesize = 100; +sim_in.Rs = 400; +sim_in.phase_offset = 0; +sim_in.phase_est = 0; +sim_in.w_offset = 0; +sim_in.plot_scatter = 1; +sim_in.hf_delay_ms = 2; +sim_in.hf_sim = 1; +sim_in.Np = 6; +sim_in.Ns = 5; +sim_in.hf_phase_only = 0; +sim_in.hf_mag_only = 1; +sim_in.ldpc_code = 0; + +Ebvec = sim_in.Esvec - 10*log10(2); +BER_theory = 0.5*erfc(sqrt(10.^(Ebvec/10))); + +sim_qpsk = ber_test(sim_in, 'qpsk'); + +figure(1); +clf; +semilogy(Ebvec, BER_theory,'r;QPSK theory;') +hold on; +semilogy(sim_qpsk.Ebvec, sim_qpsk.BERvec,'g;QPSK;') +hold off; +xlabel('Eb/N0') +ylabel('BER') +grid("minor") + + +if 0 +sim_in.hf_mag_only = 1; +sim_qpsk_mag = ber_test(sim_in, 'qpsk'); + +sim_in.hf_mag_only = 0; +sim_in.hf_phase_only = 1; +sim_in.phase_est = 1; +sim_qpsk_phase = ber_test(sim_in, 'qpsk'); + +sim_in.hf_phase_only = 0; +sim_qpsk_coh_6_5 = ber_test(sim_in, 'qpsk'); + +sim_in.phase_est = 0; +sim_dqpsk = ber_test(sim_in, 'dqpsk'); + +figure(1); +clf; +semilogy(Ebvec, BER_theory,'r;QPSK theory;') +hold on; +semilogy(sim_qpsk_mag.Ebvec, sim_qpsk_mag.BERvec,'g;QPSK CCIR poor mag;') +semilogy(sim_qpsk_phase.Ebvec, sim_qpsk_phase.BERvec,'k;QPSK CCIR poor phase;') +semilogy(sim_qpsk_coh_6_5.Ebvec, sim_qpsk_coh_6_5.BERvec,'c;QPSK CCIR poor Np=6 Ns=5;') +semilogy(sim_dqpsk.Ebvec, sim_dqpsk.BERvec,'b;DQPSK CCIR poor;') +%semilogy(sim_qpsk_coh_5_24.Ebvec, sim_qpsk_coh_5_24.BERvec,'k;QPSK Ns=5 Np=24;') +%semilogy(sim_qpsk_coh_2_12.Ebvec, sim_qpsk_coh_2_12.BERvec,'c;QPSK Ns=2 Np=12;') +hold off; +xlabel('Eb/N0') +ylabel('BER') +grid("minor") +axis([min(Ebvec)-1 max(Ebvec)+1 1E-2 1]) +end diff --git a/codec2-dev/octave/test_qpsk2.m b/codec2-dev/octave/test_qpsk2.m index dbb43cbf..978abfa9 100644 --- a/codec2-dev/octave/test_qpsk2.m +++ b/codec2-dev/octave/test_qpsk2.m @@ -1,641 +1,641 @@ -% test_qps2k.m -% David Rowe Feb 2014 -% -% QPSK modem simulation, version 2. Simplifed version of -% test_qpsk. initially based on code by Bill Cowley Generates curves -% BER versus E/No curves for different modems. Design to test -% coherent demodulation ideas on HF channels without building a full -% blown modem. Uses 'genie provided' estimates for timing estimation, -% frame sync. - -1; - -% main test function - -function sim_out = ber_test(sim_in, modulation) - Fs = 8000; - - verbose = sim_in.verbose; - framesize = sim_in.framesize; - Ntrials = sim_in.Ntrials; - Esvec = sim_in.Esvec; - phase_offset = sim_in.phase_offset; - phase_est = sim_in.phase_est; - w_offset = sim_in.w_offset; - plot_scatter = sim_in.plot_scatter; - Rs = sim_in.Rs; - hf_sim = sim_in.hf_sim; - nhfdelay = sim_in.hf_delay_ms*Rs/1000; - hf_phase_only = sim_in.hf_phase_only; - hf_mag_only = sim_in.hf_mag_only; - Nc = sim_in.Nc; - - bps = 2; - Nsymb = framesize/bps; - prev_sym_tx = qpsk_mod([0 0]); - prev_sym_rx = qpsk_mod([0 0]); - - phase_est_method = sim_in.phase_est_method; - if phase_est_method == 1 - Nps = sim_in.Np; - else - Np = sim_in.Np; - Ns = sim_in.Ns; - if Np/2 == floor(Np/2) - printf("Np must be odd\n"); - return; - end - Nps = (Np-1)*Ns+1; - end - r_delay_line = zeros(Nc, Nps); - s_delay_line = zeros(Nc, Nps); - ph_est_log = []; - - phase_noise_amp = sim_in.phase_noise_amp; - - ldpc_code = sim_in.ldpc_code; - - tx_bits_buf = zeros(1,2*framesize); - rx_bits_buf = zeros(1,2*framesize); - rx_symb_buf = zeros(1,2*Nsymb); - - % Init LDPC -------------------------------------------------------------------- - - if ldpc_code - % Start CML library - - currentdir = pwd; - addpath '/home/david/tmp/cml/mat' % assume the source files stored here - cd /home/david/tmp/cml - CmlStartup % note that this is not in the cml path! - cd(currentdir) - - % Our LDPC library - - ldpc; - - rate = sim_in.ldpc_code_rate; - mod_order = 4; - modulation = 'QPSK'; - mapping = 'gray'; - - demod_type = 0; - decoder_type = 0; - max_iterations = 100; - - code_param = ldpc_init(rate, framesize, modulation, mod_order, mapping); - code_param.code_bits_per_frame = framesize; - code_param.symbols_per_frame = framesize/bps; - else - rate = 1; - end - - % Init HF channel model from stored sample files of spreading signal ---------------------------------- - - % convert "spreading" samples from 1kHz carrier at Fs to complex - % baseband, generated by passing a 1kHz sine wave through PathSim - % with the ccir-poor model, enabling one path at a time. - - Fc = 1000; M = Fs/Rs; - fspread = fopen("../raw/sine1k_2Hz_spread.raw","rb"); - spread1k = fread(fspread, "int16")/10000; - fclose(fspread); - fspread = fopen("../raw/sine1k_2ms_delay_2Hz_spread.raw","rb"); - spread1k_2ms = fread(fspread, "int16")/10000; - fclose(fspread); - - % down convert to complex baseband - spreadbb = spread1k.*exp(-j*(2*pi*Fc/Fs)*(1:length(spread1k))'); - spreadbb_2ms = spread1k_2ms.*exp(-j*(2*pi*Fc/Fs)*(1:length(spread1k_2ms))'); - - % remove -2000 Hz image - b = fir1(50, 5/Fs); - spread = filter(b,1,spreadbb); - spread_2ms = filter(b,1,spreadbb_2ms); - - % discard first 1000 samples as these were near 0, probably as - % PathSim states were ramping up - - spread = spread(1000:length(spread)); - spread_2ms = spread_2ms(1000:length(spread_2ms)); - - % decimate down to Rs - - spread = spread(1:M:length(spread)); - spread_2ms = spread_2ms(1:M:length(spread_2ms)); - - % Determine "gain" of HF channel model, so we can normalise - % carrier power during HF channel sim to calibrate SNR. I imagine - % different implementations of ccir-poor would do this in - % different ways, leading to different BER results. Oh Well! - - hf_gain = 1.0/sqrt(var(spread)+var(spread_2ms)); - - % Start Simulation ---------------------------------------------------------------- - - for ne = 1:length(Esvec) - EsNodB = Esvec(ne); - EsNo = 10^(EsNodB/10); - - variance = 1/EsNo; - if verbose > 1 - printf("EsNo (dB): %f EsNo: %f variance: %f\n", EsNodB, EsNo, variance); - end - - Terrs = 0; Tbits = 0; Terrsldpc = 0; Tbitsldpc = 0; Ferrsldpc = 0; - - tx_symb_log = []; - rx_symb_log = []; - noise_log = []; - mod_strip_log = []; - - % init HF channel - - hf_n = 1; - hf_angle_log = []; - hf_fading = ones(1,Nsymb); % default input for ldpc dec - hf_model = ones(Ntrials*Nsymb/Nc, Nc); % defaults for plotting surface - - for nn = 1: Ntrials - - tx_bits = round( rand( 1, framesize*rate ) ); - - % modulate -------------------------------------------- - - if ldpc_code - [tx_bits, s] = ldpc_enc(tx_bits, code_param); - else - s = zeros(1, Nsymb); - for i=1:Nsymb - tx_symb = qpsk_mod(tx_bits(2*(i-1)+1:2*i)); - if strcmp(modulation,'dqpsk') - tx_symb *= prev_sym_tx; - prev_sym_tx = tx_symb; - end - s(i) = tx_symb; - end - end - tx_bits_buf(1:framesize) = tx_bits_buf(framesize+1:2*framesize); - tx_bits_buf(framesize+1:2*framesize) = tx_bits; - s_ch = s; - - % HF channel simulation ------------------------------------ - - if hf_sim - - % separation between carriers. Note this is - % effectively under samples at Rs, I dont think this - % matters. Equivalent to doing freq shift at Fs, then - % decimating to Rs. - - wsep = 2*pi*(1+0.5); % e.g. 75Hz spacing at Rs=50Hz, alpha=0.5 filters - - if Nsymb/Nc != floor(Nsymb/Nc) - printf("Error: Nsymb/Nc must be an integrer\n") - return; - end - - % arrange symbols in Nsymb/Nc by Nc matrix - - for i=1:Nc:Nsymb - - % Determine HF channel at each carrier for this symbol - - for k=1:Nc - hf_model(hf_n, k) = hf_gain*(spread(hf_n) + exp(-j*k*wsep*nhfdelay)*spread_2ms(hf_n)); - hf_fading(i+k-1) = abs(hf_model(hf_n, k)); - if hf_mag_only - s_ch(i+k-1) *= abs(hf_model(hf_n, k)); - else - s_ch(i+k-1) *= hf_model(hf_n, k); - end - end - hf_n++; - end - end - - tx_symb_log = [tx_symb_log s_ch]; - - % AWGN noise and phase/freq offset channel simulation - % 0.5 factor ensures var(noise) == variance , i.e. splits power between Re & Im - - noise = sqrt(variance*0.5)*(randn(1,Nsymb) + j*randn(1,Nsymb)); - noise_log = [noise_log noise]; - phase_noise = phase_noise_amp*(2.0*rand(1,Nsymb)-1.0); - - % organise into carriers to apply frequency and phase offset - - for i=1:Nc:Nsymb - for k=1:Nc - s_ch(i+k-1) = s_ch(i+k-1)*exp(j*(phase_offset+phase_noise(i+k-1))) + noise(i+k-1); - end - phase_offset += w_offset; - end - - % phase estimation - - ph_est = zeros(Nc,1); - - if phase_est - - % organise into carriers - - for i=1:Nc:Nsymb - - for k=1:Nc - centre = floor(Nps/2)+1; - - % delay line for phase est window - - r_delay_line(k,1:Nps-1) = r_delay_line(k,2:Nps); - r_delay_line(k,Nps) = s_ch(i+k-1); - - % delay in tx data to compensate data for phase est window - - s_delay_line(k,1:Nps-1) = s_delay_line(k,2:Nps); - s_delay_line(k,Nps) = s(i+k-1); - %tx_bits(2*(i+k-1-1)+1:2*(i+k-1)) = qpsk_demod(s_delay_line(k,centre)); - - if phase_est_method == 1 - % QPSK modulation strip and phase est - - mod_strip_pol = angle(r_delay_line(k,:)) * 4; - mod_strip_rect = exp(j*mod_strip_pol); - - ph_est_pol = atan2(sum(imag(mod_strip_rect)),sum(real(mod_strip_rect)))/4; - ph_est(k) = exp(j*ph_est_pol); - - s_ch(i+k-1) = r_delay_line(k,centre).*exp(-j*ph_est_pol); - else - - % estimate phase from surrounding known pilot symbols and correct - - corr = 0; - for m=1:Ns:Nps - if (m != centre) - corr += s_delay_line(k,m) * r_delay_line(k,m)'; - end - end - ph_est(k) = conj(corr/(1E-6+abs(corr))); - s_ch(i+k-1) = r_delay_line(k,centre).*exp(j*angle(corr)); - end - - end - - ph_est_log = [ph_est_log ph_est]; - end - %printf("corr: %f angle: %f\n", corr, angle(corr)); - end - - % de-modulate - - rx_bits = zeros(1, framesize); - for i=1:Nsymb - rx_symb = s_ch(i); - if strcmp(modulation,'dqpsk') - tmp = rx_symb; - rx_symb *= conj(prev_sym_rx/abs(prev_sym_rx)); - prev_sym_rx = tmp; - end - rx_bits((2*(i-1)+1):(2*i)) = qpsk_demod(rx_symb); - rx_symb_log = [rx_symb_log rx_symb]; - end - - rx_bits_buf(1:framesize) = rx_bits_buf(framesize+1:2*framesize); - rx_bits_buf(framesize+1:2*framesize) = rx_bits; - rx_symb_buf(1:Nsymb) = rx_symb_buf(Nsymb+1:2*Nsymb); - rx_symb_buf(Nsymb+1:2*Nsymb) = s_ch; - - % determine location of start and end of frame depending on processing delays - - if phase_est - st_rx_bits = 1+(floor(Nps/2)+1-1)*Nc*2; - st_rx_symb = 1+(floor(Nps/2)+1-1)*Nc; - else - st_rx_bits = 1; - st_rx_symb = 1; - end - en_rx_bits = st_rx_bits+framesize-1; - en_rx_symb = st_rx_symb+Nsymb-1; - - if nn > 1 - % Measure BER - - %printf("nn: %d centre: %d\n", nn, floor(Nps/2)+1); - %tx_bits_buf(1:20) - %rx_bits_buf(st_rx_bits:st_rx_bits+20-1) - error_positions = xor(rx_bits_buf(st_rx_bits:en_rx_bits), tx_bits_buf(1:framesize)); - Nerrs = sum(error_positions); - Terrs += Nerrs; - Tbits += length(tx_bits); - - % Optionally LDPC decode - - if ldpc_code - detected_data = ldpc_dec(code_param, max_iterations, demod_type, decoder_type, rx_symb_buf(st_rx_symb:en_rx_symb), min(100,EsNo), hf_fading); - %for m=1:20 - % printf("%f ", qpsk_demod(rx_symb_buf(m))); - %end - %detected_data(1:19) - error_positions = xor( detected_data(1:framesize*rate), tx_bits_buf(1:framesize*rate) ); - Nerrs = sum(error_positions); - if Nerrs - Ferrsldpc++; - end - Terrsldpc += Nerrs; - Tbitsldpc += framesize*rate; - end - end - end - - TERvec(ne) = Terrs; - BERvec(ne) = Terrs/Tbits; - if ldpc_code - TERldpcvec(ne) = Terrsldpc; - FERldpcvec(ne) = Ferrsldpc; - BERldpcvec(ne) = Terrsldpc/Tbitsldpc; - end - - if verbose - printf("EsNo (dB): %f Terrs: %d BER %f BER theory %f", EsNodB, Terrs, - Terrs/Tbits, 0.5*erfc(sqrt(EsNo/2))); - if ldpc_code - printf(" LDPC: Terrs: %d BER: %f Ferrs: %d FER: %f", - Terrsldpc, Terrsldpc/Tbitsldpc, Ferrsldpc, Ferrsldpc/(Ntrials-1)); - end - printf("\n"); - end - if verbose > 1 - printf("Terrs: %d BER %f BER theory %f C %f N %f Es %f No %f Es/No %f\n\n", Terrs, - Terrs/Tbits, 0.5*erfc(sqrt(EsNo/2)), var(tx_symb_log), var(noise_log), - var(tx_symb_log), var(noise_log), var(tx_symb_log)/var(noise_log)); - end - end - - Ebvec = Esvec - 10*log10(bps); - sim_out.BERvec = BERvec; - sim_out.Ebvec = Ebvec; - sim_out.TERvec = TERvec; - if ldpc_code - sim_out.BERldpcvec = BERldpcvec; - sim_out.TERldpcvec = TERldpcvec; - sim_out.FERldpcvec = FERldpcvec; - end - - if plot_scatter - figure(2); - clf; - scat = rx_symb_log .* exp(j*pi/4); - plot(real(scat(Nps*Nc:length(scat))), imag(scat(Nps*Nc:length(scat))),'+'); - title('Scatter plot'); - - figure(3); - clf; - - y = 1:Rs*2; - x = 1:Nc; - EsNodBSurface = 20*log10(abs(hf_model(y,:))) - 10*log10(variance); - mesh(x,y,EsNodBSurface); - grid - %axis([1 Nc 1 Rs*2 -10 10]) - title('HF Channel Es/No'); - - figure(4); - clf; - %mesh(x,y,unwrap(angle(hf_model(y,:)))); - subplot(211) - plot(y,abs(hf_model(y,1))) - title('HF Channel Carrier 1 Mag'); - subplot(212) - plot(y,angle(hf_model(y,1))) - title('HF Channel Carrier 1 Phase'); - - if phase_est - scat = ph_est_log(1,floor(Nps/2):Rs*2+floor(Nps/2)-1); - hold on; - plot(angle(scat),'r'); - hold off; - - figure(5) - clf; - scat = ph_est_log(1,y); - plot(real(scat), imag(scat),'+'); - title('Carrier 1 Phase Est'); - axis([-1 1 -1 1]) - end -if 0 - figure(5); - clf; - subplot(211) - plot(real(spread)); - hold on; - plot(imag(spread),'g'); - hold off; - subplot(212) - plot(real(spread_2ms)); - hold on; - plot(imag(spread_2ms),'g'); - hold off; - - figure(6) - tmp = []; - for i = 1:hf_n-1 - tmp = [tmp abs(hf_model(i,:))]; - end - hist(tmp); -end - end - -endfunction - -% Gray coded QPSK modulation function - -function symbol = qpsk_mod(two_bits) - two_bits_decimal = sum(two_bits .* [2 1]); - switch(two_bits_decimal) - case (0) symbol = 1; - case (1) symbol = j; - case (2) symbol = -j; - case (3) symbol = -1; - endswitch -endfunction - -% Gray coded QPSK demodulation function - -function two_bits = qpsk_demod(symbol) - if isscalar(symbol) == 0 - printf("only works with scalars\n"); - return; - end - bit0 = real(symbol*exp(j*pi/4)) < 0; - bit1 = imag(symbol*exp(j*pi/4)) < 0; - two_bits = [bit1 bit0]; -endfunction - -function sim_in = standard_init - sim_in.verbose = 1; - sim_in.plot_scatter = 0; - - sim_in.Esvec = 5; - sim_in.Ntrials = 30; - sim_in.framesize = 576; - sim_in.Rs = 100; - sim_in.Nc = 8; - - sim_in.phase_offset = 0; - sim_in.w_offset = 0; - sim_in.phase_noise_amp = 0; - - sim_in.hf_delay_ms = 2; - sim_in.hf_sim = 0; - sim_in.hf_phase_only = 0; - sim_in.hf_mag_only = 1; - - sim_in.phase_est = 0; - sim_in.phase_est_method = 1; - sim_in.Np = 5; - sim_in.Ns = 5; - - sim_in.ldpc_code_rate = 1/2; - sim_in.ldpc_code = 1; -endfunction - -function ideal - - sim_in = standard_init(); - - sim_in.verbose = 1; - sim_in.plot_scatter = 1; - - sim_in.Esvec = 5; - sim_in.hf_sim = 1; - sim_in.Ntrials = 30; - - sim_qpsk_hf = ber_test(sim_in, 'qpsk'); - - sim_in.hf_sim = 0; - sim_in.plot_scatter = 0; - sim_in.Esvec = 2:10; - sim_in.ldpc_code = 0; - Ebvec = sim_in.Esvec - 10*log10(2); - BER_theory = 0.5*erfc(sqrt(10.^(Ebvec/10))); - sim_qpsk = ber_test(sim_in, 'qpsk'); - sim_dqpsk = ber_test(sim_in, 'dqpsk'); - - sim_in.hf_sim = 1; - sim_qpsk_hf = ber_test(sim_in, 'qpsk'); - sim_dqpsk_hf = ber_test(sim_in, 'dqpsk'); - sim_in.ldpc_code_rate = 1/2; - sim_in.ldpc_code = 1; - - sim_in.hf_sim = 0; - sim_qpsk_ldpc = ber_test(sim_in, 'qpsk'); - - sim_in.hf_sim = 1; - sim_qpsk_hf_ldpc = ber_test(sim_in, 'qpsk'); - sim_in.hf_mag_only = 0; - sim_dqpsk_hf_ldpc = ber_test(sim_in, 'dqpsk'); - - figure(1); - clf; - semilogy(Ebvec, BER_theory,'r;QPSK theory;') - hold on; - semilogy(sim_qpsk.Ebvec, sim_qpsk.BERvec,'g;QPSK AWGN;') - semilogy(sim_qpsk_hf.Ebvec, sim_qpsk_hf.BERvec,'r;QPSK HF;') - semilogy(sim_qpsk_ldpc.Ebvec, sim_qpsk_ldpc.BERldpcvec,'bk;QPSK HF;') - semilogy(sim_dqpsk.Ebvec, sim_dqpsk.BERvec,'g;DQPSK AWGN;') - semilogy(sim_dqpsk_hf.Ebvec, sim_dqpsk_hf.BERvec,'r;DQPSK HF;') - semilogy(sim_qpsk_hf_ldpc.Ebvec, sim_qpsk_hf_ldpc.BERldpcvec,'b;QPSK HF LDPC 1/2;') - semilogy(sim_dqpsk_hf_ldpc.Ebvec, sim_dqpsk_hf_ldpc.BERldpcvec,'b;DQPSK HF LDPC 1/2;') - - hold off; - xlabel('Eb/N0') - ylabel('BER') - grid("minor") - axis([min(Ebvec) max(Ebvec) 1E-3 1]) -endfunction - -function phase_noise - sim_in = standard_init(); - - sim_in.verbose = 1; - sim_in.plot_scatter = 1; - - sim_in.Esvec = 100; - sim_in.Ntrials = 30; - - sim_in.ldpc_code_rate = 1/2; - sim_in.ldpc_code = 1; - - sim_in.phase_noise_amp = pi/16; - tmp = ber_test(sim_in, 'qpsk'); - - sim_in.plot_scatter = 0; - sim_in.Esvec = 2:8; - sim_qpsk_hf = ber_test(sim_in, 'qpsk'); - - Ebvec = sim_in.Esvec - 10*log10(2); - BER_theory = 0.5*erfc(sqrt(10.^(Ebvec/10))); - - sim_in.phase_noise_amp = 0; - sim_qpsk = ber_test(sim_in, 'qpsk'); - sim_in.phase_noise_amp = pi/8; - sim_qpsk_pn8 = ber_test(sim_in, 'qpsk'); - sim_in.phase_noise_amp = pi/16; - sim_qpsk_pn16 = ber_test(sim_in, 'qpsk'); - sim_in.phase_noise_amp = pi/32; - sim_qpsk_pn32 = ber_test(sim_in, 'qpsk'); - - figure(1); - clf; - semilogy(sim_qpsk.Ebvec, sim_qpsk.BERvec,'g;QPSK phase noise 0;') - hold on; - semilogy(sim_qpsk_pn8.Ebvec, sim_qpsk_pn8.BERvec,'c;QPSK phase noise +/- pi/8;') - semilogy(sim_qpsk_pn16.Ebvec, sim_qpsk_pn16.BERvec,'b;QPSK phase noise +/- pi/16;') - semilogy(sim_qpsk_pn32.Ebvec, sim_qpsk_pn32.BERvec,'k;QPSK phase noise +/- pi/32;') - - semilogy(sim_qpsk.Ebvec, sim_qpsk.BERldpcvec,'g;QPSK phase noise 0 ldpc;') - semilogy(sim_qpsk_pn8.Ebvec, sim_qpsk_pn8.BERldpcvec,'c;QPSK phase noise +/- pi/8 ldpc;') - semilogy(sim_qpsk_pn16.Ebvec, sim_qpsk_pn16.BERldpcvec,'b;QPSK phase noise +/- pi/16 ldpc;') - semilogy(sim_qpsk_pn32.Ebvec, sim_qpsk_pn32.BERldpcvec,'k;QPSK phase noise +/- pi/32 ldpc;') - - hold off; - xlabel('Eb/N0') - ylabel('BER') - grid("minor") - axis([min(Ebvec) max(Ebvec) 1E-2 1]) -endfunction - -function test_phase_est - sim_in = standard_init(); - - sim_in.Rs = 100; - sim_in.Nc = 8; - - sim_in.verbose = 1; - sim_in.plot_scatter = 1; - - sim_in.Esvec = 5; - sim_in.Ntrials = 30; - - sim_in.ldpc_code_rate = 1/2; - sim_in.ldpc_code = 1; - - sim_in.phase_est = 1; - sim_in.phase_est_method = 2; - sim_in.Np = 3; - sim_in.phase_offset = 0; - sim_in.w_offset = 0; - - sim_in.hf_sim = 1; - sim_in.hf_mag_only = 0; - - tmp = ber_test(sim_in, 'qpsk'); - -endfunction - -% Start simulations --------------------------------------- - -more off; - -ideal(); +% test_qps2k.m +% David Rowe Feb 2014 +% +% QPSK modem simulation, version 2. Simplifed version of +% test_qpsk. initially based on code by Bill Cowley Generates curves +% BER versus E/No curves for different modems. Design to test +% coherent demodulation ideas on HF channels without building a full +% blown modem. Uses 'genie provided' estimates for timing estimation, +% frame sync. + +1; + +% main test function + +function sim_out = ber_test(sim_in, modulation) + Fs = 8000; + + verbose = sim_in.verbose; + framesize = sim_in.framesize; + Ntrials = sim_in.Ntrials; + Esvec = sim_in.Esvec; + phase_offset = sim_in.phase_offset; + phase_est = sim_in.phase_est; + w_offset = sim_in.w_offset; + plot_scatter = sim_in.plot_scatter; + Rs = sim_in.Rs; + hf_sim = sim_in.hf_sim; + nhfdelay = sim_in.hf_delay_ms*Rs/1000; + hf_phase_only = sim_in.hf_phase_only; + hf_mag_only = sim_in.hf_mag_only; + Nc = sim_in.Nc; + + bps = 2; + Nsymb = framesize/bps; + prev_sym_tx = qpsk_mod([0 0]); + prev_sym_rx = qpsk_mod([0 0]); + + phase_est_method = sim_in.phase_est_method; + if phase_est_method == 1 + Nps = sim_in.Np; + else + Np = sim_in.Np; + Ns = sim_in.Ns; + if Np/2 == floor(Np/2) + printf("Np must be odd\n"); + return; + end + Nps = (Np-1)*Ns+1; + end + r_delay_line = zeros(Nc, Nps); + s_delay_line = zeros(Nc, Nps); + ph_est_log = []; + + phase_noise_amp = sim_in.phase_noise_amp; + + ldpc_code = sim_in.ldpc_code; + + tx_bits_buf = zeros(1,2*framesize); + rx_bits_buf = zeros(1,2*framesize); + rx_symb_buf = zeros(1,2*Nsymb); + + % Init LDPC -------------------------------------------------------------------- + + if ldpc_code + % Start CML library + + currentdir = pwd; + addpath '/home/david/tmp/cml/mat' % assume the source files stored here + cd /home/david/tmp/cml + CmlStartup % note that this is not in the cml path! + cd(currentdir) + + % Our LDPC library + + ldpc; + + rate = sim_in.ldpc_code_rate; + mod_order = 4; + modulation = 'QPSK'; + mapping = 'gray'; + + demod_type = 0; + decoder_type = 0; + max_iterations = 100; + + code_param = ldpc_init(rate, framesize, modulation, mod_order, mapping); + code_param.code_bits_per_frame = framesize; + code_param.symbols_per_frame = framesize/bps; + else + rate = 1; + end + + % Init HF channel model from stored sample files of spreading signal ---------------------------------- + + % convert "spreading" samples from 1kHz carrier at Fs to complex + % baseband, generated by passing a 1kHz sine wave through PathSim + % with the ccir-poor model, enabling one path at a time. + + Fc = 1000; M = Fs/Rs; + fspread = fopen("../raw/sine1k_2Hz_spread.raw","rb"); + spread1k = fread(fspread, "int16")/10000; + fclose(fspread); + fspread = fopen("../raw/sine1k_2ms_delay_2Hz_spread.raw","rb"); + spread1k_2ms = fread(fspread, "int16")/10000; + fclose(fspread); + + % down convert to complex baseband + spreadbb = spread1k.*exp(-j*(2*pi*Fc/Fs)*(1:length(spread1k))'); + spreadbb_2ms = spread1k_2ms.*exp(-j*(2*pi*Fc/Fs)*(1:length(spread1k_2ms))'); + + % remove -2000 Hz image + b = fir1(50, 5/Fs); + spread = filter(b,1,spreadbb); + spread_2ms = filter(b,1,spreadbb_2ms); + + % discard first 1000 samples as these were near 0, probably as + % PathSim states were ramping up + + spread = spread(1000:length(spread)); + spread_2ms = spread_2ms(1000:length(spread_2ms)); + + % decimate down to Rs + + spread = spread(1:M:length(spread)); + spread_2ms = spread_2ms(1:M:length(spread_2ms)); + + % Determine "gain" of HF channel model, so we can normalise + % carrier power during HF channel sim to calibrate SNR. I imagine + % different implementations of ccir-poor would do this in + % different ways, leading to different BER results. Oh Well! + + hf_gain = 1.0/sqrt(var(spread)+var(spread_2ms)); + + % Start Simulation ---------------------------------------------------------------- + + for ne = 1:length(Esvec) + EsNodB = Esvec(ne); + EsNo = 10^(EsNodB/10); + + variance = 1/EsNo; + if verbose > 1 + printf("EsNo (dB): %f EsNo: %f variance: %f\n", EsNodB, EsNo, variance); + end + + Terrs = 0; Tbits = 0; Terrsldpc = 0; Tbitsldpc = 0; Ferrsldpc = 0; + + tx_symb_log = []; + rx_symb_log = []; + noise_log = []; + mod_strip_log = []; + + % init HF channel + + hf_n = 1; + hf_angle_log = []; + hf_fading = ones(1,Nsymb); % default input for ldpc dec + hf_model = ones(Ntrials*Nsymb/Nc, Nc); % defaults for plotting surface + + for nn = 1: Ntrials + + tx_bits = round( rand( 1, framesize*rate ) ); + + % modulate -------------------------------------------- + + if ldpc_code + [tx_bits, s] = ldpc_enc(tx_bits, code_param); + else + s = zeros(1, Nsymb); + for i=1:Nsymb + tx_symb = qpsk_mod(tx_bits(2*(i-1)+1:2*i)); + if strcmp(modulation,'dqpsk') + tx_symb *= prev_sym_tx; + prev_sym_tx = tx_symb; + end + s(i) = tx_symb; + end + end + tx_bits_buf(1:framesize) = tx_bits_buf(framesize+1:2*framesize); + tx_bits_buf(framesize+1:2*framesize) = tx_bits; + s_ch = s; + + % HF channel simulation ------------------------------------ + + if hf_sim + + % separation between carriers. Note this is + % effectively under samples at Rs, I dont think this + % matters. Equivalent to doing freq shift at Fs, then + % decimating to Rs. + + wsep = 2*pi*(1+0.5); % e.g. 75Hz spacing at Rs=50Hz, alpha=0.5 filters + + if Nsymb/Nc != floor(Nsymb/Nc) + printf("Error: Nsymb/Nc must be an integrer\n") + return; + end + + % arrange symbols in Nsymb/Nc by Nc matrix + + for i=1:Nc:Nsymb + + % Determine HF channel at each carrier for this symbol + + for k=1:Nc + hf_model(hf_n, k) = hf_gain*(spread(hf_n) + exp(-j*k*wsep*nhfdelay)*spread_2ms(hf_n)); + hf_fading(i+k-1) = abs(hf_model(hf_n, k)); + if hf_mag_only + s_ch(i+k-1) *= abs(hf_model(hf_n, k)); + else + s_ch(i+k-1) *= hf_model(hf_n, k); + end + end + hf_n++; + end + end + + tx_symb_log = [tx_symb_log s_ch]; + + % AWGN noise and phase/freq offset channel simulation + % 0.5 factor ensures var(noise) == variance , i.e. splits power between Re & Im + + noise = sqrt(variance*0.5)*(randn(1,Nsymb) + j*randn(1,Nsymb)); + noise_log = [noise_log noise]; + phase_noise = phase_noise_amp*(2.0*rand(1,Nsymb)-1.0); + + % organise into carriers to apply frequency and phase offset + + for i=1:Nc:Nsymb + for k=1:Nc + s_ch(i+k-1) = s_ch(i+k-1)*exp(j*(phase_offset+phase_noise(i+k-1))) + noise(i+k-1); + end + phase_offset += w_offset; + end + + % phase estimation + + ph_est = zeros(Nc,1); + + if phase_est + + % organise into carriers + + for i=1:Nc:Nsymb + + for k=1:Nc + centre = floor(Nps/2)+1; + + % delay line for phase est window + + r_delay_line(k,1:Nps-1) = r_delay_line(k,2:Nps); + r_delay_line(k,Nps) = s_ch(i+k-1); + + % delay in tx data to compensate data for phase est window + + s_delay_line(k,1:Nps-1) = s_delay_line(k,2:Nps); + s_delay_line(k,Nps) = s(i+k-1); + %tx_bits(2*(i+k-1-1)+1:2*(i+k-1)) = qpsk_demod(s_delay_line(k,centre)); + + if phase_est_method == 1 + % QPSK modulation strip and phase est + + mod_strip_pol = angle(r_delay_line(k,:)) * 4; + mod_strip_rect = exp(j*mod_strip_pol); + + ph_est_pol = atan2(sum(imag(mod_strip_rect)),sum(real(mod_strip_rect)))/4; + ph_est(k) = exp(j*ph_est_pol); + + s_ch(i+k-1) = r_delay_line(k,centre).*exp(-j*ph_est_pol); + else + + % estimate phase from surrounding known pilot symbols and correct + + corr = 0; + for m=1:Ns:Nps + if (m != centre) + corr += s_delay_line(k,m) * r_delay_line(k,m)'; + end + end + ph_est(k) = conj(corr/(1E-6+abs(corr))); + s_ch(i+k-1) = r_delay_line(k,centre).*exp(j*angle(corr)); + end + + end + + ph_est_log = [ph_est_log ph_est]; + end + %printf("corr: %f angle: %f\n", corr, angle(corr)); + end + + % de-modulate + + rx_bits = zeros(1, framesize); + for i=1:Nsymb + rx_symb = s_ch(i); + if strcmp(modulation,'dqpsk') + tmp = rx_symb; + rx_symb *= conj(prev_sym_rx/abs(prev_sym_rx)); + prev_sym_rx = tmp; + end + rx_bits((2*(i-1)+1):(2*i)) = qpsk_demod(rx_symb); + rx_symb_log = [rx_symb_log rx_symb]; + end + + rx_bits_buf(1:framesize) = rx_bits_buf(framesize+1:2*framesize); + rx_bits_buf(framesize+1:2*framesize) = rx_bits; + rx_symb_buf(1:Nsymb) = rx_symb_buf(Nsymb+1:2*Nsymb); + rx_symb_buf(Nsymb+1:2*Nsymb) = s_ch; + + % determine location of start and end of frame depending on processing delays + + if phase_est + st_rx_bits = 1+(floor(Nps/2)+1-1)*Nc*2; + st_rx_symb = 1+(floor(Nps/2)+1-1)*Nc; + else + st_rx_bits = 1; + st_rx_symb = 1; + end + en_rx_bits = st_rx_bits+framesize-1; + en_rx_symb = st_rx_symb+Nsymb-1; + + if nn > 1 + % Measure BER + + %printf("nn: %d centre: %d\n", nn, floor(Nps/2)+1); + %tx_bits_buf(1:20) + %rx_bits_buf(st_rx_bits:st_rx_bits+20-1) + error_positions = xor(rx_bits_buf(st_rx_bits:en_rx_bits), tx_bits_buf(1:framesize)); + Nerrs = sum(error_positions); + Terrs += Nerrs; + Tbits += length(tx_bits); + + % Optionally LDPC decode + + if ldpc_code + detected_data = ldpc_dec(code_param, max_iterations, demod_type, decoder_type, rx_symb_buf(st_rx_symb:en_rx_symb), min(100,EsNo), hf_fading); + %for m=1:20 + % printf("%f ", qpsk_demod(rx_symb_buf(m))); + %end + %detected_data(1:19) + error_positions = xor( detected_data(1:framesize*rate), tx_bits_buf(1:framesize*rate) ); + Nerrs = sum(error_positions); + if Nerrs + Ferrsldpc++; + end + Terrsldpc += Nerrs; + Tbitsldpc += framesize*rate; + end + end + end + + TERvec(ne) = Terrs; + BERvec(ne) = Terrs/Tbits; + if ldpc_code + TERldpcvec(ne) = Terrsldpc; + FERldpcvec(ne) = Ferrsldpc; + BERldpcvec(ne) = Terrsldpc/Tbitsldpc; + end + + if verbose + printf("EsNo (dB): %f Terrs: %d BER %f BER theory %f", EsNodB, Terrs, + Terrs/Tbits, 0.5*erfc(sqrt(EsNo/2))); + if ldpc_code + printf(" LDPC: Terrs: %d BER: %f Ferrs: %d FER: %f", + Terrsldpc, Terrsldpc/Tbitsldpc, Ferrsldpc, Ferrsldpc/(Ntrials-1)); + end + printf("\n"); + end + if verbose > 1 + printf("Terrs: %d BER %f BER theory %f C %f N %f Es %f No %f Es/No %f\n\n", Terrs, + Terrs/Tbits, 0.5*erfc(sqrt(EsNo/2)), var(tx_symb_log), var(noise_log), + var(tx_symb_log), var(noise_log), var(tx_symb_log)/var(noise_log)); + end + end + + Ebvec = Esvec - 10*log10(bps); + sim_out.BERvec = BERvec; + sim_out.Ebvec = Ebvec; + sim_out.TERvec = TERvec; + if ldpc_code + sim_out.BERldpcvec = BERldpcvec; + sim_out.TERldpcvec = TERldpcvec; + sim_out.FERldpcvec = FERldpcvec; + end + + if plot_scatter + figure(2); + clf; + scat = rx_symb_log .* exp(j*pi/4); + plot(real(scat(Nps*Nc:length(scat))), imag(scat(Nps*Nc:length(scat))),'+'); + title('Scatter plot'); + + figure(3); + clf; + + y = 1:Rs*2; + x = 1:Nc; + EsNodBSurface = 20*log10(abs(hf_model(y,:))) - 10*log10(variance); + mesh(x,y,EsNodBSurface); + grid + %axis([1 Nc 1 Rs*2 -10 10]) + title('HF Channel Es/No'); + + figure(4); + clf; + %mesh(x,y,unwrap(angle(hf_model(y,:)))); + subplot(211) + plot(y,abs(hf_model(y,1))) + title('HF Channel Carrier 1 Mag'); + subplot(212) + plot(y,angle(hf_model(y,1))) + title('HF Channel Carrier 1 Phase'); + + if phase_est + scat = ph_est_log(1,floor(Nps/2):Rs*2+floor(Nps/2)-1); + hold on; + plot(angle(scat),'r'); + hold off; + + figure(5) + clf; + scat = ph_est_log(1,y); + plot(real(scat), imag(scat),'+'); + title('Carrier 1 Phase Est'); + axis([-1 1 -1 1]) + end +if 0 + figure(5); + clf; + subplot(211) + plot(real(spread)); + hold on; + plot(imag(spread),'g'); + hold off; + subplot(212) + plot(real(spread_2ms)); + hold on; + plot(imag(spread_2ms),'g'); + hold off; + + figure(6) + tmp = []; + for i = 1:hf_n-1 + tmp = [tmp abs(hf_model(i,:))]; + end + hist(tmp); +end + end + +endfunction + +% Gray coded QPSK modulation function + +function symbol = qpsk_mod(two_bits) + two_bits_decimal = sum(two_bits .* [2 1]); + switch(two_bits_decimal) + case (0) symbol = 1; + case (1) symbol = j; + case (2) symbol = -j; + case (3) symbol = -1; + endswitch +endfunction + +% Gray coded QPSK demodulation function + +function two_bits = qpsk_demod(symbol) + if isscalar(symbol) == 0 + printf("only works with scalars\n"); + return; + end + bit0 = real(symbol*exp(j*pi/4)) < 0; + bit1 = imag(symbol*exp(j*pi/4)) < 0; + two_bits = [bit1 bit0]; +endfunction + +function sim_in = standard_init + sim_in.verbose = 1; + sim_in.plot_scatter = 0; + + sim_in.Esvec = 5; + sim_in.Ntrials = 30; + sim_in.framesize = 576; + sim_in.Rs = 100; + sim_in.Nc = 8; + + sim_in.phase_offset = 0; + sim_in.w_offset = 0; + sim_in.phase_noise_amp = 0; + + sim_in.hf_delay_ms = 2; + sim_in.hf_sim = 0; + sim_in.hf_phase_only = 0; + sim_in.hf_mag_only = 1; + + sim_in.phase_est = 0; + sim_in.phase_est_method = 1; + sim_in.Np = 5; + sim_in.Ns = 5; + + sim_in.ldpc_code_rate = 1/2; + sim_in.ldpc_code = 1; +endfunction + +function ideal + + sim_in = standard_init(); + + sim_in.verbose = 1; + sim_in.plot_scatter = 1; + + sim_in.Esvec = 5; + sim_in.hf_sim = 1; + sim_in.Ntrials = 30; + + sim_qpsk_hf = ber_test(sim_in, 'qpsk'); + + sim_in.hf_sim = 0; + sim_in.plot_scatter = 0; + sim_in.Esvec = 2:10; + sim_in.ldpc_code = 0; + Ebvec = sim_in.Esvec - 10*log10(2); + BER_theory = 0.5*erfc(sqrt(10.^(Ebvec/10))); + sim_qpsk = ber_test(sim_in, 'qpsk'); + sim_dqpsk = ber_test(sim_in, 'dqpsk'); + + sim_in.hf_sim = 1; + sim_qpsk_hf = ber_test(sim_in, 'qpsk'); + sim_dqpsk_hf = ber_test(sim_in, 'dqpsk'); + sim_in.ldpc_code_rate = 1/2; + sim_in.ldpc_code = 1; + + sim_in.hf_sim = 0; + sim_qpsk_ldpc = ber_test(sim_in, 'qpsk'); + + sim_in.hf_sim = 1; + sim_qpsk_hf_ldpc = ber_test(sim_in, 'qpsk'); + sim_in.hf_mag_only = 0; + sim_dqpsk_hf_ldpc = ber_test(sim_in, 'dqpsk'); + + figure(1); + clf; + semilogy(Ebvec, BER_theory,'r;QPSK theory;') + hold on; + semilogy(sim_qpsk.Ebvec, sim_qpsk.BERvec,'g;QPSK AWGN;') + semilogy(sim_qpsk_hf.Ebvec, sim_qpsk_hf.BERvec,'r;QPSK HF;') + semilogy(sim_qpsk_ldpc.Ebvec, sim_qpsk_ldpc.BERldpcvec,'bk;QPSK HF;') + semilogy(sim_dqpsk.Ebvec, sim_dqpsk.BERvec,'g;DQPSK AWGN;') + semilogy(sim_dqpsk_hf.Ebvec, sim_dqpsk_hf.BERvec,'r;DQPSK HF;') + semilogy(sim_qpsk_hf_ldpc.Ebvec, sim_qpsk_hf_ldpc.BERldpcvec,'b;QPSK HF LDPC 1/2;') + semilogy(sim_dqpsk_hf_ldpc.Ebvec, sim_dqpsk_hf_ldpc.BERldpcvec,'b;DQPSK HF LDPC 1/2;') + + hold off; + xlabel('Eb/N0') + ylabel('BER') + grid("minor") + axis([min(Ebvec) max(Ebvec) 1E-3 1]) +endfunction + +function phase_noise + sim_in = standard_init(); + + sim_in.verbose = 1; + sim_in.plot_scatter = 1; + + sim_in.Esvec = 100; + sim_in.Ntrials = 30; + + sim_in.ldpc_code_rate = 1/2; + sim_in.ldpc_code = 1; + + sim_in.phase_noise_amp = pi/16; + tmp = ber_test(sim_in, 'qpsk'); + + sim_in.plot_scatter = 0; + sim_in.Esvec = 2:8; + sim_qpsk_hf = ber_test(sim_in, 'qpsk'); + + Ebvec = sim_in.Esvec - 10*log10(2); + BER_theory = 0.5*erfc(sqrt(10.^(Ebvec/10))); + + sim_in.phase_noise_amp = 0; + sim_qpsk = ber_test(sim_in, 'qpsk'); + sim_in.phase_noise_amp = pi/8; + sim_qpsk_pn8 = ber_test(sim_in, 'qpsk'); + sim_in.phase_noise_amp = pi/16; + sim_qpsk_pn16 = ber_test(sim_in, 'qpsk'); + sim_in.phase_noise_amp = pi/32; + sim_qpsk_pn32 = ber_test(sim_in, 'qpsk'); + + figure(1); + clf; + semilogy(sim_qpsk.Ebvec, sim_qpsk.BERvec,'g;QPSK phase noise 0;') + hold on; + semilogy(sim_qpsk_pn8.Ebvec, sim_qpsk_pn8.BERvec,'c;QPSK phase noise +/- pi/8;') + semilogy(sim_qpsk_pn16.Ebvec, sim_qpsk_pn16.BERvec,'b;QPSK phase noise +/- pi/16;') + semilogy(sim_qpsk_pn32.Ebvec, sim_qpsk_pn32.BERvec,'k;QPSK phase noise +/- pi/32;') + + semilogy(sim_qpsk.Ebvec, sim_qpsk.BERldpcvec,'g;QPSK phase noise 0 ldpc;') + semilogy(sim_qpsk_pn8.Ebvec, sim_qpsk_pn8.BERldpcvec,'c;QPSK phase noise +/- pi/8 ldpc;') + semilogy(sim_qpsk_pn16.Ebvec, sim_qpsk_pn16.BERldpcvec,'b;QPSK phase noise +/- pi/16 ldpc;') + semilogy(sim_qpsk_pn32.Ebvec, sim_qpsk_pn32.BERldpcvec,'k;QPSK phase noise +/- pi/32 ldpc;') + + hold off; + xlabel('Eb/N0') + ylabel('BER') + grid("minor") + axis([min(Ebvec) max(Ebvec) 1E-2 1]) +endfunction + +function test_phase_est + sim_in = standard_init(); + + sim_in.Rs = 100; + sim_in.Nc = 8; + + sim_in.verbose = 1; + sim_in.plot_scatter = 1; + + sim_in.Esvec = 5; + sim_in.Ntrials = 30; + + sim_in.ldpc_code_rate = 1/2; + sim_in.ldpc_code = 1; + + sim_in.phase_est = 1; + sim_in.phase_est_method = 2; + sim_in.Np = 3; + sim_in.phase_offset = 0; + sim_in.w_offset = 0; + + sim_in.hf_sim = 1; + sim_in.hf_mag_only = 0; + + tmp = ber_test(sim_in, 'qpsk'); + +endfunction + +% Start simulations --------------------------------------- + +more off; + +ideal(); diff --git a/codec2-dev/octave/test_qpsk3.m b/codec2-dev/octave/test_qpsk3.m index eaf69995..447a2059 100644 --- a/codec2-dev/octave/test_qpsk3.m +++ b/codec2-dev/octave/test_qpsk3.m @@ -1,957 +1,957 @@ -% test_qps3k.m -% David Rowe March 2014 -% -% QPSK modem simulation, version 2. Simplifed version of -% test_qpsk. Initially based on code by Bill Cowley Generates curves -% BER versus E/No curves for different modems. Design to test -% coherent demodulation ideas on HF channels without building a full -% blown modem. Uses 'genie provided' estimates for timing estimation, -% frame sync. -% -% Compared to test_qsk2.m this version supports phase estimation -% (coherent demod) - -1; - -% main test function - -function sim_out = ber_test(sim_in, modulation) - Fs = 8000; - - newldpc = sim_in.newldpc; - verbose = sim_in.verbose; - framesize = sim_in.framesize; - Ntrials = sim_in.Ntrials; - Esvec = sim_in.Esvec; - phase_offset = sim_in.phase_offset; - phase_est = sim_in.phase_est; - w_offset = sim_in.w_offset; - plot_scatter = sim_in.plot_scatter; - Rs = sim_in.Rs; - hf_sim = sim_in.hf_sim; - nhfdelay = sim_in.hf_delay_ms*Rs/1000; - hf_phase_only = sim_in.hf_phase_only; - hf_mag_only = sim_in.hf_mag_only; - Nc = sim_in.Nc; - sim_coh_dpsk = sim_in.sim_coh_dpsk; - - bps = 2; - Nsymb = framesize/bps; - for k=1:Nc - prev_sym_tx(k) = qpsk_mod([0 0]); - prev_sym_rx(k) = qpsk_mod([0 0]); - end - - phase_est_method = sim_in.phase_est_method; - if phase_est_method == 2 - Np = sim_in.Np; - Ns = sim_in.Ns; - if Np/2 == floor(Np/2) - printf("Np must be odd\n"); - return; - end - Nps = (Np-1)*Ns+1; - else - Nps = sim_in.Np; - end - r_delay_line = zeros(Nc, Nps); - s_delay_line = zeros(Nc, Nps); - ph_est_log = []; - - phase_noise_amp = sim_in.phase_noise_amp; - - ldpc_code = sim_in.ldpc_code; - - tx_bits_buf = zeros(1,2*framesize); - rx_bits_buf = zeros(1,2*framesize); - rx_symb_buf = zeros(1,2*Nsymb); - hf_fading_buf = zeros(1,2*Nsymb); - - % Init LDPC -------------------------------------------------------------------- - - if ldpc_code - % Start CML library - - currentdir = pwd; - addpath '/home/david/tmp/cml/mat' % assume the source files stored here - cd /home/david/tmp/cml - CmlStartup % note that this is not in the cml path! - cd(currentdir) - - % Our LDPC library - - ldpc; - - rate = sim_in.ldpc_code_rate; - mod_order = 4; - modulation2 = 'QPSK'; - mapping = 'gray'; - - demod_type = 0; - decoder_type = 0; - max_iterations = 100; - - code_param = ldpc_init(rate, framesize, modulation2, mod_order, mapping); - code_param.code_bits_per_frame = framesize; - code_param.symbols_per_frame = framesize/bps; - else - rate = 1; - end - - % Init HF channel model from stored sample files of spreading signal ---------------------------------- - - % convert "spreading" samples from 1kHz carrier at Fs to complex - % baseband, generated by passing a 1kHz sine wave through PathSim - % with the ccir-poor model, enabling one path at a time. - - Fc = 1000; M = Fs/Rs; - fspread = fopen("../raw/sine1k_2Hz_spread.raw","rb"); - spread1k = fread(fspread, "int16")/10000; - fclose(fspread); - fspread = fopen("../raw/sine1k_2ms_delay_2Hz_spread.raw","rb"); - spread1k_2ms = fread(fspread, "int16")/10000; - fclose(fspread); - - % down convert to complex baseband - spreadbb = spread1k.*exp(-j*(2*pi*Fc/Fs)*(1:length(spread1k))'); - spreadbb_2ms = spread1k_2ms.*exp(-j*(2*pi*Fc/Fs)*(1:length(spread1k_2ms))'); - - % remove -2000 Hz image - b = fir1(50, 5/Fs); - spread = filter(b,1,spreadbb); - spread_2ms = filter(b,1,spreadbb_2ms); - - % discard first 1000 samples as these were near 0, probably as - % PathSim states were ramping up - - spread = spread(1000:length(spread)); - spread_2ms = spread_2ms(1000:length(spread_2ms)); - - % decimate down to Rs - - spread = spread(1:M:length(spread)); - spread_2ms = spread_2ms(1:M:length(spread_2ms)); - - % Determine "gain" of HF channel model, so we can normalise - % carrier power during HF channel sim to calibrate SNR. I imagine - % different implementations of ccir-poor would do this in - % different ways, leading to different BER results. Oh Well! - - hf_gain = 1.0/sqrt(var(spread)+var(spread_2ms)); - - % Start Simulation ---------------------------------------------------------------- - - for ne = 1:length(Esvec) - EsNodB = Esvec(ne); - EsNo = 10^(EsNodB/10); - - variance = 1/EsNo; - if verbose > 1 - printf("EsNo (dB): %f EsNo: %f variance: %f\n", EsNodB, EsNo, variance); - end - - Terrs = 0; Tbits = 0; Terrsldpc = 0; Tbitsldpc = 0; Ferrsldpc = 0; - - tx_symb_log = []; - rx_symb_log = []; - noise_log = []; - mod_strip_log = []; - - % init HF channel - - hf_n = 1; - hf_angle_log = []; - hf_fading = ones(1,Nsymb); % default input for ldpc dec - hf_model = ones(Ntrials*Nsymb/Nc, Nc); % defaults for plotting surface - - sim_out.errors_log = []; - sim_out.ldpc_errors_log = []; - - for nn = 1: Ntrials - - tx_bits = round( rand( 1, framesize*rate ) ); - - % modulate -------------------------------------------- - - if ldpc_code - [tx_bits, s] = ldpc_enc(tx_bits, code_param); - end - s = zeros(1, Nsymb); - for i=1:Nc:Nsymb - for k=1:Nc - tx_symb = qpsk_mod(tx_bits(2*(i-1+k-1)+1:2*(i+k-1))); - if strcmp(modulation,'dqpsk') - tx_symb *= prev_sym_tx(k); - prev_sym_tx(k) = tx_symb; - end - s(i+k-1) = tx_symb; - end - end - tx_bits_buf(1:framesize) = tx_bits_buf(framesize+1:2*framesize); - tx_bits_buf(framesize+1:2*framesize) = tx_bits; - s_ch = s; - - % HF channel simulation ------------------------------------ - - if hf_sim - - % separation between carriers. Note this is - % effectively under samples at Rs, I dont think this - % matters. Equivalent to doing freq shift at Fs, then - % decimating to Rs. - - wsep = 2*pi*(1+0.5); % e.g. 75Hz spacing at Rs=50Hz, alpha=0.5 filters - - if Nsymb/Nc != floor(Nsymb/Nc) - printf("Error: Nsymb/Nc must be an integrer\n") - return; - end - - % arrange symbols in Nsymb/Nc by Nc matrix - - for i=1:Nc:Nsymb - - % Determine HF channel at each carrier for this symbol - - for k=1:Nc - hf_model(hf_n, k) = hf_gain*(spread(hf_n) + exp(-j*k*wsep*nhfdelay)*spread_2ms(hf_n)); - hf_fading(i+k-1) = abs(hf_model(hf_n, k)); - if hf_mag_only - s_ch(i+k-1) *= abs(hf_model(hf_n, k)); - else - s_ch(i+k-1) *= hf_model(hf_n, k); - end - end - hf_n++; - end - end - - tx_symb_log = [tx_symb_log s_ch]; - - % AWGN noise and phase/freq offset channel simulation - % 0.5 factor ensures var(noise) == variance , i.e. splits power between Re & Im - - noise = sqrt(variance*0.5)*(randn(1,Nsymb) + j*randn(1,Nsymb)); - noise_log = [noise_log noise]; - phase_noise = phase_noise_amp*(2.0*rand(1,Nsymb)-1.0); - - % organise into carriers to apply frequency and phase offset - - for i=1:Nc:Nsymb - for k=1:Nc - s_ch(i+k-1) = s_ch(i+k-1)*exp(j*(phase_offset+phase_noise(i+k-1))) + noise(i+k-1); - end - phase_offset += w_offset; - end - - % phase estimation - - ph_est = zeros(Nc,1); - - if phase_est - - % organise into carriers - - for i=1:Nc:Nsymb - - for k=1:Nc - centre = floor(Nps/2)+1; - - % delay line for phase est window - - r_delay_line(k,1:Nps-1) = r_delay_line(k,2:Nps); - r_delay_line(k,Nps) = s_ch(i+k-1); - - % delay in tx data to compensate data for phase est window - - s_delay_line(k,1:Nps-1) = s_delay_line(k,2:Nps); - s_delay_line(k,Nps) = s(i+k-1); - - if phase_est_method == 1 - % QPSK modulation strip and phase est - - mod_strip_pol = angle(r_delay_line(k,:)) * 4; - mod_strip_rect = exp(j*mod_strip_pol); - - ph_est_pol = atan2(sum(imag(mod_strip_rect)),sum(real(mod_strip_rect)))/4; - ph_est(k) = exp(j*ph_est_pol); - - s_ch(i+k-1) = r_delay_line(k,centre).*exp(-j*ph_est_pol); - % s_ch(i+k-1) = r_delay_line(k,centre); - end - - if phase_est_method == 3 - % QPSK modulation strip and phase est with original symbol mags - - mod_strip_pol = angle(r_delay_line(k,:)) * 4; - mod_strip_rect = abs(r_delay_line(k,:)) .* exp(j*mod_strip_pol); - - ph_est_pol = atan2(sum(imag(mod_strip_rect)),sum(real(mod_strip_rect)))/4; - ph_est(k) = exp(j*ph_est_pol); - - s_ch(i+k-1) = r_delay_line(k,centre).*exp(-j*ph_est_pol); - % s_ch(i+k-1) = r_delay_line(k,centre); - end - - if phase_est_method == 2 - - % estimate phase from surrounding known pilot symbols and correct - - corr = 0; - for m=1:Ns:Nps - if (m != centre) - corr += s_delay_line(k,m) * r_delay_line(k,m)'; - end - end - ph_est(k) = conj(corr/(1E-6+abs(corr))); - s_ch(i+k-1) = r_delay_line(k,centre).*exp(j*angle(corr)); - %s_ch(i+k-1) = r_delay_line(k,centre); - end - - end - - ph_est_log = [ph_est_log ph_est]; - end - %printf("corr: %f angle: %f\n", corr, angle(corr)); - end - - % de-modulate - - rx_bits = zeros(1, framesize); - for i=1:Nc:Nsymb - for k=1:Nc - rx_symb = s_ch(i+k-1); - if strcmp(modulation,'dqpsk') - tmp = rx_symb; - rx_symb *= conj(prev_sym_rx(k)/abs(prev_sym_rx(k))); - if sim_coh_dpsk - prev_sym_rx(k) = qpsk_mod(qpsk_demod(tmp)); - else - prev_sym_rx(k) = tmp; - end - s_ch(i+k-1) = rx_symb; - end - rx_bits((2*(i-1+k-1)+1):(2*(i+k-1))) = qpsk_demod(rx_symb); - rx_symb_log = [rx_symb_log rx_symb]; - end - end - -if newldpc - rx_bits_buf(1:framesize) = rx_bits_buf(framesize+1:2*framesize); - rx_bits_buf(framesize+1:2*framesize) = rx_bits; - rx_symb_buf(1:Nsymb) = rx_symb_buf(Nsymb+1:2*Nsymb); - rx_symb_buf(Nsymb+1:2*Nsymb) = s_ch; - hf_fading_buf(1:Nsymb) = hf_fading_buf(Nsymb+1:2*Nsymb); - hf_fading_buf(Nsymb+1:2*Nsymb) = hf_fading; - - % determine location of start and end of frame depending on processing delays - - if phase_est - st_rx_bits = 1+(floor(Nps/2)+1-1)*Nc*2; - st_rx_symb = 1+(floor(Nps/2)+1-1)*Nc; - else - st_rx_bits = 1; - st_rx_symb = 1; - end - en_rx_bits = st_rx_bits+framesize-1; - en_rx_symb = st_rx_symb+Nsymb-1; - - if nn > 1 - % Measure BER - - %printf("nn: %d centre: %d\n", nn, floor(Nps/2)+1); - %tx_bits_buf(1:20) - %rx_bits_buf(st_rx_bits:st_rx_bits+20-1) - error_positions = xor(rx_bits_buf(st_rx_bits:en_rx_bits), tx_bits_buf(1:framesize)); - Nerrs = sum(error_positions); - sim_out.errors_log = [sim_out.errors_log error_positions]; - Terrs += Nerrs; - Tbits += length(tx_bits); - - % Optionally LDPC decode - - if ldpc_code - detected_data = ldpc_dec(code_param, max_iterations, demod_type, decoder_type, ... - rx_symb_buf(st_rx_symb:en_rx_symb), min(100,EsNo), hf_fading_buf(1:Nsymb)); - error_positions = xor( detected_data(1:framesize*rate), tx_bits_buf(1:framesize*rate) ); - %detected_data = ldpc_dec(code_param, max_iterations, demod_type, decoder_type, s_ch, min(100,EsNo), hf_fading); - %error_positions = xor( detected_data(1:framesize*rate), tx_bits(1:framesize*rate) ); - Nerrs = sum(error_positions); - sim_out.ldpc_errors_log = [sim_out.ldpc_errors_log error_positions]; - if Nerrs - Ferrsldpc++; - end - Terrsldpc += Nerrs; - Tbitsldpc += framesize*rate; - end - end - -else - error_positions = xor(rx_bits, tx_bits); - Nerrs = sum(error_positions); - Terrs += Nerrs; - Tbits += length(tx_bits); - - % Optionally LDPC decode - - if ldpc_code - detected_data = ldpc_dec(code_param, max_iterations, demod_type, decoder_type, s_ch, min(100,EsNo), hf_fading); - error_positions = xor( detected_data(1:framesize*rate), tx_bits(1:framesize*rate) ); - Nerrs = sum(error_positions); - if Nerrs - Ferrsldpc++; - end - Terrsldpc += Nerrs; - Tbitsldpc += framesize*rate; - - end - end -end - - TERvec(ne) = Terrs; - BERvec(ne) = Terrs/Tbits; - if ldpc_code - TERldpcvec(ne) = Terrsldpc; - FERldpcvec(ne) = Ferrsldpc; - BERldpcvec(ne) = Terrsldpc/Tbitsldpc; - end - - if verbose - printf("EsNo (dB): %f Terrs: %d BER %f BER theory %f", EsNodB, Terrs, - Terrs/Tbits, 0.5*erfc(sqrt(EsNo/2))); - if ldpc_code - printf(" LDPC: Terrs: %d BER: %f Ferrs: %d FER: %f", - Terrsldpc, Terrsldpc/Tbitsldpc, Ferrsldpc, Ferrsldpc/(Ntrials-1)); - end - printf("\n"); - end - if verbose > 1 - printf("Terrs: %d BER %f BER theory %f C %f N %f Es %f No %f Es/No %f\n\n", Terrs, - Terrs/Tbits, 0.5*erfc(sqrt(EsNo/2)), var(tx_symb_log), var(noise_log), - var(tx_symb_log), var(noise_log), var(tx_symb_log)/var(noise_log)); - end - end - - Ebvec = Esvec - 10*log10(bps); - - % account for extra power rqd for pilot symbols - - if (phase_est_method == 2) && (phase_est) - Ebvec += 10*log10(Ns/(Ns-1)); - end - - sim_out.BERvec = BERvec; - sim_out.Ebvec = Ebvec; - sim_out.TERvec = TERvec; - if ldpc_code - sim_out.BERldpcvec = BERldpcvec; - sim_out.TERldpcvec = TERldpcvec; - sim_out.FERldpcvec = FERldpcvec; - end - - if plot_scatter - figure(2); - clf; - scat = rx_symb_log .* exp(j*pi/4); - plot(real(scat(Nps*Nc:length(scat))), imag(scat(Nps*Nc:length(scat))),'+'); - title('Scatter plot'); - - figure(3); - clf; - - y = 1:Rs*2; - x = 1:Nc; - EsNodBSurface = 20*log10(abs(hf_model(y,:))) - 10*log10(variance); - mesh(x,y,EsNodBSurface); - grid - %axis([1 Nc 1 Rs*2 -10 10]) - title('HF Channel Es/No'); - - figure(4); - clf; - %mesh(x,y,unwrap(angle(hf_model(y,:)))); - subplot(211) - plot(y,abs(hf_model(y,1))) - title('HF Channel Carrier 1 Mag'); - subplot(212) - plot(y,angle(hf_model(y,1))) - title('HF Channel Carrier 1 Phase'); - - if phase_est - scat = ph_est_log(1,floor(Nps/2):Rs*2+floor(Nps/2)-1); - hold on; - plot(angle(scat),'r'); - hold off; - - figure(5) - clf; - scat = ph_est_log(1,y); - plot(real(scat), imag(scat),'+'); - title('Carrier 1 Phase Est'); - axis([-1 1 -1 1]) - end -if 0 - figure(5); - clf; - subplot(211) - plot(real(spread)); - hold on; - plot(imag(spread),'g'); - hold off; - subplot(212) - plot(real(spread_2ms)); - hold on; - plot(imag(spread_2ms),'g'); - hold off; - - figure(6) - tmp = []; - for i = 1:hf_n-1 - tmp = [tmp abs(hf_model(i,:))]; - end - hist(tmp); -end - end - -size(sim_out.errors_log) - -endfunction - -% Gray coded QPSK modulation function - -function symbol = qpsk_mod(two_bits) - two_bits_decimal = sum(two_bits .* [2 1]); - switch(two_bits_decimal) - case (0) symbol = 1; - case (1) symbol = j; - case (2) symbol = -j; - case (3) symbol = -1; - endswitch -endfunction - -% Gray coded QPSK demodulation function - -function two_bits = qpsk_demod(symbol) - if isscalar(symbol) == 0 - printf("only works with scalars\n"); - return; - end - bit0 = real(symbol*exp(j*pi/4)) < 0; - bit1 = imag(symbol*exp(j*pi/4)) < 0; - two_bits = [bit1 bit0]; -endfunction - -function sim_in = standard_init - sim_in.verbose = 1; - sim_in.plot_scatter = 0; - - sim_in.Esvec = 5; - sim_in.Ntrials = 30; - sim_in.framesize = 576; - sim_in.Rs = 100; - sim_in.Nc = 8; - - sim_in.phase_offset = 0; - sim_in.w_offset = 0; - sim_in.phase_noise_amp = 0; - - sim_in.hf_delay_ms = 2; - sim_in.hf_sim = 0; - sim_in.hf_phase_only = 0; - sim_in.hf_mag_only = 1; - - sim_in.phase_est = 0; - sim_in.phase_est_method = 1; - sim_in.Np = 5; - sim_in.Ns = 5; - - sim_in.ldpc_code_rate = 1/2; - sim_in.ldpc_code = 1; -endfunction - -function ideal - - sim_in = standard_init(); - - sim_in.sim_coh_dpsk = 0; - sim_in.newldpc = 1; - sim_in.verbose = 2; - sim_in.plot_scatter = 1; - - sim_in.Esvec = 5; - sim_in.hf_sim = 1; - sim_in.Ntrials = 30; - - sim_qpsk_hf = ber_test(sim_in, 'qpsk'); - - sim_in.hf_sim = 0; - sim_in.plot_scatter = 0; - sim_in.Esvec = 2:15; - sim_in.ldpc_code = 0; - Ebvec = sim_in.Esvec - 10*log10(2); - BER_theory = 0.5*erfc(sqrt(10.^(Ebvec/10))); - sim_qpsk = ber_test(sim_in, 'qpsk'); - sim_dqpsk = ber_test(sim_in, 'dqpsk'); - - sim_in.hf_sim = 1; - sim_in.Esvec = 2:15; - sim_qpsk_hf = ber_test(sim_in, 'qpsk'); - sim_dqpsk_hf = ber_test(sim_in, 'dqpsk'); - sim_in.ldpc_code = 1; - sim_in.ldpc_code_rate = 3/4; - sim_qpsk_hf_ldpc1 = ber_test(sim_in, 'qpsk'); - sim_in.ldpc_code_rate = 1/2; - sim_qpsk_hf_ldpc2 = ber_test(sim_in, 'qpsk'); - sim_in.ldpc_code_rate = 3/4; - sim_in.hf_sim = 0; - sim_qpsk_awgn_ldpc = ber_test(sim_in, 'qpsk'); - - figure(1); - clf; - semilogy(Ebvec, BER_theory,'r;QPSK theory;') - hold on; - semilogy(sim_qpsk.Ebvec, sim_qpsk.BERvec,'g;QPSK AWGN;') - semilogy(sim_qpsk_hf.Ebvec, sim_qpsk_hf.BERvec,'r;QPSK HF;') - semilogy(sim_dqpsk.Ebvec, sim_dqpsk.BERvec,'c;DQPSK AWGN;') - semilogy(sim_dqpsk_hf.Ebvec, sim_dqpsk_hf.BERvec,'m;DQPSK HF;') - semilogy(sim_qpsk_hf_ldpc1.Ebvec, sim_qpsk_hf_ldpc1.BERldpcvec,'k;QPSK HF LDPC 3/4;') - semilogy(sim_qpsk_hf_ldpc2.Ebvec, sim_qpsk_hf_ldpc2.BERldpcvec,'b;QPSK HF LDPC 1/2;') - semilogy(sim_qpsk_awgn_ldpc.Ebvec, sim_qpsk_awgn_ldpc.BERldpcvec,'k;QPSK AWGN LDPC 3/4;') - - hold off; - xlabel('Eb/N0') - ylabel('BER') - grid("minor") - axis([min(Ebvec) max(Ebvec) 1E-3 1]) -endfunction - -function phase_noise - sim_in = standard_init(); - - sim_in.verbose = 1; - sim_in.plot_scatter = 1; - - sim_in.Esvec = 100; - sim_in.Ntrials = 100; - - sim_in.ldpc_code_rate = 1/2; - sim_in.ldpc_code = 1; - - sim_in.phase_noise_amp = pi/16; - tmp = ber_test(sim_in, 'qpsk'); - - sim_in.plot_scatter = 0; - sim_in.Esvec = 2:8; - sim_qpsk_hf = ber_test(sim_in, 'qpsk'); - - Ebvec = sim_in.Esvec - 10*log10(2); - BER_theory = 0.5*erfc(sqrt(10.^(Ebvec/10))); - - sim_in.phase_noise_amp = 0; - sim_qpsk = ber_test(sim_in, 'qpsk'); - sim_in.phase_noise_amp = pi/8; - sim_qpsk_pn8 = ber_test(sim_in, 'qpsk'); - sim_in.phase_noise_amp = pi/16; - sim_qpsk_pn16 = ber_test(sim_in, 'qpsk'); - sim_in.phase_noise_amp = pi/32; - sim_qpsk_pn32 = ber_test(sim_in, 'qpsk'); - - figure(1); - clf; - semilogy(sim_qpsk.Ebvec, sim_qpsk.BERvec,'g;QPSK phase noise 0;') - hold on; - semilogy(sim_qpsk_pn8.Ebvec, sim_qpsk_pn8.BERvec,'c;QPSK phase noise +/- pi/8;') - semilogy(sim_qpsk_pn16.Ebvec, sim_qpsk_pn16.BERvec,'b;QPSK phase noise +/- pi/16;') - semilogy(sim_qpsk_pn32.Ebvec, sim_qpsk_pn32.BERvec,'k;QPSK phase noise +/- pi/32;') - - semilogy(sim_qpsk.Ebvec, sim_qpsk.BERldpcvec,'g;QPSK phase noise 0 ldpc;') - semilogy(sim_qpsk_pn8.Ebvec, sim_qpsk_pn8.BERldpcvec,'c;QPSK phase noise +/- pi/8 ldpc;') - semilogy(sim_qpsk_pn16.Ebvec, sim_qpsk_pn16.BERldpcvec,'b;QPSK phase noise +/- pi/16 ldpc;') - semilogy(sim_qpsk_pn32.Ebvec, sim_qpsk_pn32.BERldpcvec,'k;QPSK phase noise +/- pi/32 ldpc;') - - hold off; - xlabel('Eb/N0') - ylabel('BER') - grid("minor") - axis([min(Ebvec) max(Ebvec) 1E-2 1]) -endfunction - -function phase_est_hf - sim_in = standard_init(); - - sim_in.Rs = 100; - sim_in.Nc = 8; - - sim_in.verbose = 1; - sim_in.plot_scatter = 0; - - sim_in.Esvec = 5:15; - sim_in.Ntrials = 100; - - sim_in.newldpc = 1; - sim_in.ldpc_code_rate = 1/2; - sim_in.ldpc_code = 1; - - sim_in.phase_est = 0; - sim_in.sim_coh_dpsk = 0; - sim_in.phase_est_method = 2; - sim_in.Np = 3; - sim_in.phase_offset = 0; - sim_in.w_offset = 0; - - sim_in.hf_sim = 1; - sim_in.hf_mag_only = 1; - - Ebvec = sim_in.Esvec - 10*log10(2); - - baseline = ber_test(sim_in, 'qpsk'); - - sim_in.hf_mag_only = 0; - sim_in.phase_est_method = 2; - sim_in.phase_est = 1; - sim_in.Np = 3; - pilot_3 = ber_test(sim_in, 'qpsk'); - sim_in.Np = 5; - pilot_5 = ber_test(sim_in, 'qpsk'); - sim_in.Np = 7; - pilot_7 = ber_test(sim_in, 'qpsk'); - -if 1 - sim_in.phase_est = 0; - dqpsk = ber_test(sim_in, 'dqpsk'); - - figure(1); - clf; - semilogy(baseline.Ebvec, baseline.BERvec,'r;QPSK CCIR poor;') - hold on; - semilogy(baseline.Ebvec, baseline.BERldpcvec,'r;QPSK CCIR poor ldpc;') - semilogy(pilot_3.Ebvec, pilot_3.BERvec,'b;QPSK CCIR poor ldpc pilot 3;') - semilogy(pilot_3.Ebvec, pilot_3.BERldpcvec,'b;QPSK CCIR poor ldpc pilot 3;') - semilogy(pilot_5.Ebvec, pilot_5.BERvec,'g;QPSK CCIR poor ldpc pilot 5;') - semilogy(pilot_5.Ebvec, pilot_5.BERldpcvec,'g;QPSK CCIR poor ldpc pilot 5;') - semilogy(pilot_7.Ebvec, pilot_7.BERvec,'m;QPSK CCIR poor ldpc pilot 7;') - semilogy(pilot_7.Ebvec, pilot_7.BERldpcvec,'m;QPSK CCIR poor ldpc pilot 7;') - semilogy(dqpsk.Ebvec, dqpsk.BERvec,'k;DQPSK CCIR poor ldpc;') - semilogy(dqpsk.Ebvec, dqpsk.BERldpcvec,'k;DQPSK CCIR poor ldpc;') - - hold off; - xlabel('Eb/N0') - ylabel('BER') - grid("minor") - axis([min(Ebvec) max(Ebvec) 1E-2 1]) -end -endfunction - -function phase_est_awgn - sim_in = standard_init(); - - sim_in.Rs = 100; - sim_in.Nc = 8; - - sim_in.verbose = 1; - sim_in.plot_scatter = 0; - - sim_in.Esvec = 0:0.5:3; - sim_in.Ntrials = 30; - - sim_in.newldpc = 1; - sim_in.ldpc_code_rate = 1/2; - sim_in.ldpc_code = 1; - - sim_in.phase_est = 0; - sim_in.phase_est_method = 1; - sim_in.Np = 3; - sim_in.phase_offset = 0; - sim_in.w_offset = 0; - - sim_in.hf_sim = 0; - sim_in.hf_mag_only = 1; - - ideal = ber_test(sim_in, 'qpsk'); - - sim_in.phase_est = 1; - sim_in.Np = 21; - sim_in.phase_est_method = 3; - strip_21_mag = ber_test(sim_in, 'qpsk'); - - sim_in.Np = 41; - strip_41_mag = ber_test(sim_in, 'qpsk'); - - sim_in.phase_est_method = 1; - sim_in.Np = 21; - strip_21 = ber_test(sim_in, 'qpsk'); - - sim_in.Np = 41; - strip_41 = ber_test(sim_in, 'qpsk'); - - sim_in.Np = 7; - sim_in.phase_est_method = 2; - pilot_7 = ber_test(sim_in, 'qpsk'); - - Ebvec = sim_in.Esvec - 10*log10(2); - - figure(1); - clf; - semilogy(ideal.Ebvec, ideal.BERvec,'r;QPSK;') - hold on; - semilogy(ideal.Ebvec, ideal.BERldpcvec,'r;QPSK LDPC;') - semilogy(strip_21.Ebvec, strip_21.BERvec,'g;QPSK strip 21;') - semilogy(strip_21.Ebvec, strip_21.BERldpcvec,'g;QPSK LDPC strip 21;') - semilogy(strip_41.Ebvec, strip_41.BERvec,'b;QPSK strip 41;') - semilogy(strip_41.Ebvec, strip_41.BERldpcvec,'b;QPSK LDPC strip 41;') - semilogy(strip_21_mag.Ebvec, strip_21_mag.BERvec,'m;QPSK strip 21 mag;') - semilogy(strip_21_mag.Ebvec, strip_21_mag.BERldpcvec,'m;QPSK LDPC strip 21 mag;') - semilogy(strip_41_mag.Ebvec, strip_41_mag.BERvec,'c;QPSK strip 41 mag;') - semilogy(strip_41_mag.Ebvec, strip_41_mag.BERldpcvec,'c;QPSK LDPC strip 41 mag;') - semilogy(pilot_7.Ebvec, pilot_7.BERvec,'k;QPSK pilot 7;') - semilogy(pilot_7.Ebvec, pilot_7.BERldpcvec,'k;QPSK LDPC pilot 7;') - - hold off; - xlabel('Eb/N0') - ylabel('BER') - grid("minor") - axis([min(Ebvec) max(Ebvec) 1E-2 1]) -endfunction - -function test_dpsk - sim_in = standard_init(); - - sim_in.Rs = 100; - sim_in.Nc = 8; - - sim_in.verbose = 1; - sim_in.plot_scatter = 0; - - sim_in.Esvec = 5; - sim_in.Ntrials = 30; - - sim_in.newldpc = 1; - sim_in.ldpc_code_rate = 1/2; - sim_in.ldpc_code = 1; - - sim_in.phase_est = 0; - sim_in.phase_est_method = 3; - sim_in.Np = 41; - sim_in.phase_offset = 0; - sim_in.w_offset = 0; - sim_in.sim_coh_dpsk = 0; - - sim_in.hf_sim = 0; - sim_in.hf_mag_only = 1; - - Ebvec = sim_in.Esvec - 10*log10(2); - - baseline = ber_test(sim_in, 'qpsk'); - sim_in.phase_est = 0; - dqpsk = ber_test(sim_in, 'dqpsk'); - - sim_in.phase_est = 1; - sim_in.phase_est_method = 3; - sim_in.sim_coh_dpsk = 1; - sim_in.Np = 41; - dqpsk_strip_41 = ber_test(sim_in, 'dqpsk'); - - figure(1); - clf; - semilogy(baseline.Ebvec, baseline.BERvec,'r;QPSK CCIR poor;') - hold on; - semilogy(baseline.Ebvec, baseline.BERldpcvec,'r;QPSK CCIR poor ldpc;') - semilogy(dqpsk.Ebvec, dqpsk.BERvec,'c;DQPSK CCIR poor ldpc;') - semilogy(dqpsk.Ebvec, dqpsk.BERldpcvec,'c;DQPSK CCIR poor ldpc;') - semilogy(dqpsk_strip_41.Ebvec, dqpsk_strip_41.BERvec,'m;DQPSK CCIR poor ldpc strip 41;') - semilogy(dqpsk_strip_41.Ebvec, dqpsk_strip_41.BERldpcvec,'m;DQPSK CCIR poor ldpc strip 41;') - - hold off; - xlabel('Eb/N0') - ylabel('BER') - grid("minor") - axis([min(Ebvec) max(Ebvec) 1E-2 1]) - -endfunction - -function gen_error_pattern_qpsk() - sim_in = standard_init(); - - % model codec and uncoded streams as 1000 bit/s each - - sim_in.Rs = 100; - sim_in.Nc = 4; - - sim_in.verbose = 1; - sim_in.plot_scatter = 0; - - sim_in.Esvec = 10; % Eb/No=2dB - sim_in.Ntrials = 30; - - sim_in.newldpc = 1; - sim_in.ldpc_code_rate = 1/2; - sim_in.ldpc_code = 1; - - sim_in.phase_est = 1; - sim_in.phase_est_method = 2; - sim_in.Np = 5; - sim_in.phase_offset = 0; - sim_in.w_offset = 0; - sim_in.sim_coh_dpsk = 0; - - sim_in.hf_sim = 1; - sim_in.hf_mag_only = 0; - - qpsk = ber_test(sim_in, 'qpsk'); - - length(qpsk.errors_log) - length(qpsk.ldpc_errors_log) - % multiplex errors into prot and unprot halves of 52 bit codec frames - - error_pattern = []; - for i=1:26:length(qpsk.ldpc_errors_log)-52 - error_pattern = [error_pattern qpsk.ldpc_errors_log(i:i+25) qpsk.errors_log(i:i+25) zeros(1,4)]; - %error_pattern = [error_pattern qpsk.ldpc_errors_log(i:i+25) zeros(1,26) zeros(1,4)]; - %error_pattern = [error_pattern zeros(1,26) qpsk.errors_log(i:i+25) zeros(1,4)]; - end - - fep=fopen("qpsk_errors_2dB.bin","wb"); fwrite(fep, error_pattern, "short"); fclose(fep); - -endfunction - -function gen_error_pattern_dpsk() - sim_in = standard_init(); - - sim_in.Rs = 50; - sim_in.Nc = 16; - - sim_in.verbose = 1; - sim_in.plot_scatter = 1; - - sim_in.Esvec = 10; % Eb/No=Es/No-3 - sim_in.Ntrials = 30; - - sim_in.newldpc = 1; - sim_in.ldpc_code_rate = 1/2; - sim_in.ldpc_code = 0; - - sim_in.phase_est = 0; - sim_in.phase_est_method = 3; - sim_in.Np = 41; - sim_in.phase_offset = 0; - sim_in.w_offset = 0; - sim_in.sim_coh_dpsk = 0; - - sim_in.hf_sim = 1; - sim_in.hf_mag_only = 1; - - dqpsk = ber_test(sim_in, 'dqpsk'); - - fep=fopen("dqpsk_errors_12dB.bin","wb"); fwrite(fep, dqpsk.errors_log, "short"); fclose(fep); - -endfunction - -% Start simulations --------------------------------------- - -more off; - -ideal(); -%phase_est_hf(); -%phase_est_awgn(); -%test_dpsk(); -%gen_error_pattern_qpsk +% test_qps3k.m +% David Rowe March 2014 +% +% QPSK modem simulation, version 2. Simplifed version of +% test_qpsk. Initially based on code by Bill Cowley Generates curves +% BER versus E/No curves for different modems. Design to test +% coherent demodulation ideas on HF channels without building a full +% blown modem. Uses 'genie provided' estimates for timing estimation, +% frame sync. +% +% Compared to test_qsk2.m this version supports phase estimation +% (coherent demod) + +1; + +% main test function + +function sim_out = ber_test(sim_in, modulation) + Fs = 8000; + + newldpc = sim_in.newldpc; + verbose = sim_in.verbose; + framesize = sim_in.framesize; + Ntrials = sim_in.Ntrials; + Esvec = sim_in.Esvec; + phase_offset = sim_in.phase_offset; + phase_est = sim_in.phase_est; + w_offset = sim_in.w_offset; + plot_scatter = sim_in.plot_scatter; + Rs = sim_in.Rs; + hf_sim = sim_in.hf_sim; + nhfdelay = sim_in.hf_delay_ms*Rs/1000; + hf_phase_only = sim_in.hf_phase_only; + hf_mag_only = sim_in.hf_mag_only; + Nc = sim_in.Nc; + sim_coh_dpsk = sim_in.sim_coh_dpsk; + + bps = 2; + Nsymb = framesize/bps; + for k=1:Nc + prev_sym_tx(k) = qpsk_mod([0 0]); + prev_sym_rx(k) = qpsk_mod([0 0]); + end + + phase_est_method = sim_in.phase_est_method; + if phase_est_method == 2 + Np = sim_in.Np; + Ns = sim_in.Ns; + if Np/2 == floor(Np/2) + printf("Np must be odd\n"); + return; + end + Nps = (Np-1)*Ns+1; + else + Nps = sim_in.Np; + end + r_delay_line = zeros(Nc, Nps); + s_delay_line = zeros(Nc, Nps); + ph_est_log = []; + + phase_noise_amp = sim_in.phase_noise_amp; + + ldpc_code = sim_in.ldpc_code; + + tx_bits_buf = zeros(1,2*framesize); + rx_bits_buf = zeros(1,2*framesize); + rx_symb_buf = zeros(1,2*Nsymb); + hf_fading_buf = zeros(1,2*Nsymb); + + % Init LDPC -------------------------------------------------------------------- + + if ldpc_code + % Start CML library + + currentdir = pwd; + addpath '/home/david/tmp/cml/mat' % assume the source files stored here + cd /home/david/tmp/cml + CmlStartup % note that this is not in the cml path! + cd(currentdir) + + % Our LDPC library + + ldpc; + + rate = sim_in.ldpc_code_rate; + mod_order = 4; + modulation2 = 'QPSK'; + mapping = 'gray'; + + demod_type = 0; + decoder_type = 0; + max_iterations = 100; + + code_param = ldpc_init(rate, framesize, modulation2, mod_order, mapping); + code_param.code_bits_per_frame = framesize; + code_param.symbols_per_frame = framesize/bps; + else + rate = 1; + end + + % Init HF channel model from stored sample files of spreading signal ---------------------------------- + + % convert "spreading" samples from 1kHz carrier at Fs to complex + % baseband, generated by passing a 1kHz sine wave through PathSim + % with the ccir-poor model, enabling one path at a time. + + Fc = 1000; M = Fs/Rs; + fspread = fopen("../raw/sine1k_2Hz_spread.raw","rb"); + spread1k = fread(fspread, "int16")/10000; + fclose(fspread); + fspread = fopen("../raw/sine1k_2ms_delay_2Hz_spread.raw","rb"); + spread1k_2ms = fread(fspread, "int16")/10000; + fclose(fspread); + + % down convert to complex baseband + spreadbb = spread1k.*exp(-j*(2*pi*Fc/Fs)*(1:length(spread1k))'); + spreadbb_2ms = spread1k_2ms.*exp(-j*(2*pi*Fc/Fs)*(1:length(spread1k_2ms))'); + + % remove -2000 Hz image + b = fir1(50, 5/Fs); + spread = filter(b,1,spreadbb); + spread_2ms = filter(b,1,spreadbb_2ms); + + % discard first 1000 samples as these were near 0, probably as + % PathSim states were ramping up + + spread = spread(1000:length(spread)); + spread_2ms = spread_2ms(1000:length(spread_2ms)); + + % decimate down to Rs + + spread = spread(1:M:length(spread)); + spread_2ms = spread_2ms(1:M:length(spread_2ms)); + + % Determine "gain" of HF channel model, so we can normalise + % carrier power during HF channel sim to calibrate SNR. I imagine + % different implementations of ccir-poor would do this in + % different ways, leading to different BER results. Oh Well! + + hf_gain = 1.0/sqrt(var(spread)+var(spread_2ms)); + + % Start Simulation ---------------------------------------------------------------- + + for ne = 1:length(Esvec) + EsNodB = Esvec(ne); + EsNo = 10^(EsNodB/10); + + variance = 1/EsNo; + if verbose > 1 + printf("EsNo (dB): %f EsNo: %f variance: %f\n", EsNodB, EsNo, variance); + end + + Terrs = 0; Tbits = 0; Terrsldpc = 0; Tbitsldpc = 0; Ferrsldpc = 0; + + tx_symb_log = []; + rx_symb_log = []; + noise_log = []; + mod_strip_log = []; + + % init HF channel + + hf_n = 1; + hf_angle_log = []; + hf_fading = ones(1,Nsymb); % default input for ldpc dec + hf_model = ones(Ntrials*Nsymb/Nc, Nc); % defaults for plotting surface + + sim_out.errors_log = []; + sim_out.ldpc_errors_log = []; + + for nn = 1: Ntrials + + tx_bits = round( rand( 1, framesize*rate ) ); + + % modulate -------------------------------------------- + + if ldpc_code + [tx_bits, s] = ldpc_enc(tx_bits, code_param); + end + s = zeros(1, Nsymb); + for i=1:Nc:Nsymb + for k=1:Nc + tx_symb = qpsk_mod(tx_bits(2*(i-1+k-1)+1:2*(i+k-1))); + if strcmp(modulation,'dqpsk') + tx_symb *= prev_sym_tx(k); + prev_sym_tx(k) = tx_symb; + end + s(i+k-1) = tx_symb; + end + end + tx_bits_buf(1:framesize) = tx_bits_buf(framesize+1:2*framesize); + tx_bits_buf(framesize+1:2*framesize) = tx_bits; + s_ch = s; + + % HF channel simulation ------------------------------------ + + if hf_sim + + % separation between carriers. Note this is + % effectively under samples at Rs, I dont think this + % matters. Equivalent to doing freq shift at Fs, then + % decimating to Rs. + + wsep = 2*pi*(1+0.5); % e.g. 75Hz spacing at Rs=50Hz, alpha=0.5 filters + + if Nsymb/Nc != floor(Nsymb/Nc) + printf("Error: Nsymb/Nc must be an integrer\n") + return; + end + + % arrange symbols in Nsymb/Nc by Nc matrix + + for i=1:Nc:Nsymb + + % Determine HF channel at each carrier for this symbol + + for k=1:Nc + hf_model(hf_n, k) = hf_gain*(spread(hf_n) + exp(-j*k*wsep*nhfdelay)*spread_2ms(hf_n)); + hf_fading(i+k-1) = abs(hf_model(hf_n, k)); + if hf_mag_only + s_ch(i+k-1) *= abs(hf_model(hf_n, k)); + else + s_ch(i+k-1) *= hf_model(hf_n, k); + end + end + hf_n++; + end + end + + tx_symb_log = [tx_symb_log s_ch]; + + % AWGN noise and phase/freq offset channel simulation + % 0.5 factor ensures var(noise) == variance , i.e. splits power between Re & Im + + noise = sqrt(variance*0.5)*(randn(1,Nsymb) + j*randn(1,Nsymb)); + noise_log = [noise_log noise]; + phase_noise = phase_noise_amp*(2.0*rand(1,Nsymb)-1.0); + + % organise into carriers to apply frequency and phase offset + + for i=1:Nc:Nsymb + for k=1:Nc + s_ch(i+k-1) = s_ch(i+k-1)*exp(j*(phase_offset+phase_noise(i+k-1))) + noise(i+k-1); + end + phase_offset += w_offset; + end + + % phase estimation + + ph_est = zeros(Nc,1); + + if phase_est + + % organise into carriers + + for i=1:Nc:Nsymb + + for k=1:Nc + centre = floor(Nps/2)+1; + + % delay line for phase est window + + r_delay_line(k,1:Nps-1) = r_delay_line(k,2:Nps); + r_delay_line(k,Nps) = s_ch(i+k-1); + + % delay in tx data to compensate data for phase est window + + s_delay_line(k,1:Nps-1) = s_delay_line(k,2:Nps); + s_delay_line(k,Nps) = s(i+k-1); + + if phase_est_method == 1 + % QPSK modulation strip and phase est + + mod_strip_pol = angle(r_delay_line(k,:)) * 4; + mod_strip_rect = exp(j*mod_strip_pol); + + ph_est_pol = atan2(sum(imag(mod_strip_rect)),sum(real(mod_strip_rect)))/4; + ph_est(k) = exp(j*ph_est_pol); + + s_ch(i+k-1) = r_delay_line(k,centre).*exp(-j*ph_est_pol); + % s_ch(i+k-1) = r_delay_line(k,centre); + end + + if phase_est_method == 3 + % QPSK modulation strip and phase est with original symbol mags + + mod_strip_pol = angle(r_delay_line(k,:)) * 4; + mod_strip_rect = abs(r_delay_line(k,:)) .* exp(j*mod_strip_pol); + + ph_est_pol = atan2(sum(imag(mod_strip_rect)),sum(real(mod_strip_rect)))/4; + ph_est(k) = exp(j*ph_est_pol); + + s_ch(i+k-1) = r_delay_line(k,centre).*exp(-j*ph_est_pol); + % s_ch(i+k-1) = r_delay_line(k,centre); + end + + if phase_est_method == 2 + + % estimate phase from surrounding known pilot symbols and correct + + corr = 0; + for m=1:Ns:Nps + if (m != centre) + corr += s_delay_line(k,m) * r_delay_line(k,m)'; + end + end + ph_est(k) = conj(corr/(1E-6+abs(corr))); + s_ch(i+k-1) = r_delay_line(k,centre).*exp(j*angle(corr)); + %s_ch(i+k-1) = r_delay_line(k,centre); + end + + end + + ph_est_log = [ph_est_log ph_est]; + end + %printf("corr: %f angle: %f\n", corr, angle(corr)); + end + + % de-modulate + + rx_bits = zeros(1, framesize); + for i=1:Nc:Nsymb + for k=1:Nc + rx_symb = s_ch(i+k-1); + if strcmp(modulation,'dqpsk') + tmp = rx_symb; + rx_symb *= conj(prev_sym_rx(k)/abs(prev_sym_rx(k))); + if sim_coh_dpsk + prev_sym_rx(k) = qpsk_mod(qpsk_demod(tmp)); + else + prev_sym_rx(k) = tmp; + end + s_ch(i+k-1) = rx_symb; + end + rx_bits((2*(i-1+k-1)+1):(2*(i+k-1))) = qpsk_demod(rx_symb); + rx_symb_log = [rx_symb_log rx_symb]; + end + end + +if newldpc + rx_bits_buf(1:framesize) = rx_bits_buf(framesize+1:2*framesize); + rx_bits_buf(framesize+1:2*framesize) = rx_bits; + rx_symb_buf(1:Nsymb) = rx_symb_buf(Nsymb+1:2*Nsymb); + rx_symb_buf(Nsymb+1:2*Nsymb) = s_ch; + hf_fading_buf(1:Nsymb) = hf_fading_buf(Nsymb+1:2*Nsymb); + hf_fading_buf(Nsymb+1:2*Nsymb) = hf_fading; + + % determine location of start and end of frame depending on processing delays + + if phase_est + st_rx_bits = 1+(floor(Nps/2)+1-1)*Nc*2; + st_rx_symb = 1+(floor(Nps/2)+1-1)*Nc; + else + st_rx_bits = 1; + st_rx_symb = 1; + end + en_rx_bits = st_rx_bits+framesize-1; + en_rx_symb = st_rx_symb+Nsymb-1; + + if nn > 1 + % Measure BER + + %printf("nn: %d centre: %d\n", nn, floor(Nps/2)+1); + %tx_bits_buf(1:20) + %rx_bits_buf(st_rx_bits:st_rx_bits+20-1) + error_positions = xor(rx_bits_buf(st_rx_bits:en_rx_bits), tx_bits_buf(1:framesize)); + Nerrs = sum(error_positions); + sim_out.errors_log = [sim_out.errors_log error_positions]; + Terrs += Nerrs; + Tbits += length(tx_bits); + + % Optionally LDPC decode + + if ldpc_code + detected_data = ldpc_dec(code_param, max_iterations, demod_type, decoder_type, ... + rx_symb_buf(st_rx_symb:en_rx_symb), min(100,EsNo), hf_fading_buf(1:Nsymb)); + error_positions = xor( detected_data(1:framesize*rate), tx_bits_buf(1:framesize*rate) ); + %detected_data = ldpc_dec(code_param, max_iterations, demod_type, decoder_type, s_ch, min(100,EsNo), hf_fading); + %error_positions = xor( detected_data(1:framesize*rate), tx_bits(1:framesize*rate) ); + Nerrs = sum(error_positions); + sim_out.ldpc_errors_log = [sim_out.ldpc_errors_log error_positions]; + if Nerrs + Ferrsldpc++; + end + Terrsldpc += Nerrs; + Tbitsldpc += framesize*rate; + end + end + +else + error_positions = xor(rx_bits, tx_bits); + Nerrs = sum(error_positions); + Terrs += Nerrs; + Tbits += length(tx_bits); + + % Optionally LDPC decode + + if ldpc_code + detected_data = ldpc_dec(code_param, max_iterations, demod_type, decoder_type, s_ch, min(100,EsNo), hf_fading); + error_positions = xor( detected_data(1:framesize*rate), tx_bits(1:framesize*rate) ); + Nerrs = sum(error_positions); + if Nerrs + Ferrsldpc++; + end + Terrsldpc += Nerrs; + Tbitsldpc += framesize*rate; + + end + end +end + + TERvec(ne) = Terrs; + BERvec(ne) = Terrs/Tbits; + if ldpc_code + TERldpcvec(ne) = Terrsldpc; + FERldpcvec(ne) = Ferrsldpc; + BERldpcvec(ne) = Terrsldpc/Tbitsldpc; + end + + if verbose + printf("EsNo (dB): %f Terrs: %d BER %f BER theory %f", EsNodB, Terrs, + Terrs/Tbits, 0.5*erfc(sqrt(EsNo/2))); + if ldpc_code + printf(" LDPC: Terrs: %d BER: %f Ferrs: %d FER: %f", + Terrsldpc, Terrsldpc/Tbitsldpc, Ferrsldpc, Ferrsldpc/(Ntrials-1)); + end + printf("\n"); + end + if verbose > 1 + printf("Terrs: %d BER %f BER theory %f C %f N %f Es %f No %f Es/No %f\n\n", Terrs, + Terrs/Tbits, 0.5*erfc(sqrt(EsNo/2)), var(tx_symb_log), var(noise_log), + var(tx_symb_log), var(noise_log), var(tx_symb_log)/var(noise_log)); + end + end + + Ebvec = Esvec - 10*log10(bps); + + % account for extra power rqd for pilot symbols + + if (phase_est_method == 2) && (phase_est) + Ebvec += 10*log10(Ns/(Ns-1)); + end + + sim_out.BERvec = BERvec; + sim_out.Ebvec = Ebvec; + sim_out.TERvec = TERvec; + if ldpc_code + sim_out.BERldpcvec = BERldpcvec; + sim_out.TERldpcvec = TERldpcvec; + sim_out.FERldpcvec = FERldpcvec; + end + + if plot_scatter + figure(2); + clf; + scat = rx_symb_log .* exp(j*pi/4); + plot(real(scat(Nps*Nc:length(scat))), imag(scat(Nps*Nc:length(scat))),'+'); + title('Scatter plot'); + + figure(3); + clf; + + y = 1:Rs*2; + x = 1:Nc; + EsNodBSurface = 20*log10(abs(hf_model(y,:))) - 10*log10(variance); + mesh(x,y,EsNodBSurface); + grid + %axis([1 Nc 1 Rs*2 -10 10]) + title('HF Channel Es/No'); + + figure(4); + clf; + %mesh(x,y,unwrap(angle(hf_model(y,:)))); + subplot(211) + plot(y,abs(hf_model(y,1))) + title('HF Channel Carrier 1 Mag'); + subplot(212) + plot(y,angle(hf_model(y,1))) + title('HF Channel Carrier 1 Phase'); + + if phase_est + scat = ph_est_log(1,floor(Nps/2):Rs*2+floor(Nps/2)-1); + hold on; + plot(angle(scat),'r'); + hold off; + + figure(5) + clf; + scat = ph_est_log(1,y); + plot(real(scat), imag(scat),'+'); + title('Carrier 1 Phase Est'); + axis([-1 1 -1 1]) + end +if 0 + figure(5); + clf; + subplot(211) + plot(real(spread)); + hold on; + plot(imag(spread),'g'); + hold off; + subplot(212) + plot(real(spread_2ms)); + hold on; + plot(imag(spread_2ms),'g'); + hold off; + + figure(6) + tmp = []; + for i = 1:hf_n-1 + tmp = [tmp abs(hf_model(i,:))]; + end + hist(tmp); +end + end + +size(sim_out.errors_log) + +endfunction + +% Gray coded QPSK modulation function + +function symbol = qpsk_mod(two_bits) + two_bits_decimal = sum(two_bits .* [2 1]); + switch(two_bits_decimal) + case (0) symbol = 1; + case (1) symbol = j; + case (2) symbol = -j; + case (3) symbol = -1; + endswitch +endfunction + +% Gray coded QPSK demodulation function + +function two_bits = qpsk_demod(symbol) + if isscalar(symbol) == 0 + printf("only works with scalars\n"); + return; + end + bit0 = real(symbol*exp(j*pi/4)) < 0; + bit1 = imag(symbol*exp(j*pi/4)) < 0; + two_bits = [bit1 bit0]; +endfunction + +function sim_in = standard_init + sim_in.verbose = 1; + sim_in.plot_scatter = 0; + + sim_in.Esvec = 5; + sim_in.Ntrials = 30; + sim_in.framesize = 576; + sim_in.Rs = 100; + sim_in.Nc = 8; + + sim_in.phase_offset = 0; + sim_in.w_offset = 0; + sim_in.phase_noise_amp = 0; + + sim_in.hf_delay_ms = 2; + sim_in.hf_sim = 0; + sim_in.hf_phase_only = 0; + sim_in.hf_mag_only = 1; + + sim_in.phase_est = 0; + sim_in.phase_est_method = 1; + sim_in.Np = 5; + sim_in.Ns = 5; + + sim_in.ldpc_code_rate = 1/2; + sim_in.ldpc_code = 1; +endfunction + +function ideal + + sim_in = standard_init(); + + sim_in.sim_coh_dpsk = 0; + sim_in.newldpc = 1; + sim_in.verbose = 2; + sim_in.plot_scatter = 1; + + sim_in.Esvec = 5; + sim_in.hf_sim = 1; + sim_in.Ntrials = 30; + + sim_qpsk_hf = ber_test(sim_in, 'qpsk'); + + sim_in.hf_sim = 0; + sim_in.plot_scatter = 0; + sim_in.Esvec = 2:15; + sim_in.ldpc_code = 0; + Ebvec = sim_in.Esvec - 10*log10(2); + BER_theory = 0.5*erfc(sqrt(10.^(Ebvec/10))); + sim_qpsk = ber_test(sim_in, 'qpsk'); + sim_dqpsk = ber_test(sim_in, 'dqpsk'); + + sim_in.hf_sim = 1; + sim_in.Esvec = 2:15; + sim_qpsk_hf = ber_test(sim_in, 'qpsk'); + sim_dqpsk_hf = ber_test(sim_in, 'dqpsk'); + sim_in.ldpc_code = 1; + sim_in.ldpc_code_rate = 3/4; + sim_qpsk_hf_ldpc1 = ber_test(sim_in, 'qpsk'); + sim_in.ldpc_code_rate = 1/2; + sim_qpsk_hf_ldpc2 = ber_test(sim_in, 'qpsk'); + sim_in.ldpc_code_rate = 3/4; + sim_in.hf_sim = 0; + sim_qpsk_awgn_ldpc = ber_test(sim_in, 'qpsk'); + + figure(1); + clf; + semilogy(Ebvec, BER_theory,'r;QPSK theory;') + hold on; + semilogy(sim_qpsk.Ebvec, sim_qpsk.BERvec,'g;QPSK AWGN;') + semilogy(sim_qpsk_hf.Ebvec, sim_qpsk_hf.BERvec,'r;QPSK HF;') + semilogy(sim_dqpsk.Ebvec, sim_dqpsk.BERvec,'c;DQPSK AWGN;') + semilogy(sim_dqpsk_hf.Ebvec, sim_dqpsk_hf.BERvec,'m;DQPSK HF;') + semilogy(sim_qpsk_hf_ldpc1.Ebvec, sim_qpsk_hf_ldpc1.BERldpcvec,'k;QPSK HF LDPC 3/4;') + semilogy(sim_qpsk_hf_ldpc2.Ebvec, sim_qpsk_hf_ldpc2.BERldpcvec,'b;QPSK HF LDPC 1/2;') + semilogy(sim_qpsk_awgn_ldpc.Ebvec, sim_qpsk_awgn_ldpc.BERldpcvec,'k;QPSK AWGN LDPC 3/4;') + + hold off; + xlabel('Eb/N0') + ylabel('BER') + grid("minor") + axis([min(Ebvec) max(Ebvec) 1E-3 1]) +endfunction + +function phase_noise + sim_in = standard_init(); + + sim_in.verbose = 1; + sim_in.plot_scatter = 1; + + sim_in.Esvec = 100; + sim_in.Ntrials = 100; + + sim_in.ldpc_code_rate = 1/2; + sim_in.ldpc_code = 1; + + sim_in.phase_noise_amp = pi/16; + tmp = ber_test(sim_in, 'qpsk'); + + sim_in.plot_scatter = 0; + sim_in.Esvec = 2:8; + sim_qpsk_hf = ber_test(sim_in, 'qpsk'); + + Ebvec = sim_in.Esvec - 10*log10(2); + BER_theory = 0.5*erfc(sqrt(10.^(Ebvec/10))); + + sim_in.phase_noise_amp = 0; + sim_qpsk = ber_test(sim_in, 'qpsk'); + sim_in.phase_noise_amp = pi/8; + sim_qpsk_pn8 = ber_test(sim_in, 'qpsk'); + sim_in.phase_noise_amp = pi/16; + sim_qpsk_pn16 = ber_test(sim_in, 'qpsk'); + sim_in.phase_noise_amp = pi/32; + sim_qpsk_pn32 = ber_test(sim_in, 'qpsk'); + + figure(1); + clf; + semilogy(sim_qpsk.Ebvec, sim_qpsk.BERvec,'g;QPSK phase noise 0;') + hold on; + semilogy(sim_qpsk_pn8.Ebvec, sim_qpsk_pn8.BERvec,'c;QPSK phase noise +/- pi/8;') + semilogy(sim_qpsk_pn16.Ebvec, sim_qpsk_pn16.BERvec,'b;QPSK phase noise +/- pi/16;') + semilogy(sim_qpsk_pn32.Ebvec, sim_qpsk_pn32.BERvec,'k;QPSK phase noise +/- pi/32;') + + semilogy(sim_qpsk.Ebvec, sim_qpsk.BERldpcvec,'g;QPSK phase noise 0 ldpc;') + semilogy(sim_qpsk_pn8.Ebvec, sim_qpsk_pn8.BERldpcvec,'c;QPSK phase noise +/- pi/8 ldpc;') + semilogy(sim_qpsk_pn16.Ebvec, sim_qpsk_pn16.BERldpcvec,'b;QPSK phase noise +/- pi/16 ldpc;') + semilogy(sim_qpsk_pn32.Ebvec, sim_qpsk_pn32.BERldpcvec,'k;QPSK phase noise +/- pi/32 ldpc;') + + hold off; + xlabel('Eb/N0') + ylabel('BER') + grid("minor") + axis([min(Ebvec) max(Ebvec) 1E-2 1]) +endfunction + +function phase_est_hf + sim_in = standard_init(); + + sim_in.Rs = 100; + sim_in.Nc = 8; + + sim_in.verbose = 1; + sim_in.plot_scatter = 0; + + sim_in.Esvec = 5:15; + sim_in.Ntrials = 100; + + sim_in.newldpc = 1; + sim_in.ldpc_code_rate = 1/2; + sim_in.ldpc_code = 1; + + sim_in.phase_est = 0; + sim_in.sim_coh_dpsk = 0; + sim_in.phase_est_method = 2; + sim_in.Np = 3; + sim_in.phase_offset = 0; + sim_in.w_offset = 0; + + sim_in.hf_sim = 1; + sim_in.hf_mag_only = 1; + + Ebvec = sim_in.Esvec - 10*log10(2); + + baseline = ber_test(sim_in, 'qpsk'); + + sim_in.hf_mag_only = 0; + sim_in.phase_est_method = 2; + sim_in.phase_est = 1; + sim_in.Np = 3; + pilot_3 = ber_test(sim_in, 'qpsk'); + sim_in.Np = 5; + pilot_5 = ber_test(sim_in, 'qpsk'); + sim_in.Np = 7; + pilot_7 = ber_test(sim_in, 'qpsk'); + +if 1 + sim_in.phase_est = 0; + dqpsk = ber_test(sim_in, 'dqpsk'); + + figure(1); + clf; + semilogy(baseline.Ebvec, baseline.BERvec,'r;QPSK CCIR poor;') + hold on; + semilogy(baseline.Ebvec, baseline.BERldpcvec,'r;QPSK CCIR poor ldpc;') + semilogy(pilot_3.Ebvec, pilot_3.BERvec,'b;QPSK CCIR poor ldpc pilot 3;') + semilogy(pilot_3.Ebvec, pilot_3.BERldpcvec,'b;QPSK CCIR poor ldpc pilot 3;') + semilogy(pilot_5.Ebvec, pilot_5.BERvec,'g;QPSK CCIR poor ldpc pilot 5;') + semilogy(pilot_5.Ebvec, pilot_5.BERldpcvec,'g;QPSK CCIR poor ldpc pilot 5;') + semilogy(pilot_7.Ebvec, pilot_7.BERvec,'m;QPSK CCIR poor ldpc pilot 7;') + semilogy(pilot_7.Ebvec, pilot_7.BERldpcvec,'m;QPSK CCIR poor ldpc pilot 7;') + semilogy(dqpsk.Ebvec, dqpsk.BERvec,'k;DQPSK CCIR poor ldpc;') + semilogy(dqpsk.Ebvec, dqpsk.BERldpcvec,'k;DQPSK CCIR poor ldpc;') + + hold off; + xlabel('Eb/N0') + ylabel('BER') + grid("minor") + axis([min(Ebvec) max(Ebvec) 1E-2 1]) +end +endfunction + +function phase_est_awgn + sim_in = standard_init(); + + sim_in.Rs = 100; + sim_in.Nc = 8; + + sim_in.verbose = 1; + sim_in.plot_scatter = 0; + + sim_in.Esvec = 0:0.5:3; + sim_in.Ntrials = 30; + + sim_in.newldpc = 1; + sim_in.ldpc_code_rate = 1/2; + sim_in.ldpc_code = 1; + + sim_in.phase_est = 0; + sim_in.phase_est_method = 1; + sim_in.Np = 3; + sim_in.phase_offset = 0; + sim_in.w_offset = 0; + + sim_in.hf_sim = 0; + sim_in.hf_mag_only = 1; + + ideal = ber_test(sim_in, 'qpsk'); + + sim_in.phase_est = 1; + sim_in.Np = 21; + sim_in.phase_est_method = 3; + strip_21_mag = ber_test(sim_in, 'qpsk'); + + sim_in.Np = 41; + strip_41_mag = ber_test(sim_in, 'qpsk'); + + sim_in.phase_est_method = 1; + sim_in.Np = 21; + strip_21 = ber_test(sim_in, 'qpsk'); + + sim_in.Np = 41; + strip_41 = ber_test(sim_in, 'qpsk'); + + sim_in.Np = 7; + sim_in.phase_est_method = 2; + pilot_7 = ber_test(sim_in, 'qpsk'); + + Ebvec = sim_in.Esvec - 10*log10(2); + + figure(1); + clf; + semilogy(ideal.Ebvec, ideal.BERvec,'r;QPSK;') + hold on; + semilogy(ideal.Ebvec, ideal.BERldpcvec,'r;QPSK LDPC;') + semilogy(strip_21.Ebvec, strip_21.BERvec,'g;QPSK strip 21;') + semilogy(strip_21.Ebvec, strip_21.BERldpcvec,'g;QPSK LDPC strip 21;') + semilogy(strip_41.Ebvec, strip_41.BERvec,'b;QPSK strip 41;') + semilogy(strip_41.Ebvec, strip_41.BERldpcvec,'b;QPSK LDPC strip 41;') + semilogy(strip_21_mag.Ebvec, strip_21_mag.BERvec,'m;QPSK strip 21 mag;') + semilogy(strip_21_mag.Ebvec, strip_21_mag.BERldpcvec,'m;QPSK LDPC strip 21 mag;') + semilogy(strip_41_mag.Ebvec, strip_41_mag.BERvec,'c;QPSK strip 41 mag;') + semilogy(strip_41_mag.Ebvec, strip_41_mag.BERldpcvec,'c;QPSK LDPC strip 41 mag;') + semilogy(pilot_7.Ebvec, pilot_7.BERvec,'k;QPSK pilot 7;') + semilogy(pilot_7.Ebvec, pilot_7.BERldpcvec,'k;QPSK LDPC pilot 7;') + + hold off; + xlabel('Eb/N0') + ylabel('BER') + grid("minor") + axis([min(Ebvec) max(Ebvec) 1E-2 1]) +endfunction + +function test_dpsk + sim_in = standard_init(); + + sim_in.Rs = 100; + sim_in.Nc = 8; + + sim_in.verbose = 1; + sim_in.plot_scatter = 0; + + sim_in.Esvec = 5; + sim_in.Ntrials = 30; + + sim_in.newldpc = 1; + sim_in.ldpc_code_rate = 1/2; + sim_in.ldpc_code = 1; + + sim_in.phase_est = 0; + sim_in.phase_est_method = 3; + sim_in.Np = 41; + sim_in.phase_offset = 0; + sim_in.w_offset = 0; + sim_in.sim_coh_dpsk = 0; + + sim_in.hf_sim = 0; + sim_in.hf_mag_only = 1; + + Ebvec = sim_in.Esvec - 10*log10(2); + + baseline = ber_test(sim_in, 'qpsk'); + sim_in.phase_est = 0; + dqpsk = ber_test(sim_in, 'dqpsk'); + + sim_in.phase_est = 1; + sim_in.phase_est_method = 3; + sim_in.sim_coh_dpsk = 1; + sim_in.Np = 41; + dqpsk_strip_41 = ber_test(sim_in, 'dqpsk'); + + figure(1); + clf; + semilogy(baseline.Ebvec, baseline.BERvec,'r;QPSK CCIR poor;') + hold on; + semilogy(baseline.Ebvec, baseline.BERldpcvec,'r;QPSK CCIR poor ldpc;') + semilogy(dqpsk.Ebvec, dqpsk.BERvec,'c;DQPSK CCIR poor ldpc;') + semilogy(dqpsk.Ebvec, dqpsk.BERldpcvec,'c;DQPSK CCIR poor ldpc;') + semilogy(dqpsk_strip_41.Ebvec, dqpsk_strip_41.BERvec,'m;DQPSK CCIR poor ldpc strip 41;') + semilogy(dqpsk_strip_41.Ebvec, dqpsk_strip_41.BERldpcvec,'m;DQPSK CCIR poor ldpc strip 41;') + + hold off; + xlabel('Eb/N0') + ylabel('BER') + grid("minor") + axis([min(Ebvec) max(Ebvec) 1E-2 1]) + +endfunction + +function gen_error_pattern_qpsk() + sim_in = standard_init(); + + % model codec and uncoded streams as 1000 bit/s each + + sim_in.Rs = 100; + sim_in.Nc = 4; + + sim_in.verbose = 1; + sim_in.plot_scatter = 0; + + sim_in.Esvec = 10; % Eb/No=2dB + sim_in.Ntrials = 30; + + sim_in.newldpc = 1; + sim_in.ldpc_code_rate = 1/2; + sim_in.ldpc_code = 1; + + sim_in.phase_est = 1; + sim_in.phase_est_method = 2; + sim_in.Np = 5; + sim_in.phase_offset = 0; + sim_in.w_offset = 0; + sim_in.sim_coh_dpsk = 0; + + sim_in.hf_sim = 1; + sim_in.hf_mag_only = 0; + + qpsk = ber_test(sim_in, 'qpsk'); + + length(qpsk.errors_log) + length(qpsk.ldpc_errors_log) + % multiplex errors into prot and unprot halves of 52 bit codec frames + + error_pattern = []; + for i=1:26:length(qpsk.ldpc_errors_log)-52 + error_pattern = [error_pattern qpsk.ldpc_errors_log(i:i+25) qpsk.errors_log(i:i+25) zeros(1,4)]; + %error_pattern = [error_pattern qpsk.ldpc_errors_log(i:i+25) zeros(1,26) zeros(1,4)]; + %error_pattern = [error_pattern zeros(1,26) qpsk.errors_log(i:i+25) zeros(1,4)]; + end + + fep=fopen("qpsk_errors_2dB.bin","wb"); fwrite(fep, error_pattern, "short"); fclose(fep); + +endfunction + +function gen_error_pattern_dpsk() + sim_in = standard_init(); + + sim_in.Rs = 50; + sim_in.Nc = 16; + + sim_in.verbose = 1; + sim_in.plot_scatter = 1; + + sim_in.Esvec = 10; % Eb/No=Es/No-3 + sim_in.Ntrials = 30; + + sim_in.newldpc = 1; + sim_in.ldpc_code_rate = 1/2; + sim_in.ldpc_code = 0; + + sim_in.phase_est = 0; + sim_in.phase_est_method = 3; + sim_in.Np = 41; + sim_in.phase_offset = 0; + sim_in.w_offset = 0; + sim_in.sim_coh_dpsk = 0; + + sim_in.hf_sim = 1; + sim_in.hf_mag_only = 1; + + dqpsk = ber_test(sim_in, 'dqpsk'); + + fep=fopen("dqpsk_errors_12dB.bin","wb"); fwrite(fep, dqpsk.errors_log, "short"); fclose(fep); + +endfunction + +% Start simulations --------------------------------------- + +more off; + +ideal(); +%phase_est_hf(); +%phase_est_awgn(); +%test_dpsk(); +%gen_error_pattern_qpsk diff --git a/codec2-dev/stm32/inc/stm32f4xx_conf.h b/codec2-dev/stm32/inc/stm32f4xx_conf.h index a791166d..74447a8f 100644 --- a/codec2-dev/stm32/inc/stm32f4xx_conf.h +++ b/codec2-dev/stm32/inc/stm32f4xx_conf.h @@ -1,94 +1,94 @@ -/** - ****************************************************************************** - * @file stm32f4xx_conf.h - * @author MCD Application Team - * @version V1.0.0 - * @date 19-September-2011 - * @brief Library configuration file. - ****************************************************************************** - * @attention - * - * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS - * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE - * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY - * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING - * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE - * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. - * - *

© COPYRIGHT 2011 STMicroelectronics

- ****************************************************************************** - */ - -/* Define to prevent recursive inclusion -------------------------------------*/ -#ifndef __STM32F4xx_CONF_H -#define __STM32F4xx_CONF_H - -#if defined (HSE_VALUE) -/* Redefine the HSE value; it's equal to 8 MHz on the STM32F4-DISCOVERY Kit */ - #undef HSE_VALUE - #define HSE_VALUE ((uint32_t)8000000) -#endif /* HSE_VALUE */ - -/* Includes ------------------------------------------------------------------*/ -/* Uncomment the line below to enable peripheral header file inclusion */ -#include "stm32f4xx_adc.h" -#include "stm32f4xx_can.h" -#include "stm32f4xx_crc.h" -#include "stm32f4xx_cryp.h" -#include "stm32f4xx_dac.h" -#include "stm32f4xx_dbgmcu.h" -#include "stm32f4xx_dcmi.h" -#include "stm32f4xx_dma.h" -#include "stm32f4xx_exti.h" -#include "stm32f4xx_flash.h" -#include "stm32f4xx_fsmc.h" -#include "stm32f4xx_hash.h" -#include "stm32f4xx_gpio.h" -#include "stm32f4xx_i2c.h" -#include "stm32f4xx_iwdg.h" -#include "stm32f4xx_pwr.h" -#include "stm32f4xx_rcc.h" -#include "stm32f4xx_rng.h" -#include "stm32f4xx_rtc.h" -#include "stm32f4xx_sdio.h" -#include "stm32f4xx_spi.h" -#include "stm32f4xx_syscfg.h" -#include "stm32f4xx_tim.h" -#include "stm32f4xx_usart.h" -#include "stm32f4xx_wwdg.h" -#include "misc.h" /* High level functions for NVIC and SysTick (add-on to CMSIS functions) */ - -/* Exported types ------------------------------------------------------------*/ -/* Exported constants --------------------------------------------------------*/ - -/* If an external clock source is used, then the value of the following define - should be set to the value of the external clock source, else, if no external - clock is used, keep this define commented */ -/*#define I2S_EXTERNAL_CLOCK_VAL 12288000 */ /* Value of the external clock in Hz */ - - -/* Uncomment the line below to expanse the "assert_param" macro in the - Standard Peripheral Library drivers code */ -/* #define USE_FULL_ASSERT 1 */ - -/* Exported macro ------------------------------------------------------------*/ -#ifdef USE_FULL_ASSERT - -/** - * @brief The assert_param macro is used for function's parameters check. - * @param expr: If expr is false, it calls assert_failed function - * which reports the name of the source file and the source - * line number of the call that failed. - * If expr is true, it returns no value. - * @retval None - */ - #define assert_param(expr) ((expr) ? (void)0 : assert_failed((uint8_t *)__FILE__, __LINE__)) -/* Exported functions ------------------------------------------------------- */ - void assert_failed(uint8_t* file, uint32_t line); -#else - #define assert_param(expr) ((void)0) -#endif /* USE_FULL_ASSERT */ - -#endif /* __STM32F4xx_CONF_H */ - -/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/ +/** + ****************************************************************************** + * @file stm32f4xx_conf.h + * @author MCD Application Team + * @version V1.0.0 + * @date 19-September-2011 + * @brief Library configuration file. + ****************************************************************************** + * @attention + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2011 STMicroelectronics

+ ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __STM32F4xx_CONF_H +#define __STM32F4xx_CONF_H + +#if defined (HSE_VALUE) +/* Redefine the HSE value; it's equal to 8 MHz on the STM32F4-DISCOVERY Kit */ + #undef HSE_VALUE + #define HSE_VALUE ((uint32_t)8000000) +#endif /* HSE_VALUE */ + +/* Includes ------------------------------------------------------------------*/ +/* Uncomment the line below to enable peripheral header file inclusion */ +#include "stm32f4xx_adc.h" +#include "stm32f4xx_can.h" +#include "stm32f4xx_crc.h" +#include "stm32f4xx_cryp.h" +#include "stm32f4xx_dac.h" +#include "stm32f4xx_dbgmcu.h" +#include "stm32f4xx_dcmi.h" +#include "stm32f4xx_dma.h" +#include "stm32f4xx_exti.h" +#include "stm32f4xx_flash.h" +#include "stm32f4xx_fsmc.h" +#include "stm32f4xx_hash.h" +#include "stm32f4xx_gpio.h" +#include "stm32f4xx_i2c.h" +#include "stm32f4xx_iwdg.h" +#include "stm32f4xx_pwr.h" +#include "stm32f4xx_rcc.h" +#include "stm32f4xx_rng.h" +#include "stm32f4xx_rtc.h" +#include "stm32f4xx_sdio.h" +#include "stm32f4xx_spi.h" +#include "stm32f4xx_syscfg.h" +#include "stm32f4xx_tim.h" +#include "stm32f4xx_usart.h" +#include "stm32f4xx_wwdg.h" +#include "misc.h" /* High level functions for NVIC and SysTick (add-on to CMSIS functions) */ + +/* Exported types ------------------------------------------------------------*/ +/* Exported constants --------------------------------------------------------*/ + +/* If an external clock source is used, then the value of the following define + should be set to the value of the external clock source, else, if no external + clock is used, keep this define commented */ +/*#define I2S_EXTERNAL_CLOCK_VAL 12288000 */ /* Value of the external clock in Hz */ + + +/* Uncomment the line below to expanse the "assert_param" macro in the + Standard Peripheral Library drivers code */ +/* #define USE_FULL_ASSERT 1 */ + +/* Exported macro ------------------------------------------------------------*/ +#ifdef USE_FULL_ASSERT + +/** + * @brief The assert_param macro is used for function's parameters check. + * @param expr: If expr is false, it calls assert_failed function + * which reports the name of the source file and the source + * line number of the call that failed. + * If expr is true, it returns no value. + * @retval None + */ + #define assert_param(expr) ((expr) ? (void)0 : assert_failed((uint8_t *)__FILE__, __LINE__)) +/* Exported functions ------------------------------------------------------- */ + void assert_failed(uint8_t* file, uint32_t line); +#else + #define assert_param(expr) ((void)0) +#endif /* USE_FULL_ASSERT */ + +#endif /* __STM32F4xx_CONF_H */ + +/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/ diff --git a/codec2-dev/stm32/src/adc_rec.c b/codec2-dev/stm32/src/adc_rec.c index c31c899d..4f6574bd 100644 --- a/codec2-dev/stm32/src/adc_rec.c +++ b/codec2-dev/stm32/src/adc_rec.c @@ -1,76 +1,76 @@ -/*---------------------------------------------------------------------------*\ - - FILE........: adc_rec.c - AUTHOR......: David Rowe - DATE CREATED: 30 May 2014 - - Records a 16 kHz sample rate raw file from one of the ADC channels, - which are connected to pins PA1 (ADC1) and PA2 (ADC2). - - Note the semi-hosting system isn't fast enough to transfer 2 16 kHz - streams at once. - - ~/stlink$ sudo ./st-util -f ~/codec2-dev/stm32/adc_rec.elf - ~/codec2-dev/stm32$ ~/gcc-arm-none-eabi-4_7-2013q1/bin/arm-none-eabi-gdb adc_rec.elf - - (when finished) - $ play -r 16000 -s -2 ~/stlink/adc.raw - - adc1 -> "from radio" - adc2 -> "mic amp" - -\*---------------------------------------------------------------------------*/ - -/* - Copyright (C) 2014 David Rowe - - All rights reserved. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License version 2.1, as - published by the Free Software Foundation. This program is - distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or - FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public - License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program; if not, see . -*/ - -#include -#include "stm32f4_adc.h" -#include "gdb_stdio.h" -#include "stm32f4xx_gpio.h" - -#define REC_TIME_SECS 10 -#define N (ADC_BUF_SZ*6) -#define FS 16000 - -extern int adc_overflow1; -extern int adc_overflow2; - -int main(void){ - short buf[N]; - FILE *fadc; - int i, bufs; - - fadc = fopen("adc.raw", "wb"); - if (fadc == NULL) { - printf("Error opening input file: adc.raw\n\nTerminating....\n"); - exit(1); - } - bufs = FS*REC_TIME_SECS/N; - - printf("Starting!\n"); - adc_open(4*N); - - for(i=0; i "from radio" + adc2 -> "mic amp" + +\*---------------------------------------------------------------------------*/ + +/* + Copyright (C) 2014 David Rowe + + All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 2.1, as + published by the Free Software Foundation. This program is + distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, see . +*/ + +#include +#include "stm32f4_adc.h" +#include "gdb_stdio.h" +#include "stm32f4xx_gpio.h" + +#define REC_TIME_SECS 10 +#define N (ADC_BUF_SZ*6) +#define FS 16000 + +extern int adc_overflow1; +extern int adc_overflow2; + +int main(void){ + short buf[N]; + FILE *fadc; + int i, bufs; + + fadc = fopen("adc.raw", "wb"); + if (fadc == NULL) { + printf("Error opening input file: adc.raw\n\nTerminating....\n"); + exit(1); + } + bufs = FS*REC_TIME_SECS/N; + + printf("Starting!\n"); + adc_open(4*N); + + for(i=0; i. -*/ - -#include -#include -#include "stm32f4_adc.h" -#include "stm32f4_dac.h" -#include "gdb_stdio.h" - -#define REC_TIME_SECS 10 -#define N (ADC_BUF_SZ*4) -#define FS 16000 - -static float calc_sd(short x[], int n) { - float sum, mean, sum_diff, sd; - int i; - - sum = 0.0; - for(i=0; i. +*/ + +#include +#include +#include "stm32f4_adc.h" +#include "stm32f4_dac.h" +#include "gdb_stdio.h" + +#define REC_TIME_SECS 10 +#define N (ADC_BUF_SZ*4) +#define FS 16000 + +static float calc_sd(short x[], int n) { + float sum, mean, sum_diff, sd; + int i; + + sum = 0.0; + for(i=0; i. -*/ - -#include -#include -#include "gdb_stdio.h" -#include "stm32f4_dac.h" -#include "stm32f4_adc_tuner.h" -#include "iir_tuner.h" -#include "sm1000_leds_switches.h" -#include "../src/codec2_fm.h" -#include "stm32f4xx.h" - -#define BUFS 10 -#define FS 2E6 -#define N 1024 - -extern int adc_overflow1; - -int main(void) { - unsigned short unsigned_buf[N]; - short buf[N]; - int sam; - int i, j, fifo_sz; - FILE *fadc; - - fadc = fopen("adc.raw", "wb"); - if (fadc == NULL) { - printf("Error opening output file: adc.raw\n\nTerminating....\n"); - exit(1); - } - fifo_sz = ADC_TUNER_BUF_SZ; - printf("Starting! bufs: %d %d\n", BUFS, fifo_sz); - - adc_open(fifo_sz); - adc_set_tuner_en(0); /* dump raw samples, no tuner */ - - sm1000_leds_switches_init(); - - for (i=0; iODR |= (1 << 3); - fwrite(buf, sizeof(short), N, fadc); - GPIOE->ODR &= ~(1 << 3); - } - fclose(fadc); - - printf("Finished!\n"); -} - +/*---------------------------------------------------------------------------*\ + + FILE........: adc_sfdr_ut.c + AUTHOR......: David Rowe + DATE CREATED: August 2015 + + Unit test for high speed ADC SFDR testing. Samples ADC1 from in PA1 at + Fs=2 MHz and write raw samples to a file, in discontinuus blocks of + ADC_TUNER_BUF_SZ/2 samples. The blocks are discontinuous as we + don'thave the bandwitdh back to the host to support continuous sampling. + + To process the blocks, fread() ADC_TUNER_BUF_SZ/2 samples at a time, + abs(fft) and sum results from next block. + +\*---------------------------------------------------------------------------*/ + +/* + Copyright (C) 2015 David Rowe + + All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 2.1, as + published by the Free Software Foundation. This program is + distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, see . +*/ + +#include +#include +#include "gdb_stdio.h" +#include "stm32f4_dac.h" +#include "stm32f4_adc_tuner.h" +#include "iir_tuner.h" +#include "sm1000_leds_switches.h" +#include "../src/codec2_fm.h" +#include "stm32f4xx.h" + +#define BUFS 10 +#define FS 2E6 +#define N 1024 + +extern int adc_overflow1; + +int main(void) { + unsigned short unsigned_buf[N]; + short buf[N]; + int sam; + int i, j, fifo_sz; + FILE *fadc; + + fadc = fopen("adc.raw", "wb"); + if (fadc == NULL) { + printf("Error opening output file: adc.raw\n\nTerminating....\n"); + exit(1); + } + fifo_sz = ADC_TUNER_BUF_SZ; + printf("Starting! bufs: %d %d\n", BUFS, fifo_sz); + + adc_open(fifo_sz); + adc_set_tuner_en(0); /* dump raw samples, no tuner */ + + sm1000_leds_switches_init(); + + for (i=0; iODR |= (1 << 3); + fwrite(buf, sizeof(short), N, fadc); + GPIOE->ODR &= ~(1 << 3); + } + fclose(fadc); + + printf("Finished!\n"); +} + diff --git a/codec2-dev/stm32/src/adcdac_ut.c b/codec2-dev/stm32/src/adcdac_ut.c index 423f45aa..e79fc5a2 100644 --- a/codec2-dev/stm32/src/adcdac_ut.c +++ b/codec2-dev/stm32/src/adcdac_ut.c @@ -1,70 +1,70 @@ -/*---------------------------------------------------------------------------*\ - - FILE........: adcdac_ut.c - AUTHOR......: David Rowe - DATE CREATED: May 31 201310 Aug 2014 - - Echoes ADC2 input (mic) to DAC2 output (speaker) on SM1000. - -\*---------------------------------------------------------------------------*/ - -/* - Copyright (C) 2013 David Rowe - - All rights reserved. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License version 2.1, as - published by the Free Software Foundation. This program is - distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or - FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public - License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program; if not, see . -*/ - -#include -#include "stm32f4_dac.h" -#include "stm32f4_adc.h" -#include "sm1000_leds_switches.h" - -#define SINE_SAMPLES 32 - - -/* 32 sample sine wave which at Fs=16kHz will be 500Hz. Note samples - are 16 bit 2's complement, the DAC driver convertsto 12 bit - unsigned. */ - -short aSine[] = { - -16, 6384, 12528, 18192, 23200, 27232, 30256, 32128, - 32752, 32128, 30256, 27232, 23152, 18192, 12528, 6384, - -16, -6416, -12560, -18224, -23184, -27264, -30288, -32160, - -32768, -32160, -30288, -27264, -23184, -18224, -12560, -6416 -}; - -int main(void) { - short buf[SINE_SAMPLES]; - int i; - - dac_open(4*DAC_BUF_SZ); - adc_open(4*ADC_BUF_SZ); - sm1000_leds_switches_init(); - - while (1) { - - /* keep DAC FIFOs topped up */ - - while(adc2_read(buf, SINE_SAMPLES) == -1); - - if (!switch_select()) { - for(i=0; i. +*/ + +#include +#include "stm32f4_dac.h" +#include "stm32f4_adc.h" +#include "sm1000_leds_switches.h" + +#define SINE_SAMPLES 32 + + +/* 32 sample sine wave which at Fs=16kHz will be 500Hz. Note samples + are 16 bit 2's complement, the DAC driver convertsto 12 bit + unsigned. */ + +short aSine[] = { + -16, 6384, 12528, 18192, 23200, 27232, 30256, 32128, + 32752, 32128, 30256, 27232, 23152, 18192, 12528, 6384, + -16, -6416, -12560, -18224, -23184, -27264, -30288, -32160, + -32768, -32160, -30288, -27264, -23184, -18224, -12560, -6416 +}; + +int main(void) { + short buf[SINE_SAMPLES]; + int i; + + dac_open(4*DAC_BUF_SZ); + adc_open(4*ADC_BUF_SZ); + sm1000_leds_switches_init(); + + while (1) { + + /* keep DAC FIFOs topped up */ + + while(adc2_read(buf, SINE_SAMPLES) == -1); + + if (!switch_select()) { + for(i=0; i. -*/ - -#include -#include -#include -#include - -#include "stm32f4xx_conf.h" -#include "stm32f4xx.h" -#include "gdb_stdio.h" -#include "codec2.h" -#include "dump.h" -#include "sine.h" -#include "machdep.h" - -#ifdef __EMBEDDED__ -#define printf gdb_stdio_printf -#define fopen gdb_stdio_fopen -#define fclose gdb_stdio_fclose -#define fread gdb_stdio_fread -#define fwrite gdb_stdio_fwrite -#endif - -static void c2demo(int mode, char inputfile[], char outputfile[]) -{ - struct CODEC2 *codec2; - short *inbuf, *outbuf; - unsigned char *bits; - int nsam, nbit; - FILE *fin, *fout; - int frame; - PROFILE_VAR(enc_start, dec_start); - - codec2 = codec2_create(mode); - nsam = codec2_samples_per_frame(codec2); - outbuf = (short*)malloc(nsam*sizeof(short)); - inbuf = (short*)malloc(nsam*sizeof(short)); - nbit = codec2_bits_per_frame(codec2); - bits = (unsigned char*)malloc(nbit*sizeof(char)); - - fin = fopen(inputfile, "rb"); - if (fin == NULL) { - printf("Error opening input file: %s\n\nTerminating....\n",inputfile); - exit(1); - } - - fout = fopen(outputfile, "wb"); - if (fout == NULL) { - printf("Error opening output file: %s\n\nTerminating....\n",outputfile); - exit(1); - } - - #ifdef DUMP - dump_on("stm32f4"); - #endif - frame = 0; - - while (fread(inbuf, sizeof(short), nsam, fin) == nsam) { - PROFILE_SAMPLE(enc_start); - codec2_encode(codec2, bits, inbuf); - PROFILE_SAMPLE_AND_LOG(dec_start, enc_start, " enc"); - codec2_decode(codec2, outbuf, bits); - PROFILE_SAMPLE_AND_LOG2(dec_start, " dec"); - PROFILE_SAMPLE_AND_LOG2(enc_start, " enc & dec"); - fwrite((char*)outbuf, sizeof(short), nsam, fout); - printf("frame: %d\n", ++frame); - machdep_profile_print_logged_samples(); - } - - #ifdef DUMP - dump_off("sm32f4"); - #endif - - fclose(fin); - fclose(fout); - free(inbuf); - free(outbuf); - free(bits); - codec2_destroy(codec2); -} - -#define SPEED_TEST_SAMPLES 24000 - -static void c2speedtest(int mode, char inputfile[]) -{ - struct CODEC2 *codec2; - short *inbuf, *outbuf, *pinbuf; - unsigned char *bits; - int nsam, nbit, nframes; - FILE *fin; - int f, nread; - - codec2 = codec2_create(mode); - nsam = codec2_samples_per_frame(codec2); - nframes = SPEED_TEST_SAMPLES/nsam; - outbuf = (short*)malloc(nsam*sizeof(short)); - inbuf = (short*)malloc(SPEED_TEST_SAMPLES*sizeof(short)); - nbit = codec2_bits_per_frame(codec2); - bits = (unsigned char*)malloc(nbit*sizeof(char)); - - fin = fopen(inputfile, "rb"); - if (fin == NULL) { - printf("Error opening input file: %s\nTerminating....\n",inputfile); - exit(1); - } - - nread = fread(inbuf, sizeof(short), SPEED_TEST_SAMPLES, fin); - if (nread != SPEED_TEST_SAMPLES) { - printf("error reading %s, %d samples reqd, %d read\n", - inputfile, SPEED_TEST_SAMPLES, nread); - } - fclose(fin); - - pinbuf = inbuf; - for(f=0; fODR = (1 << 13); - codec2_encode(codec2, bits, pinbuf); - pinbuf += nsam; - GPIOD->ODR &= ~(1 << 13); - codec2_decode(codec2, outbuf, bits); - } - - free(inbuf); - free(outbuf); - free(bits); - codec2_destroy(codec2); -} - -void gpio_init() { - RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN; // enable the clock to GPIOD - GPIOD->MODER = (1 << 26); // set pin 13 to be general - // purpose output -} - -int main(int argc, char *argv[]) { - gpio_init(); - machdep_profile_init (); - - printf("Starting c2demo\n"); - - /* File I/O test for profiling or (with #define DUMP) - dumping states for optimisation and tiuning */ - - c2demo(CODEC2_MODE_1600, "stm_in.raw", "stm_out.raw"); - - printf("Starting c2 speed test\n"); - - /* Another test of execution speed. Look at PD13 with a - oscilliscope. On time is enc, off is dec */ - - c2speedtest(CODEC2_MODE_1600, "stm_in.raw"); - - printf("Finished\n"); - - return 0; -} - +/*---------------------------------------------------------------------------*\ + + FILE........: codec2_profile.c + AUTHOR......: David Rowe + DATE CREATED: 30 May 2013 + + Profiling Codec 2 operation on the STM32F4. + +\*---------------------------------------------------------------------------*/ + +/* + Copyright (C) 2014 David Rowe + + All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 2.1, as + published by the Free Software Foundation. This program is + distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, see . +*/ + +#include +#include +#include +#include + +#include "stm32f4xx_conf.h" +#include "stm32f4xx.h" +#include "gdb_stdio.h" +#include "codec2.h" +#include "dump.h" +#include "sine.h" +#include "machdep.h" + +#ifdef __EMBEDDED__ +#define printf gdb_stdio_printf +#define fopen gdb_stdio_fopen +#define fclose gdb_stdio_fclose +#define fread gdb_stdio_fread +#define fwrite gdb_stdio_fwrite +#endif + +static void c2demo(int mode, char inputfile[], char outputfile[]) +{ + struct CODEC2 *codec2; + short *inbuf, *outbuf; + unsigned char *bits; + int nsam, nbit; + FILE *fin, *fout; + int frame; + PROFILE_VAR(enc_start, dec_start); + + codec2 = codec2_create(mode); + nsam = codec2_samples_per_frame(codec2); + outbuf = (short*)malloc(nsam*sizeof(short)); + inbuf = (short*)malloc(nsam*sizeof(short)); + nbit = codec2_bits_per_frame(codec2); + bits = (unsigned char*)malloc(nbit*sizeof(char)); + + fin = fopen(inputfile, "rb"); + if (fin == NULL) { + printf("Error opening input file: %s\n\nTerminating....\n",inputfile); + exit(1); + } + + fout = fopen(outputfile, "wb"); + if (fout == NULL) { + printf("Error opening output file: %s\n\nTerminating....\n",outputfile); + exit(1); + } + + #ifdef DUMP + dump_on("stm32f4"); + #endif + frame = 0; + + while (fread(inbuf, sizeof(short), nsam, fin) == nsam) { + PROFILE_SAMPLE(enc_start); + codec2_encode(codec2, bits, inbuf); + PROFILE_SAMPLE_AND_LOG(dec_start, enc_start, " enc"); + codec2_decode(codec2, outbuf, bits); + PROFILE_SAMPLE_AND_LOG2(dec_start, " dec"); + PROFILE_SAMPLE_AND_LOG2(enc_start, " enc & dec"); + fwrite((char*)outbuf, sizeof(short), nsam, fout); + printf("frame: %d\n", ++frame); + machdep_profile_print_logged_samples(); + } + + #ifdef DUMP + dump_off("sm32f4"); + #endif + + fclose(fin); + fclose(fout); + free(inbuf); + free(outbuf); + free(bits); + codec2_destroy(codec2); +} + +#define SPEED_TEST_SAMPLES 24000 + +static void c2speedtest(int mode, char inputfile[]) +{ + struct CODEC2 *codec2; + short *inbuf, *outbuf, *pinbuf; + unsigned char *bits; + int nsam, nbit, nframes; + FILE *fin; + int f, nread; + + codec2 = codec2_create(mode); + nsam = codec2_samples_per_frame(codec2); + nframes = SPEED_TEST_SAMPLES/nsam; + outbuf = (short*)malloc(nsam*sizeof(short)); + inbuf = (short*)malloc(SPEED_TEST_SAMPLES*sizeof(short)); + nbit = codec2_bits_per_frame(codec2); + bits = (unsigned char*)malloc(nbit*sizeof(char)); + + fin = fopen(inputfile, "rb"); + if (fin == NULL) { + printf("Error opening input file: %s\nTerminating....\n",inputfile); + exit(1); + } + + nread = fread(inbuf, sizeof(short), SPEED_TEST_SAMPLES, fin); + if (nread != SPEED_TEST_SAMPLES) { + printf("error reading %s, %d samples reqd, %d read\n", + inputfile, SPEED_TEST_SAMPLES, nread); + } + fclose(fin); + + pinbuf = inbuf; + for(f=0; fODR = (1 << 13); + codec2_encode(codec2, bits, pinbuf); + pinbuf += nsam; + GPIOD->ODR &= ~(1 << 13); + codec2_decode(codec2, outbuf, bits); + } + + free(inbuf); + free(outbuf); + free(bits); + codec2_destroy(codec2); +} + +void gpio_init() { + RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN; // enable the clock to GPIOD + GPIOD->MODER = (1 << 26); // set pin 13 to be general + // purpose output +} + +int main(int argc, char *argv[]) { + gpio_init(); + machdep_profile_init (); + + printf("Starting c2demo\n"); + + /* File I/O test for profiling or (with #define DUMP) + dumping states for optimisation and tiuning */ + + c2demo(CODEC2_MODE_1600, "stm_in.raw", "stm_out.raw"); + + printf("Starting c2 speed test\n"); + + /* Another test of execution speed. Look at PD13 with a + oscilliscope. On time is enc, off is dec */ + + c2speedtest(CODEC2_MODE_1600, "stm_in.raw"); + + printf("Finished\n"); + + return 0; +} + diff --git a/codec2-dev/stm32/src/dac_it.c b/codec2-dev/stm32/src/dac_it.c index 2b614fc4..83803752 100644 --- a/codec2-dev/stm32/src/dac_it.c +++ b/codec2-dev/stm32/src/dac_it.c @@ -1,205 +1,205 @@ -/** - ****************************************************************************** - * @file DMA/DMA_FLASHToRAM/stm32f4xx_it.c - * @author MCD Application Team - * @version V1.1.0 - * @date 18-January-2013 - * @brief Main Interrupt Service Routines. - * This file provides template for all exceptions handler and - * peripherals interrupt service routine. - ****************************************************************************** - * @attention - * - *

© COPYRIGHT 2013 STMicroelectronics

- * - * Licensed under MCD-ST Liberty SW License Agreement V2, (the "License"); - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.st.com/software_license_agreement_liberty_v2 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - ****************************************************************************** - */ -int interrupts; - - -/* Includes ------------------------------------------------------------------*/ -#include "dac_it.h" - -/** @addtogroup STM32F4xx_StdPeriph_Examples - * @{ - */ - -/** @addtogroup DMA_FLASHToRAM - * @{ - */ - -/* Private typedef -----------------------------------------------------------*/ -/* Private define ------------------------------------------------------------*/ -/* Private macro -------------------------------------------------------------*/ -/* Private variables ---------------------------------------------------------*/ -/* Private function prototypes -----------------------------------------------*/ -/* Private functions ---------------------------------------------------------*/ - -/******************************************************************************/ -/* Cortex-M4 Processor Exceptions Handlers */ -/******************************************************************************/ - -/** - * @brief This function handles NMI exception. - * @param None - * @retval None - */ -void NMI_Handler(void) -{ -} - -/** - * @brief This function handles Hard Fault exception. - * @param None - * @retval None - */ -void HardFault_Handler(void) -{ - /* Go to infinite loop when Hard Fault exception occurs */ - while (1) - { - } -} - -/** - * @brief This function handles Memory Manage exception. - * @param None - * @retval None - */ -void MemManage_Handler(void) -{ - /* Go to infinite loop when Memory Manage exception occurs */ - while (1) - { - } -} - -/** - * @brief This function handles Bus Fault exception. - * @param None - * @retval None - */ -void BusFault_Handler(void) -{ - /* Go to infinite loop when Bus Fault exception occurs */ - while (1) - { - } -} - -/** - * @brief This function handles Usage Fault exception. - * @param None - * @retval None - */ -void UsageFault_Handler(void) -{ - /* Go to infinite loop when Usage Fault exception occurs */ - while (1) - { - } -} - -/** - * @brief This function handles SVCall exception. - * @param None - * @retval None - */ -void SVC_Handler(void) -{ -} - -/** - * @brief This function handles Debug Monitor exception. - * @param None - * @retval None - */ -void DebugMon_Handler(void) -{ -} - -/** - * @brief This function handles PendSVC exception. - * @param None - * @retval None - */ -void PendSV_Handler(void) -{ -} - -/** - * @brief This function handles SysTick Handler. - * @param None - * @retval None - */ -void SysTick_Handler(void) -{ -} - -/******************************************************************************/ -/* STM32F4xx Peripherals Interrupt Handlers */ -/* Add here the Interrupt Handler for the used peripheral(s) (PPP), for the */ -/* available peripheral interrupt handler's name please refer to the startup */ -/* file (startup_stm32f40xx.s/startup_stm32f427x.s). */ -/******************************************************************************/ - -/** - * @brief This function handles DMA Stream interrupt request. - * @param None - * @retval None - */ -void DMA1_Stream6_IRQHandler(void) -{ - - /* Transfer half empty interrupt */ - - if(DMA_GetITStatus(DMA1_Stream6, DMA_IT_HTIF6) != RESET)) - { - /* fill first half from fifo */ - - fifo_read(DMA1_Stream6_fifo, dac_buf, DAC_BUF_SZ/2); - - /* Clear DMA Stream Transfer Complete interrupt pending bit */ - - DMA_ClearITPendingBit(DMA1_Stream6, DMA_IT_HTIF6); - - interrupts++; - } - - /* Transfer complete interrupt */ - - if(DMA_GetITStatus(DMA1_Stream6, DMA_IT_TCIF6) != RESET)) - { - /* fill second half from fifo */ - - fifo_read(DMA1_Stream6_fifo, &dac_buf[DAC_BUF_SZ/2], DAC_BUF_SZ/2); - - /* Clear DMA Stream Transfer Complete interrupt pending bit */ - - DMA_ClearITPendingBit(DMA1_Stream6, DMA_IT_TCIF6); - - interrupts++; - } -} - -/** - * @} - */ - -/** - * @} - */ - -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ +/** + ****************************************************************************** + * @file DMA/DMA_FLASHToRAM/stm32f4xx_it.c + * @author MCD Application Team + * @version V1.1.0 + * @date 18-January-2013 + * @brief Main Interrupt Service Routines. + * This file provides template for all exceptions handler and + * peripherals interrupt service routine. + ****************************************************************************** + * @attention + * + *

© COPYRIGHT 2013 STMicroelectronics

+ * + * Licensed under MCD-ST Liberty SW License Agreement V2, (the "License"); + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.st.com/software_license_agreement_liberty_v2 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ****************************************************************************** + */ +int interrupts; + + +/* Includes ------------------------------------------------------------------*/ +#include "dac_it.h" + +/** @addtogroup STM32F4xx_StdPeriph_Examples + * @{ + */ + +/** @addtogroup DMA_FLASHToRAM + * @{ + */ + +/* Private typedef -----------------------------------------------------------*/ +/* Private define ------------------------------------------------------------*/ +/* Private macro -------------------------------------------------------------*/ +/* Private variables ---------------------------------------------------------*/ +/* Private function prototypes -----------------------------------------------*/ +/* Private functions ---------------------------------------------------------*/ + +/******************************************************************************/ +/* Cortex-M4 Processor Exceptions Handlers */ +/******************************************************************************/ + +/** + * @brief This function handles NMI exception. + * @param None + * @retval None + */ +void NMI_Handler(void) +{ +} + +/** + * @brief This function handles Hard Fault exception. + * @param None + * @retval None + */ +void HardFault_Handler(void) +{ + /* Go to infinite loop when Hard Fault exception occurs */ + while (1) + { + } +} + +/** + * @brief This function handles Memory Manage exception. + * @param None + * @retval None + */ +void MemManage_Handler(void) +{ + /* Go to infinite loop when Memory Manage exception occurs */ + while (1) + { + } +} + +/** + * @brief This function handles Bus Fault exception. + * @param None + * @retval None + */ +void BusFault_Handler(void) +{ + /* Go to infinite loop when Bus Fault exception occurs */ + while (1) + { + } +} + +/** + * @brief This function handles Usage Fault exception. + * @param None + * @retval None + */ +void UsageFault_Handler(void) +{ + /* Go to infinite loop when Usage Fault exception occurs */ + while (1) + { + } +} + +/** + * @brief This function handles SVCall exception. + * @param None + * @retval None + */ +void SVC_Handler(void) +{ +} + +/** + * @brief This function handles Debug Monitor exception. + * @param None + * @retval None + */ +void DebugMon_Handler(void) +{ +} + +/** + * @brief This function handles PendSVC exception. + * @param None + * @retval None + */ +void PendSV_Handler(void) +{ +} + +/** + * @brief This function handles SysTick Handler. + * @param None + * @retval None + */ +void SysTick_Handler(void) +{ +} + +/******************************************************************************/ +/* STM32F4xx Peripherals Interrupt Handlers */ +/* Add here the Interrupt Handler for the used peripheral(s) (PPP), for the */ +/* available peripheral interrupt handler's name please refer to the startup */ +/* file (startup_stm32f40xx.s/startup_stm32f427x.s). */ +/******************************************************************************/ + +/** + * @brief This function handles DMA Stream interrupt request. + * @param None + * @retval None + */ +void DMA1_Stream6_IRQHandler(void) +{ + + /* Transfer half empty interrupt */ + + if(DMA_GetITStatus(DMA1_Stream6, DMA_IT_HTIF6) != RESET)) + { + /* fill first half from fifo */ + + fifo_read(DMA1_Stream6_fifo, dac_buf, DAC_BUF_SZ/2); + + /* Clear DMA Stream Transfer Complete interrupt pending bit */ + + DMA_ClearITPendingBit(DMA1_Stream6, DMA_IT_HTIF6); + + interrupts++; + } + + /* Transfer complete interrupt */ + + if(DMA_GetITStatus(DMA1_Stream6, DMA_IT_TCIF6) != RESET)) + { + /* fill second half from fifo */ + + fifo_read(DMA1_Stream6_fifo, &dac_buf[DAC_BUF_SZ/2], DAC_BUF_SZ/2); + + /* Clear DMA Stream Transfer Complete interrupt pending bit */ + + DMA_ClearITPendingBit(DMA1_Stream6, DMA_IT_TCIF6); + + interrupts++; + } +} + +/** + * @} + */ + +/** + * @} + */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/codec2-dev/stm32/src/dac_play.c b/codec2-dev/stm32/src/dac_play.c index 96777ebb..644c6859 100644 --- a/codec2-dev/stm32/src/dac_play.c +++ b/codec2-dev/stm32/src/dac_play.c @@ -1,63 +1,63 @@ -/*---------------------------------------------------------------------------*\ - - FILE........: dac_play.c - AUTHOR......: David Rowe - DATE CREATED: 1 June 2013 - - Plays a 16 kHz sample rate raw file to the STM32F4 DACs. DAC1 is - connected to pin PA4, DAC2 is connected to pin PA5. - -\*---------------------------------------------------------------------------*/ - -/* - Copyright (C) 2013 David Rowe - - All rights reserved. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License version 2.1, as - published by the Free Software Foundation. This program is - distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or - FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public - License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program; if not, see . -*/ - -#include -#include "stm32f4_dac.h" -#include "gdb_stdio.h" - -#define N (5*DAC_BUF_SZ) - -int main(void) { - short buf[N]; - FILE *fplay; - - dac_open(2*N); - - while(1) { - fplay = fopen("stm_in.raw", "rb"); - if (fplay == NULL) { - printf("Error opening input file: stm_in.raw\n\nTerminating....\n"); - exit(1); - } - - printf("Starting!\n"); - - while(fread(buf, sizeof(short), N, fplay) == N) { - while(dac1_write(buf, N) == -1); - while(dac2_write(buf, N) == -1); - } - - printf("Finished!\n"); - fclose(fplay); - } - - /* let FIFO empty */ - - while(1); -} - +/*---------------------------------------------------------------------------*\ + + FILE........: dac_play.c + AUTHOR......: David Rowe + DATE CREATED: 1 June 2013 + + Plays a 16 kHz sample rate raw file to the STM32F4 DACs. DAC1 is + connected to pin PA4, DAC2 is connected to pin PA5. + +\*---------------------------------------------------------------------------*/ + +/* + Copyright (C) 2013 David Rowe + + All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 2.1, as + published by the Free Software Foundation. This program is + distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, see . +*/ + +#include +#include "stm32f4_dac.h" +#include "gdb_stdio.h" + +#define N (5*DAC_BUF_SZ) + +int main(void) { + short buf[N]; + FILE *fplay; + + dac_open(2*N); + + while(1) { + fplay = fopen("stm_in.raw", "rb"); + if (fplay == NULL) { + printf("Error opening input file: stm_in.raw\n\nTerminating....\n"); + exit(1); + } + + printf("Starting!\n"); + + while(fread(buf, sizeof(short), N, fplay) == N) { + while(dac1_write(buf, N) == -1); + while(dac2_write(buf, N) == -1); + } + + printf("Finished!\n"); + fclose(fplay); + } + + /* let FIFO empty */ + + while(1); +} + diff --git a/codec2-dev/stm32/src/dac_ut.c b/codec2-dev/stm32/src/dac_ut.c index bfe9def1..b660f0ca 100644 --- a/codec2-dev/stm32/src/dac_ut.c +++ b/codec2-dev/stm32/src/dac_ut.c @@ -1,59 +1,59 @@ -/*---------------------------------------------------------------------------*\ - - FILE........: dac_ut.c - AUTHOR......: David Rowe - DATE CREATED: May 31 2013 - - Plays a 500 Hz sine wave sampled at 16 kHz out of PA5 on a Discovery board, - or the speaker output of the SM1000. - -\*---------------------------------------------------------------------------*/ - -/* - Copyright (C) 2013 David Rowe - - All rights reserved. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License version 2.1, as - published by the Free Software Foundation. This program is - distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or - FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public - License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program; if not, see . -*/ - -#include -#include "stm32f4_dac.h" - -#define SINE_SAMPLES 32 - - -/* 32 sample sine wave which at Fs=16kHz will be 500Hz. Note samples - are 16 bit 2's complement, the DAC driver convertsto 12 bit - unsigned. */ - -short aSine[] = { - -16, 6384, 12528, 18192, 23200, 27232, 30256, 32128, - 32752, 32128, 30256, 27232, 23152, 18192, 12528, 6384, - -16, -6416, -12560, -18224, -23184, -27264, -30288, -32160, - -32768, -32160, -30288, -27264, -23184, -18224, -12560, -6416 -}; - -int main(void) { - - dac_open(4*DAC_BUF_SZ); - - while (1) { - - /* keep DAC FIFOs topped up */ - - dac1_write((short*)aSine, SINE_SAMPLES); - dac2_write((short*)aSine, SINE_SAMPLES); - } - -} - +/*---------------------------------------------------------------------------*\ + + FILE........: dac_ut.c + AUTHOR......: David Rowe + DATE CREATED: May 31 2013 + + Plays a 500 Hz sine wave sampled at 16 kHz out of PA5 on a Discovery board, + or the speaker output of the SM1000. + +\*---------------------------------------------------------------------------*/ + +/* + Copyright (C) 2013 David Rowe + + All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 2.1, as + published by the Free Software Foundation. This program is + distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, see . +*/ + +#include +#include "stm32f4_dac.h" + +#define SINE_SAMPLES 32 + + +/* 32 sample sine wave which at Fs=16kHz will be 500Hz. Note samples + are 16 bit 2's complement, the DAC driver convertsto 12 bit + unsigned. */ + +short aSine[] = { + -16, 6384, 12528, 18192, 23200, 27232, 30256, 32128, + 32752, 32128, 30256, 27232, 23152, 18192, 12528, 6384, + -16, -6416, -12560, -18224, -23184, -27264, -30288, -32160, + -32768, -32160, -30288, -27264, -23184, -18224, -12560, -6416 +}; + +int main(void) { + + dac_open(4*DAC_BUF_SZ); + + while (1) { + + /* keep DAC FIFOs topped up */ + + dac1_write((short*)aSine, SINE_SAMPLES); + dac2_write((short*)aSine, SINE_SAMPLES); + } + +} + diff --git a/codec2-dev/stm32/src/debugblinky.c b/codec2-dev/stm32/src/debugblinky.c index 7e38fd17..2c694fdf 100644 --- a/codec2-dev/stm32/src/debugblinky.c +++ b/codec2-dev/stm32/src/debugblinky.c @@ -1,45 +1,45 @@ -/*---------------------------------------------------------------------------*\ - - FILE........: debugblinky.c - AUTHOR......: David Rowe - DATE CREATED: 12 August 2014 - - Configures GPIO pins used for debug blinkies - -\*---------------------------------------------------------------------------*/ - -/* - Copyright (C) 2014 David Rowe - - All rights reserved. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License version 2.1, as - published by the Free Software Foundation. This program is - distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or - FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public - License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program; if not, see . -*/ - -#include "stm32f4xx.h" - -void init_debug_blinky(void) { - GPIO_InitTypeDef GPIO_InitStruct; - - /* PE0-3 used to indicate activity */ - - RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE); - - GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3; - GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT; - GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; - GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; - GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL; - GPIO_Init(GPIOE, &GPIO_InitStruct); - -} - +/*---------------------------------------------------------------------------*\ + + FILE........: debugblinky.c + AUTHOR......: David Rowe + DATE CREATED: 12 August 2014 + + Configures GPIO pins used for debug blinkies + +\*---------------------------------------------------------------------------*/ + +/* + Copyright (C) 2014 David Rowe + + All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 2.1, as + published by the Free Software Foundation. This program is + distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, see . +*/ + +#include "stm32f4xx.h" + +void init_debug_blinky(void) { + GPIO_InitTypeDef GPIO_InitStruct; + + /* PE0-3 used to indicate activity */ + + RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE); + + GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3; + GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT; + GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; + GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; + GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL; + GPIO_Init(GPIOE, &GPIO_InitStruct); + +} + diff --git a/codec2-dev/stm32/src/fast_dac_ut.c b/codec2-dev/stm32/src/fast_dac_ut.c index faa8865c..7678a3d9 100644 --- a/codec2-dev/stm32/src/fast_dac_ut.c +++ b/codec2-dev/stm32/src/fast_dac_ut.c @@ -1,115 +1,115 @@ -/*---------------------------------------------------------------------------*\ - - FILE........: dac_ut.c - AUTHOR......: David Rowe - DATE CREATED: May 31 2013 - - Plays a 500 Hz sine wave sampled at 16 kHz out of PA5 on a Discovery board, - or the speaker output of the SM1000. - -\*---------------------------------------------------------------------------*/ - -/* - Copyright (C) 2013 David Rowe - - All rights reserved. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License version 2.1, as - published by the Free Software Foundation. This program is - distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or - FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public - License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program; if not, see . -*/ - -#include -#include -#include "stm32f4_dacduc.h" -#include "iir_duc.h" -#include "stm32f4xx.h" -#include -#include -#include "gdb_stdio.h" -#include "comp.h" -//#include "gmsk_test_dat_m4.h" -#define SINE_SAMPLES 32 - - -/* 32 sample sine wave which at Fs=16kHz will be 500Hz. Note samples - are 16 bit 2's complement, the DAC driver convertsto 12 bit - unsigned. */ - -short aWave[] = {4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0, - 4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,}; - -short aSine[] = {1600, 3200, 1601, 0, 1600, 3200, 1601, 0, 1600, 3200, 1601, 0, 1600, 3200, 1601, 0, 1600, 3200, 1601, 0, 1600, 3200, 1601, 0, 1600, 3200, 1600, 0, 1600, 3200, 1601, 0 -}; - -//Sine at Fs/4 -float f4sine[] = {1,0,-1,0,1,0,-1,0,1,0,-1,0,1,0,-1,0,1,0,-1,0,1,0,-1,0,1,0,-1,0,1,0,-1,0,1,0,-1,0,1,0,-1,0,1,0,-1,0,1,0,-1,0, -1,0,-1,0,1,0,-1,0,1,0,-1,0,1,0,-1,0,1,0,-1,0,1,0,-1,0,1,0,-1,0,1,0,-1,0,1,0,-1,0,1,0,-1,0,1,0,-1,0,1,0,-1,0, -1,0,-1,0,1,0,-1,0,1,0,-1,0,1,0,-1,0,1,0,-1,0,1,0,-1,0,1,0,-1,0,1,0,-1,0,1,0,-1,0,1,0,-1,0,1,0,-1,0,1,0,-1,0, -1,0,-1,0,1,0,-1,0,1,0,-1,0,1,0,-1,0,1,0,-1,0,1,0,-1,0,1,0,-1,0,1,0,-1,0,}; - -//Intermediate 80k real before tx -int tx_imm[DUC_N]; - -//Complex input to chain -COMP comp_in[DUC_N/10]; - -unsigned short outbuf[DAC_DUC_BUF_SZ]; - -void setup_timer() -{ - RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); - - TIM_TimeBaseInitTypeDef timerInitStructure; - timerInitStructure.TIM_Prescaler = 84; - timerInitStructure.TIM_CounterMode = TIM_CounterMode_Up; - timerInitStructure.TIM_Period = 0x8FFFFFFF; - timerInitStructure.TIM_ClockDivision = 0; - timerInitStructure.TIM_RepetitionCounter = 0; - TIM_TimeBaseInit(TIM2, &timerInitStructure); - TIM_Cmd(TIM2, ENABLE); -} - -int main(void) { - int tstart,tup,tend,cyc,i; - - memset((void*)outbuf,0,sizeof(short)*DAC_DUC_BUF_SZ); - setup_timer(); - fast_dac_open(2*DAC_DUC_BUF_SZ,2*DAC_BUF_SZ); - tstart=tend=tup=cyc=0; - //Initalize complex input with signal at zero - for(i=0;iGMSK_TEST_LEN) - // cyc=0; - if(cyc%10000==0){ - printf("48c80r takes %d uSecs\n",tup-tstart); - printf("iir upconvert takes %d uSecs\n",tend-tup); - } - tstart = TIM_GetCounter(TIM2); - - upconv_48c_80r(comp_in,tx_imm,1); - - tup = TIM_GetCounter(TIM2); - - iir_upconv_fixp(tx_imm,outbuf); - - tend = TIM_GetCounter(TIM2); - - //Sit and spin until we can get more samples into the dac - while(dac1_write((short*)outbuf,DAC_DUC_BUF_SZ)<0); - } - -} - +/*---------------------------------------------------------------------------*\ + + FILE........: dac_ut.c + AUTHOR......: David Rowe + DATE CREATED: May 31 2013 + + Plays a 500 Hz sine wave sampled at 16 kHz out of PA5 on a Discovery board, + or the speaker output of the SM1000. + +\*---------------------------------------------------------------------------*/ + +/* + Copyright (C) 2013 David Rowe + + All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 2.1, as + published by the Free Software Foundation. This program is + distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, see . +*/ + +#include +#include +#include "stm32f4_dacduc.h" +#include "iir_duc.h" +#include "stm32f4xx.h" +#include +#include +#include "gdb_stdio.h" +#include "comp.h" +//#include "gmsk_test_dat_m4.h" +#define SINE_SAMPLES 32 + + +/* 32 sample sine wave which at Fs=16kHz will be 500Hz. Note samples + are 16 bit 2's complement, the DAC driver convertsto 12 bit + unsigned. */ + +short aWave[] = {4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0, + 4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,}; + +short aSine[] = {1600, 3200, 1601, 0, 1600, 3200, 1601, 0, 1600, 3200, 1601, 0, 1600, 3200, 1601, 0, 1600, 3200, 1601, 0, 1600, 3200, 1601, 0, 1600, 3200, 1600, 0, 1600, 3200, 1601, 0 +}; + +//Sine at Fs/4 +float f4sine[] = {1,0,-1,0,1,0,-1,0,1,0,-1,0,1,0,-1,0,1,0,-1,0,1,0,-1,0,1,0,-1,0,1,0,-1,0,1,0,-1,0,1,0,-1,0,1,0,-1,0,1,0,-1,0, +1,0,-1,0,1,0,-1,0,1,0,-1,0,1,0,-1,0,1,0,-1,0,1,0,-1,0,1,0,-1,0,1,0,-1,0,1,0,-1,0,1,0,-1,0,1,0,-1,0,1,0,-1,0, +1,0,-1,0,1,0,-1,0,1,0,-1,0,1,0,-1,0,1,0,-1,0,1,0,-1,0,1,0,-1,0,1,0,-1,0,1,0,-1,0,1,0,-1,0,1,0,-1,0,1,0,-1,0, +1,0,-1,0,1,0,-1,0,1,0,-1,0,1,0,-1,0,1,0,-1,0,1,0,-1,0,1,0,-1,0,1,0,-1,0,}; + +//Intermediate 80k real before tx +int tx_imm[DUC_N]; + +//Complex input to chain +COMP comp_in[DUC_N/10]; + +unsigned short outbuf[DAC_DUC_BUF_SZ]; + +void setup_timer() +{ + RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); + + TIM_TimeBaseInitTypeDef timerInitStructure; + timerInitStructure.TIM_Prescaler = 84; + timerInitStructure.TIM_CounterMode = TIM_CounterMode_Up; + timerInitStructure.TIM_Period = 0x8FFFFFFF; + timerInitStructure.TIM_ClockDivision = 0; + timerInitStructure.TIM_RepetitionCounter = 0; + TIM_TimeBaseInit(TIM2, &timerInitStructure); + TIM_Cmd(TIM2, ENABLE); +} + +int main(void) { + int tstart,tup,tend,cyc,i; + + memset((void*)outbuf,0,sizeof(short)*DAC_DUC_BUF_SZ); + setup_timer(); + fast_dac_open(2*DAC_DUC_BUF_SZ,2*DAC_BUF_SZ); + tstart=tend=tup=cyc=0; + //Initalize complex input with signal at zero + for(i=0;iGMSK_TEST_LEN) + // cyc=0; + if(cyc%10000==0){ + printf("48c80r takes %d uSecs\n",tup-tstart); + printf("iir upconvert takes %d uSecs\n",tend-tup); + } + tstart = TIM_GetCounter(TIM2); + + upconv_48c_80r(comp_in,tx_imm,1); + + tup = TIM_GetCounter(TIM2); + + iir_upconv_fixp(tx_imm,outbuf); + + tend = TIM_GetCounter(TIM2); + + //Sit and spin until we can get more samples into the dac + while(dac1_write((short*)outbuf,DAC_DUC_BUF_SZ)<0); + } + +} + diff --git a/codec2-dev/stm32/src/fdmdv_dump_rt.c b/codec2-dev/stm32/src/fdmdv_dump_rt.c index 00cdccad..d4926cf2 100644 --- a/codec2-dev/stm32/src/fdmdv_dump_rt.c +++ b/codec2-dev/stm32/src/fdmdv_dump_rt.c @@ -1,154 +1,154 @@ -/*---------------------------------------------------------------------------*\ - - FILE........: fdmdv_dump_rt.c - AUTHOR......: David Rowe - DATE CREATED: 9 Sep 2014 - - Runs the fdmdv demod in real time for a few seconds then dumps some - modem info to a text file for plotting in Octave. Way to verify the - "from radio" SM1000 hardware, ADC, and demod on the SM1000. - - Requires FreeDV signal to be sent to CN6 of SM1000. - - Octave: - - load scatter.txt - l=length(scatter) - plot(scatter(:,1:2:l),scatter(:,2:2:l),'+') - -\*---------------------------------------------------------------------------*/ - -/* - Copyright (C) 2014 David Rowe - - All rights reserved. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License version 2.1, as - published by the Free Software Foundation. This program is - distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or - FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public - License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program; if not, see . -*/ - -#include -#include -#include -#include -#include - -#include -#include "stm32f4_adc.h" -#include "stm32f4_dac.h" -#include "freedv_api.h" -#include "codec2_fdmdv.h" -#include "sm1000_leds_switches.h" -#include "gdb_stdio.h" - -#ifdef __EMBEDDED__ -#define printf gdb_stdio_printf -#define fprintf gdb_stdio_fprintf -#define fopen gdb_stdio_fopen -#define fclose gdb_stdio_fclose -#define fread gdb_stdio_fread -#define fwrite gdb_stdio_fwrite -#endif - -#define FREEDV_NSAMPLES_16K (2*FREEDV_NSAMPLES) -#define START_LOG_FRAMES 100 -#define LOG_FRAMES 10 -#define STOP_LOG_FRAMES (START_LOG_FRAMES+LOG_FRAMES) -#define NC 16 - -int main(void) { - struct freedv *f; - short adc16k[FDMDV_OS_TAPS_16K+FREEDV_NSAMPLES_16K]; - short dac16k[FREEDV_NSAMPLES_16K]; - short adc8k[FREEDV_NSAMPLES]; - short dac8k[FDMDV_OS_TAPS_8K+FREEDV_NSAMPLES]; - - int nin, nout, i, j, frames, lines; - - COMP *symb, *psymb; - - /* init all the drivers for various peripherals */ - - sm1000_leds_switches_init(); - dac_open(4*DAC_BUF_SZ); - adc_open(4*ADC_BUF_SZ); - f = freedv_open(FREEDV_MODE_1600); - - /* clear filter memories */ - - for(i=0; itotal_bit_errors = 0; - - if (adc1_read(&adc16k[FDMDV_OS_TAPS_16K], 2*nin) == 0) { - GPIOE->ODR = (1 << 3); - fdmdv_16_to_8_short(adc8k, &adc16k[FDMDV_OS_TAPS_16K], nin); - nout = freedv_rx(f, &dac8k[FDMDV_OS_TAPS_8K], adc8k); - fdmdv_8_to_16_short(dac16k, &dac8k[FDMDV_OS_TAPS_8K], nout); - dac2_write(dac16k, 2*nout); - led_ptt(0); led_rt(f->fdmdv_stats.sync); led_err(f->total_bit_errors); - GPIOE->ODR &= ~(1 << 3); - -#define TMP1 -#ifdef TMP1 - if (f->fdmdv_stats.sync) - frames++; - if ((frames >= START_LOG_FRAMES) && (lines < LOG_FRAMES)) { - for(i=0; i<=f->fdmdv_stats.Nc; i++) - psymb[i] = f->fdmdv_stats.rx_symbols[i]; - psymb += (f->fdmdv_stats.Nc+1); - lines++; - } - - if (frames >= STOP_LOG_FRAMES) { - FILE *ft = fopen("scatter.txt", "wt"); - assert(ft != NULL); - printf("Writing scatter file....\n"); - for(j=0; jfdmdv_stats.Nc; i++) { - fprintf(ft, "%f\t%f\t", - (double)symb[j*(f->fdmdv_stats.Nc+1)+i].real, - (double)symb[j*(f->fdmdv_stats.Nc+1)+i].imag); - printf("line: %d\n", j); - } - fprintf(ft, "\n"); - } - fclose(ft); - printf("SNR = %3.2f dB\nfinished!\n", (double)f->fdmdv_stats.snr_est); - while(1); - } -#endif - } - - } /* while(1) ... */ -} - +/*---------------------------------------------------------------------------*\ + + FILE........: fdmdv_dump_rt.c + AUTHOR......: David Rowe + DATE CREATED: 9 Sep 2014 + + Runs the fdmdv demod in real time for a few seconds then dumps some + modem info to a text file for plotting in Octave. Way to verify the + "from radio" SM1000 hardware, ADC, and demod on the SM1000. + + Requires FreeDV signal to be sent to CN6 of SM1000. + + Octave: + + load scatter.txt + l=length(scatter) + plot(scatter(:,1:2:l),scatter(:,2:2:l),'+') + +\*---------------------------------------------------------------------------*/ + +/* + Copyright (C) 2014 David Rowe + + All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 2.1, as + published by the Free Software Foundation. This program is + distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, see . +*/ + +#include +#include +#include +#include +#include + +#include +#include "stm32f4_adc.h" +#include "stm32f4_dac.h" +#include "freedv_api.h" +#include "codec2_fdmdv.h" +#include "sm1000_leds_switches.h" +#include "gdb_stdio.h" + +#ifdef __EMBEDDED__ +#define printf gdb_stdio_printf +#define fprintf gdb_stdio_fprintf +#define fopen gdb_stdio_fopen +#define fclose gdb_stdio_fclose +#define fread gdb_stdio_fread +#define fwrite gdb_stdio_fwrite +#endif + +#define FREEDV_NSAMPLES_16K (2*FREEDV_NSAMPLES) +#define START_LOG_FRAMES 100 +#define LOG_FRAMES 10 +#define STOP_LOG_FRAMES (START_LOG_FRAMES+LOG_FRAMES) +#define NC 16 + +int main(void) { + struct freedv *f; + short adc16k[FDMDV_OS_TAPS_16K+FREEDV_NSAMPLES_16K]; + short dac16k[FREEDV_NSAMPLES_16K]; + short adc8k[FREEDV_NSAMPLES]; + short dac8k[FDMDV_OS_TAPS_8K+FREEDV_NSAMPLES]; + + int nin, nout, i, j, frames, lines; + + COMP *symb, *psymb; + + /* init all the drivers for various peripherals */ + + sm1000_leds_switches_init(); + dac_open(4*DAC_BUF_SZ); + adc_open(4*ADC_BUF_SZ); + f = freedv_open(FREEDV_MODE_1600); + + /* clear filter memories */ + + for(i=0; itotal_bit_errors = 0; + + if (adc1_read(&adc16k[FDMDV_OS_TAPS_16K], 2*nin) == 0) { + GPIOE->ODR = (1 << 3); + fdmdv_16_to_8_short(adc8k, &adc16k[FDMDV_OS_TAPS_16K], nin); + nout = freedv_rx(f, &dac8k[FDMDV_OS_TAPS_8K], adc8k); + fdmdv_8_to_16_short(dac16k, &dac8k[FDMDV_OS_TAPS_8K], nout); + dac2_write(dac16k, 2*nout); + led_ptt(0); led_rt(f->fdmdv_stats.sync); led_err(f->total_bit_errors); + GPIOE->ODR &= ~(1 << 3); + +#define TMP1 +#ifdef TMP1 + if (f->fdmdv_stats.sync) + frames++; + if ((frames >= START_LOG_FRAMES) && (lines < LOG_FRAMES)) { + for(i=0; i<=f->fdmdv_stats.Nc; i++) + psymb[i] = f->fdmdv_stats.rx_symbols[i]; + psymb += (f->fdmdv_stats.Nc+1); + lines++; + } + + if (frames >= STOP_LOG_FRAMES) { + FILE *ft = fopen("scatter.txt", "wt"); + assert(ft != NULL); + printf("Writing scatter file....\n"); + for(j=0; jfdmdv_stats.Nc; i++) { + fprintf(ft, "%f\t%f\t", + (double)symb[j*(f->fdmdv_stats.Nc+1)+i].real, + (double)symb[j*(f->fdmdv_stats.Nc+1)+i].imag); + printf("line: %d\n", j); + } + fprintf(ft, "\n"); + } + fclose(ft); + printf("SNR = %3.2f dB\nfinished!\n", (double)f->fdmdv_stats.snr_est); + while(1); + } +#endif + } + + } /* while(1) ... */ +} + diff --git a/codec2-dev/stm32/src/fdmdv_profile.c b/codec2-dev/stm32/src/fdmdv_profile.c index 05b8d623..ee4f8b8b 100644 --- a/codec2-dev/stm32/src/fdmdv_profile.c +++ b/codec2-dev/stm32/src/fdmdv_profile.c @@ -1,149 +1,149 @@ -/*---------------------------------------------------------------------------*\ - - FILE........: fdmdv_profile.c - AUTHOR......: David Rowe - DATE CREATED: 18 July 2014 - - Profiling FDMDV modem operation on the STM32F4. - -\*---------------------------------------------------------------------------*/ - -/* - Copyright (C) 2014 David Rowe - - All rights reserved. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License version 2.1, as - published by the Free Software Foundation. This program is - distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or - FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public - License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program; if not, see . -*/ - -#include -#include -#include -#include -#include - -#include "stm32f4xx_conf.h" -#include "stm32f4xx.h" -#include "gdb_stdio.h" -#include "codec2_fdmdv.h" -#include "dump.h" -#include "sine.h" -#include "machdep.h" - -#ifdef __EMBEDDED__ -#define printf gdb_stdio_printf -#define fopen gdb_stdio_fopen -#define fclose gdb_stdio_fclose -#define fread gdb_stdio_fread -#define fwrite gdb_stdio_fwrite -#endif - -#define TEST_FRAMES 25 -#define CHANNEL_BUF_SIZE (10*FDMDV_NOM_SAMPLES_PER_FRAME) - -static int channel_count = 0; -static COMP channel[CHANNEL_BUF_SIZE]; - -static void channel_in(COMP tx_fdm[], int nout) { - int i; - - /* add M tx samples to end of buffer */ - - assert((channel_count + nout) < CHANNEL_BUF_SIZE); - for(i=0; i. +*/ + +#include +#include +#include +#include +#include + +#include "stm32f4xx_conf.h" +#include "stm32f4xx.h" +#include "gdb_stdio.h" +#include "codec2_fdmdv.h" +#include "dump.h" +#include "sine.h" +#include "machdep.h" + +#ifdef __EMBEDDED__ +#define printf gdb_stdio_printf +#define fopen gdb_stdio_fopen +#define fclose gdb_stdio_fclose +#define fread gdb_stdio_fread +#define fwrite gdb_stdio_fwrite +#endif + +#define TEST_FRAMES 25 +#define CHANNEL_BUF_SIZE (10*FDMDV_NOM_SAMPLES_PER_FRAME) + +static int channel_count = 0; +static COMP channel[CHANNEL_BUF_SIZE]; + +static void channel_in(COMP tx_fdm[], int nout) { + int i; + + /* add M tx samples to end of buffer */ + + assert((channel_count + nout) < CHANNEL_BUF_SIZE); + for(i=0; i Refer - * \link arm_fft_bin_example_f32.c \endlink - * - */ - - -/** \example arm_fft_bin_example_f32.c - */ - - -#include "arm_math.h" -#include "gdb_stdio.h" -#include "machdep.h" -#include "kiss_fft.h" - -#define TEST_LENGTH_SAMPLES 1024 - -/* ------------------------------------------------------------------- -* External Input and Output buffer Declarations for FFT Bin Example -* ------------------------------------------------------------------- */ -extern float32_t testInput_f32_10khz[TEST_LENGTH_SAMPLES]; -static float32_t testOutput[TEST_LENGTH_SAMPLES/2]; -static float32_t kiss_complex_out[TEST_LENGTH_SAMPLES]; - -/* ------------------------------------------------------------------ -* Global variables for FFT Bin Example -* ------------------------------------------------------------------- */ -uint32_t fftSize = TEST_LENGTH_SAMPLES/2; -uint32_t ifftFlag = 0; -uint32_t doBitReverse = 1; - -/* Reference index at which max energy of bin ocuurs */ -uint32_t refIndex = 213, testIndex = 0; - -/* ---------------------------------------------------------------------- -* Max magnitude FFT Bin test -* ------------------------------------------------------------------- */ - -void SystemInit(void); - -int main(void) -{ - - arm_status status; - arm_cfft_radix2_instance_f32 S; - float32_t maxValue; - unsigned int fft_start, kiss_fft_start; - kiss_fft_cfg fft_fwd_cfg; - - SystemInit(); - machdep_profile_init(); - fft_fwd_cfg = kiss_fft_alloc(fftSize, 0, NULL, NULL); - kiss_fft_start = machdep_profile_sample(); - kiss_fft(fft_fwd_cfg, (kiss_fft_cpx *)testInput_f32_10khz, - (kiss_fft_cpx *)kiss_complex_out); - machdep_profile_sample_and_log(kiss_fft_start, " kiss_fft"); - - status = ARM_MATH_SUCCESS; - - /* Initialize the CFFT/CIFFT module */ - status = arm_cfft_radix2_init_f32(&S, fftSize, ifftFlag, doBitReverse); - - /* Process the data through the CFFT/CIFFT module */ - fft_start = machdep_profile_sample(); - arm_cfft_radix2_f32(&S, testInput_f32_10khz); - machdep_profile_sample_and_log(fft_start, " fft"); - machdep_profile_print_logged_samples(); - - /* Process the data through the Complex Magnitude Module for - calculating the magnitude at each bin */ - arm_cmplx_mag_f32(testInput_f32_10khz, testOutput,fftSize); - - /* Calculates maxValue and returns corresponding BIN value */ - arm_max_f32(testOutput, fftSize, &maxValue, &testIndex); - - if(testIndex != refIndex) - { - status = ARM_MATH_TEST_FAILURE; - } - - /* ---------------------------------------------------------------------- - ** Loop here if the signals fail the PASS check. - ** This denotes a test failure - ** ------------------------------------------------------------------- */ - - if( status != ARM_MATH_SUCCESS) - { - while(1); - } - - while(1); /* main function does not return */ - - return 0; -} - - /** \endlink */ - - -/* - * Dummy function to avoid compiler error - */ -void _init() { } - - - +/* ---------------------------------------------------------------------- +* Copyright (C) 2010 ARM Limited. All rights reserved. +* +* $Date: 29. November 2010 +* $Revision: V1.0.3 +* +* Project: CMSIS DSP Library +* Title: arm_fft_bin_example_f32.c +* +* Description: Example code demonstrating calculation of Max energy bin of +* frequency domain of input signal. +* +* Target Processor: Cortex-M4/Cortex-M3 +* +* +* Version 1.0.3 2010/11/29 +* Re-organized the CMSIS folders and updated documentation. +* +* Version 1.0.1 2010/10/05 KK +* Production release and review comments incorporated. +* +* Version 1.0.0 2010/09/20 KK +* Production release and review comments incorporated. +* ------------------------------------------------------------------- */ + +/** + * @ingroup groupExamples + */ + +/** + * @defgroup FrequencyBin Frequency Bin Example + * + * \par Description + * \par + * Demonstrates the calculation of the maximum energy bin in the frequency + * domain of the input signal with the use of Complex FFT, Complex + * Magnitude, and Maximum functions. + * + * \par Algorithm: + * \par + * The input test signal contains a 10 kHz signal with uniformly distributed white noise. + * Calculating the FFT of the input signal will give us the maximum energy of the + * bin corresponding to the input frequency of 10 kHz. + * + * \par Block Diagram: + * \image html FFTBin.gif "Block Diagram" + * \par + * The figure below shows the time domain signal of 10 kHz signal with + * uniformly distributed white noise, and the next figure shows the input + * in the frequency domain. The bin with maximum energy corresponds to 10 kHz signal. + * \par + * \image html FFTBinInput.gif "Input signal in Time domain" + * \image html FFTBinOutput.gif "Input signal in Frequency domain" + * + * \par Variables Description: + * \par + * \li \c testInput_f32_10khz points to the input data + * \li \c testOutput points to the output data + * \li \c fftSize length of FFT + * \li \c ifftFlag flag for the selection of CFFT/CIFFT + * \li \c doBitReverse Flag for selection of normal order or bit reversed order + * \li \c refIndex reference index value at which maximum energy of bin ocuurs + * \li \c testIndex calculated index value at which maximum energy of bin ocuurs + * + * \par CMSIS DSP Software Library Functions Used: + * \par + * - arm_cfft_radix4_init_f32() + * - arm_cfft_radix4_f32() + * - arm_cmplx_mag_f32() + * - arm_max_f32() + * + * Refer + * \link arm_fft_bin_example_f32.c \endlink + * + */ + + +/** \example arm_fft_bin_example_f32.c + */ + + +#include "arm_math.h" +#include "gdb_stdio.h" +#include "machdep.h" +#include "kiss_fft.h" + +#define TEST_LENGTH_SAMPLES 1024 + +/* ------------------------------------------------------------------- +* External Input and Output buffer Declarations for FFT Bin Example +* ------------------------------------------------------------------- */ +extern float32_t testInput_f32_10khz[TEST_LENGTH_SAMPLES]; +static float32_t testOutput[TEST_LENGTH_SAMPLES/2]; +static float32_t kiss_complex_out[TEST_LENGTH_SAMPLES]; + +/* ------------------------------------------------------------------ +* Global variables for FFT Bin Example +* ------------------------------------------------------------------- */ +uint32_t fftSize = TEST_LENGTH_SAMPLES/2; +uint32_t ifftFlag = 0; +uint32_t doBitReverse = 1; + +/* Reference index at which max energy of bin ocuurs */ +uint32_t refIndex = 213, testIndex = 0; + +/* ---------------------------------------------------------------------- +* Max magnitude FFT Bin test +* ------------------------------------------------------------------- */ + +void SystemInit(void); + +int main(void) +{ + + arm_status status; + arm_cfft_radix2_instance_f32 S; + float32_t maxValue; + unsigned int fft_start, kiss_fft_start; + kiss_fft_cfg fft_fwd_cfg; + + SystemInit(); + machdep_profile_init(); + fft_fwd_cfg = kiss_fft_alloc(fftSize, 0, NULL, NULL); + kiss_fft_start = machdep_profile_sample(); + kiss_fft(fft_fwd_cfg, (kiss_fft_cpx *)testInput_f32_10khz, + (kiss_fft_cpx *)kiss_complex_out); + machdep_profile_sample_and_log(kiss_fft_start, " kiss_fft"); + + status = ARM_MATH_SUCCESS; + + /* Initialize the CFFT/CIFFT module */ + status = arm_cfft_radix2_init_f32(&S, fftSize, ifftFlag, doBitReverse); + + /* Process the data through the CFFT/CIFFT module */ + fft_start = machdep_profile_sample(); + arm_cfft_radix2_f32(&S, testInput_f32_10khz); + machdep_profile_sample_and_log(fft_start, " fft"); + machdep_profile_print_logged_samples(); + + /* Process the data through the Complex Magnitude Module for + calculating the magnitude at each bin */ + arm_cmplx_mag_f32(testInput_f32_10khz, testOutput,fftSize); + + /* Calculates maxValue and returns corresponding BIN value */ + arm_max_f32(testOutput, fftSize, &maxValue, &testIndex); + + if(testIndex != refIndex) + { + status = ARM_MATH_TEST_FAILURE; + } + + /* ---------------------------------------------------------------------- + ** Loop here if the signals fail the PASS check. + ** This denotes a test failure + ** ------------------------------------------------------------------- */ + + if( status != ARM_MATH_SUCCESS) + { + while(1); + } + + while(1); /* main function does not return */ + + return 0; +} + + /** \endlink */ + + +/* + * Dummy function to avoid compiler error + */ +void _init() { } + + + diff --git a/codec2-dev/stm32/src/freedv_rx_profile.c b/codec2-dev/stm32/src/freedv_rx_profile.c index 65b43ae1..f91fc4fe 100644 --- a/codec2-dev/stm32/src/freedv_rx_profile.c +++ b/codec2-dev/stm32/src/freedv_rx_profile.c @@ -1,136 +1,136 @@ -/*---------------------------------------------------------------------------*\ - - FILE........: freedv_rx_profile.c - AUTHOR......: David Rowe - DATE CREATED: 13 August 2014 - - Profiling freedv_rx() operation on the STM32F4. - -\*---------------------------------------------------------------------------*/ - -/* - Copyright (C) 2014 David Rowe - - All rights reserved. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License version 2.1, as - published by the Free Software Foundation. This program is - distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or - FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public - License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program; if not, see . -*/ - -#define PROFILE - -#include -#include -#include -#include -#include - -#include "stm32f4xx_conf.h" -#include "stm32f4xx.h" -#include "gdb_stdio.h" -#include "freedv_api.h" -#include "machdep.h" -#include "codec2_fdmdv.h" - -#ifdef __EMBEDDED__ -#define printf gdb_stdio_printf -#define fopen gdb_stdio_fopen -#define fclose gdb_stdio_fclose -#define fread gdb_stdio_fread -#define fwrite gdb_stdio_fwrite -#define fprintf gdb_stdio_fprintf -#endif - -#define FREEDV_NSAMPLES_16K (2*FREEDV_NSAMPLES) - -int main(int argc, char *argv[]) { - struct freedv *f; - FILE *fin, *fout, *ftotal; - int frame, nin_16k, nin, i, nout = 0; - int n_samples, n_samples_16k; - int sync; - float snr_est; - - PROFILE_VAR(fdmdv_16_to_8_start, freedv_rx_start, fdmdv_8_to_16_start); - - machdep_profile_init(); - - f = freedv_open(FREEDV_MODE_1600); - n_samples = freedv_get_n_speech_samples(f); - n_samples_16k = 2*n_samples; - - short adc16k[FDMDV_OS_TAPS_16K+n_samples_16k]; - short dac16k[n_samples_16k]; - short adc8k[n_samples]; - short dac8k[FDMDV_OS_TAPS_8K+n_samples]; - - // Receive --------------------------------------------------------------------- - - frame = 0; - - fin = fopen("mod_16k.raw", "rb"); - if (fin == NULL) { - printf("Error opening input file\n"); - exit(1); - } - - fout = fopen("speechout_16k.raw", "wb"); - if (fout == NULL) { - printf("Error opening output file\n"); - exit(1); - } - - ftotal = fopen("total.txt", "wt"); - assert(ftotal != NULL); - - /* clear filter memories */ - - for(i=0; i. +*/ + +#define PROFILE + +#include +#include +#include +#include +#include + +#include "stm32f4xx_conf.h" +#include "stm32f4xx.h" +#include "gdb_stdio.h" +#include "freedv_api.h" +#include "machdep.h" +#include "codec2_fdmdv.h" + +#ifdef __EMBEDDED__ +#define printf gdb_stdio_printf +#define fopen gdb_stdio_fopen +#define fclose gdb_stdio_fclose +#define fread gdb_stdio_fread +#define fwrite gdb_stdio_fwrite +#define fprintf gdb_stdio_fprintf +#endif + +#define FREEDV_NSAMPLES_16K (2*FREEDV_NSAMPLES) + +int main(int argc, char *argv[]) { + struct freedv *f; + FILE *fin, *fout, *ftotal; + int frame, nin_16k, nin, i, nout = 0; + int n_samples, n_samples_16k; + int sync; + float snr_est; + + PROFILE_VAR(fdmdv_16_to_8_start, freedv_rx_start, fdmdv_8_to_16_start); + + machdep_profile_init(); + + f = freedv_open(FREEDV_MODE_1600); + n_samples = freedv_get_n_speech_samples(f); + n_samples_16k = 2*n_samples; + + short adc16k[FDMDV_OS_TAPS_16K+n_samples_16k]; + short dac16k[n_samples_16k]; + short adc8k[n_samples]; + short dac8k[FDMDV_OS_TAPS_8K+n_samples]; + + // Receive --------------------------------------------------------------------- + + frame = 0; + + fin = fopen("mod_16k.raw", "rb"); + if (fin == NULL) { + printf("Error opening input file\n"); + exit(1); + } + + fout = fopen("speechout_16k.raw", "wb"); + if (fout == NULL) { + printf("Error opening output file\n"); + exit(1); + } + + ftotal = fopen("total.txt", "wt"); + assert(ftotal != NULL); + + /* clear filter memories */ + + for(i=0; i. -*/ - -#include -#include -#include -#include - -#include "stm32f4xx_conf.h" -#include "stm32f4xx.h" -#include "gdb_stdio.h" -#include "freedv_api.h" -#include "machdep.h" - -#ifdef __EMBEDDED__ -#define printf gdb_stdio_printf -#define fopen gdb_stdio_fopen -#define fclose gdb_stdio_fclose -#define fread gdb_stdio_fread -#define fwrite gdb_stdio_fwrite -#endif - -int main(int argc, char *argv[]) { - struct freedv *f; - FILE *fin, *fout; - int frame, n_samples; - PROFILE_VAR(freedv_start); - - machdep_profile_init(); - - f = freedv_open(FREEDV_MODE_1600); - n_samples = freedv_get_n_speech_samples(f); - short inbuf[n_samples], outbuf[n_samples]; - - // Transmit --------------------------------------------------------------------- - - fin = fopen("stm_in.raw", "rb"); - if (fin == NULL) { - printf("Error opening input file\n"); - exit(1); - } - - fout = fopen("mod.raw", "wb"); - if (fout == NULL) { - printf("Error opening output file\n"); - exit(1); - } - - frame = 0; - - while (fread(inbuf, sizeof(short), n_samples, fin) == n_samples) { - PROFILE_SAMPLE(freedv_start); - freedv_tx(f, outbuf, inbuf); - PROFILE_SAMPLE_AND_LOG2(freedv_start, " freedv_tx"); - - fwrite(outbuf, sizeof(short), n_samples, fout); - printf("frame: %d\n", ++frame); - machdep_profile_print_logged_samples(); - } - - fclose(fin); - fclose(fout); - - return 0; -} - +/*---------------------------------------------------------------------------*\ + + FILE........: freedv_tx_profile.c + AUTHOR......: David Rowe + DATE CREATED: 13 August 2014 + + Profiling freedv_tx() operation on the STM32F4. + +\*---------------------------------------------------------------------------*/ + +/* + Copyright (C) 2014 David Rowe + + All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 2.1, as + published by the Free Software Foundation. This program is + distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, see . +*/ + +#include +#include +#include +#include + +#include "stm32f4xx_conf.h" +#include "stm32f4xx.h" +#include "gdb_stdio.h" +#include "freedv_api.h" +#include "machdep.h" + +#ifdef __EMBEDDED__ +#define printf gdb_stdio_printf +#define fopen gdb_stdio_fopen +#define fclose gdb_stdio_fclose +#define fread gdb_stdio_fread +#define fwrite gdb_stdio_fwrite +#endif + +int main(int argc, char *argv[]) { + struct freedv *f; + FILE *fin, *fout; + int frame, n_samples; + PROFILE_VAR(freedv_start); + + machdep_profile_init(); + + f = freedv_open(FREEDV_MODE_1600); + n_samples = freedv_get_n_speech_samples(f); + short inbuf[n_samples], outbuf[n_samples]; + + // Transmit --------------------------------------------------------------------- + + fin = fopen("stm_in.raw", "rb"); + if (fin == NULL) { + printf("Error opening input file\n"); + exit(1); + } + + fout = fopen("mod.raw", "wb"); + if (fout == NULL) { + printf("Error opening output file\n"); + exit(1); + } + + frame = 0; + + while (fread(inbuf, sizeof(short), n_samples, fin) == n_samples) { + PROFILE_SAMPLE(freedv_start); + freedv_tx(f, outbuf, inbuf); + PROFILE_SAMPLE_AND_LOG2(freedv_start, " freedv_tx"); + + fwrite(outbuf, sizeof(short), n_samples, fout); + printf("frame: %d\n", ++frame); + machdep_profile_print_logged_samples(); + } + + fclose(fin); + fclose(fout); + + return 0; +} + diff --git a/codec2-dev/stm32/src/power_ut.c b/codec2-dev/stm32/src/power_ut.c index 267d5f99..c52cef4d 100644 --- a/codec2-dev/stm32/src/power_ut.c +++ b/codec2-dev/stm32/src/power_ut.c @@ -1,135 +1,135 @@ -/*---------------------------------------------------------------------------*\ - - FILE........: power_ut.c - AUTHOR......: David Rowe - DATE CREATED: 30 May 2014 - - Runs Codec 2, ADC, and DAC, to fully exercise STM32C so we can a feel for - run-time power consumption for SM1000 and hence dimension regulators. - -\*---------------------------------------------------------------------------*/ - -/* - Copyright (C) 2014 David Rowe - - All rights reserved. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License version 2.1, as - published by the Free Software Foundation. This program is - distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or - FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public - License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program; if not, see . -*/ - -#include -#include -#include -#include - -#include "stm32f4xx_conf.h" -#include "stm32f4xx.h" -#include "stm32f4_adc.h" -#include "stm32f4_dac.h" -#include "gdb_stdio.h" -#include "codec2.h" -#include "dump.h" -#include "sine.h" -#include "machdep.h" - -#ifdef __EMBEDDED__ -#define printf gdb_stdio_printf -#define fopen gdb_stdio_fopen -#define fclose gdb_stdio_fclose -#define fread gdb_stdio_fread -#define fwrite gdb_stdio_fwrite -#endif - -#define SPEED_TEST_SAMPLES 24000 - -/* modification of test used to measure codec2 execuation speed. We read/write ADC/DAC - but dont do anything with the samples, as they are at 16 kHz and codec needs 8 kHz. Just - trying to exercise everything to get a feel for power consumption */ - -static void c2speedtest(int mode, char inputfile[]) -{ - struct CODEC2 *codec2; - short *inbuf, *outbuf, *pinbuf, *dummy_buf; - unsigned char *bits; - int nsam, nbit, nframes; - FILE *fin; - int f, nread; - - codec2 = codec2_create(mode); - nsam = codec2_samples_per_frame(codec2); - nframes = SPEED_TEST_SAMPLES/nsam; - outbuf = (short*)malloc(nsam*sizeof(short)); - inbuf = (short*)malloc(SPEED_TEST_SAMPLES*sizeof(short)); - nbit = codec2_bits_per_frame(codec2); - bits = (unsigned char*)malloc(nbit*sizeof(char)); - dummy_buf = (short*)malloc(2*nsam*sizeof(short)); - - fin = fopen(inputfile, "rb"); - if (fin == NULL) { - printf("Error opening input file: %s\nTerminating....\n",inputfile); - exit(1); - } - - printf("reading samples ....\n"); - nread = fread(inbuf, sizeof(short), SPEED_TEST_SAMPLES, fin); - if (nread != SPEED_TEST_SAMPLES) { - printf("error reading %s, %d samples reqd, %d read\n", - inputfile, SPEED_TEST_SAMPLES, nread); - } - fclose(fin); - - pinbuf = inbuf; - for(f=0; fODR = (1 << 13); - codec2_encode(codec2, bits, pinbuf); - pinbuf += nsam; - GPIOD->ODR &= ~(1 << 13); - //printf("Codec 2 dec\n"); - codec2_decode(codec2, outbuf, bits); - - //printf("write to DAC\n"); - while(dac1_write(dummy_buf, nsam*2) == -1); /* runs at Fs = 16kHz */ - //printf("."); - } - - free(inbuf); - free(outbuf); - free(bits); - codec2_destroy(codec2); -} - -void gpio_init() { - RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN; // enable the clock to GPIOD - GPIOD->MODER = (1 << 26); // set pin 13 to be general - // purpose output -} - -int main(int argc, char *argv[]) { - SystemInit(); - gpio_init(); - machdep_profile_init (); - adc_open(4*DAC_BUF_SZ); - dac_open(4*DAC_BUF_SZ); - - printf("Starting power_ut\n"); - - c2speedtest(CODEC2_MODE_1600, "stm_in.raw"); - - printf("Finished\n"); - - return 0; -} - +/*---------------------------------------------------------------------------*\ + + FILE........: power_ut.c + AUTHOR......: David Rowe + DATE CREATED: 30 May 2014 + + Runs Codec 2, ADC, and DAC, to fully exercise STM32C so we can a feel for + run-time power consumption for SM1000 and hence dimension regulators. + +\*---------------------------------------------------------------------------*/ + +/* + Copyright (C) 2014 David Rowe + + All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 2.1, as + published by the Free Software Foundation. This program is + distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, see . +*/ + +#include +#include +#include +#include + +#include "stm32f4xx_conf.h" +#include "stm32f4xx.h" +#include "stm32f4_adc.h" +#include "stm32f4_dac.h" +#include "gdb_stdio.h" +#include "codec2.h" +#include "dump.h" +#include "sine.h" +#include "machdep.h" + +#ifdef __EMBEDDED__ +#define printf gdb_stdio_printf +#define fopen gdb_stdio_fopen +#define fclose gdb_stdio_fclose +#define fread gdb_stdio_fread +#define fwrite gdb_stdio_fwrite +#endif + +#define SPEED_TEST_SAMPLES 24000 + +/* modification of test used to measure codec2 execuation speed. We read/write ADC/DAC + but dont do anything with the samples, as they are at 16 kHz and codec needs 8 kHz. Just + trying to exercise everything to get a feel for power consumption */ + +static void c2speedtest(int mode, char inputfile[]) +{ + struct CODEC2 *codec2; + short *inbuf, *outbuf, *pinbuf, *dummy_buf; + unsigned char *bits; + int nsam, nbit, nframes; + FILE *fin; + int f, nread; + + codec2 = codec2_create(mode); + nsam = codec2_samples_per_frame(codec2); + nframes = SPEED_TEST_SAMPLES/nsam; + outbuf = (short*)malloc(nsam*sizeof(short)); + inbuf = (short*)malloc(SPEED_TEST_SAMPLES*sizeof(short)); + nbit = codec2_bits_per_frame(codec2); + bits = (unsigned char*)malloc(nbit*sizeof(char)); + dummy_buf = (short*)malloc(2*nsam*sizeof(short)); + + fin = fopen(inputfile, "rb"); + if (fin == NULL) { + printf("Error opening input file: %s\nTerminating....\n",inputfile); + exit(1); + } + + printf("reading samples ....\n"); + nread = fread(inbuf, sizeof(short), SPEED_TEST_SAMPLES, fin); + if (nread != SPEED_TEST_SAMPLES) { + printf("error reading %s, %d samples reqd, %d read\n", + inputfile, SPEED_TEST_SAMPLES, nread); + } + fclose(fin); + + pinbuf = inbuf; + for(f=0; fODR = (1 << 13); + codec2_encode(codec2, bits, pinbuf); + pinbuf += nsam; + GPIOD->ODR &= ~(1 << 13); + //printf("Codec 2 dec\n"); + codec2_decode(codec2, outbuf, bits); + + //printf("write to DAC\n"); + while(dac1_write(dummy_buf, nsam*2) == -1); /* runs at Fs = 16kHz */ + //printf("."); + } + + free(inbuf); + free(outbuf); + free(bits); + codec2_destroy(codec2); +} + +void gpio_init() { + RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN; // enable the clock to GPIOD + GPIOD->MODER = (1 << 26); // set pin 13 to be general + // purpose output +} + +int main(int argc, char *argv[]) { + SystemInit(); + gpio_init(); + machdep_profile_init (); + adc_open(4*DAC_BUF_SZ); + dac_open(4*DAC_BUF_SZ); + + printf("Starting power_ut\n"); + + c2speedtest(CODEC2_MODE_1600, "stm_in.raw"); + + printf("Finished\n"); + + return 0; +} + diff --git a/codec2-dev/stm32/src/sm1000_leds_switches_ut.c b/codec2-dev/stm32/src/sm1000_leds_switches_ut.c index ae0e8fe7..9209bfc0 100644 --- a/codec2-dev/stm32/src/sm1000_leds_switches_ut.c +++ b/codec2-dev/stm32/src/sm1000_leds_switches_ut.c @@ -1,41 +1,41 @@ -/*---------------------------------------------------------------------------*\ - - FILE........: sm1000_leds_switches_ut.c - AUTHOR......: David Rowe - DATE CREATED: August 5 2014 - - Unit Test program for the SM1000 switches and LEDs driver. - -\*---------------------------------------------------------------------------*/ - -/* - Copyright (C) 2014 David Rowe - - All rights reserved. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License version 2.1, as - published by the Free Software Foundation. This program is - distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or - FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public - License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program; if not, see . -*/ - -#include -#include "sm1000_leds_switches.h" - -int main(void) { - sm1000_leds_switches_init(); - - while(1) { - led_pwr(switch_select()); - led_ptt(switch_ptt()); - led_rt(switch_back()); - led_err(!switch_back()); - } -} - +/*---------------------------------------------------------------------------*\ + + FILE........: sm1000_leds_switches_ut.c + AUTHOR......: David Rowe + DATE CREATED: August 5 2014 + + Unit Test program for the SM1000 switches and LEDs driver. + +\*---------------------------------------------------------------------------*/ + +/* + Copyright (C) 2014 David Rowe + + All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 2.1, as + published by the Free Software Foundation. This program is + distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, see . +*/ + +#include +#include "sm1000_leds_switches.h" + +int main(void) { + sm1000_leds_switches_init(); + + while(1) { + led_pwr(switch_select()); + led_ptt(switch_ptt()); + led_rt(switch_back()); + led_err(!switch_back()); + } +} + diff --git a/codec2-dev/stm32/src/startup_stm32f4xx.s b/codec2-dev/stm32/src/startup_stm32f4xx.s index 658ab363..c5c67707 100644 --- a/codec2-dev/stm32/src/startup_stm32f4xx.s +++ b/codec2-dev/stm32/src/startup_stm32f4xx.s @@ -1,512 +1,512 @@ -/** - ****************************************************************************** - * @file startup_stm32f4xx.s - * @author MCD Application Team - * @version V1.0.0 - * @date 30-September-2011 - * @brief STM32F4xx Devices vector table for Atollic TrueSTUDIO toolchain. - * This module performs: - * - Set the initial SP - * - Set the initial PC == Reset_Handler, - * - Set the vector table entries with the exceptions ISR address - * - Configure the clock system and the external SRAM mounted on - * STM324xG-EVAL board to be used as data memory (optional, - * to be enabled by user) - * - Branches to main in the C library (which eventually - * calls main()). - * After Reset the Cortex-M4 processor is in Thread mode, - * priority is Privileged, and the Stack is set to Main. - ****************************************************************************** - * @attention - * - * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS - * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE - * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY - * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING - * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE - * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. - * - *

© COPYRIGHT 2011 STMicroelectronics

- ****************************************************************************** - */ - - .syntax unified - .cpu cortex-m3 - .fpu softvfp - .thumb - -.global g_pfnVectors -.global Default_Handler - -/* start address for the initialization values of the .data section. -defined in linker script */ -.word _sidata -/* start address for the .data section. defined in linker script */ -.word _sdata -/* end address for the .data section. defined in linker script */ -.word _edata -/* start address for the .bss section. defined in linker script */ -.word _sbss -/* end address for the .bss section. defined in linker script */ -.word _ebss -/* stack used for SystemInit_ExtMemCtl; always internal RAM used */ - -/** - * @brief This is the code that gets called when the processor first - * starts execution following a reset event. Only the absolutely - * necessary set is performed, after which the application - * supplied main() routine is called. - * @param None - * @retval : None -*/ - - .section .text.Reset_Handler - .weak Reset_Handler - .type Reset_Handler, %function -Reset_Handler: - -/* Copy the data segment initializers from flash to SRAM */ - movs r1, #0 - b LoopCopyDataInit - -CopyDataInit: - ldr r3, =_sidata - ldr r3, [r3, r1] - str r3, [r0, r1] - adds r1, r1, #4 - -LoopCopyDataInit: - ldr r0, =_sdata - ldr r3, =_edata - adds r2, r0, r1 - cmp r2, r3 - bcc CopyDataInit - ldr r2, =_sbss - b LoopFillZerobss -/* Zero fill the bss segment. */ -FillZerobss: - movs r3, #0 - str r3, [r2], #4 - -LoopFillZerobss: - ldr r3, = _ebss - cmp r2, r3 - bcc FillZerobss - -/* Call the clock system intitialization function.*/ - bl SystemInit -/* Call static constructors */ - bl __libc_init_array -/* Call the application's entry point.*/ - bl main - bx lr -.size Reset_Handler, .-Reset_Handler - -/** - * @brief This is the code that gets called when the processor receives an - * unexpected interrupt. This simply enters an infinite loop, preserving - * the system state for examination by a debugger. - * @param None - * @retval None -*/ - .section .text.Default_Handler,"ax",%progbits -Default_Handler: -Infinite_Loop: - b Infinite_Loop - .size Default_Handler, .-Default_Handler -/****************************************************************************** -* -* The minimal vector table for a Cortex M3. Note that the proper constructs -* must be placed on this to ensure that it ends up at physical address -* 0x0000.0000. -* -*******************************************************************************/ - .section .isr_vector,"a",%progbits - .type g_pfnVectors, %object - .size g_pfnVectors, .-g_pfnVectors - - -g_pfnVectors: - .word _estack - .word Reset_Handler - .word NMI_Handler - .word HardFault_Handler - .word MemManage_Handler - .word BusFault_Handler - .word UsageFault_Handler - .word 0 - .word 0 - .word 0 - .word 0 - .word SVC_Handler - .word DebugMon_Handler - .word 0 - .word PendSV_Handler - .word SysTick_Handler - - /* External Interrupts */ - .word WWDG_IRQHandler /* Window WatchDog */ - .word PVD_IRQHandler /* PVD through EXTI Line detection */ - .word TAMP_STAMP_IRQHandler /* Tamper and TimeStamps through the EXTI line */ - .word RTC_WKUP_IRQHandler /* RTC Wakeup through the EXTI line */ - .word FLASH_IRQHandler /* FLASH */ - .word RCC_IRQHandler /* RCC */ - .word EXTI0_IRQHandler /* EXTI Line0 */ - .word EXTI1_IRQHandler /* EXTI Line1 */ - .word EXTI2_IRQHandler /* EXTI Line2 */ - .word EXTI3_IRQHandler /* EXTI Line3 */ - .word EXTI4_IRQHandler /* EXTI Line4 */ - .word DMA1_Stream0_IRQHandler /* DMA1 Stream 0 */ - .word DMA1_Stream1_IRQHandler /* DMA1 Stream 1 */ - .word DMA1_Stream2_IRQHandler /* DMA1 Stream 2 */ - .word DMA1_Stream3_IRQHandler /* DMA1 Stream 3 */ - .word DMA1_Stream4_IRQHandler /* DMA1 Stream 4 */ - .word DMA1_Stream5_IRQHandler /* DMA1 Stream 5 */ - .word DMA1_Stream6_IRQHandler /* DMA1 Stream 6 */ - .word ADC_IRQHandler /* ADC1, ADC2 and ADC3s */ - .word CAN1_TX_IRQHandler /* CAN1 TX */ - .word CAN1_RX0_IRQHandler /* CAN1 RX0 */ - .word CAN1_RX1_IRQHandler /* CAN1 RX1 */ - .word CAN1_SCE_IRQHandler /* CAN1 SCE */ - .word EXTI9_5_IRQHandler /* External Line[9:5]s */ - .word TIM1_BRK_TIM9_IRQHandler /* TIM1 Break and TIM9 */ - .word TIM1_UP_TIM10_IRQHandler /* TIM1 Update and TIM10 */ - .word TIM1_TRG_COM_TIM11_IRQHandler /* TIM1 Trigger and Commutation and TIM11 */ - .word TIM1_CC_IRQHandler /* TIM1 Capture Compare */ - .word TIM2_IRQHandler /* TIM2 */ - .word TIM3_IRQHandler /* TIM3 */ - .word TIM4_IRQHandler /* TIM4 */ - .word I2C1_EV_IRQHandler /* I2C1 Event */ - .word I2C1_ER_IRQHandler /* I2C1 Error */ - .word I2C2_EV_IRQHandler /* I2C2 Event */ - .word I2C2_ER_IRQHandler /* I2C2 Error */ - .word SPI1_IRQHandler /* SPI1 */ - .word SPI2_IRQHandler /* SPI2 */ - .word USART1_IRQHandler /* USART1 */ - .word USART2_IRQHandler /* USART2 */ - .word USART3_IRQHandler /* USART3 */ - .word EXTI15_10_IRQHandler /* External Line[15:10]s */ - .word RTC_Alarm_IRQHandler /* RTC Alarm (A and B) through EXTI Line */ - .word OTG_FS_WKUP_IRQHandler /* USB OTG FS Wakeup through EXTI line */ - .word TIM8_BRK_TIM12_IRQHandler /* TIM8 Break and TIM12 */ - .word TIM8_UP_TIM13_IRQHandler /* TIM8 Update and TIM13 */ - .word TIM8_TRG_COM_TIM14_IRQHandler /* TIM8 Trigger and Commutation and TIM14 */ - .word TIM8_CC_IRQHandler /* TIM8 Capture Compare */ - .word DMA1_Stream7_IRQHandler /* DMA1 Stream7 */ - .word FSMC_IRQHandler /* FSMC */ - .word SDIO_IRQHandler /* SDIO */ - .word TIM5_IRQHandler /* TIM5 */ - .word SPI3_IRQHandler /* SPI3 */ - .word UART4_IRQHandler /* UART4 */ - .word UART5_IRQHandler /* UART5 */ - .word TIM6_DAC_IRQHandler /* TIM6 and DAC1&2 underrun errors */ - .word TIM7_IRQHandler /* TIM7 */ - .word DMA2_Stream0_IRQHandler /* DMA2 Stream 0 */ - .word DMA2_Stream1_IRQHandler /* DMA2 Stream 1 */ - .word DMA2_Stream2_IRQHandler /* DMA2 Stream 2 */ - .word DMA2_Stream3_IRQHandler /* DMA2 Stream 3 */ - .word DMA2_Stream4_IRQHandler /* DMA2 Stream 4 */ - .word ETH_IRQHandler /* Ethernet */ - .word ETH_WKUP_IRQHandler /* Ethernet Wakeup through EXTI line */ - .word CAN2_TX_IRQHandler /* CAN2 TX */ - .word CAN2_RX0_IRQHandler /* CAN2 RX0 */ - .word CAN2_RX1_IRQHandler /* CAN2 RX1 */ - .word CAN2_SCE_IRQHandler /* CAN2 SCE */ - .word OTG_FS_IRQHandler /* USB OTG FS */ - .word DMA2_Stream5_IRQHandler /* DMA2 Stream 5 */ - .word DMA2_Stream6_IRQHandler /* DMA2 Stream 6 */ - .word DMA2_Stream7_IRQHandler /* DMA2 Stream 7 */ - .word USART6_IRQHandler /* USART6 */ - .word I2C3_EV_IRQHandler /* I2C3 event */ - .word I2C3_ER_IRQHandler /* I2C3 error */ - .word OTG_HS_EP1_OUT_IRQHandler /* USB OTG HS End Point 1 Out */ - .word OTG_HS_EP1_IN_IRQHandler /* USB OTG HS End Point 1 In */ - .word OTG_HS_WKUP_IRQHandler /* USB OTG HS Wakeup through EXTI */ - .word OTG_HS_IRQHandler /* USB OTG HS */ - .word DCMI_IRQHandler /* DCMI */ - .word CRYP_IRQHandler /* CRYP crypto */ - .word HASH_RNG_IRQHandler /* Hash and Rng */ - .word FPU_IRQHandler /* FPU */ - - -/******************************************************************************* -* -* Provide weak aliases for each Exception handler to the Default_Handler. -* As they are weak aliases, any function with the same name will override -* this definition. -* -*******************************************************************************/ - .weak NMI_Handler - .thumb_set NMI_Handler,Default_Handler - - .weak HardFault_Handler - .thumb_set HardFault_Handler,Default_Handler - - .weak MemManage_Handler - .thumb_set MemManage_Handler,Default_Handler - - .weak BusFault_Handler - .thumb_set BusFault_Handler,Default_Handler - - .weak UsageFault_Handler - .thumb_set UsageFault_Handler,Default_Handler - - .weak SVC_Handler - .thumb_set SVC_Handler,Default_Handler - - .weak DebugMon_Handler - .thumb_set DebugMon_Handler,Default_Handler - - .weak PendSV_Handler - .thumb_set PendSV_Handler,Default_Handler - - .weak SysTick_Handler - .thumb_set SysTick_Handler,Default_Handler - - .weak WWDG_IRQHandler - .thumb_set WWDG_IRQHandler,Default_Handler - - .weak PVD_IRQHandler - .thumb_set PVD_IRQHandler,Default_Handler - - .weak TAMP_STAMP_IRQHandler - .thumb_set TAMP_STAMP_IRQHandler,Default_Handler - - .weak RTC_WKUP_IRQHandler - .thumb_set RTC_WKUP_IRQHandler,Default_Handler - - .weak FLASH_IRQHandler - .thumb_set FLASH_IRQHandler,Default_Handler - - .weak RCC_IRQHandler - .thumb_set RCC_IRQHandler,Default_Handler - - .weak EXTI0_IRQHandler - .thumb_set EXTI0_IRQHandler,Default_Handler - - .weak EXTI1_IRQHandler - .thumb_set EXTI1_IRQHandler,Default_Handler - - .weak EXTI2_IRQHandler - .thumb_set EXTI2_IRQHandler,Default_Handler - - .weak EXTI3_IRQHandler - .thumb_set EXTI3_IRQHandler,Default_Handler - - .weak EXTI4_IRQHandler - .thumb_set EXTI4_IRQHandler,Default_Handler - - .weak DMA1_Stream0_IRQHandler - .thumb_set DMA1_Stream0_IRQHandler,Default_Handler - - .weak DMA1_Stream1_IRQHandler - .thumb_set DMA1_Stream1_IRQHandler,Default_Handler - - .weak DMA1_Stream2_IRQHandler - .thumb_set DMA1_Stream2_IRQHandler,Default_Handler - - .weak DMA1_Stream3_IRQHandler - .thumb_set DMA1_Stream3_IRQHandler,Default_Handler - - .weak DMA1_Stream4_IRQHandler - .thumb_set DMA1_Stream4_IRQHandler,Default_Handler - - .weak DMA1_Stream5_IRQHandler - .thumb_set DMA1_Stream5_IRQHandler,Default_Handler - - .weak DMA1_Stream6_IRQHandler - .thumb_set DMA1_Stream6_IRQHandler,Default_Handler - - .weak ADC_IRQHandler - .thumb_set ADC_IRQHandler,Default_Handler - - .weak CAN1_TX_IRQHandler - .thumb_set CAN1_TX_IRQHandler,Default_Handler - - .weak CAN1_RX0_IRQHandler - .thumb_set CAN1_RX0_IRQHandler,Default_Handler - - .weak CAN1_RX1_IRQHandler - .thumb_set CAN1_RX1_IRQHandler,Default_Handler - - .weak CAN1_SCE_IRQHandler - .thumb_set CAN1_SCE_IRQHandler,Default_Handler - - .weak EXTI9_5_IRQHandler - .thumb_set EXTI9_5_IRQHandler,Default_Handler - - .weak TIM1_BRK_TIM9_IRQHandler - .thumb_set TIM1_BRK_TIM9_IRQHandler,Default_Handler - - .weak TIM1_UP_TIM10_IRQHandler - .thumb_set TIM1_UP_TIM10_IRQHandler,Default_Handler - - .weak TIM1_TRG_COM_TIM11_IRQHandler - .thumb_set TIM1_TRG_COM_TIM11_IRQHandler,Default_Handler - - .weak TIM1_CC_IRQHandler - .thumb_set TIM1_CC_IRQHandler,Default_Handler - - .weak TIM2_IRQHandler - .thumb_set TIM2_IRQHandler,Default_Handler - - .weak TIM3_IRQHandler - .thumb_set TIM3_IRQHandler,Default_Handler - - .weak TIM4_IRQHandler - .thumb_set TIM4_IRQHandler,Default_Handler - - .weak I2C1_EV_IRQHandler - .thumb_set I2C1_EV_IRQHandler,Default_Handler - - .weak I2C1_ER_IRQHandler - .thumb_set I2C1_ER_IRQHandler,Default_Handler - - .weak I2C2_EV_IRQHandler - .thumb_set I2C2_EV_IRQHandler,Default_Handler - - .weak I2C2_ER_IRQHandler - .thumb_set I2C2_ER_IRQHandler,Default_Handler - - .weak SPI1_IRQHandler - .thumb_set SPI1_IRQHandler,Default_Handler - - .weak SPI2_IRQHandler - .thumb_set SPI2_IRQHandler,Default_Handler - - .weak USART1_IRQHandler - .thumb_set USART1_IRQHandler,Default_Handler - - .weak USART2_IRQHandler - .thumb_set USART2_IRQHandler,Default_Handler - - .weak USART3_IRQHandler - .thumb_set USART3_IRQHandler,Default_Handler - - .weak EXTI15_10_IRQHandler - .thumb_set EXTI15_10_IRQHandler,Default_Handler - - .weak RTC_Alarm_IRQHandler - .thumb_set RTC_Alarm_IRQHandler,Default_Handler - - .weak OTG_FS_WKUP_IRQHandler - .thumb_set OTG_FS_WKUP_IRQHandler,Default_Handler - - .weak TIM8_BRK_TIM12_IRQHandler - .thumb_set TIM8_BRK_TIM12_IRQHandler,Default_Handler - - .weak TIM8_UP_TIM13_IRQHandler - .thumb_set TIM8_UP_TIM13_IRQHandler,Default_Handler - - .weak TIM8_TRG_COM_TIM14_IRQHandler - .thumb_set TIM8_TRG_COM_TIM14_IRQHandler,Default_Handler - - .weak TIM8_CC_IRQHandler - .thumb_set TIM8_CC_IRQHandler,Default_Handler - - .weak DMA1_Stream7_IRQHandler - .thumb_set DMA1_Stream7_IRQHandler,Default_Handler - - .weak FSMC_IRQHandler - .thumb_set FSMC_IRQHandler,Default_Handler - - .weak SDIO_IRQHandler - .thumb_set SDIO_IRQHandler,Default_Handler - - .weak TIM5_IRQHandler - .thumb_set TIM5_IRQHandler,Default_Handler - - .weak SPI3_IRQHandler - .thumb_set SPI3_IRQHandler,Default_Handler - - .weak UART4_IRQHandler - .thumb_set UART4_IRQHandler,Default_Handler - - .weak UART5_IRQHandler - .thumb_set UART5_IRQHandler,Default_Handler - - .weak TIM6_DAC_IRQHandler - .thumb_set TIM6_DAC_IRQHandler,Default_Handler - - .weak TIM7_IRQHandler - .thumb_set TIM7_IRQHandler,Default_Handler - - .weak DMA2_Stream0_IRQHandler - .thumb_set DMA2_Stream0_IRQHandler,Default_Handler - - .weak DMA2_Stream1_IRQHandler - .thumb_set DMA2_Stream1_IRQHandler,Default_Handler - - .weak DMA2_Stream2_IRQHandler - .thumb_set DMA2_Stream2_IRQHandler,Default_Handler - - .weak DMA2_Stream3_IRQHandler - .thumb_set DMA2_Stream3_IRQHandler,Default_Handler - - .weak DMA2_Stream4_IRQHandler - .thumb_set DMA2_Stream4_IRQHandler,Default_Handler - - .weak ETH_IRQHandler - .thumb_set ETH_IRQHandler,Default_Handler - - .weak ETH_WKUP_IRQHandler - .thumb_set ETH_WKUP_IRQHandler,Default_Handler - - .weak CAN2_TX_IRQHandler - .thumb_set CAN2_TX_IRQHandler,Default_Handler - - .weak CAN2_RX0_IRQHandler - .thumb_set CAN2_RX0_IRQHandler,Default_Handler - - .weak CAN2_RX1_IRQHandler - .thumb_set CAN2_RX1_IRQHandler,Default_Handler - - .weak CAN2_SCE_IRQHandler - .thumb_set CAN2_SCE_IRQHandler,Default_Handler - - .weak OTG_FS_IRQHandler - .thumb_set OTG_FS_IRQHandler,Default_Handler - - .weak DMA2_Stream5_IRQHandler - .thumb_set DMA2_Stream5_IRQHandler,Default_Handler - - .weak DMA2_Stream6_IRQHandler - .thumb_set DMA2_Stream6_IRQHandler,Default_Handler - - .weak DMA2_Stream7_IRQHandler - .thumb_set DMA2_Stream7_IRQHandler,Default_Handler - - .weak USART6_IRQHandler - .thumb_set USART6_IRQHandler,Default_Handler - - .weak I2C3_EV_IRQHandler - .thumb_set I2C3_EV_IRQHandler,Default_Handler - - .weak I2C3_ER_IRQHandler - .thumb_set I2C3_ER_IRQHandler,Default_Handler - - .weak OTG_HS_EP1_OUT_IRQHandler - .thumb_set OTG_HS_EP1_OUT_IRQHandler,Default_Handler - - .weak OTG_HS_EP1_IN_IRQHandler - .thumb_set OTG_HS_EP1_IN_IRQHandler,Default_Handler - - .weak OTG_HS_WKUP_IRQHandler - .thumb_set OTG_HS_WKUP_IRQHandler,Default_Handler - - .weak OTG_HS_IRQHandler - .thumb_set OTG_HS_IRQHandler,Default_Handler - - .weak DCMI_IRQHandler - .thumb_set DCMI_IRQHandler,Default_Handler - - .weak CRYP_IRQHandler - .thumb_set CRYP_IRQHandler,Default_Handler - - .weak HASH_RNG_IRQHandler - .thumb_set HASH_RNG_IRQHandler,Default_Handler - - .weak FPU_IRQHandler - .thumb_set FPU_IRQHandler,Default_Handler - -/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/ +/** + ****************************************************************************** + * @file startup_stm32f4xx.s + * @author MCD Application Team + * @version V1.0.0 + * @date 30-September-2011 + * @brief STM32F4xx Devices vector table for Atollic TrueSTUDIO toolchain. + * This module performs: + * - Set the initial SP + * - Set the initial PC == Reset_Handler, + * - Set the vector table entries with the exceptions ISR address + * - Configure the clock system and the external SRAM mounted on + * STM324xG-EVAL board to be used as data memory (optional, + * to be enabled by user) + * - Branches to main in the C library (which eventually + * calls main()). + * After Reset the Cortex-M4 processor is in Thread mode, + * priority is Privileged, and the Stack is set to Main. + ****************************************************************************** + * @attention + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2011 STMicroelectronics

+ ****************************************************************************** + */ + + .syntax unified + .cpu cortex-m3 + .fpu softvfp + .thumb + +.global g_pfnVectors +.global Default_Handler + +/* start address for the initialization values of the .data section. +defined in linker script */ +.word _sidata +/* start address for the .data section. defined in linker script */ +.word _sdata +/* end address for the .data section. defined in linker script */ +.word _edata +/* start address for the .bss section. defined in linker script */ +.word _sbss +/* end address for the .bss section. defined in linker script */ +.word _ebss +/* stack used for SystemInit_ExtMemCtl; always internal RAM used */ + +/** + * @brief This is the code that gets called when the processor first + * starts execution following a reset event. Only the absolutely + * necessary set is performed, after which the application + * supplied main() routine is called. + * @param None + * @retval : None +*/ + + .section .text.Reset_Handler + .weak Reset_Handler + .type Reset_Handler, %function +Reset_Handler: + +/* Copy the data segment initializers from flash to SRAM */ + movs r1, #0 + b LoopCopyDataInit + +CopyDataInit: + ldr r3, =_sidata + ldr r3, [r3, r1] + str r3, [r0, r1] + adds r1, r1, #4 + +LoopCopyDataInit: + ldr r0, =_sdata + ldr r3, =_edata + adds r2, r0, r1 + cmp r2, r3 + bcc CopyDataInit + ldr r2, =_sbss + b LoopFillZerobss +/* Zero fill the bss segment. */ +FillZerobss: + movs r3, #0 + str r3, [r2], #4 + +LoopFillZerobss: + ldr r3, = _ebss + cmp r2, r3 + bcc FillZerobss + +/* Call the clock system intitialization function.*/ + bl SystemInit +/* Call static constructors */ + bl __libc_init_array +/* Call the application's entry point.*/ + bl main + bx lr +.size Reset_Handler, .-Reset_Handler + +/** + * @brief This is the code that gets called when the processor receives an + * unexpected interrupt. This simply enters an infinite loop, preserving + * the system state for examination by a debugger. + * @param None + * @retval None +*/ + .section .text.Default_Handler,"ax",%progbits +Default_Handler: +Infinite_Loop: + b Infinite_Loop + .size Default_Handler, .-Default_Handler +/****************************************************************************** +* +* The minimal vector table for a Cortex M3. Note that the proper constructs +* must be placed on this to ensure that it ends up at physical address +* 0x0000.0000. +* +*******************************************************************************/ + .section .isr_vector,"a",%progbits + .type g_pfnVectors, %object + .size g_pfnVectors, .-g_pfnVectors + + +g_pfnVectors: + .word _estack + .word Reset_Handler + .word NMI_Handler + .word HardFault_Handler + .word MemManage_Handler + .word BusFault_Handler + .word UsageFault_Handler + .word 0 + .word 0 + .word 0 + .word 0 + .word SVC_Handler + .word DebugMon_Handler + .word 0 + .word PendSV_Handler + .word SysTick_Handler + + /* External Interrupts */ + .word WWDG_IRQHandler /* Window WatchDog */ + .word PVD_IRQHandler /* PVD through EXTI Line detection */ + .word TAMP_STAMP_IRQHandler /* Tamper and TimeStamps through the EXTI line */ + .word RTC_WKUP_IRQHandler /* RTC Wakeup through the EXTI line */ + .word FLASH_IRQHandler /* FLASH */ + .word RCC_IRQHandler /* RCC */ + .word EXTI0_IRQHandler /* EXTI Line0 */ + .word EXTI1_IRQHandler /* EXTI Line1 */ + .word EXTI2_IRQHandler /* EXTI Line2 */ + .word EXTI3_IRQHandler /* EXTI Line3 */ + .word EXTI4_IRQHandler /* EXTI Line4 */ + .word DMA1_Stream0_IRQHandler /* DMA1 Stream 0 */ + .word DMA1_Stream1_IRQHandler /* DMA1 Stream 1 */ + .word DMA1_Stream2_IRQHandler /* DMA1 Stream 2 */ + .word DMA1_Stream3_IRQHandler /* DMA1 Stream 3 */ + .word DMA1_Stream4_IRQHandler /* DMA1 Stream 4 */ + .word DMA1_Stream5_IRQHandler /* DMA1 Stream 5 */ + .word DMA1_Stream6_IRQHandler /* DMA1 Stream 6 */ + .word ADC_IRQHandler /* ADC1, ADC2 and ADC3s */ + .word CAN1_TX_IRQHandler /* CAN1 TX */ + .word CAN1_RX0_IRQHandler /* CAN1 RX0 */ + .word CAN1_RX1_IRQHandler /* CAN1 RX1 */ + .word CAN1_SCE_IRQHandler /* CAN1 SCE */ + .word EXTI9_5_IRQHandler /* External Line[9:5]s */ + .word TIM1_BRK_TIM9_IRQHandler /* TIM1 Break and TIM9 */ + .word TIM1_UP_TIM10_IRQHandler /* TIM1 Update and TIM10 */ + .word TIM1_TRG_COM_TIM11_IRQHandler /* TIM1 Trigger and Commutation and TIM11 */ + .word TIM1_CC_IRQHandler /* TIM1 Capture Compare */ + .word TIM2_IRQHandler /* TIM2 */ + .word TIM3_IRQHandler /* TIM3 */ + .word TIM4_IRQHandler /* TIM4 */ + .word I2C1_EV_IRQHandler /* I2C1 Event */ + .word I2C1_ER_IRQHandler /* I2C1 Error */ + .word I2C2_EV_IRQHandler /* I2C2 Event */ + .word I2C2_ER_IRQHandler /* I2C2 Error */ + .word SPI1_IRQHandler /* SPI1 */ + .word SPI2_IRQHandler /* SPI2 */ + .word USART1_IRQHandler /* USART1 */ + .word USART2_IRQHandler /* USART2 */ + .word USART3_IRQHandler /* USART3 */ + .word EXTI15_10_IRQHandler /* External Line[15:10]s */ + .word RTC_Alarm_IRQHandler /* RTC Alarm (A and B) through EXTI Line */ + .word OTG_FS_WKUP_IRQHandler /* USB OTG FS Wakeup through EXTI line */ + .word TIM8_BRK_TIM12_IRQHandler /* TIM8 Break and TIM12 */ + .word TIM8_UP_TIM13_IRQHandler /* TIM8 Update and TIM13 */ + .word TIM8_TRG_COM_TIM14_IRQHandler /* TIM8 Trigger and Commutation and TIM14 */ + .word TIM8_CC_IRQHandler /* TIM8 Capture Compare */ + .word DMA1_Stream7_IRQHandler /* DMA1 Stream7 */ + .word FSMC_IRQHandler /* FSMC */ + .word SDIO_IRQHandler /* SDIO */ + .word TIM5_IRQHandler /* TIM5 */ + .word SPI3_IRQHandler /* SPI3 */ + .word UART4_IRQHandler /* UART4 */ + .word UART5_IRQHandler /* UART5 */ + .word TIM6_DAC_IRQHandler /* TIM6 and DAC1&2 underrun errors */ + .word TIM7_IRQHandler /* TIM7 */ + .word DMA2_Stream0_IRQHandler /* DMA2 Stream 0 */ + .word DMA2_Stream1_IRQHandler /* DMA2 Stream 1 */ + .word DMA2_Stream2_IRQHandler /* DMA2 Stream 2 */ + .word DMA2_Stream3_IRQHandler /* DMA2 Stream 3 */ + .word DMA2_Stream4_IRQHandler /* DMA2 Stream 4 */ + .word ETH_IRQHandler /* Ethernet */ + .word ETH_WKUP_IRQHandler /* Ethernet Wakeup through EXTI line */ + .word CAN2_TX_IRQHandler /* CAN2 TX */ + .word CAN2_RX0_IRQHandler /* CAN2 RX0 */ + .word CAN2_RX1_IRQHandler /* CAN2 RX1 */ + .word CAN2_SCE_IRQHandler /* CAN2 SCE */ + .word OTG_FS_IRQHandler /* USB OTG FS */ + .word DMA2_Stream5_IRQHandler /* DMA2 Stream 5 */ + .word DMA2_Stream6_IRQHandler /* DMA2 Stream 6 */ + .word DMA2_Stream7_IRQHandler /* DMA2 Stream 7 */ + .word USART6_IRQHandler /* USART6 */ + .word I2C3_EV_IRQHandler /* I2C3 event */ + .word I2C3_ER_IRQHandler /* I2C3 error */ + .word OTG_HS_EP1_OUT_IRQHandler /* USB OTG HS End Point 1 Out */ + .word OTG_HS_EP1_IN_IRQHandler /* USB OTG HS End Point 1 In */ + .word OTG_HS_WKUP_IRQHandler /* USB OTG HS Wakeup through EXTI */ + .word OTG_HS_IRQHandler /* USB OTG HS */ + .word DCMI_IRQHandler /* DCMI */ + .word CRYP_IRQHandler /* CRYP crypto */ + .word HASH_RNG_IRQHandler /* Hash and Rng */ + .word FPU_IRQHandler /* FPU */ + + +/******************************************************************************* +* +* Provide weak aliases for each Exception handler to the Default_Handler. +* As they are weak aliases, any function with the same name will override +* this definition. +* +*******************************************************************************/ + .weak NMI_Handler + .thumb_set NMI_Handler,Default_Handler + + .weak HardFault_Handler + .thumb_set HardFault_Handler,Default_Handler + + .weak MemManage_Handler + .thumb_set MemManage_Handler,Default_Handler + + .weak BusFault_Handler + .thumb_set BusFault_Handler,Default_Handler + + .weak UsageFault_Handler + .thumb_set UsageFault_Handler,Default_Handler + + .weak SVC_Handler + .thumb_set SVC_Handler,Default_Handler + + .weak DebugMon_Handler + .thumb_set DebugMon_Handler,Default_Handler + + .weak PendSV_Handler + .thumb_set PendSV_Handler,Default_Handler + + .weak SysTick_Handler + .thumb_set SysTick_Handler,Default_Handler + + .weak WWDG_IRQHandler + .thumb_set WWDG_IRQHandler,Default_Handler + + .weak PVD_IRQHandler + .thumb_set PVD_IRQHandler,Default_Handler + + .weak TAMP_STAMP_IRQHandler + .thumb_set TAMP_STAMP_IRQHandler,Default_Handler + + .weak RTC_WKUP_IRQHandler + .thumb_set RTC_WKUP_IRQHandler,Default_Handler + + .weak FLASH_IRQHandler + .thumb_set FLASH_IRQHandler,Default_Handler + + .weak RCC_IRQHandler + .thumb_set RCC_IRQHandler,Default_Handler + + .weak EXTI0_IRQHandler + .thumb_set EXTI0_IRQHandler,Default_Handler + + .weak EXTI1_IRQHandler + .thumb_set EXTI1_IRQHandler,Default_Handler + + .weak EXTI2_IRQHandler + .thumb_set EXTI2_IRQHandler,Default_Handler + + .weak EXTI3_IRQHandler + .thumb_set EXTI3_IRQHandler,Default_Handler + + .weak EXTI4_IRQHandler + .thumb_set EXTI4_IRQHandler,Default_Handler + + .weak DMA1_Stream0_IRQHandler + .thumb_set DMA1_Stream0_IRQHandler,Default_Handler + + .weak DMA1_Stream1_IRQHandler + .thumb_set DMA1_Stream1_IRQHandler,Default_Handler + + .weak DMA1_Stream2_IRQHandler + .thumb_set DMA1_Stream2_IRQHandler,Default_Handler + + .weak DMA1_Stream3_IRQHandler + .thumb_set DMA1_Stream3_IRQHandler,Default_Handler + + .weak DMA1_Stream4_IRQHandler + .thumb_set DMA1_Stream4_IRQHandler,Default_Handler + + .weak DMA1_Stream5_IRQHandler + .thumb_set DMA1_Stream5_IRQHandler,Default_Handler + + .weak DMA1_Stream6_IRQHandler + .thumb_set DMA1_Stream6_IRQHandler,Default_Handler + + .weak ADC_IRQHandler + .thumb_set ADC_IRQHandler,Default_Handler + + .weak CAN1_TX_IRQHandler + .thumb_set CAN1_TX_IRQHandler,Default_Handler + + .weak CAN1_RX0_IRQHandler + .thumb_set CAN1_RX0_IRQHandler,Default_Handler + + .weak CAN1_RX1_IRQHandler + .thumb_set CAN1_RX1_IRQHandler,Default_Handler + + .weak CAN1_SCE_IRQHandler + .thumb_set CAN1_SCE_IRQHandler,Default_Handler + + .weak EXTI9_5_IRQHandler + .thumb_set EXTI9_5_IRQHandler,Default_Handler + + .weak TIM1_BRK_TIM9_IRQHandler + .thumb_set TIM1_BRK_TIM9_IRQHandler,Default_Handler + + .weak TIM1_UP_TIM10_IRQHandler + .thumb_set TIM1_UP_TIM10_IRQHandler,Default_Handler + + .weak TIM1_TRG_COM_TIM11_IRQHandler + .thumb_set TIM1_TRG_COM_TIM11_IRQHandler,Default_Handler + + .weak TIM1_CC_IRQHandler + .thumb_set TIM1_CC_IRQHandler,Default_Handler + + .weak TIM2_IRQHandler + .thumb_set TIM2_IRQHandler,Default_Handler + + .weak TIM3_IRQHandler + .thumb_set TIM3_IRQHandler,Default_Handler + + .weak TIM4_IRQHandler + .thumb_set TIM4_IRQHandler,Default_Handler + + .weak I2C1_EV_IRQHandler + .thumb_set I2C1_EV_IRQHandler,Default_Handler + + .weak I2C1_ER_IRQHandler + .thumb_set I2C1_ER_IRQHandler,Default_Handler + + .weak I2C2_EV_IRQHandler + .thumb_set I2C2_EV_IRQHandler,Default_Handler + + .weak I2C2_ER_IRQHandler + .thumb_set I2C2_ER_IRQHandler,Default_Handler + + .weak SPI1_IRQHandler + .thumb_set SPI1_IRQHandler,Default_Handler + + .weak SPI2_IRQHandler + .thumb_set SPI2_IRQHandler,Default_Handler + + .weak USART1_IRQHandler + .thumb_set USART1_IRQHandler,Default_Handler + + .weak USART2_IRQHandler + .thumb_set USART2_IRQHandler,Default_Handler + + .weak USART3_IRQHandler + .thumb_set USART3_IRQHandler,Default_Handler + + .weak EXTI15_10_IRQHandler + .thumb_set EXTI15_10_IRQHandler,Default_Handler + + .weak RTC_Alarm_IRQHandler + .thumb_set RTC_Alarm_IRQHandler,Default_Handler + + .weak OTG_FS_WKUP_IRQHandler + .thumb_set OTG_FS_WKUP_IRQHandler,Default_Handler + + .weak TIM8_BRK_TIM12_IRQHandler + .thumb_set TIM8_BRK_TIM12_IRQHandler,Default_Handler + + .weak TIM8_UP_TIM13_IRQHandler + .thumb_set TIM8_UP_TIM13_IRQHandler,Default_Handler + + .weak TIM8_TRG_COM_TIM14_IRQHandler + .thumb_set TIM8_TRG_COM_TIM14_IRQHandler,Default_Handler + + .weak TIM8_CC_IRQHandler + .thumb_set TIM8_CC_IRQHandler,Default_Handler + + .weak DMA1_Stream7_IRQHandler + .thumb_set DMA1_Stream7_IRQHandler,Default_Handler + + .weak FSMC_IRQHandler + .thumb_set FSMC_IRQHandler,Default_Handler + + .weak SDIO_IRQHandler + .thumb_set SDIO_IRQHandler,Default_Handler + + .weak TIM5_IRQHandler + .thumb_set TIM5_IRQHandler,Default_Handler + + .weak SPI3_IRQHandler + .thumb_set SPI3_IRQHandler,Default_Handler + + .weak UART4_IRQHandler + .thumb_set UART4_IRQHandler,Default_Handler + + .weak UART5_IRQHandler + .thumb_set UART5_IRQHandler,Default_Handler + + .weak TIM6_DAC_IRQHandler + .thumb_set TIM6_DAC_IRQHandler,Default_Handler + + .weak TIM7_IRQHandler + .thumb_set TIM7_IRQHandler,Default_Handler + + .weak DMA2_Stream0_IRQHandler + .thumb_set DMA2_Stream0_IRQHandler,Default_Handler + + .weak DMA2_Stream1_IRQHandler + .thumb_set DMA2_Stream1_IRQHandler,Default_Handler + + .weak DMA2_Stream2_IRQHandler + .thumb_set DMA2_Stream2_IRQHandler,Default_Handler + + .weak DMA2_Stream3_IRQHandler + .thumb_set DMA2_Stream3_IRQHandler,Default_Handler + + .weak DMA2_Stream4_IRQHandler + .thumb_set DMA2_Stream4_IRQHandler,Default_Handler + + .weak ETH_IRQHandler + .thumb_set ETH_IRQHandler,Default_Handler + + .weak ETH_WKUP_IRQHandler + .thumb_set ETH_WKUP_IRQHandler,Default_Handler + + .weak CAN2_TX_IRQHandler + .thumb_set CAN2_TX_IRQHandler,Default_Handler + + .weak CAN2_RX0_IRQHandler + .thumb_set CAN2_RX0_IRQHandler,Default_Handler + + .weak CAN2_RX1_IRQHandler + .thumb_set CAN2_RX1_IRQHandler,Default_Handler + + .weak CAN2_SCE_IRQHandler + .thumb_set CAN2_SCE_IRQHandler,Default_Handler + + .weak OTG_FS_IRQHandler + .thumb_set OTG_FS_IRQHandler,Default_Handler + + .weak DMA2_Stream5_IRQHandler + .thumb_set DMA2_Stream5_IRQHandler,Default_Handler + + .weak DMA2_Stream6_IRQHandler + .thumb_set DMA2_Stream6_IRQHandler,Default_Handler + + .weak DMA2_Stream7_IRQHandler + .thumb_set DMA2_Stream7_IRQHandler,Default_Handler + + .weak USART6_IRQHandler + .thumb_set USART6_IRQHandler,Default_Handler + + .weak I2C3_EV_IRQHandler + .thumb_set I2C3_EV_IRQHandler,Default_Handler + + .weak I2C3_ER_IRQHandler + .thumb_set I2C3_ER_IRQHandler,Default_Handler + + .weak OTG_HS_EP1_OUT_IRQHandler + .thumb_set OTG_HS_EP1_OUT_IRQHandler,Default_Handler + + .weak OTG_HS_EP1_IN_IRQHandler + .thumb_set OTG_HS_EP1_IN_IRQHandler,Default_Handler + + .weak OTG_HS_WKUP_IRQHandler + .thumb_set OTG_HS_WKUP_IRQHandler,Default_Handler + + .weak OTG_HS_IRQHandler + .thumb_set OTG_HS_IRQHandler,Default_Handler + + .weak DCMI_IRQHandler + .thumb_set DCMI_IRQHandler,Default_Handler + + .weak CRYP_IRQHandler + .thumb_set CRYP_IRQHandler,Default_Handler + + .weak HASH_RNG_IRQHandler + .thumb_set HASH_RNG_IRQHandler,Default_Handler + + .weak FPU_IRQHandler + .thumb_set FPU_IRQHandler,Default_Handler + +/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/ diff --git a/codec2-dev/stm32/src/stm32f4_dac.c b/codec2-dev/stm32/src/stm32f4_dac.c index ef65a7f5..cba9ea79 100644 --- a/codec2-dev/stm32/src/stm32f4_dac.c +++ b/codec2-dev/stm32/src/stm32f4_dac.c @@ -1,406 +1,406 @@ -/*---------------------------------------------------------------------------*\ - - FILE........: stm32f4_dac.c - AUTHOR......: David Rowe - DATE CREATED: 1 June 2013 - - DAC driver module for STM32F4. DAC1 is connected to pin PA4, DAC2 - is connected to pin PA5. - -\*---------------------------------------------------------------------------*/ - -/* - Copyright (C) 2013 David Rowe - - All rights reserved. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License version 2.1, as - published by the Free Software Foundation. This program is - distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or - FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public - License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program; if not, see . -*/ - -#include -#include -#include -#include "stm32f4xx.h" -#include "codec2_fifo.h" -#include "stm32f4_dac.h" -#include "debugblinky.h" - -/* write to these registers for 12 bit left aligned data, as per data sheet - make sure 4 least sig bits set to 0 */ - -#define DAC_DHR12R1_ADDRESS 0x40007408 -#define DAC_DHR12R2_ADDRESS 0x40007414 - -#define DAC_MAX 4096 /* maximum amplitude */ - -/* y=mx+c mapping of samples16 bit shorts to DAC samples. Table: 74 - of data sheet indicates With DAC buffer on, DAC range is limited to - 0x0E0 to 0xF1C at VREF+ = 3.6 V, we have Vref=3.3V which is close. - */ - -#define M ((3868.0-224.0)/65536.0) -#define C 2047.0 - -static struct FIFO *dac1_fifo; -static struct FIFO *dac2_fifo; - -static unsigned short dac1_buf[DAC_BUF_SZ]; -static unsigned short dac2_buf[DAC_BUF_SZ]; - -static void tim6_config(void); -static void dac1_config(void); -static void dac2_config(void); - -int dac_underflow; - -void dac_open(int fifo_size) { - - memset(dac1_buf, 32768, sizeof(short)*DAC_BUF_SZ); - memset(dac2_buf, 32768, sizeof(short)*DAC_BUF_SZ); - - /* Create fifos */ - - dac1_fifo = fifo_create(fifo_size); - dac2_fifo = fifo_create(fifo_size); - assert(dac1_fifo != NULL); - assert(dac2_fifo != NULL); - - /* Turn on the clocks we need -----------------------------------------------*/ - - /* DMA1 clock enable */ - RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE); - /* GPIOA clock enable (to be used with DAC) */ - RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); - /* DAC Periph clock enable */ - RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE); - - /* GPIO Pin configuration DAC1->PA.4, DAC2->PA.5 configuration --------------*/ - - GPIO_InitTypeDef GPIO_InitStructure; - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5; - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN; - GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; - GPIO_Init(GPIOA, &GPIO_InitStructure); - - /* Timer and DAC 1 & 2 Configuration ----------------------------------------*/ - - tim6_config(); - dac1_config(); - dac2_config(); - - init_debug_blinky(); -} - -/* Call these puppies to send samples to the DACs. For your - convenience they accept signed 16 bit samples. */ - -int dac1_write(short buf[], int n) { - return fifo_write(dac1_fifo, buf, n); -} - -int dac2_write(short buf[], int n) { - return fifo_write(dac2_fifo, buf, n); -} - -int dac1_free() { - return fifo_free(dac1_fifo); -} - -int dac2_free() { - return fifo_free(dac2_fifo); -} - -static void tim6_config(void) -{ - TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; - - /* TIM6 Periph clock enable */ - RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE); - - /* -------------------------------------------------------- - - TIM6 input clock (TIM6CLK) is set to 2 * APB1 clock (PCLK1), since - APB1 prescaler is different from 1 (see system_stm32f4xx.c and Fig - 13 clock tree figure in DM0031020.pdf). - - Sample rate Fs = 2*PCLK1/TIM_ClockDivision - = (HCLK/2)/TIM_ClockDivision - - ----------------------------------------------------------- */ - - /* Time base configuration */ - - TIM_TimeBaseStructInit(&TIM_TimeBaseStructure); - TIM_TimeBaseStructure.TIM_Period = 5250; - TIM_TimeBaseStructure.TIM_Prescaler = 0; - TIM_TimeBaseStructure.TIM_ClockDivision = 0; - TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; - TIM_TimeBaseInit(TIM6, &TIM_TimeBaseStructure); - - /* TIM6 TRGO selection */ - - TIM_SelectOutputTrigger(TIM6, TIM_TRGOSource_Update); - - /* TIM6 enable counter */ - - TIM_Cmd(TIM6, ENABLE); -} - -static void dac1_config(void) -{ - DAC_InitTypeDef DAC_InitStructure; - DMA_InitTypeDef DMA_InitStructure; - NVIC_InitTypeDef NVIC_InitStructure; - - /* DAC channel 1 Configuration */ - - /* - This line fixed a bug that cost me 5 days, bad wave amplitude - value, and some STM32F4 periph library bugs caused triangle wave - geneartion to be enable resulting in a low level tone on the - SM1000, that we thought was caused by analog issues like layour - or power supply biasing - */ - DAC_StructInit(&DAC_InitStructure); - - DAC_InitStructure.DAC_Trigger = DAC_Trigger_T6_TRGO; - DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None; - DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Enable; - DAC_Init(DAC_Channel_1, &DAC_InitStructure); - - /* DMA1_Stream5 channel7 configuration **************************************/ - /* Table 35 page 219 of the monster data sheet */ - - DMA_DeInit(DMA1_Stream5); - DMA_InitStructure.DMA_Channel = DMA_Channel_7; - DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)DAC_DHR12R1_ADDRESS; - DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)dac1_buf; - DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral; - DMA_InitStructure.DMA_BufferSize = DAC_BUF_SZ; - DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; - DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; - DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; - DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; - DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; - DMA_InitStructure.DMA_Priority = DMA_Priority_High; - DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; - DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull; - DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; - DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; - DMA_Init(DMA1_Stream5, &DMA_InitStructure); - - /* Enable DMA Half & Complete interrupts */ - - DMA_ITConfig(DMA1_Stream5, DMA_IT_TC | DMA_IT_HT, ENABLE); - - /* Enable the DMA Stream IRQ Channel */ - - NVIC_InitStructure.NVIC_IRQChannel = DMA1_Stream5_IRQn; - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; - NVIC_Init(&NVIC_InitStructure); - - /* Enable DMA1_Stream5 */ - - DMA_Cmd(DMA1_Stream5, ENABLE); - - /* Enable DAC Channel 1 */ - - DAC_Cmd(DAC_Channel_1, ENABLE); - - /* Enable DMA for DAC Channel 1 */ - - DAC_DMACmd(DAC_Channel_1, ENABLE); -} - -static void dac2_config(void) -{ - DAC_InitTypeDef DAC_InitStructure; - DMA_InitTypeDef DMA_InitStructure; - NVIC_InitTypeDef NVIC_InitStructure; - - /* DAC channel 2 Configuration (see notes in dac1_config() above) */ - - DAC_StructInit(&DAC_InitStructure); - DAC_InitStructure.DAC_Trigger = DAC_Trigger_T6_TRGO; - DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None; - DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Enable; - DAC_Init(DAC_Channel_2, &DAC_InitStructure); - - /* DMA1_Stream6 channel7 configuration **************************************/ - - DMA_DeInit(DMA1_Stream6); - DMA_InitStructure.DMA_Channel = DMA_Channel_7; - DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)DAC_DHR12R2_ADDRESS; - DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)dac2_buf; - DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral; - DMA_InitStructure.DMA_BufferSize = DAC_BUF_SZ; - DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; - DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; - DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; - DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; - DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; - DMA_InitStructure.DMA_Priority = DMA_Priority_High; - DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; - DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull; - DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; - DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; - DMA_Init(DMA1_Stream6, &DMA_InitStructure); - - /* Enable DMA Half & Complete interrupts */ - - DMA_ITConfig(DMA1_Stream6, DMA_IT_TC | DMA_IT_HT, ENABLE); - - /* Enable the DMA Stream IRQ Channel */ - - NVIC_InitStructure.NVIC_IRQChannel = DMA1_Stream6_IRQn; - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; - NVIC_Init(&NVIC_InitStructure); - - /* Enable DMA1_Stream6 */ - - DMA_Cmd(DMA1_Stream6, ENABLE); - - /* Enable DAC Channel 2 */ - - DAC_Cmd(DAC_Channel_2, ENABLE); - - /* Enable DMA for DAC Channel 2 */ - - DAC_DMACmd(DAC_Channel_2, ENABLE); - -} - -/******************************************************************************/ -/* STM32F4xx Peripherals Interrupt Handlers */ -/* Add here the Interrupt Handler for the used peripheral(s) (PPP), for the */ -/* available peripheral interrupt handler's name please refer to the startup */ -/* file (startup_stm32f40xx.s/startup_stm32f427x.s). */ -/******************************************************************************/ - -/* - This function handles DMA1 Stream 5 interrupt request for DAC1. -*/ - -void DMA1_Stream5_IRQHandler(void) { - int i, j, sam; - short signed_buf[DAC_BUF_SZ/2]; - - GPIOE->ODR |= (1 << 1); - - /* Transfer half empty interrupt - refill first half */ - - if(DMA_GetITStatus(DMA1_Stream5, DMA_IT_HTIF5) != RESET) { - /* fill first half from fifo */ - - if (fifo_read(dac1_fifo, signed_buf, DAC_BUF_SZ/2) == -1) { - memset(signed_buf, 0, sizeof(short)*DAC_BUF_SZ/2); - dac_underflow++; - } - - /* convert to unsigned */ - - for(i=0; iODR &= ~(1 << 1); -} - -/* - This function handles DMA1 Stream 6 interrupt request for DAC2. -*/ - -void DMA1_Stream6_IRQHandler(void) { - int i, j, sam; - short signed_buf[DAC_BUF_SZ/2]; - - GPIOE->ODR |= (1 << 2); - - /* Transfer half empty interrupt - refill first half */ - - if(DMA_GetITStatus(DMA1_Stream6, DMA_IT_HTIF6) != RESET) { - /* fill first half from fifo */ - - if (fifo_read(dac2_fifo, signed_buf, DAC_BUF_SZ/2) == -1) { - memset(signed_buf, 0, sizeof(short)*DAC_BUF_SZ/2); - dac_underflow++; - } - - /* convert to unsigned */ - - for(i=0; iODR &= ~(1 << 2); -} - +/*---------------------------------------------------------------------------*\ + + FILE........: stm32f4_dac.c + AUTHOR......: David Rowe + DATE CREATED: 1 June 2013 + + DAC driver module for STM32F4. DAC1 is connected to pin PA4, DAC2 + is connected to pin PA5. + +\*---------------------------------------------------------------------------*/ + +/* + Copyright (C) 2013 David Rowe + + All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 2.1, as + published by the Free Software Foundation. This program is + distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, see . +*/ + +#include +#include +#include +#include "stm32f4xx.h" +#include "codec2_fifo.h" +#include "stm32f4_dac.h" +#include "debugblinky.h" + +/* write to these registers for 12 bit left aligned data, as per data sheet + make sure 4 least sig bits set to 0 */ + +#define DAC_DHR12R1_ADDRESS 0x40007408 +#define DAC_DHR12R2_ADDRESS 0x40007414 + +#define DAC_MAX 4096 /* maximum amplitude */ + +/* y=mx+c mapping of samples16 bit shorts to DAC samples. Table: 74 + of data sheet indicates With DAC buffer on, DAC range is limited to + 0x0E0 to 0xF1C at VREF+ = 3.6 V, we have Vref=3.3V which is close. + */ + +#define M ((3868.0-224.0)/65536.0) +#define C 2047.0 + +static struct FIFO *dac1_fifo; +static struct FIFO *dac2_fifo; + +static unsigned short dac1_buf[DAC_BUF_SZ]; +static unsigned short dac2_buf[DAC_BUF_SZ]; + +static void tim6_config(void); +static void dac1_config(void); +static void dac2_config(void); + +int dac_underflow; + +void dac_open(int fifo_size) { + + memset(dac1_buf, 32768, sizeof(short)*DAC_BUF_SZ); + memset(dac2_buf, 32768, sizeof(short)*DAC_BUF_SZ); + + /* Create fifos */ + + dac1_fifo = fifo_create(fifo_size); + dac2_fifo = fifo_create(fifo_size); + assert(dac1_fifo != NULL); + assert(dac2_fifo != NULL); + + /* Turn on the clocks we need -----------------------------------------------*/ + + /* DMA1 clock enable */ + RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE); + /* GPIOA clock enable (to be used with DAC) */ + RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); + /* DAC Periph clock enable */ + RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE); + + /* GPIO Pin configuration DAC1->PA.4, DAC2->PA.5 configuration --------------*/ + + GPIO_InitTypeDef GPIO_InitStructure; + GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; + GPIO_Init(GPIOA, &GPIO_InitStructure); + + /* Timer and DAC 1 & 2 Configuration ----------------------------------------*/ + + tim6_config(); + dac1_config(); + dac2_config(); + + init_debug_blinky(); +} + +/* Call these puppies to send samples to the DACs. For your + convenience they accept signed 16 bit samples. */ + +int dac1_write(short buf[], int n) { + return fifo_write(dac1_fifo, buf, n); +} + +int dac2_write(short buf[], int n) { + return fifo_write(dac2_fifo, buf, n); +} + +int dac1_free() { + return fifo_free(dac1_fifo); +} + +int dac2_free() { + return fifo_free(dac2_fifo); +} + +static void tim6_config(void) +{ + TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; + + /* TIM6 Periph clock enable */ + RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE); + + /* -------------------------------------------------------- + + TIM6 input clock (TIM6CLK) is set to 2 * APB1 clock (PCLK1), since + APB1 prescaler is different from 1 (see system_stm32f4xx.c and Fig + 13 clock tree figure in DM0031020.pdf). + + Sample rate Fs = 2*PCLK1/TIM_ClockDivision + = (HCLK/2)/TIM_ClockDivision + + ----------------------------------------------------------- */ + + /* Time base configuration */ + + TIM_TimeBaseStructInit(&TIM_TimeBaseStructure); + TIM_TimeBaseStructure.TIM_Period = 5250; + TIM_TimeBaseStructure.TIM_Prescaler = 0; + TIM_TimeBaseStructure.TIM_ClockDivision = 0; + TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; + TIM_TimeBaseInit(TIM6, &TIM_TimeBaseStructure); + + /* TIM6 TRGO selection */ + + TIM_SelectOutputTrigger(TIM6, TIM_TRGOSource_Update); + + /* TIM6 enable counter */ + + TIM_Cmd(TIM6, ENABLE); +} + +static void dac1_config(void) +{ + DAC_InitTypeDef DAC_InitStructure; + DMA_InitTypeDef DMA_InitStructure; + NVIC_InitTypeDef NVIC_InitStructure; + + /* DAC channel 1 Configuration */ + + /* + This line fixed a bug that cost me 5 days, bad wave amplitude + value, and some STM32F4 periph library bugs caused triangle wave + geneartion to be enable resulting in a low level tone on the + SM1000, that we thought was caused by analog issues like layour + or power supply biasing + */ + DAC_StructInit(&DAC_InitStructure); + + DAC_InitStructure.DAC_Trigger = DAC_Trigger_T6_TRGO; + DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None; + DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Enable; + DAC_Init(DAC_Channel_1, &DAC_InitStructure); + + /* DMA1_Stream5 channel7 configuration **************************************/ + /* Table 35 page 219 of the monster data sheet */ + + DMA_DeInit(DMA1_Stream5); + DMA_InitStructure.DMA_Channel = DMA_Channel_7; + DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)DAC_DHR12R1_ADDRESS; + DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)dac1_buf; + DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral; + DMA_InitStructure.DMA_BufferSize = DAC_BUF_SZ; + DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; + DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; + DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; + DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; + DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; + DMA_InitStructure.DMA_Priority = DMA_Priority_High; + DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; + DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull; + DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; + DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; + DMA_Init(DMA1_Stream5, &DMA_InitStructure); + + /* Enable DMA Half & Complete interrupts */ + + DMA_ITConfig(DMA1_Stream5, DMA_IT_TC | DMA_IT_HT, ENABLE); + + /* Enable the DMA Stream IRQ Channel */ + + NVIC_InitStructure.NVIC_IRQChannel = DMA1_Stream5_IRQn; + NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; + NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; + NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; + NVIC_Init(&NVIC_InitStructure); + + /* Enable DMA1_Stream5 */ + + DMA_Cmd(DMA1_Stream5, ENABLE); + + /* Enable DAC Channel 1 */ + + DAC_Cmd(DAC_Channel_1, ENABLE); + + /* Enable DMA for DAC Channel 1 */ + + DAC_DMACmd(DAC_Channel_1, ENABLE); +} + +static void dac2_config(void) +{ + DAC_InitTypeDef DAC_InitStructure; + DMA_InitTypeDef DMA_InitStructure; + NVIC_InitTypeDef NVIC_InitStructure; + + /* DAC channel 2 Configuration (see notes in dac1_config() above) */ + + DAC_StructInit(&DAC_InitStructure); + DAC_InitStructure.DAC_Trigger = DAC_Trigger_T6_TRGO; + DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None; + DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Enable; + DAC_Init(DAC_Channel_2, &DAC_InitStructure); + + /* DMA1_Stream6 channel7 configuration **************************************/ + + DMA_DeInit(DMA1_Stream6); + DMA_InitStructure.DMA_Channel = DMA_Channel_7; + DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)DAC_DHR12R2_ADDRESS; + DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)dac2_buf; + DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral; + DMA_InitStructure.DMA_BufferSize = DAC_BUF_SZ; + DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; + DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; + DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; + DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; + DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; + DMA_InitStructure.DMA_Priority = DMA_Priority_High; + DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; + DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull; + DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; + DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; + DMA_Init(DMA1_Stream6, &DMA_InitStructure); + + /* Enable DMA Half & Complete interrupts */ + + DMA_ITConfig(DMA1_Stream6, DMA_IT_TC | DMA_IT_HT, ENABLE); + + /* Enable the DMA Stream IRQ Channel */ + + NVIC_InitStructure.NVIC_IRQChannel = DMA1_Stream6_IRQn; + NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; + NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; + NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; + NVIC_Init(&NVIC_InitStructure); + + /* Enable DMA1_Stream6 */ + + DMA_Cmd(DMA1_Stream6, ENABLE); + + /* Enable DAC Channel 2 */ + + DAC_Cmd(DAC_Channel_2, ENABLE); + + /* Enable DMA for DAC Channel 2 */ + + DAC_DMACmd(DAC_Channel_2, ENABLE); + +} + +/******************************************************************************/ +/* STM32F4xx Peripherals Interrupt Handlers */ +/* Add here the Interrupt Handler for the used peripheral(s) (PPP), for the */ +/* available peripheral interrupt handler's name please refer to the startup */ +/* file (startup_stm32f40xx.s/startup_stm32f427x.s). */ +/******************************************************************************/ + +/* + This function handles DMA1 Stream 5 interrupt request for DAC1. +*/ + +void DMA1_Stream5_IRQHandler(void) { + int i, j, sam; + short signed_buf[DAC_BUF_SZ/2]; + + GPIOE->ODR |= (1 << 1); + + /* Transfer half empty interrupt - refill first half */ + + if(DMA_GetITStatus(DMA1_Stream5, DMA_IT_HTIF5) != RESET) { + /* fill first half from fifo */ + + if (fifo_read(dac1_fifo, signed_buf, DAC_BUF_SZ/2) == -1) { + memset(signed_buf, 0, sizeof(short)*DAC_BUF_SZ/2); + dac_underflow++; + } + + /* convert to unsigned */ + + for(i=0; iODR &= ~(1 << 1); +} + +/* + This function handles DMA1 Stream 6 interrupt request for DAC2. +*/ + +void DMA1_Stream6_IRQHandler(void) { + int i, j, sam; + short signed_buf[DAC_BUF_SZ/2]; + + GPIOE->ODR |= (1 << 2); + + /* Transfer half empty interrupt - refill first half */ + + if(DMA_GetITStatus(DMA1_Stream6, DMA_IT_HTIF6) != RESET) { + /* fill first half from fifo */ + + if (fifo_read(dac2_fifo, signed_buf, DAC_BUF_SZ/2) == -1) { + memset(signed_buf, 0, sizeof(short)*DAC_BUF_SZ/2); + dac_underflow++; + } + + /* convert to unsigned */ + + for(i=0; iODR &= ~(1 << 2); +} + diff --git a/codec2-dev/stm32/src/stm32f4_dacduc.c b/codec2-dev/stm32/src/stm32f4_dacduc.c index ec683c37..da2dc459 100644 --- a/codec2-dev/stm32/src/stm32f4_dacduc.c +++ b/codec2-dev/stm32/src/stm32f4_dacduc.c @@ -1,416 +1,416 @@ -/*---------------------------------------------------------------------------*\ - - FILE........: stm32f4_dac.c - AUTHOR......: David Rowe - DATE CREATED: 1 June 2013 - - DAC driver module for STM32F4. DAC1 if fixed at Fs of 2Mhz. - -\*---------------------------------------------------------------------------*/ - -/* - Copyright (C) 2013 David Rowe - - All rights reserved. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License version 2.1, as - published by the Free Software Foundation. This program is - distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or - FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public - License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program; if not, see . -*/ - -#include -#include -#include -#include "stm32f4xx.h" -#include "codec2_fifo.h" -#include "stm32f4_dacduc.h" -#include "debugblinky.h" - -/* write to these registers for 12 bit left aligned data, as per data sheet - make sure 4 least sig bits set to 0 */ - -#define DAC_DHR12R1_ADDRESS 0x40007408 -#define DAC_DHR12R2_ADDRESS 0x40007414 - -#define DAC_MAX 4096 /* maximum amplitude */ - -/* y=mx+c mapping of samples16 bit shorts to DAC samples. Table: 74 - of data sheet indicates With DAC buffer on, DAC range is limited to - 0x0E0 to 0xF1C at VREF+ = 3.6 V, we have Vref=3.3V which is close. - */ - -#define M ((3868.0-224.0)/65536.0) -#define C 2047.0 - - -static struct FIFO *dac1_fifo; -static struct FIFO *dac2_fifo; - -static unsigned short dac1_buf[DAC_DUC_BUF_SZ]; -static unsigned short dac2_buf[DAC_BUF_SZ]; - -static void tim6_config(void); -static void tim7_config(void); -static void dac1_config(void); -static void dac2_config(void); - -int dac_underflow; - -void fast_dac_open(int dac1_fifo_size,int dac2_fifo_size) { - - memset(dac1_buf, 32768, sizeof(short)*DAC_DUC_BUF_SZ); - memset(dac2_buf, 32768, sizeof(short)*DAC_BUF_SZ); - - /* Create fifos */ - - dac1_fifo = fifo_create(dac1_fifo_size); - dac2_fifo = fifo_create(dac2_fifo_size); - assert(dac1_fifo != NULL); - assert(dac2_fifo != NULL); - - /* Turn on the clocks we need -----------------------------------------------*/ - - /* DMA1 clock enable */ - RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE); - /* GPIOA clock enable (to be used with DAC) */ - RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); - /* DAC Periph clock enable */ - RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE); - - /* GPIO Pin configuration DAC1->PA.4, DAC2->PA.5 configuration --------------*/ - - GPIO_InitTypeDef GPIO_InitStructure; - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5; - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN; - GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; - GPIO_Init(GPIOA, &GPIO_InitStructure); - - /* Timer and DAC 1 & 2 Configuration ----------------------------------------*/ - tim7_config(); - tim6_config(); - dac1_config(); - dac2_config(); - - init_debug_blinky(); -} - - -/* Call these puppies to send samples to the DACs. For your - convenience they accept signed 16 bit samples. */ - -int dac1_write(short buf[], int n) { - return fifo_write(dac1_fifo, buf, n); -} - -int dac2_write(short buf[], int n) { - return fifo_write(dac2_fifo, buf, n); -} - -static void tim6_config(void) -{ - TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; - - /* TIM6 Periph clock enable */ - RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE); - - /* -------------------------------------------------------- - - TIM6 input clock (TIM6CLK) is set to 2 * APB1 clock (PCLK1), since - APB1 prescaler is different from 1 (see system_stm32f4xx.c and Fig - 13 clock tree figure in DM0031020.pdf). - - Sample rate Fs = 2*PCLK1/TIM_ClockDivision - = (HCLK/2)/TIM_ClockDivision - - ----------------------------------------------------------- */ - - /* Time base configuration */ - - TIM_TimeBaseStructInit(&TIM_TimeBaseStructure); - TIM_TimeBaseStructure.TIM_Period = 5250; - TIM_TimeBaseStructure.TIM_Prescaler = 0; - TIM_TimeBaseStructure.TIM_ClockDivision = 0; - TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; - TIM_TimeBaseInit(TIM6, &TIM_TimeBaseStructure); - - /* TIM6 TRGO selection */ - - TIM_SelectOutputTrigger(TIM6, TIM_TRGOSource_Update); - - /* TIM6 enable counter */ - - TIM_Cmd(TIM6, ENABLE); -} - -/* Sets up tim7 to run at a high sample rate */ -void tim7_config(void) -{ - /* Set up tim7 */ - - - TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; - - /* TIM7 Periph clock enable */ - RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7, ENABLE); - - /* -------------------------------------------------------- - - - TIM7 input clock (TIM7CLK) is set to 2 * APB1 clock (PCLK1), since - APB1 prescaler is different from 1 (see system_stm32f4xx.c and Fig - 13 clock tree figure in DM0031020.pdf). - - Sample rate Fs = 2*PCLK1/TIM_ClockDivision - = (HCLK/2)/TIM_ClockDivision - - ----------------------------------------------------------- */ - - /* Time base configuration */ - - TIM_TimeBaseStructInit(&TIM_TimeBaseStructure); - TIM_TimeBaseStructure.TIM_Period = 41; - TIM_TimeBaseStructure.TIM_Prescaler = 0; - TIM_TimeBaseStructure.TIM_ClockDivision = 0; - TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; - TIM_TimeBaseInit(TIM7, &TIM_TimeBaseStructure); - - /* TIM7 TRGO selection */ - - TIM_SelectOutputTrigger(TIM7, TIM_TRGOSource_Update); - - /* TIM7 enable counter */ - - TIM_Cmd(TIM7, ENABLE); -} - -static void dac1_config(void) -{ - DAC_InitTypeDef DAC_InitStructure; - DMA_InitTypeDef DMA_InitStructure; - NVIC_InitTypeDef NVIC_InitStructure; - - /* DAC channel 1 Configuration */ - - /* - This line fixed a bug that cost me 5 days, bad wave amplitude - value, and some STM32F4 periph library bugs caused triangle wave - geneartion to be enable resulting in a low level tone on the - SM1000, that we thought was caused by analog issues like layour - or power supply biasing - */ - DAC_StructInit(&DAC_InitStructure); - - DAC_InitStructure.DAC_Trigger = DAC_Trigger_T7_TRGO; - DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None; - - /*External buffering is needed to get nice square samples at Fs=2Mhz. See DM00129215.pdf */ - DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Disable; - DAC_Init(DAC_Channel_1, &DAC_InitStructure); - - /* DMA1_Stream5 channel7 configuration **************************************/ - /* Table 35 page 219 of the monster data sheet */ - - DMA_DeInit(DMA1_Stream5); - DMA_InitStructure.DMA_Channel = DMA_Channel_7; - DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)DAC_DHR12R1_ADDRESS; - DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)dac1_buf; - DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral; - DMA_InitStructure.DMA_BufferSize = DAC_DUC_BUF_SZ; - DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; - DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; - DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; - DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; - DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; - DMA_InitStructure.DMA_Priority = DMA_Priority_High; - DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; - DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull; - DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; - DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; - DMA_Init(DMA1_Stream5, &DMA_InitStructure); - - /* Enable DMA Half & Complete interrupts */ - - DMA_ITConfig(DMA1_Stream5, DMA_IT_TC | DMA_IT_HT, ENABLE); - - /* Enable the DMA Stream IRQ Channel */ - - NVIC_InitStructure.NVIC_IRQChannel = DMA1_Stream5_IRQn; - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; - NVIC_Init(&NVIC_InitStructure); - - /* Enable DMA1_Stream5 */ - - DMA_Cmd(DMA1_Stream5, ENABLE); - - /* Enable DAC Channel 1 */ - - DAC_Cmd(DAC_Channel_1, ENABLE); - - /* Enable DMA for DAC Channel 1 */ - - DAC_DMACmd(DAC_Channel_1, ENABLE); -} - -static void dac2_config(void) -{ - DAC_InitTypeDef DAC_InitStructure; - DMA_InitTypeDef DMA_InitStructure; - NVIC_InitTypeDef NVIC_InitStructure; - - /* DAC channel 2 Configuration (see notes in dac1_config() above) */ - - DAC_StructInit(&DAC_InitStructure); - DAC_InitStructure.DAC_Trigger = DAC_Trigger_T6_TRGO; - DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None; - DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Enable; - DAC_Init(DAC_Channel_2, &DAC_InitStructure); - - /* DMA1_Stream6 channel7 configuration **************************************/ - - DMA_DeInit(DMA1_Stream6); - DMA_InitStructure.DMA_Channel = DMA_Channel_7; - DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)DAC_DHR12R2_ADDRESS; - DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)dac2_buf; - DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral; - DMA_InitStructure.DMA_BufferSize = DAC_BUF_SZ; - DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; - DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; - DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; - DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; - DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; - DMA_InitStructure.DMA_Priority = DMA_Priority_High; - DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; - DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull; - DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; - DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; - DMA_Init(DMA1_Stream6, &DMA_InitStructure); - - /* Enable DMA Half & Complete interrupts */ - - DMA_ITConfig(DMA1_Stream6, DMA_IT_TC | DMA_IT_HT, ENABLE); - - /* Enable the DMA Stream IRQ Channel */ - - NVIC_InitStructure.NVIC_IRQChannel = DMA1_Stream6_IRQn; - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; - NVIC_Init(&NVIC_InitStructure); - - /* Enable DMA1_Stream6 */ - - DMA_Cmd(DMA1_Stream6, ENABLE); - - /* Enable DAC Channel 2 */ - - DAC_Cmd(DAC_Channel_2, ENABLE); - - /* Enable DMA for DAC Channel 2 */ - - DAC_DMACmd(DAC_Channel_2, ENABLE); - -} - -/******************************************************************************/ -/* STM32F4xx Peripherals Interrupt Handlers */ -/* Add here the Interrupt Handler for the used peripheral(s) (PPP), for the */ -/* available peripheral interrupt handler's name please refer to the startup */ -/* file (startup_stm32f40xx.s/startup_stm32f427x.s). */ -/******************************************************************************/ - -/* - This function handles DMA1 Stream 5 interrupt request for DAC1. -*/ - -void DMA1_Stream5_IRQHandler(void) { - GPIOE->ODR |= (1 << 1); - - /* Transfer half empty interrupt - refill first half */ - - if(DMA_GetITStatus(DMA1_Stream5, DMA_IT_HTIF5) != RESET) { - /* fill first half from fifo */ - fifo_read(dac1_fifo, (short*)dac1_buf, DAC_DUC_BUF_SZ/2); - - /* Clear DMA Stream Transfer Complete interrupt pending bit */ - DMA_ClearITPendingBit(DMA1_Stream5, DMA_IT_HTIF5); - } - - /* Transfer complete interrupt - refill 2nd half */ - - if(DMA_GetITStatus(DMA1_Stream5, DMA_IT_TCIF5) != RESET) { - /* fill second half from fifo */ - fifo_read(dac1_fifo, (short*)(dac1_buf+DAC_DUC_BUF_SZ/2), DAC_DUC_BUF_SZ/2); - - /* Clear DMA Stream Transfer Complete interrupt pending bit */ - DMA_ClearITPendingBit(DMA1_Stream5, DMA_IT_TCIF5); - } - - GPIOE->ODR &= ~(1 << 1); -} - -/* - This function handles DMA1 Stream 6 interrupt request for DAC2. -*/ - -void DMA1_Stream6_IRQHandler(void) { - int i, j, sam; - short signed_buf[DAC_BUF_SZ/2]; - - GPIOE->ODR |= (1 << 2); - - /* Transfer half empty interrupt - refill first half */ - - if(DMA_GetITStatus(DMA1_Stream6, DMA_IT_HTIF6) != RESET) { - /* fill first half from fifo */ - - if (fifo_read(dac2_fifo, signed_buf, DAC_BUF_SZ/2) == -1) { - memset(signed_buf, 0, sizeof(short)*DAC_BUF_SZ/2); - dac_underflow++; - } - - /* convert to unsigned */ - - for(i=0; iODR &= ~(1 << 2); -} - +/*---------------------------------------------------------------------------*\ + + FILE........: stm32f4_dac.c + AUTHOR......: David Rowe + DATE CREATED: 1 June 2013 + + DAC driver module for STM32F4. DAC1 if fixed at Fs of 2Mhz. + +\*---------------------------------------------------------------------------*/ + +/* + Copyright (C) 2013 David Rowe + + All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 2.1, as + published by the Free Software Foundation. This program is + distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, see . +*/ + +#include +#include +#include +#include "stm32f4xx.h" +#include "codec2_fifo.h" +#include "stm32f4_dacduc.h" +#include "debugblinky.h" + +/* write to these registers for 12 bit left aligned data, as per data sheet + make sure 4 least sig bits set to 0 */ + +#define DAC_DHR12R1_ADDRESS 0x40007408 +#define DAC_DHR12R2_ADDRESS 0x40007414 + +#define DAC_MAX 4096 /* maximum amplitude */ + +/* y=mx+c mapping of samples16 bit shorts to DAC samples. Table: 74 + of data sheet indicates With DAC buffer on, DAC range is limited to + 0x0E0 to 0xF1C at VREF+ = 3.6 V, we have Vref=3.3V which is close. + */ + +#define M ((3868.0-224.0)/65536.0) +#define C 2047.0 + + +static struct FIFO *dac1_fifo; +static struct FIFO *dac2_fifo; + +static unsigned short dac1_buf[DAC_DUC_BUF_SZ]; +static unsigned short dac2_buf[DAC_BUF_SZ]; + +static void tim6_config(void); +static void tim7_config(void); +static void dac1_config(void); +static void dac2_config(void); + +int dac_underflow; + +void fast_dac_open(int dac1_fifo_size,int dac2_fifo_size) { + + memset(dac1_buf, 32768, sizeof(short)*DAC_DUC_BUF_SZ); + memset(dac2_buf, 32768, sizeof(short)*DAC_BUF_SZ); + + /* Create fifos */ + + dac1_fifo = fifo_create(dac1_fifo_size); + dac2_fifo = fifo_create(dac2_fifo_size); + assert(dac1_fifo != NULL); + assert(dac2_fifo != NULL); + + /* Turn on the clocks we need -----------------------------------------------*/ + + /* DMA1 clock enable */ + RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE); + /* GPIOA clock enable (to be used with DAC) */ + RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); + /* DAC Periph clock enable */ + RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE); + + /* GPIO Pin configuration DAC1->PA.4, DAC2->PA.5 configuration --------------*/ + + GPIO_InitTypeDef GPIO_InitStructure; + GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; + GPIO_Init(GPIOA, &GPIO_InitStructure); + + /* Timer and DAC 1 & 2 Configuration ----------------------------------------*/ + tim7_config(); + tim6_config(); + dac1_config(); + dac2_config(); + + init_debug_blinky(); +} + + +/* Call these puppies to send samples to the DACs. For your + convenience they accept signed 16 bit samples. */ + +int dac1_write(short buf[], int n) { + return fifo_write(dac1_fifo, buf, n); +} + +int dac2_write(short buf[], int n) { + return fifo_write(dac2_fifo, buf, n); +} + +static void tim6_config(void) +{ + TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; + + /* TIM6 Periph clock enable */ + RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE); + + /* -------------------------------------------------------- + + TIM6 input clock (TIM6CLK) is set to 2 * APB1 clock (PCLK1), since + APB1 prescaler is different from 1 (see system_stm32f4xx.c and Fig + 13 clock tree figure in DM0031020.pdf). + + Sample rate Fs = 2*PCLK1/TIM_ClockDivision + = (HCLK/2)/TIM_ClockDivision + + ----------------------------------------------------------- */ + + /* Time base configuration */ + + TIM_TimeBaseStructInit(&TIM_TimeBaseStructure); + TIM_TimeBaseStructure.TIM_Period = 5250; + TIM_TimeBaseStructure.TIM_Prescaler = 0; + TIM_TimeBaseStructure.TIM_ClockDivision = 0; + TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; + TIM_TimeBaseInit(TIM6, &TIM_TimeBaseStructure); + + /* TIM6 TRGO selection */ + + TIM_SelectOutputTrigger(TIM6, TIM_TRGOSource_Update); + + /* TIM6 enable counter */ + + TIM_Cmd(TIM6, ENABLE); +} + +/* Sets up tim7 to run at a high sample rate */ +void tim7_config(void) +{ + /* Set up tim7 */ + + + TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; + + /* TIM7 Periph clock enable */ + RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7, ENABLE); + + /* -------------------------------------------------------- + + + TIM7 input clock (TIM7CLK) is set to 2 * APB1 clock (PCLK1), since + APB1 prescaler is different from 1 (see system_stm32f4xx.c and Fig + 13 clock tree figure in DM0031020.pdf). + + Sample rate Fs = 2*PCLK1/TIM_ClockDivision + = (HCLK/2)/TIM_ClockDivision + + ----------------------------------------------------------- */ + + /* Time base configuration */ + + TIM_TimeBaseStructInit(&TIM_TimeBaseStructure); + TIM_TimeBaseStructure.TIM_Period = 41; + TIM_TimeBaseStructure.TIM_Prescaler = 0; + TIM_TimeBaseStructure.TIM_ClockDivision = 0; + TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; + TIM_TimeBaseInit(TIM7, &TIM_TimeBaseStructure); + + /* TIM7 TRGO selection */ + + TIM_SelectOutputTrigger(TIM7, TIM_TRGOSource_Update); + + /* TIM7 enable counter */ + + TIM_Cmd(TIM7, ENABLE); +} + +static void dac1_config(void) +{ + DAC_InitTypeDef DAC_InitStructure; + DMA_InitTypeDef DMA_InitStructure; + NVIC_InitTypeDef NVIC_InitStructure; + + /* DAC channel 1 Configuration */ + + /* + This line fixed a bug that cost me 5 days, bad wave amplitude + value, and some STM32F4 periph library bugs caused triangle wave + geneartion to be enable resulting in a low level tone on the + SM1000, that we thought was caused by analog issues like layour + or power supply biasing + */ + DAC_StructInit(&DAC_InitStructure); + + DAC_InitStructure.DAC_Trigger = DAC_Trigger_T7_TRGO; + DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None; + + /*External buffering is needed to get nice square samples at Fs=2Mhz. See DM00129215.pdf */ + DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Disable; + DAC_Init(DAC_Channel_1, &DAC_InitStructure); + + /* DMA1_Stream5 channel7 configuration **************************************/ + /* Table 35 page 219 of the monster data sheet */ + + DMA_DeInit(DMA1_Stream5); + DMA_InitStructure.DMA_Channel = DMA_Channel_7; + DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)DAC_DHR12R1_ADDRESS; + DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)dac1_buf; + DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral; + DMA_InitStructure.DMA_BufferSize = DAC_DUC_BUF_SZ; + DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; + DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; + DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; + DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; + DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; + DMA_InitStructure.DMA_Priority = DMA_Priority_High; + DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; + DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull; + DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; + DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; + DMA_Init(DMA1_Stream5, &DMA_InitStructure); + + /* Enable DMA Half & Complete interrupts */ + + DMA_ITConfig(DMA1_Stream5, DMA_IT_TC | DMA_IT_HT, ENABLE); + + /* Enable the DMA Stream IRQ Channel */ + + NVIC_InitStructure.NVIC_IRQChannel = DMA1_Stream5_IRQn; + NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; + NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; + NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; + NVIC_Init(&NVIC_InitStructure); + + /* Enable DMA1_Stream5 */ + + DMA_Cmd(DMA1_Stream5, ENABLE); + + /* Enable DAC Channel 1 */ + + DAC_Cmd(DAC_Channel_1, ENABLE); + + /* Enable DMA for DAC Channel 1 */ + + DAC_DMACmd(DAC_Channel_1, ENABLE); +} + +static void dac2_config(void) +{ + DAC_InitTypeDef DAC_InitStructure; + DMA_InitTypeDef DMA_InitStructure; + NVIC_InitTypeDef NVIC_InitStructure; + + /* DAC channel 2 Configuration (see notes in dac1_config() above) */ + + DAC_StructInit(&DAC_InitStructure); + DAC_InitStructure.DAC_Trigger = DAC_Trigger_T6_TRGO; + DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None; + DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Enable; + DAC_Init(DAC_Channel_2, &DAC_InitStructure); + + /* DMA1_Stream6 channel7 configuration **************************************/ + + DMA_DeInit(DMA1_Stream6); + DMA_InitStructure.DMA_Channel = DMA_Channel_7; + DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)DAC_DHR12R2_ADDRESS; + DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)dac2_buf; + DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral; + DMA_InitStructure.DMA_BufferSize = DAC_BUF_SZ; + DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; + DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; + DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; + DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; + DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; + DMA_InitStructure.DMA_Priority = DMA_Priority_High; + DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; + DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull; + DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; + DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; + DMA_Init(DMA1_Stream6, &DMA_InitStructure); + + /* Enable DMA Half & Complete interrupts */ + + DMA_ITConfig(DMA1_Stream6, DMA_IT_TC | DMA_IT_HT, ENABLE); + + /* Enable the DMA Stream IRQ Channel */ + + NVIC_InitStructure.NVIC_IRQChannel = DMA1_Stream6_IRQn; + NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; + NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; + NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; + NVIC_Init(&NVIC_InitStructure); + + /* Enable DMA1_Stream6 */ + + DMA_Cmd(DMA1_Stream6, ENABLE); + + /* Enable DAC Channel 2 */ + + DAC_Cmd(DAC_Channel_2, ENABLE); + + /* Enable DMA for DAC Channel 2 */ + + DAC_DMACmd(DAC_Channel_2, ENABLE); + +} + +/******************************************************************************/ +/* STM32F4xx Peripherals Interrupt Handlers */ +/* Add here the Interrupt Handler for the used peripheral(s) (PPP), for the */ +/* available peripheral interrupt handler's name please refer to the startup */ +/* file (startup_stm32f40xx.s/startup_stm32f427x.s). */ +/******************************************************************************/ + +/* + This function handles DMA1 Stream 5 interrupt request for DAC1. +*/ + +void DMA1_Stream5_IRQHandler(void) { + GPIOE->ODR |= (1 << 1); + + /* Transfer half empty interrupt - refill first half */ + + if(DMA_GetITStatus(DMA1_Stream5, DMA_IT_HTIF5) != RESET) { + /* fill first half from fifo */ + fifo_read(dac1_fifo, (short*)dac1_buf, DAC_DUC_BUF_SZ/2); + + /* Clear DMA Stream Transfer Complete interrupt pending bit */ + DMA_ClearITPendingBit(DMA1_Stream5, DMA_IT_HTIF5); + } + + /* Transfer complete interrupt - refill 2nd half */ + + if(DMA_GetITStatus(DMA1_Stream5, DMA_IT_TCIF5) != RESET) { + /* fill second half from fifo */ + fifo_read(dac1_fifo, (short*)(dac1_buf+DAC_DUC_BUF_SZ/2), DAC_DUC_BUF_SZ/2); + + /* Clear DMA Stream Transfer Complete interrupt pending bit */ + DMA_ClearITPendingBit(DMA1_Stream5, DMA_IT_TCIF5); + } + + GPIOE->ODR &= ~(1 << 1); +} + +/* + This function handles DMA1 Stream 6 interrupt request for DAC2. +*/ + +void DMA1_Stream6_IRQHandler(void) { + int i, j, sam; + short signed_buf[DAC_BUF_SZ/2]; + + GPIOE->ODR |= (1 << 2); + + /* Transfer half empty interrupt - refill first half */ + + if(DMA_GetITStatus(DMA1_Stream6, DMA_IT_HTIF6) != RESET) { + /* fill first half from fifo */ + + if (fifo_read(dac2_fifo, signed_buf, DAC_BUF_SZ/2) == -1) { + memset(signed_buf, 0, sizeof(short)*DAC_BUF_SZ/2); + dac_underflow++; + } + + /* convert to unsigned */ + + for(i=0; iODR &= ~(1 << 2); +} + diff --git a/codec2-dev/stm32/src/system_stm32f4xx.c b/codec2-dev/stm32/src/system_stm32f4xx.c index 86953137..08f36260 100644 --- a/codec2-dev/stm32/src/system_stm32f4xx.c +++ b/codec2-dev/stm32/src/system_stm32f4xx.c @@ -1,584 +1,584 @@ -/** - ****************************************************************************** - * @file system_stm32f4xx.c - * @author MCD Application Team - * @version V1.0.1 - * @date 10-July-2012 - * @brief CMSIS Cortex-M4 Device Peripheral Access Layer System Source File. - * This file contains the system clock configuration for STM32F4xx devices, - * and is generated by the clock configuration tool - * stm32f4xx_Clock_Configuration_V1.0.1.xls - * - * 1. This file provides two functions and one global variable to be called from - * user application: - * - SystemInit(): Setups the system clock (System clock source, PLL Multiplier - * and Divider factors, AHB/APBx prescalers and Flash settings), - * depending on the configuration made in the clock xls tool. - * This function is called at startup just after reset and - * before branch to main program. This call is made inside - * the "startup_stm32f4xx.s" file. - * - * - SystemCoreClock variable: Contains the core clock (HCLK), it can be used - * by the user application to setup the SysTick - * timer or configure other parameters. - * - * - SystemCoreClockUpdate(): Updates the variable SystemCoreClock and must - * be called whenever the core clock is changed - * during program execution. - * - * 2. After each device reset the HSI (16 MHz) is used as system clock source. - * Then SystemInit() function is called, in "startup_stm32f4xx.s" file, to - * configure the system clock before to branch to main program. - * - * 3. If the system clock source selected by user fails to startup, the SystemInit() - * function will do nothing and HSI still used as system clock source. User can - * add some code to deal with this issue inside the SetSysClock() function. - * - * 4. The default value of HSE crystal is set to 25MHz, refer to "HSE_VALUE" define - * in "stm32f4xx.h" file. When HSE is used as system clock source, directly or - * through PLL, and you are using different crystal you have to adapt the HSE - * value to your own configuration. - * - * 5. This file configures the system clock as follows: - *============================================================================= - *============================================================================= - * Supported STM32F4xx device revision | Rev A - *----------------------------------------------------------------------------- - * System Clock source | PLL (HSE) - *----------------------------------------------------------------------------- - * SYSCLK(Hz) | 168000000 - *----------------------------------------------------------------------------- - * HCLK(Hz) | 168000000 - *----------------------------------------------------------------------------- - * AHB Prescaler | 1 - *----------------------------------------------------------------------------- - * APB1 Prescaler | 4 - *----------------------------------------------------------------------------- - * APB2 Prescaler | 2 - *----------------------------------------------------------------------------- - * HSE Frequency(Hz) | 8000000 - *----------------------------------------------------------------------------- - * PLL_M | 8 - *----------------------------------------------------------------------------- - * PLL_N | 336 - *----------------------------------------------------------------------------- - * PLL_P | 2 - *----------------------------------------------------------------------------- - * PLL_Q | 7 - *----------------------------------------------------------------------------- - * PLLI2S_N | 352 - *----------------------------------------------------------------------------- - * PLLI2S_R | 2 - *----------------------------------------------------------------------------- - * I2S input clock(Hz) | 176000000 - * | - * To achieve the following I2S config: | - * - Master clock output (MCKO): OFF | - * - Frame wide : 16bit | - * - Error % : 0,0000 | - * - Prescaler Odd factor (ODD): 1 | - * - Linear prescaler (DIV) : 14 | - *----------------------------------------------------------------------------- - * VDD(V) | 3,3 - *----------------------------------------------------------------------------- - * Main regulator output voltage | Scale1 mode - *----------------------------------------------------------------------------- - * Flash Latency(WS) | 5 - *----------------------------------------------------------------------------- - * Prefetch Buffer | OFF - *----------------------------------------------------------------------------- - * Instruction cache | ON - *----------------------------------------------------------------------------- - * Data cache | ON - *----------------------------------------------------------------------------- - * Require 48MHz for USB OTG FS, | Enabled - * SDIO and RNG clock | - *----------------------------------------------------------------------------- - *============================================================================= - ****************************************************************************** - * @attention - * - * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS - * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE - * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY - * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING - * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE - * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. - * - *

© COPYRIGHT 2011 STMicroelectronics

- ****************************************************************************** - */ - -/** @addtogroup CMSIS - * @{ - */ - -/** @addtogroup stm32f4xx_system - * @{ - */ - -/** @addtogroup STM32F4xx_System_Private_Includes - * @{ - */ - -#include "stm32f4xx.h" - -/** - * @} - */ - -/** @addtogroup STM32F4xx_System_Private_TypesDefinitions - * @{ - */ - -/** - * @} - */ - -/** @addtogroup STM32F4xx_System_Private_Defines - * @{ - */ - -/************************* Miscellaneous Configuration ************************/ -/*!< Uncomment the following line if you need to use external SRAM mounted - on STM324xG_EVAL board as data memory */ -/* #define DATA_IN_ExtSRAM */ - -/*!< Uncomment the following line if you need to relocate your vector Table in - Internal SRAM. */ -/* #define VECT_TAB_SRAM */ -#define VECT_TAB_OFFSET 0x00 /*!< Vector Table base offset field. - This value must be a multiple of 0x200. */ -/******************************************************************************/ - -/************************* PLL Parameters *************************************/ -/* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N */ -#define PLL_M 8 -#define PLL_N 336 - -/* SYSCLK = PLL_VCO / PLL_P */ -#define PLL_P 2 - -/* USB OTG FS, SDIO and RNG Clock = PLL_VCO / PLLQ */ -#define PLL_Q 7 - -/* PLLI2S_VCO = (HSE_VALUE Or HSI_VALUE / PLL_M) * PLLI2S_N - I2SCLK = PLLI2S_VCO / PLLI2S_R */ -#define START_I2SCLOCK 0 -#define PLLI2S_N 352 -#define PLLI2S_R 2 - -/******************************************************************************/ - -/** - * @} - */ - -/** @addtogroup STM32F4xx_System_Private_Macros - * @{ - */ - -/** - * @} - */ - -/** @addtogroup STM32F4xx_System_Private_Variables - * @{ - */ - -uint32_t SystemCoreClock = 168000000; - -__I uint8_t AHBPrescTable[16] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9}; - -/** - * @} - */ - -/** @addtogroup STM32F4xx_System_Private_FunctionPrototypes - * @{ - */ - -static void SetSysClock(void); -#ifdef DATA_IN_ExtSRAM -static void SystemInit_ExtMemCtl(void); -#endif /* DATA_IN_ExtSRAM */ - -/** - * @} - */ - -/** @addtogroup STM32F4xx_System_Private_Functions - * @{ - */ - -/** - * @brief Setup the microcontroller system - * Initialize the Embedded Flash Interface, the PLL and update the - * SystemFrequency variable. - * @param None - * @retval None - */ -void SystemInit(void) -{ - /* FPU settings ------------------------------------------------------------*/ -#if (__FPU_PRESENT == 1) && (__FPU_USED == 1) - SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2)); /* set CP10 and CP11 Full Access */ -#endif - /* Reset the RCC clock configuration to the default reset state ------------*/ - /* Set HSION bit */ - RCC->CR |= (uint32_t)0x00000001; - - /* Reset CFGR register */ - RCC->CFGR = 0x00000000; - - /* Reset HSEON, CSSON and PLLON bits */ - RCC->CR &= (uint32_t)0xFEF6FFFF; - - /* Reset PLLCFGR register */ - RCC->PLLCFGR = 0x24003010; - - /* Reset HSEBYP bit */ - RCC->CR &= (uint32_t)0xFFFBFFFF; - - /* Disable all interrupts */ - RCC->CIR = 0x00000000; - -#ifdef DATA_IN_ExtSRAM - SystemInit_ExtMemCtl(); -#endif /* DATA_IN_ExtSRAM */ - - /* Configure the System clock source, PLL Multiplier and Divider factors, - AHB/APBx prescalers and Flash settings ----------------------------------*/ - SetSysClock(); - - /* Configure the Vector Table location add offset address ------------------*/ -#ifdef VECT_TAB_SRAM - SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */ -#else - SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */ -#endif -} - -/** - * @brief Update SystemCoreClock variable according to Clock Register Values. - * The SystemCoreClock variable contains the core clock (HCLK), it can - * be used by the user application to setup the SysTick timer or configure - * other parameters. - * - * @note Each time the core clock (HCLK) changes, this function must be called - * to update SystemCoreClock variable value. Otherwise, any configuration - * based on this variable will be incorrect. - * - * @note - The system frequency computed by this function is not the real - * frequency in the chip. It is calculated based on the predefined - * constant and the selected clock source: - * - * - If SYSCLK source is HSI, SystemCoreClock will contain the HSI_VALUE(*) - * - * - If SYSCLK source is HSE, SystemCoreClock will contain the HSE_VALUE(**) - * - * - If SYSCLK source is PLL, SystemCoreClock will contain the HSE_VALUE(**) - * or HSI_VALUE(*) multiplied/divided by the PLL factors. - * - * (*) HSI_VALUE is a constant defined in stm32f4xx.h file (default value - * 16 MHz) but the real value may vary depending on the variations - * in voltage and temperature. - * - * (**) HSE_VALUE is a constant defined in stm32f4xx.h file (default value - * 25 MHz), user has to ensure that HSE_VALUE is same as the real - * frequency of the crystal used. Otherwise, this function may - * have wrong result. - * - * - The result of this function could be not correct when using fractional - * value for HSE crystal. - * - * @param None - * @retval None - */ -void SystemCoreClockUpdate(void) -{ - uint32_t tmp = 0, pllvco = 0, pllp = 2, pllsource = 0, pllm = 2; - - /* Get SYSCLK source -------------------------------------------------------*/ - tmp = RCC->CFGR & RCC_CFGR_SWS; - - switch (tmp) - { - case 0x00: /* HSI used as system clock source */ - SystemCoreClock = HSI_VALUE; - break; - case 0x04: /* HSE used as system clock source */ - SystemCoreClock = HSE_VALUE; - break; - case 0x08: /* PLL used as system clock source */ - - /* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N - SYSCLK = PLL_VCO / PLL_P - */ - pllsource = (RCC->PLLCFGR & RCC_PLLCFGR_PLLSRC) >> 22; - pllm = RCC->PLLCFGR & RCC_PLLCFGR_PLLM; - - if (pllsource != 0) - { - /* HSE used as PLL clock source */ - pllvco = (HSE_VALUE / pllm) * ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> 6); - } - else - { - /* HSI used as PLL clock source */ - pllvco = (HSI_VALUE / pllm) * ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> 6); - } - - pllp = (((RCC->PLLCFGR & RCC_PLLCFGR_PLLP) >>16) + 1 ) *2; - SystemCoreClock = pllvco/pllp; - break; - default: - SystemCoreClock = HSI_VALUE; - break; - } - /* Compute HCLK frequency --------------------------------------------------*/ - /* Get HCLK prescaler */ - tmp = AHBPrescTable[((RCC->CFGR & RCC_CFGR_HPRE) >> 4)]; - /* HCLK frequency */ - SystemCoreClock >>= tmp; -} - -/** - * @brief Configures the System clock source, PLL Multiplier and Divider factors, - * AHB/APBx prescalers and Flash settings - * @Note This function should be called only once the RCC clock configuration - * is reset to the default reset state (done in SystemInit() function). - * @param None - * @retval None - */ -static void SetSysClock(void) -{ - /******************************************************************************/ - /* PLL (clocked by HSE) used as System clock source */ - /******************************************************************************/ - __IO uint32_t StartUpCounter = 0, HSEStatus = 0; - - /* Enable HSE */ - RCC->CR |= ((uint32_t)RCC_CR_HSEON); - - /* Wait till HSE is ready and if Time out is reached exit */ - do - { - HSEStatus = RCC->CR & RCC_CR_HSERDY; - StartUpCounter++; - } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT)); - - if ((RCC->CR & RCC_CR_HSERDY) != RESET) - { - HSEStatus = (uint32_t)0x01; - } - else - { - HSEStatus = (uint32_t)0x00; - } - - if (HSEStatus == (uint32_t)0x01) - { - /* Select regulator voltage output Scale 1 mode, System frequency up to 168 MHz */ - RCC->APB1ENR |= RCC_APB1ENR_PWREN; - PWR->CR |= PWR_CR_VOS; - - /* HCLK = SYSCLK / 1*/ - RCC->CFGR |= RCC_CFGR_HPRE_DIV1; - - /* PCLK2 = HCLK / 2*/ - RCC->CFGR |= RCC_CFGR_PPRE2_DIV2; - - /* PCLK1 = HCLK / 4*/ - RCC->CFGR |= RCC_CFGR_PPRE1_DIV4; - - /* Configure the main PLL */ - RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) | - (RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24); - - /* Enable the main PLL */ - RCC->CR |= RCC_CR_PLLON; - - /* Wait till the main PLL is ready */ - while((RCC->CR & RCC_CR_PLLRDY) == 0) - { - } - - /* Configure Flash prefetch, Instruction cache, Data cache and wait state */ - FLASH->ACR = FLASH_ACR_ICEN |FLASH_ACR_DCEN |FLASH_ACR_LATENCY_5WS; - - /* Select the main PLL as system clock source */ - RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW)); - RCC->CFGR |= RCC_CFGR_SW_PLL; - - /* Wait till the main PLL is used as system clock source */ - while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS ) != RCC_CFGR_SWS_PLL); - { - } - } - else - { /* If HSE fails to start-up, the application will have wrong clock - configuration. User can add here some code to deal with this error */ - } - - - /******************************************************************************/ - /* I2S clock configuration */ - /******************************************************************************/ - -#if START_I2SCLOCK - /* PLLI2S clock used as I2S clock source */ - RCC->CFGR &= ~RCC_CFGR_I2SSRC; - - /* Configure PLLI2S */ - RCC->PLLI2SCFGR = (PLLI2S_N << 6) | (PLLI2S_R << 28); - - /* Enable PLLI2S */ - RCC->CR |= ((uint32_t)RCC_CR_PLLI2SON); - - /* Wait till PLLI2S is ready */ - while((RCC->CR & RCC_CR_PLLI2SRDY) == 0) - { - } -#endif -} - -/** - * @brief Setup the external memory controller. Called in startup_stm32f4xx.s - * before jump to __main - * @param None - * @retval None - */ -#ifdef DATA_IN_ExtSRAM -/** - * @brief Setup the external memory controller. - * Called in startup_stm32f4xx.s before jump to main. - * This function configures the external SRAM mounted on STM324xG_EVAL board - * This SRAM will be used as program data memory (including heap and stack). - * @param None - * @retval None - */ -void SystemInit_ExtMemCtl(void) -{ - /*-- GPIOs Configuration -----------------------------------------------------*/ - /* - +-------------------+--------------------+------------------+------------------+ - + SRAM pins assignment + - +-------------------+--------------------+------------------+------------------+ - | PD0 <-> FSMC_D2 | PE0 <-> FSMC_NBL0 | PF0 <-> FSMC_A0 | PG0 <-> FSMC_A10 | - | PD1 <-> FSMC_D3 | PE1 <-> FSMC_NBL1 | PF1 <-> FSMC_A1 | PG1 <-> FSMC_A11 | - | PD4 <-> FSMC_NOE | PE3 <-> FSMC_A19 | PF2 <-> FSMC_A2 | PG2 <-> FSMC_A12 | - | PD5 <-> FSMC_NWE | PE4 <-> FSMC_A20 | PF3 <-> FSMC_A3 | PG3 <-> FSMC_A13 | - | PD8 <-> FSMC_D13 | PE7 <-> FSMC_D4 | PF4 <-> FSMC_A4 | PG4 <-> FSMC_A14 | - | PD9 <-> FSMC_D14 | PE8 <-> FSMC_D5 | PF5 <-> FSMC_A5 | PG5 <-> FSMC_A15 | - | PD10 <-> FSMC_D15 | PE9 <-> FSMC_D6 | PF12 <-> FSMC_A6 | PG9 <-> FSMC_NE2 | - | PD11 <-> FSMC_A16 | PE10 <-> FSMC_D7 | PF13 <-> FSMC_A7 |------------------+ - | PD12 <-> FSMC_A17 | PE11 <-> FSMC_D8 | PF14 <-> FSMC_A8 | - | PD13 <-> FSMC_A18 | PE12 <-> FSMC_D9 | PF15 <-> FSMC_A9 | - | PD14 <-> FSMC_D0 | PE13 <-> FSMC_D10 |------------------+ - | PD15 <-> FSMC_D1 | PE14 <-> FSMC_D11 | - | | PE15 <-> FSMC_D12 | - +-------------------+--------------------+ - */ - /* Enable GPIOD, GPIOE, GPIOF and GPIOG interface clock */ - RCC->AHB1ENR = 0x00000078; - - /* Connect PDx pins to FSMC Alternate function */ - GPIOD->AFR[0] = 0x00cc00cc; - GPIOD->AFR[1] = 0xcc0ccccc; - /* Configure PDx pins in Alternate function mode */ - GPIOD->MODER = 0xaaaa0a0a; - /* Configure PDx pins speed to 100 MHz */ - GPIOD->OSPEEDR = 0xffff0f0f; - /* Configure PDx pins Output type to push-pull */ - GPIOD->OTYPER = 0x00000000; - /* No pull-up, pull-down for PDx pins */ - GPIOD->PUPDR = 0x00000000; - - /* Connect PEx pins to FSMC Alternate function */ - GPIOE->AFR[0] = 0xc00cc0cc; - GPIOE->AFR[1] = 0xcccccccc; - /* Configure PEx pins in Alternate function mode */ - GPIOE->MODER = 0xaaaa828a; - /* Configure PEx pins speed to 100 MHz */ - GPIOE->OSPEEDR = 0xffffc3cf; - /* Configure PEx pins Output type to push-pull */ - GPIOE->OTYPER = 0x00000000; - /* No pull-up, pull-down for PEx pins */ - GPIOE->PUPDR = 0x00000000; - - /* Connect PFx pins to FSMC Alternate function */ - GPIOF->AFR[0] = 0x00cccccc; - GPIOF->AFR[1] = 0xcccc0000; - /* Configure PFx pins in Alternate function mode */ - GPIOF->MODER = 0xaa000aaa; - /* Configure PFx pins speed to 100 MHz */ - GPIOF->OSPEEDR = 0xff000fff; - /* Configure PFx pins Output type to push-pull */ - GPIOF->OTYPER = 0x00000000; - /* No pull-up, pull-down for PFx pins */ - GPIOF->PUPDR = 0x00000000; - - /* Connect PGx pins to FSMC Alternate function */ - GPIOG->AFR[0] = 0x00cccccc; - GPIOG->AFR[1] = 0x000000c0; - /* Configure PGx pins in Alternate function mode */ - GPIOG->MODER = 0x00080aaa; - /* Configure PGx pins speed to 100 MHz */ - GPIOG->OSPEEDR = 0x000c0fff; - /* Configure PGx pins Output type to push-pull */ - GPIOG->OTYPER = 0x00000000; - /* No pull-up, pull-down for PGx pins */ - GPIOG->PUPDR = 0x00000000; - - /*-- FSMC Configuration ------------------------------------------------------*/ - /* Enable the FSMC interface clock */ - RCC->AHB3ENR = 0x00000001; - - /* Configure and enable Bank1_SRAM2 */ - FSMC_Bank1->BTCR[2] = 0x00001015; - FSMC_Bank1->BTCR[3] = 0x00010603; - FSMC_Bank1E->BWTR[2] = 0x0fffffff; - /* - Bank1_SRAM2 is configured as follow: - - p.FSMC_AddressSetupTime = 3; - p.FSMC_AddressHoldTime = 0; - p.FSMC_DataSetupTime = 6; - p.FSMC_BusTurnAroundDuration = 1; - p.FSMC_CLKDivision = 0; - p.FSMC_DataLatency = 0; - p.FSMC_AccessMode = FSMC_AccessMode_A; - - FSMC_NORSRAMInitStructure.FSMC_Bank = FSMC_Bank1_NORSRAM2; - FSMC_NORSRAMInitStructure.FSMC_DataAddressMux = FSMC_DataAddressMux_Disable; - FSMC_NORSRAMInitStructure.FSMC_MemoryType = FSMC_MemoryType_PSRAM; - FSMC_NORSRAMInitStructure.FSMC_MemoryDataWidth = FSMC_MemoryDataWidth_16b; - FSMC_NORSRAMInitStructure.FSMC_BurstAccessMode = FSMC_BurstAccessMode_Disable; - FSMC_NORSRAMInitStructure.FSMC_AsynchronousWait = FSMC_AsynchronousWait_Disable; - FSMC_NORSRAMInitStructure.FSMC_WaitSignalPolarity = FSMC_WaitSignalPolarity_Low; - FSMC_NORSRAMInitStructure.FSMC_WrapMode = FSMC_WrapMode_Disable; - FSMC_NORSRAMInitStructure.FSMC_WaitSignalActive = FSMC_WaitSignalActive_BeforeWaitState; - FSMC_NORSRAMInitStructure.FSMC_WriteOperation = FSMC_WriteOperation_Enable; - FSMC_NORSRAMInitStructure.FSMC_WaitSignal = FSMC_WaitSignal_Disable; - FSMC_NORSRAMInitStructure.FSMC_ExtendedMode = FSMC_ExtendedMode_Disable; - FSMC_NORSRAMInitStructure.FSMC_WriteBurst = FSMC_WriteBurst_Disable; - FSMC_NORSRAMInitStructure.FSMC_ReadWriteTimingStruct = &p; - FSMC_NORSRAMInitStructure.FSMC_WriteTimingStruct = &p; - */ -} -#endif /* DATA_IN_ExtSRAM */ - - -/** - * @} - */ - -/** - * @} - */ - -/** - * @} - */ -/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/ +/** + ****************************************************************************** + * @file system_stm32f4xx.c + * @author MCD Application Team + * @version V1.0.1 + * @date 10-July-2012 + * @brief CMSIS Cortex-M4 Device Peripheral Access Layer System Source File. + * This file contains the system clock configuration for STM32F4xx devices, + * and is generated by the clock configuration tool + * stm32f4xx_Clock_Configuration_V1.0.1.xls + * + * 1. This file provides two functions and one global variable to be called from + * user application: + * - SystemInit(): Setups the system clock (System clock source, PLL Multiplier + * and Divider factors, AHB/APBx prescalers and Flash settings), + * depending on the configuration made in the clock xls tool. + * This function is called at startup just after reset and + * before branch to main program. This call is made inside + * the "startup_stm32f4xx.s" file. + * + * - SystemCoreClock variable: Contains the core clock (HCLK), it can be used + * by the user application to setup the SysTick + * timer or configure other parameters. + * + * - SystemCoreClockUpdate(): Updates the variable SystemCoreClock and must + * be called whenever the core clock is changed + * during program execution. + * + * 2. After each device reset the HSI (16 MHz) is used as system clock source. + * Then SystemInit() function is called, in "startup_stm32f4xx.s" file, to + * configure the system clock before to branch to main program. + * + * 3. If the system clock source selected by user fails to startup, the SystemInit() + * function will do nothing and HSI still used as system clock source. User can + * add some code to deal with this issue inside the SetSysClock() function. + * + * 4. The default value of HSE crystal is set to 25MHz, refer to "HSE_VALUE" define + * in "stm32f4xx.h" file. When HSE is used as system clock source, directly or + * through PLL, and you are using different crystal you have to adapt the HSE + * value to your own configuration. + * + * 5. This file configures the system clock as follows: + *============================================================================= + *============================================================================= + * Supported STM32F4xx device revision | Rev A + *----------------------------------------------------------------------------- + * System Clock source | PLL (HSE) + *----------------------------------------------------------------------------- + * SYSCLK(Hz) | 168000000 + *----------------------------------------------------------------------------- + * HCLK(Hz) | 168000000 + *----------------------------------------------------------------------------- + * AHB Prescaler | 1 + *----------------------------------------------------------------------------- + * APB1 Prescaler | 4 + *----------------------------------------------------------------------------- + * APB2 Prescaler | 2 + *----------------------------------------------------------------------------- + * HSE Frequency(Hz) | 8000000 + *----------------------------------------------------------------------------- + * PLL_M | 8 + *----------------------------------------------------------------------------- + * PLL_N | 336 + *----------------------------------------------------------------------------- + * PLL_P | 2 + *----------------------------------------------------------------------------- + * PLL_Q | 7 + *----------------------------------------------------------------------------- + * PLLI2S_N | 352 + *----------------------------------------------------------------------------- + * PLLI2S_R | 2 + *----------------------------------------------------------------------------- + * I2S input clock(Hz) | 176000000 + * | + * To achieve the following I2S config: | + * - Master clock output (MCKO): OFF | + * - Frame wide : 16bit | + * - Error % : 0,0000 | + * - Prescaler Odd factor (ODD): 1 | + * - Linear prescaler (DIV) : 14 | + *----------------------------------------------------------------------------- + * VDD(V) | 3,3 + *----------------------------------------------------------------------------- + * Main regulator output voltage | Scale1 mode + *----------------------------------------------------------------------------- + * Flash Latency(WS) | 5 + *----------------------------------------------------------------------------- + * Prefetch Buffer | OFF + *----------------------------------------------------------------------------- + * Instruction cache | ON + *----------------------------------------------------------------------------- + * Data cache | ON + *----------------------------------------------------------------------------- + * Require 48MHz for USB OTG FS, | Enabled + * SDIO and RNG clock | + *----------------------------------------------------------------------------- + *============================================================================= + ****************************************************************************** + * @attention + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2011 STMicroelectronics

+ ****************************************************************************** + */ + +/** @addtogroup CMSIS + * @{ + */ + +/** @addtogroup stm32f4xx_system + * @{ + */ + +/** @addtogroup STM32F4xx_System_Private_Includes + * @{ + */ + +#include "stm32f4xx.h" + +/** + * @} + */ + +/** @addtogroup STM32F4xx_System_Private_TypesDefinitions + * @{ + */ + +/** + * @} + */ + +/** @addtogroup STM32F4xx_System_Private_Defines + * @{ + */ + +/************************* Miscellaneous Configuration ************************/ +/*!< Uncomment the following line if you need to use external SRAM mounted + on STM324xG_EVAL board as data memory */ +/* #define DATA_IN_ExtSRAM */ + +/*!< Uncomment the following line if you need to relocate your vector Table in + Internal SRAM. */ +/* #define VECT_TAB_SRAM */ +#define VECT_TAB_OFFSET 0x00 /*!< Vector Table base offset field. + This value must be a multiple of 0x200. */ +/******************************************************************************/ + +/************************* PLL Parameters *************************************/ +/* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N */ +#define PLL_M 8 +#define PLL_N 336 + +/* SYSCLK = PLL_VCO / PLL_P */ +#define PLL_P 2 + +/* USB OTG FS, SDIO and RNG Clock = PLL_VCO / PLLQ */ +#define PLL_Q 7 + +/* PLLI2S_VCO = (HSE_VALUE Or HSI_VALUE / PLL_M) * PLLI2S_N + I2SCLK = PLLI2S_VCO / PLLI2S_R */ +#define START_I2SCLOCK 0 +#define PLLI2S_N 352 +#define PLLI2S_R 2 + +/******************************************************************************/ + +/** + * @} + */ + +/** @addtogroup STM32F4xx_System_Private_Macros + * @{ + */ + +/** + * @} + */ + +/** @addtogroup STM32F4xx_System_Private_Variables + * @{ + */ + +uint32_t SystemCoreClock = 168000000; + +__I uint8_t AHBPrescTable[16] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9}; + +/** + * @} + */ + +/** @addtogroup STM32F4xx_System_Private_FunctionPrototypes + * @{ + */ + +static void SetSysClock(void); +#ifdef DATA_IN_ExtSRAM +static void SystemInit_ExtMemCtl(void); +#endif /* DATA_IN_ExtSRAM */ + +/** + * @} + */ + +/** @addtogroup STM32F4xx_System_Private_Functions + * @{ + */ + +/** + * @brief Setup the microcontroller system + * Initialize the Embedded Flash Interface, the PLL and update the + * SystemFrequency variable. + * @param None + * @retval None + */ +void SystemInit(void) +{ + /* FPU settings ------------------------------------------------------------*/ +#if (__FPU_PRESENT == 1) && (__FPU_USED == 1) + SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2)); /* set CP10 and CP11 Full Access */ +#endif + /* Reset the RCC clock configuration to the default reset state ------------*/ + /* Set HSION bit */ + RCC->CR |= (uint32_t)0x00000001; + + /* Reset CFGR register */ + RCC->CFGR = 0x00000000; + + /* Reset HSEON, CSSON and PLLON bits */ + RCC->CR &= (uint32_t)0xFEF6FFFF; + + /* Reset PLLCFGR register */ + RCC->PLLCFGR = 0x24003010; + + /* Reset HSEBYP bit */ + RCC->CR &= (uint32_t)0xFFFBFFFF; + + /* Disable all interrupts */ + RCC->CIR = 0x00000000; + +#ifdef DATA_IN_ExtSRAM + SystemInit_ExtMemCtl(); +#endif /* DATA_IN_ExtSRAM */ + + /* Configure the System clock source, PLL Multiplier and Divider factors, + AHB/APBx prescalers and Flash settings ----------------------------------*/ + SetSysClock(); + + /* Configure the Vector Table location add offset address ------------------*/ +#ifdef VECT_TAB_SRAM + SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */ +#else + SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */ +#endif +} + +/** + * @brief Update SystemCoreClock variable according to Clock Register Values. + * The SystemCoreClock variable contains the core clock (HCLK), it can + * be used by the user application to setup the SysTick timer or configure + * other parameters. + * + * @note Each time the core clock (HCLK) changes, this function must be called + * to update SystemCoreClock variable value. Otherwise, any configuration + * based on this variable will be incorrect. + * + * @note - The system frequency computed by this function is not the real + * frequency in the chip. It is calculated based on the predefined + * constant and the selected clock source: + * + * - If SYSCLK source is HSI, SystemCoreClock will contain the HSI_VALUE(*) + * + * - If SYSCLK source is HSE, SystemCoreClock will contain the HSE_VALUE(**) + * + * - If SYSCLK source is PLL, SystemCoreClock will contain the HSE_VALUE(**) + * or HSI_VALUE(*) multiplied/divided by the PLL factors. + * + * (*) HSI_VALUE is a constant defined in stm32f4xx.h file (default value + * 16 MHz) but the real value may vary depending on the variations + * in voltage and temperature. + * + * (**) HSE_VALUE is a constant defined in stm32f4xx.h file (default value + * 25 MHz), user has to ensure that HSE_VALUE is same as the real + * frequency of the crystal used. Otherwise, this function may + * have wrong result. + * + * - The result of this function could be not correct when using fractional + * value for HSE crystal. + * + * @param None + * @retval None + */ +void SystemCoreClockUpdate(void) +{ + uint32_t tmp = 0, pllvco = 0, pllp = 2, pllsource = 0, pllm = 2; + + /* Get SYSCLK source -------------------------------------------------------*/ + tmp = RCC->CFGR & RCC_CFGR_SWS; + + switch (tmp) + { + case 0x00: /* HSI used as system clock source */ + SystemCoreClock = HSI_VALUE; + break; + case 0x04: /* HSE used as system clock source */ + SystemCoreClock = HSE_VALUE; + break; + case 0x08: /* PLL used as system clock source */ + + /* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N + SYSCLK = PLL_VCO / PLL_P + */ + pllsource = (RCC->PLLCFGR & RCC_PLLCFGR_PLLSRC) >> 22; + pllm = RCC->PLLCFGR & RCC_PLLCFGR_PLLM; + + if (pllsource != 0) + { + /* HSE used as PLL clock source */ + pllvco = (HSE_VALUE / pllm) * ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> 6); + } + else + { + /* HSI used as PLL clock source */ + pllvco = (HSI_VALUE / pllm) * ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> 6); + } + + pllp = (((RCC->PLLCFGR & RCC_PLLCFGR_PLLP) >>16) + 1 ) *2; + SystemCoreClock = pllvco/pllp; + break; + default: + SystemCoreClock = HSI_VALUE; + break; + } + /* Compute HCLK frequency --------------------------------------------------*/ + /* Get HCLK prescaler */ + tmp = AHBPrescTable[((RCC->CFGR & RCC_CFGR_HPRE) >> 4)]; + /* HCLK frequency */ + SystemCoreClock >>= tmp; +} + +/** + * @brief Configures the System clock source, PLL Multiplier and Divider factors, + * AHB/APBx prescalers and Flash settings + * @Note This function should be called only once the RCC clock configuration + * is reset to the default reset state (done in SystemInit() function). + * @param None + * @retval None + */ +static void SetSysClock(void) +{ + /******************************************************************************/ + /* PLL (clocked by HSE) used as System clock source */ + /******************************************************************************/ + __IO uint32_t StartUpCounter = 0, HSEStatus = 0; + + /* Enable HSE */ + RCC->CR |= ((uint32_t)RCC_CR_HSEON); + + /* Wait till HSE is ready and if Time out is reached exit */ + do + { + HSEStatus = RCC->CR & RCC_CR_HSERDY; + StartUpCounter++; + } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT)); + + if ((RCC->CR & RCC_CR_HSERDY) != RESET) + { + HSEStatus = (uint32_t)0x01; + } + else + { + HSEStatus = (uint32_t)0x00; + } + + if (HSEStatus == (uint32_t)0x01) + { + /* Select regulator voltage output Scale 1 mode, System frequency up to 168 MHz */ + RCC->APB1ENR |= RCC_APB1ENR_PWREN; + PWR->CR |= PWR_CR_VOS; + + /* HCLK = SYSCLK / 1*/ + RCC->CFGR |= RCC_CFGR_HPRE_DIV1; + + /* PCLK2 = HCLK / 2*/ + RCC->CFGR |= RCC_CFGR_PPRE2_DIV2; + + /* PCLK1 = HCLK / 4*/ + RCC->CFGR |= RCC_CFGR_PPRE1_DIV4; + + /* Configure the main PLL */ + RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) | + (RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24); + + /* Enable the main PLL */ + RCC->CR |= RCC_CR_PLLON; + + /* Wait till the main PLL is ready */ + while((RCC->CR & RCC_CR_PLLRDY) == 0) + { + } + + /* Configure Flash prefetch, Instruction cache, Data cache and wait state */ + FLASH->ACR = FLASH_ACR_ICEN |FLASH_ACR_DCEN |FLASH_ACR_LATENCY_5WS; + + /* Select the main PLL as system clock source */ + RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW)); + RCC->CFGR |= RCC_CFGR_SW_PLL; + + /* Wait till the main PLL is used as system clock source */ + while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS ) != RCC_CFGR_SWS_PLL); + { + } + } + else + { /* If HSE fails to start-up, the application will have wrong clock + configuration. User can add here some code to deal with this error */ + } + + + /******************************************************************************/ + /* I2S clock configuration */ + /******************************************************************************/ + +#if START_I2SCLOCK + /* PLLI2S clock used as I2S clock source */ + RCC->CFGR &= ~RCC_CFGR_I2SSRC; + + /* Configure PLLI2S */ + RCC->PLLI2SCFGR = (PLLI2S_N << 6) | (PLLI2S_R << 28); + + /* Enable PLLI2S */ + RCC->CR |= ((uint32_t)RCC_CR_PLLI2SON); + + /* Wait till PLLI2S is ready */ + while((RCC->CR & RCC_CR_PLLI2SRDY) == 0) + { + } +#endif +} + +/** + * @brief Setup the external memory controller. Called in startup_stm32f4xx.s + * before jump to __main + * @param None + * @retval None + */ +#ifdef DATA_IN_ExtSRAM +/** + * @brief Setup the external memory controller. + * Called in startup_stm32f4xx.s before jump to main. + * This function configures the external SRAM mounted on STM324xG_EVAL board + * This SRAM will be used as program data memory (including heap and stack). + * @param None + * @retval None + */ +void SystemInit_ExtMemCtl(void) +{ + /*-- GPIOs Configuration -----------------------------------------------------*/ + /* + +-------------------+--------------------+------------------+------------------+ + + SRAM pins assignment + + +-------------------+--------------------+------------------+------------------+ + | PD0 <-> FSMC_D2 | PE0 <-> FSMC_NBL0 | PF0 <-> FSMC_A0 | PG0 <-> FSMC_A10 | + | PD1 <-> FSMC_D3 | PE1 <-> FSMC_NBL1 | PF1 <-> FSMC_A1 | PG1 <-> FSMC_A11 | + | PD4 <-> FSMC_NOE | PE3 <-> FSMC_A19 | PF2 <-> FSMC_A2 | PG2 <-> FSMC_A12 | + | PD5 <-> FSMC_NWE | PE4 <-> FSMC_A20 | PF3 <-> FSMC_A3 | PG3 <-> FSMC_A13 | + | PD8 <-> FSMC_D13 | PE7 <-> FSMC_D4 | PF4 <-> FSMC_A4 | PG4 <-> FSMC_A14 | + | PD9 <-> FSMC_D14 | PE8 <-> FSMC_D5 | PF5 <-> FSMC_A5 | PG5 <-> FSMC_A15 | + | PD10 <-> FSMC_D15 | PE9 <-> FSMC_D6 | PF12 <-> FSMC_A6 | PG9 <-> FSMC_NE2 | + | PD11 <-> FSMC_A16 | PE10 <-> FSMC_D7 | PF13 <-> FSMC_A7 |------------------+ + | PD12 <-> FSMC_A17 | PE11 <-> FSMC_D8 | PF14 <-> FSMC_A8 | + | PD13 <-> FSMC_A18 | PE12 <-> FSMC_D9 | PF15 <-> FSMC_A9 | + | PD14 <-> FSMC_D0 | PE13 <-> FSMC_D10 |------------------+ + | PD15 <-> FSMC_D1 | PE14 <-> FSMC_D11 | + | | PE15 <-> FSMC_D12 | + +-------------------+--------------------+ + */ + /* Enable GPIOD, GPIOE, GPIOF and GPIOG interface clock */ + RCC->AHB1ENR = 0x00000078; + + /* Connect PDx pins to FSMC Alternate function */ + GPIOD->AFR[0] = 0x00cc00cc; + GPIOD->AFR[1] = 0xcc0ccccc; + /* Configure PDx pins in Alternate function mode */ + GPIOD->MODER = 0xaaaa0a0a; + /* Configure PDx pins speed to 100 MHz */ + GPIOD->OSPEEDR = 0xffff0f0f; + /* Configure PDx pins Output type to push-pull */ + GPIOD->OTYPER = 0x00000000; + /* No pull-up, pull-down for PDx pins */ + GPIOD->PUPDR = 0x00000000; + + /* Connect PEx pins to FSMC Alternate function */ + GPIOE->AFR[0] = 0xc00cc0cc; + GPIOE->AFR[1] = 0xcccccccc; + /* Configure PEx pins in Alternate function mode */ + GPIOE->MODER = 0xaaaa828a; + /* Configure PEx pins speed to 100 MHz */ + GPIOE->OSPEEDR = 0xffffc3cf; + /* Configure PEx pins Output type to push-pull */ + GPIOE->OTYPER = 0x00000000; + /* No pull-up, pull-down for PEx pins */ + GPIOE->PUPDR = 0x00000000; + + /* Connect PFx pins to FSMC Alternate function */ + GPIOF->AFR[0] = 0x00cccccc; + GPIOF->AFR[1] = 0xcccc0000; + /* Configure PFx pins in Alternate function mode */ + GPIOF->MODER = 0xaa000aaa; + /* Configure PFx pins speed to 100 MHz */ + GPIOF->OSPEEDR = 0xff000fff; + /* Configure PFx pins Output type to push-pull */ + GPIOF->OTYPER = 0x00000000; + /* No pull-up, pull-down for PFx pins */ + GPIOF->PUPDR = 0x00000000; + + /* Connect PGx pins to FSMC Alternate function */ + GPIOG->AFR[0] = 0x00cccccc; + GPIOG->AFR[1] = 0x000000c0; + /* Configure PGx pins in Alternate function mode */ + GPIOG->MODER = 0x00080aaa; + /* Configure PGx pins speed to 100 MHz */ + GPIOG->OSPEEDR = 0x000c0fff; + /* Configure PGx pins Output type to push-pull */ + GPIOG->OTYPER = 0x00000000; + /* No pull-up, pull-down for PGx pins */ + GPIOG->PUPDR = 0x00000000; + + /*-- FSMC Configuration ------------------------------------------------------*/ + /* Enable the FSMC interface clock */ + RCC->AHB3ENR = 0x00000001; + + /* Configure and enable Bank1_SRAM2 */ + FSMC_Bank1->BTCR[2] = 0x00001015; + FSMC_Bank1->BTCR[3] = 0x00010603; + FSMC_Bank1E->BWTR[2] = 0x0fffffff; + /* + Bank1_SRAM2 is configured as follow: + + p.FSMC_AddressSetupTime = 3; + p.FSMC_AddressHoldTime = 0; + p.FSMC_DataSetupTime = 6; + p.FSMC_BusTurnAroundDuration = 1; + p.FSMC_CLKDivision = 0; + p.FSMC_DataLatency = 0; + p.FSMC_AccessMode = FSMC_AccessMode_A; + + FSMC_NORSRAMInitStructure.FSMC_Bank = FSMC_Bank1_NORSRAM2; + FSMC_NORSRAMInitStructure.FSMC_DataAddressMux = FSMC_DataAddressMux_Disable; + FSMC_NORSRAMInitStructure.FSMC_MemoryType = FSMC_MemoryType_PSRAM; + FSMC_NORSRAMInitStructure.FSMC_MemoryDataWidth = FSMC_MemoryDataWidth_16b; + FSMC_NORSRAMInitStructure.FSMC_BurstAccessMode = FSMC_BurstAccessMode_Disable; + FSMC_NORSRAMInitStructure.FSMC_AsynchronousWait = FSMC_AsynchronousWait_Disable; + FSMC_NORSRAMInitStructure.FSMC_WaitSignalPolarity = FSMC_WaitSignalPolarity_Low; + FSMC_NORSRAMInitStructure.FSMC_WrapMode = FSMC_WrapMode_Disable; + FSMC_NORSRAMInitStructure.FSMC_WaitSignalActive = FSMC_WaitSignalActive_BeforeWaitState; + FSMC_NORSRAMInitStructure.FSMC_WriteOperation = FSMC_WriteOperation_Enable; + FSMC_NORSRAMInitStructure.FSMC_WaitSignal = FSMC_WaitSignal_Disable; + FSMC_NORSRAMInitStructure.FSMC_ExtendedMode = FSMC_ExtendedMode_Disable; + FSMC_NORSRAMInitStructure.FSMC_WriteBurst = FSMC_WriteBurst_Disable; + FSMC_NORSRAMInitStructure.FSMC_ReadWriteTimingStruct = &p; + FSMC_NORSRAMInitStructure.FSMC_WriteTimingStruct = &p; + */ +} +#endif /* DATA_IN_ExtSRAM */ + + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ +/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/ diff --git a/codec2-dev/stm32/src/tuner_ut.c b/codec2-dev/stm32/src/tuner_ut.c index ac522000..0bd590d2 100644 --- a/codec2-dev/stm32/src/tuner_ut.c +++ b/codec2-dev/stm32/src/tuner_ut.c @@ -1,122 +1,122 @@ -/*---------------------------------------------------------------------------*\ - - FILE........: tuner_ut.c - AUTHOR......: David Rowe - DATE CREATED: 20 Feb 2015 - - Unit test for high speed ADC radio tuner, samples signal centred at - 500kHz using Fs=2 MHz and uploads to host at Fs=10 kHz. - -\*---------------------------------------------------------------------------*/ - -/* - Copyright (C) 2015 David Rowe - - All rights reserved. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License version 2.1, as - published by the Free Software Foundation. This program is - distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or - FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public - License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program; if not, see . -*/ - -#include -#include -#include "gdb_stdio.h" -#include "stm32f4_dac.h" -#include "stm32f4_adc_tuner.h" -#include "iir_tuner.h" -#include "sm1000_leds_switches.h" -#include "../src/codec2_fm.h" -#include "stm32f4xx.h" - -#define REC_TIME_SECS 10 -#define FS 50000 -#define N 5000 - -extern int adc_overflow1; - -int main(void) { - float tuner_out[IIR_TUNER_DEC_50_10_FILT_MEM+N/2]; - float fm_out[N/2]; - //float dec_10[(N/2)/5]; - short dec_10_short[(N/2)]; - int bufs, i, j, k, fifo_sz, bn; - FILE *ftuner; - struct FM *fm; - - ftuner = fopen("tuner.raw", "wb"); - if (ftuner == NULL) { - printf("Error opening input file: tuner.raw\n\nTerminating....\n"); - exit(1); - } - bufs = FS*REC_TIME_SECS/N; - fifo_sz = ((4*N/ADC_TUNER_N)+1)*ADC_TUNER_N; - printf("Starting! bufs: %d %d\n", bufs, fifo_sz); - - //dac_open(DAC_BUF_SZ); - adc_open(fifo_sz); - sm1000_leds_switches_init(); - - fm = fm_create(N/2); - fm->Fs = 44400.0; - fm->fm_max = 3000.0; - fm->fd = 5000.0; - fm->fc = fm->Fs/4; - - i = 0; bn = 0; - while(1) { - /* wait for buffer of Fs=50kHz tuner output samples */ - - while(adc1_read((short *)&tuner_out[IIR_TUNER_DEC_50_10_FILT_MEM], N) == -1); - - /* The semi-hosting system can only handle Fs=16kHz and below so resample down - to Fs=10 kHz and convert to shorts */ - - #ifdef SSB - iir_tuner_dec_50_to_10(dec_10, &tuner_out[IIR_TUNER_DEC_50_10_FILT_MEM], N/2); - for(j=0; jODR |= (1 << 3); - /* - for(j=0; jODR &= ~(1 << 3); - - for(j=0,k=0; j. +*/ + +#include +#include +#include "gdb_stdio.h" +#include "stm32f4_dac.h" +#include "stm32f4_adc_tuner.h" +#include "iir_tuner.h" +#include "sm1000_leds_switches.h" +#include "../src/codec2_fm.h" +#include "stm32f4xx.h" + +#define REC_TIME_SECS 10 +#define FS 50000 +#define N 5000 + +extern int adc_overflow1; + +int main(void) { + float tuner_out[IIR_TUNER_DEC_50_10_FILT_MEM+N/2]; + float fm_out[N/2]; + //float dec_10[(N/2)/5]; + short dec_10_short[(N/2)]; + int bufs, i, j, k, fifo_sz, bn; + FILE *ftuner; + struct FM *fm; + + ftuner = fopen("tuner.raw", "wb"); + if (ftuner == NULL) { + printf("Error opening input file: tuner.raw\n\nTerminating....\n"); + exit(1); + } + bufs = FS*REC_TIME_SECS/N; + fifo_sz = ((4*N/ADC_TUNER_N)+1)*ADC_TUNER_N; + printf("Starting! bufs: %d %d\n", bufs, fifo_sz); + + //dac_open(DAC_BUF_SZ); + adc_open(fifo_sz); + sm1000_leds_switches_init(); + + fm = fm_create(N/2); + fm->Fs = 44400.0; + fm->fm_max = 3000.0; + fm->fd = 5000.0; + fm->fc = fm->Fs/4; + + i = 0; bn = 0; + while(1) { + /* wait for buffer of Fs=50kHz tuner output samples */ + + while(adc1_read((short *)&tuner_out[IIR_TUNER_DEC_50_10_FILT_MEM], N) == -1); + + /* The semi-hosting system can only handle Fs=16kHz and below so resample down + to Fs=10 kHz and convert to shorts */ + + #ifdef SSB + iir_tuner_dec_50_to_10(dec_10, &tuner_out[IIR_TUNER_DEC_50_10_FILT_MEM], N/2); + for(j=0; jODR |= (1 << 3); + /* + for(j=0; jODR &= ~(1 << 3); + + for(j=0,k=0; j