# snr_curves.sh
#
# Library of bash functions to generate data for SNR curves.
#
# testing a function example:
#   $ bash -c "source ./snr_curves.sh; generate_octave_tx_data datac0 awgn"

set -x

PATH=${PATH}:${HOME}/codec2/build_linux/src
CODEC2=${HOME}/codec2
FADING_DIR=${CODEC2}/build_linux/unittest

snr_list='-5 -4 -3 -2 0 1 2 4'
No_list='-13 -14 -15 -16 -18 -20 -22 -24 -26'
Nbursts_awgn=20
Nbursts_mpp=100

# Octave Tx injects noise and is source of truth for SNR, measure BER/PER v SNR
function generate_octave_tx_data {
  mode=$1
  channel=$2
  
  Nbursts=$Nbursts_awgn
  snr_nudge=0
  if [ "$channel" == "mpp" ]; then
    Nbursts=$Nbursts_mpp
    snr_nudge=4  
  fi
  
  rx_log=$(mktemp)

  i=1
  rm -f snr_oct_${mode}_${channel}*.txt
  rm -f ber_oct_${mode}_${channel}*.txt
  rm -f per_oct_${mode}_${channel}*.txt
  for snr in $snr_list
  do
    snr_adj=$((${snr}+${snr_nudge}))  
    echo "warning ('off', 'Octave:data-file-in-path');
    ofdm_ldpc_tx('test_${mode}.raw','${mode}',1,${snr_adj},'${channel}','bursts',${Nbursts},'crc');
    quit" | DISPLAY="" octave-cli -p ${CODEC2}/octave
    freedv_data_raw_rx --testframes $mode test_${mode}.raw /dev/null 2>${rx_log} -v
    BERmeas=$(cat ${rx_log} | grep 'BER......:' | cut -d' ' -f2)
    PERmeas=$(cat ${rx_log} | grep 'Coded FER' | cut -d' ' -f3)
    
    echo ${snr_adj} >> snr_oct_${mode}_${channel}.txt
    echo ${BERmeas} >> ber_oct_${mode}_${channel}.txt
    echo ${PERmeas} >> per_oct_${mode}_${channel}.txt
    i=$((i+1))
  done
  echo 0 > offset_oct_${mode}_${channel}.txt
}

# ch injects noise and is source of truth for SNR, measure BER/PER v SNR
# Octave Tx
function generate_ch_data {
  mode=$1
  channel=$2

  ch_multipath=''
  Nbursts=$Nbursts_awgn
  snr_nudge=0  
  if [ "$channel" == "mpp" ]; then
    ch_multipath='--mpp'
    Nbursts=$Nbursts_mpp
    snr_nudge=4  
  fi    
    
  octave_log=$(mktemp)
  ch_log=$(mktemp)
  rx_log=$(mktemp)

  i=1
  rm -f snr_ch_${mode}_${channel}*.txt
  rm -f ber_ch_${mode}_${channel}*.txt
  rm -f per_ch_${mode}_${channel}*.txt
  for No in $No_list
  do
    No_adj=$((${No}-${snr_nudge}))  
    echo "warning ('off', 'Octave:data-file-in-path');
    ofdm_ldpc_tx('test_${mode}.raw','${mode}',1,100,'awgn','bursts',${Nbursts},'crc'); 
    quit" | DISPLAY="" octave-cli -p ${CODEC2}/octave 1>${octave_log}
    SNRoffset=$(cat ${octave_log} | grep 'Burst offset:' | cut -d' ' -f5)
    
    ch test_${mode}.raw - --No $No_adj ${ch_multipath} --fading_dir ${FADING_DIR} 2>>${ch_log} | \
    freedv_data_raw_rx --testframes $mode - /dev/null -v 2>${rx_log}
    BERmeas=$(cat ${rx_log} | grep 'BER......:' | cut -d' ' -f2)
    PERmeas=$(cat ${rx_log} | grep 'Coded FER' | cut -d' ' -f3)
    
    echo ${BERmeas} >> ber_ch_${mode}_${channel}.txt
    echo ${PERmeas} >> per_ch_${mode}_${channel}.txt
    i=$((i+1))

    # trap not enough fading file samples (with mpp)
    grep "Fading file finished" ${ch_log}
    if [ $? -eq 0 ]; then
        cat ${ch_log}
        exit 1
    fi
  done
  
  echo ${SNRoffset} > offset_ch_${mode}_${channel}.txt
  SNRch=$(cat ${ch_log} | grep SNR3k | tr -s ' ' | cut -d' ' -f3)
  echo ${SNRch} > snr_ch_${mode}_${channel}.txt
}

