building up SSTV tests with complex signals
authordrowe67 <drowe67@01035d8c-6547-0410-b346-abe4f91aad63>
Fri, 7 Oct 2016 02:41:39 +0000 (02:41 +0000)
committerdrowe67 <drowe67@01035d8c-6547-0410-b346-abe4f91aad63>
Fri, 7 Oct 2016 02:41:39 +0000 (02:41 +0000)
git-svn-id: https://svn.code.sf.net/p/freetel/code@2884 01035d8c-6547-0410-b346-abe4f91aad63

codec2-dev/octave/ldpc_fsk_lib.m
codec2-dev/octave/test_ldpc_fsk_lib.m

index 72d931487152396b10593b186063766a6756c9b7..701a6f29901d0b0516625d663ad5ce509f14af8d 100644 (file)
@@ -270,7 +270,7 @@ function frame_rs232 = gen_sstv_frame
 \r
   % unpack bytes to bits and LPDC encode\r
 \r
-  mask = 2.^(0:7);\r
+  mask = 2.^(0:7);          % LSB to MSB\r
   unpacked_data = [];\r
   for b=1:length(data)\r
     unpacked_data = [unpacked_data bitand(data(b), mask) > 0];\r
@@ -295,3 +295,30 @@ function frame_rs232 = gen_sstv_frame
   end\r
 endfunction\r
 \r
+\r
+% calculates and compares the checksum of a SSTV frame, that has RS232\r
+% start and stop bits\r
+\r
+function checksum_ok = sstv_checksum(frame_rs232)\r
+  l = length(frame_rs232);\r
+  expected_l = (256+2)*10;\r
+  assert(l == expected_l);\r
+\r
+  % extract rx bytes\r
+\r
+  rx_data = zeros(1,256);\r
+  mask = 2.^(0:7);          % LSB to MSB\r
+  k = 1;\r
+  for i=1:10:expected_l\r
+    rx_bits = frame_rs232(i+1:i+8);\r
+    rx_data(k) = sum(rx_bits .* mask); \r
+    k++;\r
+  end\r
+\r
+  % calc rx checksum and extract tx checksum\r
+\r
+  rx_checksum = crc16(rx_data(1:256));\r
+  tx_checksum = sprintf("%02X%02X", rx_data(258), rx_data(257));\r
+  %printf("tx_checksum: %s rx_checksum: %s\n", tx_checksum, rx_checksum);\r
+  checksum_ok = strcmp(tx_checksum, rx_checksum);\r
+endfunction\r
index 19e27ddd7a216c25b586c6006de969b9723a1b9b..16e9c3264a4042283cb74cacd3064e31ad26ad05 100644 (file)
@@ -57,7 +57,7 @@ function detected_data = decode_from_file(filename)
 end\r
 \r
 \r
-% plots a BER cuvre for the decoder.  Takes a while to run, uses parallel cores\r
+% plots a BER curve for the decoder.  Takes a while to run, uses parallel cores\r
 \r
 function plot_curve\r
   num_cores = 4;              % set this to the number of cores you have\r
@@ -246,6 +246,165 @@ function save_rtlsdr(filename, s)
   fclose(fs);\r
 endfunction\r
 \r
