From 1ccb05ed73dce314bfdd3af06c4b82941996ed06 Mon Sep 17 00:00:00 2001 From: drowe67 Date: Sun, 2 Oct 2016 18:10:39 +0000 Subject: [PATCH] building up PER/BER tests git-svn-id: https://svn.code.sf.net/p/freetel/code@2882 01035d8c-6547-0410-b346-abe4f91aad63 --- codec2-dev/octave/test_ldpc_fsk_lib.m | 311 +++++++++++++++++++++++++- 1 file changed, 307 insertions(+), 4 deletions(-) diff --git a/codec2-dev/octave/test_ldpc_fsk_lib.m b/codec2-dev/octave/test_ldpc_fsk_lib.m index 43a1348c..19e27ddd 100644 --- a/codec2-dev/octave/test_ldpc_fsk_lib.m +++ b/codec2-dev/octave/test_ldpc_fsk_lib.m @@ -196,7 +196,7 @@ function test_c_decoder f = fopen("data_out.bin","rb"); data_out = fread(f, "uint8")'; fclose(f); - Nerrs = Nerrs2 = zeros(1,frames);; + Nerrs = Nerrs2 = zeros(1,frames); for i=1:frames % Check C decoder @@ -231,7 +231,20 @@ function test_c_decoder end - +% Saves a complex vector s to a file "filename" of IQ unsigned 8 bit +% chars, same as RTLSDR format. + +function save_rtlsdr(filename, s) + mx = max(abs(s)); + re = real(s); im = imag(s); + l = length(s); + iq = zeros(1,2*l); + iq(1:2:2*l) = 127 + re*(127/mx); + iq(2:2:2*l) = 127 + im*(127/mx); + fs = fopen(filename,"wb"); + fwrite(fs,iq,"uchar"); + fclose(fs); +endfunction % Start simulation -------------------------------------------------------- @@ -264,9 +277,9 @@ ldpc_fsk_lib; randn('state',1); rand('state',1); -% binary flags to run various demos, e.g. "15" to run 1 .. 8 +% ------------------ select which demo/test to run here --------------- -demo = 8; +demo = 10; if demo == 1 printf("simple_ut....\n"); @@ -341,3 +354,293 @@ if demo == 8 r = s + noise; f = fopen("sstv_sd.bin","wb"); fwrite(f, r, "float32"); fclose(f); end + +if demo == 9 + frames = 100; + EbNodB = 11; + + frame_rs232 = []; + for i=1:frames + frame_rs232 = [frame_rs232 gen_sstv_frame]; + end + + % Use C FSK modulator to generate modulated signal + + f = fopen("sstv.bin","wb"); fwrite(f, frame_rs232, "char"); fclose(f); + system("../build_linux/src/fsk_mod 2 9600 1200 1200 2400 sstv.bin fsk_mod.raw"); + + % Add some channel noise here in Octave + + f = fopen("fsk_mod.raw","rb"); tx = fread(f, "short")'; fclose(f); tx_pwr = var(tx); + Fs = 9600; Rs=1200; EbNolin = 10 ^ (EbNodB/10); + variance = (tx_pwr/2)*states.Fs/(states.Rs*EbNolin*states.bitspersymbol); + noise = sqrt(variance)*randn(1,length(tx)); + printf("SNRdB meas: %4.1f\n", 10*log10(var(tx)/var(noise))); + rx = tx + noise; + f = fopen("fsk_demod.raw","wb"); tx = fwrite(f, rx, "short"); fclose(f); + + % Demodulate using C modem and C de-framer/LDPC decoder + + system("../build_linux/src/fsk_demod 2XS 8 9600 1200 fsk_demod.raw - | ../src/drs232_ldpc - dummy_out.bin"); +end + + +% Using simulated SSTV packet, generate complex fsk mod signals, 8-bit +% unsigned IQ for feeding into demod chain +% todo: [ ] uncoded BER +% [ ] octave fsk demod +% [ ] compared unsigned 8 bit IQ to regular 16-bit +% [ ] run python dashboard +% [ ] compare measured Eb/No +% [ ] drs232_ldpc has built in test frame +% [ ] use test frame that comes with code? +% [ ] Octave code to generate +% [ ] print test results +% [ ] test with resampler + +if demo == 10 +% init LDPC code + +load('H2064_516_sparse.mat'); +HRA = full(HRA); +max_iterations = 100; +decoder_type = 0; +mod_order = 2; + +code_param = ldpc_init(HRA, mod_order); +tx_codeword = gen_sstv_frame; + +% init FSK modem + +fsk_horus_as_a_lib = 1; +fsk_horus; +states = fsk_horus_init_hbr(9600, 8, 1200, 2, length(tx_codeword)); +states.ftx = [1200 2400]; +states.df(1:states.M) = 0; +states.dA(1:states.M) = 1; + +% set up AWGN channel + +frames = 10; +EbNodB = 9; +EbNo = 10^(EbNodB/10); +variance = states.Fs/(states.Rs*EbNo*states.bitspersymbol); + +% start simulation ---------------------------------------- + +%tx_data = round(rand(1, code_param.data_bits_per_frame)); +%tx_codeword = ldpc_encode(code_param, tx_data); +tx_bit_stream = []; +for i=1:frames+1 + tx_bit_stream = [tx_bit_stream tx_codeword]; +end + +% modulate and channel model + +tx = fsk_horus_mod(states, tx_bit_stream); +noise = sqrt(variance)*randn(length(tx),1); +rx = tx + noise; +printf("SNRdB meas: %4.1f\n", 10*log10(var(tx)/var(noise))); + +% demodulate frame by frame + +st = 1; +run_frames = floor(length(rx)/states.N); +rx_bit_stream = []; +rx_sd_stream = []; +for f=1:run_frames + + % extract nin samples from rx sample stream + + nin = states.nin; + en = st + states.nin - 1; + + if en <= length(rx) % due to nin variations its possible to overrun buffer + sf = rx(st:en); + st += nin; + + % demodulate to stream of bits + + states.f = [1200 2400]; + [rx_bits states] = fsk_horus_demod(states, sf); + rx_bit_stream = [rx_bit_stream rx_bits]; + rx_sd_stream = [rx_sd_stream states.rx_bits_sd]; + end +end + +#{ +% measure BER, there is an annoying one bit delay in rx +% which means we waste a frame + +n_coded_bits = n_uncoded_bits = n_coded_errs = n_uncoded_errs = 0; +for f=1:run_frames + st = (f-1)*states.nbit + 2; en = st+states.nbit-1; + if en < length(rx_bit_stream) + rx_codeword = rx_bit_stream(st:en); + n_uncoded_errs += sum(xor(tx_codeword, rx_codeword)); + n_uncoded_bits += code_param.symbols_per_frame; + + #{ + r = rx_sd_stream(st:en); + [detected_data Niters] = ldpc_decode(r, code_param, max_iterations, decoder_type); + coded_errors = sum(xor(detected_data(1:code_param.data_bits_per_frame), tx_data)); + n_coded_errs += coded_errors; + n_coded_bits += code_param.data_bits_per_frame; + printf("f: %d Niters: %d coded_errors: %d\n", f, Niters, coded_errors); + #} + end +end + +uncoded_ber = n_uncoded_errs/n_uncoded_bits; +printf("n_uncoded_bits: %d n_uncoded_errs: %d BER: %4.3f\n", n_uncoded_bits, n_uncoded_errs, uncoded_ber); +%coded_ber = n_coded_errs/n_coded_bits; +%printf("n_coded_bits..: %d n_coded_errs..: %d BER: %4.3f\n", n_coded_bits, n_coded_errs, coded_ber); +#} + + % state machine. look for UW. When found count bit errors over one frame of bits + + state = "wait for uw"; + start_uw_ind = 16*10+1; end_uw_ind = start_uw_ind + 2*10 - 1; + uw_rs232 = tx_codeword(start_uw_ind:end_uw_ind); luw = length(uw_rs232); + start_frame_ind = end_uw_ind + 1; + nbits = length(rx_bit_stream); + uw_thresh = 0; + n_uncoded_errs = 0; + n_uncoded_bits = 0; + + % might as well count uncoded errors use RS232 framing bits + + nbits_frame = code_param.data_bits_per_frame*10/8; + + for i=luw:nbits + next_state = state; + if strcmp(state, 'wait for uw') + uw_errs = xor(rx_bit_stream(i-luw+1:i), uw_rs232); + if uw_errs <= uw_thresh + next_state = 'count errors'; + tx_frame_ind = start_frame_ind; + %printf("%d %s %s\n", i, state, next_state); + end + end + if strcmp(state, 'count errors') + n_uncoded_errs += xor(rx_bit_stream(i), tx_codeword(tx_frame_ind)); + n_uncoded_bits++; + tx_frame_ind++; + if tx_frame_ind == (start_frame_ind+nbits_frame) + next_state = 'wait for uw'; + end + end + state = next_state; + end + +uncoded_ber = n_uncoded_errs/n_uncoded_bits; +printf("n_uncoded_bits: %d n_uncoded_errs: %d BER: %4.3f\n", n_uncoded_bits, n_uncoded_errs, uncoded_ber); + +#{ + frames = 2; + EbNodB = 100; + + % init LPDC code - we need code_param.symbols_per_frame to init fsk modem, + % which is actually unused as we aren't running the fsk modem lol + + load('H2064_516_sparse.mat'); + HRA = full(HRA); + max_iterations = 100; decoder_type = 0; mod_order = 2; + code_param = ldpc_init(HRA, mod_order); + + % init 8x oversampled FSK modulator + + fsk_horus_as_a_lib = 1; + fsk_horus; + states = fsk_horus_init_hbr(9600, 8, 1200, 2, code_param.symbols_per_frame); + states.ftx = [1200 2400]; + states.tx_real = 0; + + % Generate some SSTV frames, note it's the same frame repeated which + % isn't ideal for testing the LDPC code but good for uncoded BER + % testing. For testing PER best to send frames of random payload data + % and measure PER based on checksum/LDPC decoder convergence. + + printf("generating LPDC encoded frames...\n"); + aframe_rs232 = gen_sstv_frame; + frames_rs232 = []; + for i=1:frames + frames_rs232 = [frames_rs232 aframe_rs232]; + end + nbits = length(frame_rs232); bit_rate = 115200; Nsecs = nbits/bit_rate; + printf("%d frames, %d bits, %3.1f seconds at %d bit/s\n", frames, nbits, Nsecs, bit_rate); + + % FSK modulate and add noise + + printf("fsk mod and AWGN noise...\n"); + tx = fsk_horus_mod(states, frame_rs232); + + EbNolin = 10 ^ (EbNodB/10); + tx_pwr = var(tx); + variance = (tx_pwr)*states.Fs/(states.Rs*EbNolin*states.bitspersymbol); + noise = sqrt(variance/2)*(randn(length(tx),1) + j*randn(length(tx),1)); + rx = tx + noise; + + % Octave demodulator + + printf("fsk demod ....\n"); + st = 1; + run_frames = floor(length(rx)/states.N); + rx_bit_stream = []; + rx_sd_stream = []; + for f=1:run_frames + + % extract nin samples from rx sample stream + + nin = states.nin; + en = st + states.nin - 1; + + if en <= length(rx) % due to nin variations its possible to overrun buffer + sf = rx(st:en); + st += nin; + + % demodulate to stream of bits + + states.f = [1200 2400]; + [rx_bits states] = fsk_horus_demod(states, sf); + rx_bit_stream = [rx_bit_stream rx_bits]; + rx_sd_stream = [rx_sd_stream states.rx_bits_sd]; + end + end + + % arrange into 10 bit columns + + % look for SSTV packet UW + + uw_rs232 = aframe_rs232(16*10+1:18*10); luw = length(uw_rs232); + nbits = length(rx_bit_stream); + uw_score = zeros(1, nbits); + for i=1:nbits-luw + errs = xor(rx_bit_stream(i:i+luw-1), uw_rs232); + uw_score(i) = sum(errs); + end + figure(1) + clf + plot(uw_score) + + % pass through drs232 to provide UW sync + +#} +#{ + % determine raw BER from rx_frames + + % read in test frame output from drs232_ldpc, determine raw BER + + + % Use Octave demodulator to check uncoded BER and PER. Use UW to sync. + % Idea - how abt using C code for this? Or just copy state machine logic + % here. Wld need drs232_ldpc to have known test frame. Hmmm... that is a + % great idea for testing ... especially repeats across different rx set-ups.ñ + + printf("save IQ to disk...\n"); + figure(1); + subplot(211); plot(real(rx(1:1000))); + subplot(212); plot(imag(rx(1:1000))); + save_hackrf("fsk_modiq.raw", rx); +#} +end -- 2.25.1