# ch injects noise and is source of truth for SNR, measure BER/PER v SNR and
# SNR estimates v SNR from rx, C Tx
function generate_snrest_v_snr_data {
  mode=$1
  channel=$2  

  snr_nudge=0
  aNo_list=$No_list

  # nudge SNR test range to get meaningful results for these tests  
  if [ "$mode" == "datac1" ]; then
    snr_nudge=4
  fi
  if [[ "$mode" == "datac4" || "$mode" == "datac13" || "$mode" == "datac14" ]]; then
    snr_nudge=-6
  fi
  
  ch_multipath=''
  Nbursts=$Nbursts_awgn
  if [ "$channel" == "mpp" ]; then
    ch_multipath='--mpp'
    Nbursts=$Nbursts_mpp
    snr_nudge=$((${snr_nudge}+4))
  fi    

  clip=0
  id='ctx'
  if [ "$#" -eq 3 ]; then
    clip=$3
    id='ctxc'
    snr_nudge=$((${snr_nudge}-4))
  fi

  tx_log=$(mktemp)
  ch_log=$(mktemp)
  rx_log=$(mktemp)

  i=1
  rm -f snrest_${id}_${mode}_${channel}*.txt
  rm -f ber_${id}_${mode}_${channel}*.txt
  rm -f per_${id}_${mode}_${channel}*.txt
  for No in $aNo_list
  do
    No_adj=$((${No}-${snr_nudge}))  
    freedv_data_raw_tx --clip ${clip} --delay 1000 --txbpf ${clip} --bursts $Nbursts --testframes $Nbursts $mode /dev/zero - 2>${tx_log} | \
    ch - - --No $No_adj ${ch_multipath} --fading_dir ${FADING_DIR} 2>>${ch_log} | \
    freedv_data_raw_rx --testframes $mode - /dev/null 2>${rx_log} -v
    SNRoffset=$(cat ${tx_log} | grep "mark:space" | tr -s ' ' | cut -d' ' -f 5)
   
    SNRest=$(cat ${rx_log} | grep '\-BS\-' | tr -s ' ' | cut -d' ' -f17)
    if [ ! -z "$SNRest" ]; then
      echo ${SNRest} > snrest_${id}_${mode}_${channel}_${i}.txt
    fi
    BERmeas=$(cat ${rx_log} | grep 'BER......:' | cut -d' ' -f2)
    PERmeas=$(cat ${rx_log} | grep 'Coded FER' | cut -d' ' -f3)
    echo ${BERmeas} >> ber_${id}_${mode}_${channel}.txt
    echo ${PERmeas} >> per_${id}_${mode}_${channel}.txt
    i=$((i+1))
  done

  echo ${SNRoffset} > offset_${id}_${mode}_${channel}.txt
 
  # trap not enough fading file samples (with mpp)
  grep "Fading file finished" ${ch_log}
  if [ $? -eq 0 ]; then
      cat ${ch_log}
      exit 1
  fi
  SNRch=$(cat ${ch_log} | grep SNR3k | tr -s ' ' | cut -d' ' -f3)
  echo ${SNRch} > snr_${id}_${mode}_${channel}.txt
}

# Sanity check to make sure Octave/CML is set up OK
function test_ldpc {
  echo "ldpcut; quit" | DISPLAY="" octave-cli -p ${CODEC2}/octave
  if [ "$?" -ne 0 ]; then
    echo "basic octave test failed, you may need to"
    echo "(a) run ctests to create build_xxx/cml"
    echo "(b) set up ~/.octaverc as per octave/ldpc.m"
    exit 1
  else
      echo "OK"
  fi
}