+\r
+% Using simulated SSTV packet, generate complex fsk mod signals, 8-bit\r
+% unsigned IQ for feeding into demod chain\r
+#{\r
+todo: [X] uncoded BER\r
+          [X] octave fsk demod\r
+          [X] use C demod\r
+      [ ] compared unsigned 8 bit IQ to regular 16-bit\r
+          + modulate complex signal\r
+          + check BER\r
+          + feed into csdr stack\r
+      [ ] test with resampler\r
+      [ ] measure effect on PER with coding\r
+#}\r
+\r
+function [n_uncoded_errs n_uncoded_bits] = run_sstv_sim(sim_in, EbNodB)\r
+\r
+  frames = sim_in.frames;\r
+  c_demod = sim_in.c_demod;\r
+\r
+  % init LDPC code\r
+\r
+  load('H2064_516_sparse.mat');\r
+  HRA = full(HRA);  \r
+  max_iterations = 100;\r
+  decoder_type = 0;\r
+  mod_order = 2;\r
+\r
+  code_param = ldpc_init(HRA, mod_order);\r
+\r
+  % note fixed frame of bits used for BER testing\r
+\r
+  tx_codeword = gen_sstv_frame;\r
+\r
+  % init FSK modem\r
+\r
+  fsk_horus_as_a_lib = 1;\r
+  fsk_horus;\r
+  states         = fsk_horus_init_hbr(9600, 8, 1200, 2, length(tx_codeword));\r
+  states.ftx     = [1200 2400];\r
+  states.df(1:states.M) = 0;\r
+  states.dA(1:states.M) = 1;\r
+  states.tx_real = 0;\r
+\r
+  % set up AWGN channel \r
+\r
+  EbNo = 10^(EbNodB/10);\r
+  variance = states.Fs/(states.Rs*EbNo*states.bitspersymbol);\r
+\r
+  % start simulation ----------------------------------------\r
+\r
+  tx_bit_stream = [];\r
+  for i=1:frames+1\r
+    tx_bit_stream = [tx_bit_stream tx_codeword];\r
+  end\r
+\r
+  % modulate and channel model\r
+\r
+  tx = fsk_horus_mod(states, tx_bit_stream);\r
+  noise_real = sqrt(variance)*randn(length(tx),1);\r
+  noise_complex = sqrt(variance/2)*(randn(length(tx),1) + j*randn(length(tx),1));\r
+\r
+  % demodulate\r
+\r
+  if c_demod\r
+    if states.tx_real\r
+      rx = tx + noise_real;\r
+    else\r
+      rx = 2*real(tx) + noise_real;\r
+    end\r
+    SNRdB = 10*log10(var(tx)/var(noise_real));\r
+    rx_scaled = 1000*real(rx);\r
+    f = fopen("fsk_demod.raw","wb"); fwrite(f, rx_scaled, "short"); fclose(f);\r
+    system("../build_linux/src/fsk_demod 2X 8 9600 1200 fsk_demod.raw fsk_demod.bin");\r
+    f = fopen("fsk_demod.bin","rb"); rx_bit_stream = fread(f, "uint8")'; fclose(f);\r
+  else\r
+    if states.tx_real\r
+      rx = tx + noise_real;\r
+    else\r
+      rx = tx + noise_complex;\r
+    end\r
+    SNRdB = 10*log10(var(tx)/var(noise_complex));\r
+\r
+    % demodulate frame by frame using Octave demod\r
+\r
+    st = 1;\r
+    run_frames = floor(length(rx)/states.N);\r
+    rx_bit_stream = [];\r
+    rx_sd_stream = [];\r
+    for f=1:run_frames\r
+\r
+      % extract nin samples from rx sample stream\r
+\r
+      nin = states.nin;\r
+      en = st + states.nin - 1;\r
+\r
+      if en <= length(rx) % due to nin variations its possible to overrun buffer\r
+        sf = rx(st:en);\r
+        st += nin;\r
+\r
+        % demodulate to stream of bits\r
+\r
+        states.f = [1200 2400];\r
+        [rx_bits states] = fsk_horus_demod(states, sf);\r
+        rx_bit_stream = [rx_bit_stream rx_bits];\r
+        rx_sd_stream = [rx_sd_stream states.rx_bits_sd];\r
+      end\r
+    end\r
+  end\r
+\r
+  % state machine. Look for SSTV UW.  When found count bit errors over one frame of bits\r
+\r
+  state = "wait for uw";\r
+  start_uw_ind = 16*10+1; end_uw_ind = start_uw_ind + 2*10 - 1;\r
+  uw_rs232 = tx_codeword(start_uw_ind:end_uw_ind); luw = length(uw_rs232);\r
+  start_frame_ind =  end_uw_ind + 1;\r
+  nbits = length(rx_bit_stream);\r
+  uw_thresh = 0;\r
+  n_uncoded_errs = 0;\r
+  n_uncoded_bits = 0;\r
+\r
+  % might as well include RS232 framing bits in uncoded error count\r
+\r
+  nbits_frame = code_param.data_bits_per_frame*10/8;  \r
+\r
+  for i=luw:nbits\r
+    next_state = state;\r
+    if strcmp(state, 'wait for uw')\r
+      uw_errs = xor(rx_bit_stream(i-luw+1:i), uw_rs232);\r
+      if uw_errs <= uw_thresh\r
+        next_state = 'count errors';\r
+        tx_frame_ind = start_frame_ind;\r
+        rx_frame_ind = i + 1;\r
+        n_uncoded_errs_this_frame = 0;\r
+        %printf("%d %s %s\n", i, state, next_state);\r
+      end\r
+    end\r
+    if strcmp(state, 'count errors')\r
+      n_uncoded_errs_this_frame += xor(rx_bit_stream(i), tx_codeword(tx_frame_ind));\r
+      n_uncoded_bits++;\r
+      tx_frame_ind++;\r
+      if tx_frame_ind == (start_frame_ind+nbits_frame)\r
+        n_uncoded_errs += n_uncoded_errs_this_frame;\r
+        printf("n_uncoded_errs_this_frame: %d\n", n_uncoded_errs_this_frame);\r
+        frame_rx232_rx = rx_bit_stream(rx_frame_ind:rx_frame_ind+nbits_frame-1);\r
+        %tx_codeword(start_frame_ind+1:start_frame_ind+10)\r
+        %frame_rx232_rx(1:10)\r
+        sstv_checksum(frame_rx232_rx);\r
+        next_state = 'wait for uw';\r
+      end\r
+    end\r
+    state = next_state;\r
+  end\r
+\r
+  uncoded_ber = n_uncoded_errs/n_uncoded_bits;\r
+  printf("EbNodB: %4.1f SNRdB: %4.1f n_uncoded_bits: %d n_uncoded_errs: %d BER: %4.3f\n", \r
+          EbNodB, SNRdB, n_uncoded_bits, n_uncoded_errs, uncoded_ber);  \r
+endfunction\r
+\r
 % Start simulation --------------------------------------------------------\r
 \r
 more off;\r
@@ -355,6 +514,7 @@ if demo == 8
   f = fopen("sstv_sd.bin","wb"); fwrite(f, r, "float32"); fclose(f);\r
 end\r
 \r
+\r
 if demo == 9\r
   frames = 100;\r
   EbNodB = 11;\r
@@ -375,7 +535,7 @@ if demo == 9
   Fs = 9600; Rs=1200; EbNolin = 10 ^ (EbNodB/10);\r
   variance = (tx_pwr/2)*states.Fs/(states.Rs*EbNolin*states.bitspersymbol);\r
   noise = sqrt(variance)*randn(1,length(tx)); \r
-  printf("SNRdB meas: %4.1f\n", 10*log10(var(tx)/var(noise)));\r
+  SNRdB = 10*log10(var(tx)/var(noise));\r
   rx = tx + noise;\r
   f = fopen("fsk_demod.raw","wb"); tx = fwrite(f, rx, "short"); fclose(f);\r
  \r
@@ -385,262 +545,34 @@ if demo == 9
 end\r
 \r
 \r
-% Using simulated SSTV packet, generate complex fsk mod signals, 8-bit\r
-% unsigned IQ for feeding into demod chain\r
-% todo: [ ] uncoded BER\r
-%       [ ] octave fsk demod\r
-%       [ ] compared unsigned 8 bit IQ to regular 16-bit\r
-%       [ ] run python dashboard\r
-%       [ ] compare measured Eb/No\r
-%       [ ] drs232_ldpc has built in test frame\r
-%           [ ] use test frame that comes with code?\r
-%           [ ] Octave code to generate\r
-%           [ ] print test results\r
-%       [ ] test with resampler\r
-\r
 if demo == 10\r
-% init LDPC code\r
-\r
-load('H2064_516_sparse.mat');\r
-HRA = full(HRA);  \r
-max_iterations = 100;\r
-decoder_type = 0;\r
-mod_order = 2;\r
-\r
-code_param = ldpc_init(HRA, mod_order);\r
-tx_codeword = gen_sstv_frame;\r
-\r
-% init FSK modem\r
-\r
-fsk_horus_as_a_lib = 1;\r
-fsk_horus;\r
-states         = fsk_horus_init_hbr(9600, 8, 1200, 2, length(tx_codeword));\r
-states.ftx     = [1200 2400];\r
-states.df(1:states.M) = 0;\r
-states.dA(1:states.M) = 1;\r
-\r
-% set up AWGN channel \r
-\r
-frames = 10;\r
-EbNodB = 9;\r
-EbNo = 10^(EbNodB/10);\r
-variance = states.Fs/(states.Rs*EbNo*states.bitspersymbol);\r
-\r
-% start simulation ----------------------------------------\r
-\r
-%tx_data = round(rand(1, code_param.data_bits_per_frame));\r
-%tx_codeword = ldpc_encode(code_param, tx_data);\r
-tx_bit_stream = [];\r
-for i=1:frames+1\r
-  tx_bit_stream = [tx_bit_stream tx_codeword];\r
-end\r
-\r
-% modulate and channel model\r
-\r
-tx = fsk_horus_mod(states, tx_bit_stream);\r
-noise = sqrt(variance)*randn(length(tx),1);\r
-rx = tx + noise;\r
-printf("SNRdB meas: %4.1f\n", 10*log10(var(tx)/var(noise)));\r
-\r
-% demodulate frame by frame\r
-\r
-st = 1;\r
-run_frames = floor(length(rx)/states.N);\r
-rx_bit_stream = [];\r
-rx_sd_stream = [];\r
-for f=1:run_frames\r
-\r
-  % extract nin samples from rx sample stream\r
-\r
-  nin = states.nin;\r
-  en = st + states.nin - 1;\r
-\r
-  if en <= length(rx) % due to nin variations its possible to overrun buffer\r
-    sf = rx(st:en);\r
-    st += nin;\r
-\r
-    % demodulate to stream of bits\r
-\r
-    states.f = [1200 2400];\r
-    [rx_bits states] = fsk_horus_demod(states, sf);\r
-    rx_bit_stream = [rx_bit_stream rx_bits];\r
-    rx_sd_stream = [rx_sd_stream states.rx_bits_sd];\r
-  end\r
-end\r
-\r
-#{\r
-% measure BER, there is an annoying one bit delay in rx\r
-% which means we waste a frame\r
-\r
-n_coded_bits = n_uncoded_bits = n_coded_errs = n_uncoded_errs = 0;\r
-for f=1:run_frames\r
-  st = (f-1)*states.nbit + 2; en = st+states.nbit-1;\r
-  if en < length(rx_bit_stream)\r
-    rx_codeword =  rx_bit_stream(st:en);\r
-    n_uncoded_errs += sum(xor(tx_codeword, rx_codeword));\r
-    n_uncoded_bits += code_param.symbols_per_frame;\r
-\r
-    #{\r
-    r = rx_sd_stream(st:en);\r
-    [detected_data Niters] = ldpc_decode(r, code_param, max_iterations, decoder_type);\r
-    coded_errors = sum(xor(detected_data(1:code_param.data_bits_per_frame), tx_data));\r
-    n_coded_errs += coded_errors;\r
-    n_coded_bits += code_param.data_bits_per_frame;\r
-    printf("f: %d Niters: %d coded_errors: %d\n", f, Niters, coded_errors);\r
-    #}\r
-  end\r
-end\r
-\r
-uncoded_ber = n_uncoded_errs/n_uncoded_bits;\r
-printf("n_uncoded_bits: %d n_uncoded_errs: %d BER: %4.3f\n", n_uncoded_bits, n_uncoded_errs, uncoded_ber);\r
-%coded_ber = n_coded_errs/n_coded_bits;\r
-%printf("n_coded_bits..: %d n_coded_errs..: %d BER: %4.3f\n", n_coded_bits, n_coded_errs, coded_ber);\r
-#}\r
-\r
-  % state machine. look for UW.  When found count bit errors over one frame of bits\r
-\r
-  state = "wait for uw";\r
-  start_uw_ind = 16*10+1; end_uw_ind = start_uw_ind + 2*10 - 1;\r
-  uw_rs232 = tx_codeword(start_uw_ind:end_uw_ind); luw = length(uw_rs232);\r
-  start_frame_ind =  end_uw_ind + 1;\r
-  nbits = length(rx_bit_stream);\r
-  uw_thresh = 0;\r
-  n_uncoded_errs = 0;\r
-  n_uncoded_bits = 0;\r
-\r
-  % might as well count uncoded errors use RS232 framing bits\r
-\r
-  nbits_frame = code_param.data_bits_per_frame*10/8;  \r
-\r
-  for i=luw:nbits\r
-    next_state = state;\r
-    if strcmp(state, 'wait for uw')\r
-      uw_errs = xor(rx_bit_stream(i-luw+1:i), uw_rs232);\r
-      if uw_errs <= uw_thresh\r
-        next_state = 'count errors';\r
-        tx_frame_ind = start_frame_ind;\r
-        %printf("%d %s %s\n", i, state, next_state);\r
-      end\r
-    end\r
-    if strcmp(state, 'count errors')\r
-      n_uncoded_errs += xor(rx_bit_stream(i), tx_codeword(tx_frame_ind));\r
-      n_uncoded_bits++;\r
-      tx_frame_ind++;\r
-      if tx_frame_ind == (start_frame_ind+nbits_frame)\r
-        next_state = 'wait for uw';\r
-      end\r
-    end\r
-    state = next_state;\r
-  end\r
-\r
-uncoded_ber = n_uncoded_errs/n_uncoded_bits;\r
-printf("n_uncoded_bits: %d n_uncoded_errs: %d BER: %4.3f\n", n_uncoded_bits, n_uncoded_errs, uncoded_ber);\r
+  sim_in.frames = 3;\r
+  EbNodBvec = 9;\r
   \r
-#{\r
-  frames = 2;\r
-  EbNodB = 100;\r
-\r
-  % init LPDC code - we need code_param.symbols_per_frame to init fsk modem,\r
-  % which is actually unused as we aren't running the fsk modem lol\r
-\r
-  load('H2064_516_sparse.mat');\r
-  HRA = full(HRA);  \r
-  max_iterations = 100; decoder_type = 0; mod_order = 2;\r
-  code_param = ldpc_init(HRA, mod_order);\r
-\r
-  % init 8x oversampled FSK modulator\r
-\r
-  fsk_horus_as_a_lib = 1;\r
-  fsk_horus;\r
-  states = fsk_horus_init_hbr(9600, 8, 1200, 2, code_param.symbols_per_frame);\r
-  states.ftx = [1200 2400];\r
-  states.tx_real = 0;\r
-\r
-  % Generate some SSTV frames, note it's the same frame repeated which\r
-  % isn't ideal for testing the LDPC code but good for uncoded BER\r
-  % testing.  For testing PER best to send frames of random payload data\r
-  % and measure PER based on checksum/LDPC decoder convergence.\r
-\r
-  printf("generating LPDC encoded frames...\n");\r
-  aframe_rs232 = gen_sstv_frame;\r
-  frames_rs232 = [];\r
-  for i=1:frames\r
-    frames_rs232 = [frames_rs232 aframe_rs232];\r
-  end\r
-  nbits = length(frame_rs232); bit_rate = 115200; Nsecs = nbits/bit_rate;\r
-  printf("%d frames, %d bits, %3.1f seconds at %d bit/s\n", frames, nbits, Nsecs, bit_rate);\r
-\r
-  % FSK modulate and add noise\r
-\r
-  printf("fsk mod and AWGN noise...\n");\r
-  tx = fsk_horus_mod(states, frame_rs232);\r
-\r
-  EbNolin = 10 ^ (EbNodB/10);\r
-  tx_pwr = var(tx);\r
-  variance = (tx_pwr)*states.Fs/(states.Rs*EbNolin*states.bitspersymbol);\r
-  noise = sqrt(variance/2)*(randn(length(tx),1) + j*randn(length(tx),1)); \r
-  rx = tx + noise;\r
-\r
-  % Octave demodulator \r
-\r
-  printf("fsk demod ....\n");\r
-  st = 1;\r
-  run_frames = floor(length(rx)/states.N);\r
-  rx_bit_stream = [];\r
-  rx_sd_stream = [];\r
-  for f=1:run_frames\r
-\r
-    % extract nin samples from rx sample stream\r
-\r
-    nin = states.nin;\r
-    en = st + states.nin - 1;\r
-\r
-    if en <= length(rx) % due to nin variations its possible to overrun buffer\r
-      sf = rx(st:en);\r
-      st += nin;\r
-\r
-      % demodulate to stream of bits\r
-\r
-      states.f = [1200 2400];\r
-      [rx_bits states] = fsk_horus_demod(states, sf);\r
-      rx_bit_stream = [rx_bit_stream rx_bits];\r
-      rx_sd_stream = [rx_sd_stream states.rx_bits_sd];\r
-    end\r
+  sim_in.c_demod = 0;\r
+  ber_octave = [];\r
+  for i = 1:length(EbNodBvec)\r
+    [n_uncoded_errs n_uncoded_bits] = run_sstv_sim(sim_in, EbNodBvec(i));\r
+    ber_octave(i) = n_uncoded_errs/n_uncoded_bits;\r
   end\r
 \r
-  % arrange into 10 bit columns\r
-\r
-  % look for SSTV packet UW\r
-\r
-  uw_rs232 = aframe_rs232(16*10+1:18*10); luw = length(uw_rs232);\r
-  nbits = length(rx_bit_stream);\r
-  uw_score = zeros(1, nbits);\r
-  for i=1:nbits-luw\r
-    errs = xor(rx_bit_stream(i:i+luw-1), uw_rs232);\r
-    uw_score(i) = sum(errs);\r
+  sim_in.c_demod = 1;\r
+  ber_c = [];\r
+  for i = 1:length(EbNodBvec)\r
+    [n_uncoded_errs n_uncoded_bits] = run_sstv_sim(sim_in, EbNodBvec(i));\r
+    ber_c(i) = n_uncoded_errs/n_uncoded_bits;\r
   end\r
-  figure(1)\r
-  clf\r
-  plot(uw_score)\r
 \r
-  % pass through drs232 to provide UW sync \r
-\r
-#}\r
-#{\r
-  % determine raw BER from rx_frames\r
-\r
-  % read in test frame output from drs232_ldpc, determine raw BER\r
-\r
-\r
-  % Use Octave demodulator to check uncoded BER and PER.  Use UW to sync.\r
-  % Idea - how abt using C code for this?  Or just copy state machine logic\r
-  % here.  Wld need drs232_ldpc to have known test frame.  Hmmm... that is a \r
-  % great idea for testing ... especially repeats across different rx set-ups.ñ\r
\r
-  printf("save IQ to disk...\n");\r
   figure(1);\r
-  subplot(211); plot(real(rx(1:1000)));\r
-  subplot(212); plot(imag(rx(1:1000)));\r
-  save_hackrf("fsk_modiq.raw", rx);\r
-#}\r
+  clf;\r
+  semilogy(EbNodBvec,  ber_octave, '+-;Octave demod;')\r
+  grid;\r
+  xlabel('Eb/No (dB)')\r
+  ylabel('BER')\r
+\r
+  hold on;\r
+  semilogy(EbNodBvec,  ber_c, 'g+-;C demod;')\r
+  legend("boxoff");\r
+  hold off;\r
 end\r
+\r