From bd8afdee52374ca63ea5e8595fb518075f31acf8 Mon Sep 17 00:00:00 2001 From: David Date: Thu, 12 Dec 2024 13:15:28 +1030 Subject: [PATCH 01/20] wip ASR - building up test framework --- asr_test.sh | 82 +++++++++++++++++++++++++++++ asr_wer.py | 145 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 227 insertions(+) create mode 100755 asr_test.sh create mode 100644 asr_wer.py diff --git a/asr_test.sh b/asr_test.sh new file mode 100755 index 0000000..368f1c8 --- /dev/null +++ b/asr_test.sh @@ -0,0 +1,82 @@ +#!/usr/bin/env bash +# asr_test.sh +# +# Automatic Speech Recognition (ASR) testing for the Radio Autoencoder + +CODEC2_DEV=${CODEC2_DEV:-${HOME}/codec2-dev} +PATH=${PATH}:${CODEC2_DEV}/build_linux/src:${CODEC2_DEV}/build_linux/misc:${PWD}/build/src + +which ch >/dev/null || { printf "\n**** Can't find ch - check CODEC2_PATH **** \n\n"; exit 1; } + +source utils.sh + +function print_help { + echo + echo "Automated Speech Recognition (ASR) testing for the Radio Autoencoder" + echo + echo " usage ./asr_test.sh path/to/source dest [test option below]" + echo " usage ./ota_test.sh ~/.cache/LibriSpeech/test-clean ~/.cache/LibriSpeech/test-awgn-2dB --awgn 2" + echo + echo " --awgn SNRdB AWGN channel simulation" + echo " -d verbose debug information" + exit +} + +POSITIONAL=() +while [[ $# -gt 0 ]] +do +key="$1" +case $key in + --awgn) + awgn_snr_dB="$2" + shift + shift + ;; + -d) + set -x; + shift + ;; + -h) + print_help + ;; + *) + POSITIONAL+=("$1") # save it in an array for later + shift + ;; +esac +done +set -- "${POSITIONAL[@]}" # restore positional parameters + +if [ $# -lt 2 ]; then + print_help +fi + +source=$1 +dest=$2 + +# cp translation files to new test directory +function cp_translation_files { + pushd $source; trans=$(find . -name '*.txt'); popd + for f in $trans + do + d=$(dirname $f) + mkdir -p ${dest}/${d} + cp ${source}/${f} ${dest}/${f} + done +} + +# process audio files and place in new test directory +function process { + pushd $source; flac=$(find . -name '*.flac'); popd + for f in $flac + do + d=$(dirname $f) + mkdir -p ${dest}/${d} + sox ${source}/${f} -t .s16 -r 8000 - | ch - - --No -30 | sox -t .s16 -r 8000 -c 1 - -r 16000 ${dest}/${f} + done + pwd +} + +process +#mkidr -p ${test-clean} + diff --git a/asr_wer.py b/asr_wer.py new file mode 100644 index 0000000..553f3ac --- /dev/null +++ b/asr_wer.py @@ -0,0 +1,145 @@ +# coding: utf-8 + +# # Installing Whisper +# +# The commands below will install the Python packages needed to use Whisper models and evaluate the transcription results. + +# In[1]: + + +#get_ipython().system(' pip install git+https://github.com/openai/whisper.git') +#get_ipython().system(' pip install jiwer') + + +# # Loading the LibriSpeech dataset +# +# The following will load the test-clean split of the LibriSpeech corpus using torchaudio. + +# In[2]: + + +import os,argparse +import numpy as np + +#try: +# import tensorflow # required in Colab to avoid protobuf compatibility issues +#except ImportError: +# pass + +import torch +import pandas as pd +import whisper +import torchaudio + +from tqdm.notebook import tqdm + + +DEVICE = "cuda" if torch.cuda.is_available() else "cpu" + + +# In[3]: + + +class LibriSpeech(torch.utils.data.Dataset): + """ + A simple class to wrap LibriSpeech and trim/pad the audio to 30 seconds. + It will drop the last few seconds of a very small portion of the utterances. + """ + def __init__(self, split="test-clean", device=DEVICE): + self.dataset = torchaudio.datasets.LIBRISPEECH( + root=os.path.expanduser("~/.cache"), + url=split, + download=True, + ) + self.device = device + + def __len__(self): + return len(self.dataset) + + def __getitem__(self, item): + audio, sample_rate, text, _, _, _ = self.dataset[item] + assert sample_rate == 16000 + audio = whisper.pad_or_trim(audio.flatten()).to(self.device) + mel = whisper.log_mel_spectrogram(audio) + + return (mel, text) + + +parser = argparse.ArgumentParser() +parser.add_argument('--test_name', default="test-clean", type=str, help='Librispeech test name') +args = parser.parse_args() + +print("start"); +dataset = LibriSpeech(args.test_name) +print("dataset") +loader = torch.utils.data.DataLoader(dataset, batch_size=16) +print("loader") + +# # Running inference on the dataset using a base Whisper model +# +# The following will take a few minutes to transcribe all utterances in the dataset. + +# In[5]: + + +model = whisper.load_model("base.en") +print( + f"Model is {'multilingual' if model.is_multilingual else 'English-only'} " + f"and has {sum(np.prod(p.shape) for p in model.parameters()):,} parameters." +) + + +# In[6]: + + +# predict without timestamps for short-form transcription +options = whisper.DecodingOptions(language="en", without_timestamps=True) + + +# In[7]: + + +hypotheses = [] +references = [] + +for mels, texts in loader: + results = model.decode(mels, options) + hypotheses.extend([result.text for result in results]) + references.extend(texts) + + +# In[8]: + + +data = pd.DataFrame(dict(hypothesis=hypotheses, reference=references)) +data + + +# # Calculating the word error rate +# +# Now, we use our English normalizer implementation to standardize the transcription and calculate the WER. + +# In[9]: + + +import jiwer +from whisper.normalizers import EnglishTextNormalizer + +normalizer = EnglishTextNormalizer() + + +# In[10]: + + +data["hypothesis_clean"] = [normalizer(text) for text in data["hypothesis"]] +data["reference_clean"] = [normalizer(text) for text in data["reference"]] +print(data) + + +# In[11]: + + +wer = jiwer.wer(list(data["reference_clean"]), list(data["hypothesis_clean"])) + +print(f"WER: {wer * 100:.2f} %") + From 67a669f2bf57f9fe03592735c729c6829bd87065 Mon Sep 17 00:00:00 2001 From: David Date: Fri, 13 Dec 2024 12:29:12 +1030 Subject: [PATCH 02/20] processing a subset of dataset --- asr_test.sh | 40 +++++++++++++++++++++--------- asr_wer.py | 71 ++++++----------------------------------------------- 2 files changed, 35 insertions(+), 76 deletions(-) diff --git a/asr_test.sh b/asr_test.sh index 368f1c8..a858dd1 100755 --- a/asr_test.sh +++ b/asr_test.sh @@ -1,7 +1,9 @@ #!/usr/bin/env bash # asr_test.sh # -# Automatic Speech Recognition (ASR) testing for the Radio Autoencoder +# Automatic Speech Recognition (ASR) testing for the Radio Autoencoder. This script +# takes the samples from a clean dataset (e.g. Librispeech test-clean), and generates +# a dataset with channel simulations (RADE, SSB etc) applied. CODEC2_DEV=${CODEC2_DEV:-${HOME}/codec2-dev} PATH=${PATH}:${CODEC2_DEV}/build_linux/src:${CODEC2_DEV}/build_linux/misc:${PWD}/build/src @@ -12,16 +14,19 @@ source utils.sh function print_help { echo - echo "Automated Speech Recognition (ASR) testing for the Radio Autoencoder" + echo "Automated Speech Recognition (ASR) dataset processing for Radio Autoencoder testing" echo echo " usage ./asr_test.sh path/to/source dest [test option below]" echo " usage ./ota_test.sh ~/.cache/LibriSpeech/test-clean ~/.cache/LibriSpeech/test-awgn-2dB --awgn 2" echo echo " --awgn SNRdB AWGN channel simulation" + echo " -n numSamples number of dataset samples to process (default all)" echo " -d verbose debug information" exit } +n_samples=0 + POSITIONAL=() while [[ $# -gt 0 ]] do @@ -32,6 +37,11 @@ case $key in shift shift ;; + -n) + n_samples="$2" + shift + shift + ;; -d) set -x; shift @@ -54,29 +64,35 @@ fi source=$1 dest=$2 -# cp translation files to new test directory +# cp translation files to new dataset directory function cp_translation_files { pushd $source; trans=$(find . -name '*.txt'); popd for f in $trans do - d=$(dirname $f) - mkdir -p ${dest}/${d} - cp ${source}/${f} ${dest}/${f} + d=$(dirname $f) + mkdir -p ${dest}/${d} + cp ${source}/${f} ${dest}/${f} done } -# process audio files and place in new test directory +# process audio files and place in new dataset directory function process { pushd $source; flac=$(find . -name '*.flac'); popd + if [ $n_samples -ne 0 ]; then + flac=$(echo "$flac" | head -n $n_samples) + fi + + n=$(echo "$flac" | wc -l) + printf "Processing %d samples in dataset\n" $n + for f in $flac do - d=$(dirname $f) - mkdir -p ${dest}/${d} - sox ${source}/${f} -t .s16 -r 8000 - | ch - - --No -30 | sox -t .s16 -r 8000 -c 1 - -r 16000 ${dest}/${f} + d=$(dirname $f) + mkdir -p ${dest}/${d} + sox ${source}/${f} -t .s16 -r 8000 - | ch - - --No -30 | sox -t .s16 -r 8000 -c 1 - -r 16000 ${dest}/${f} done - pwd } +#cp_translation_files process -#mkidr -p ${test-clean} diff --git a/asr_wer.py b/asr_wer.py index 553f3ac..7b5b26b 100644 --- a/asr_wer.py +++ b/asr_wer.py @@ -1,45 +1,18 @@ # coding: utf-8 -# # Installing Whisper -# -# The commands below will install the Python packages needed to use Whisper models and evaluate the transcription results. - -# In[1]: - - -#get_ipython().system(' pip install git+https://github.com/openai/whisper.git') -#get_ipython().system(' pip install jiwer') - - -# # Loading the LibriSpeech dataset -# -# The following will load the test-clean split of the LibriSpeech corpus using torchaudio. - -# In[2]: - +# derived from: https://github.com/openai/whisper/blob/main/notebooks/LibriSpeech.ipynb import os,argparse import numpy as np - -#try: -# import tensorflow # required in Colab to avoid protobuf compatibility issues -#except ImportError: -# pass - import torch import pandas as pd import whisper import torchaudio - from tqdm.notebook import tqdm - DEVICE = "cuda" if torch.cuda.is_available() else "cpu" -# In[3]: - - class LibriSpeech(torch.utils.data.Dataset): """ A simple class to wrap LibriSpeech and trim/pad the audio to 30 seconds. @@ -66,39 +39,25 @@ def __getitem__(self, item): parser = argparse.ArgumentParser() -parser.add_argument('--test_name', default="test-clean", type=str, help='Librispeech test name') +parser.add_argument('test_name', type=str, help='Librispeech dataset name (e.g. test-clean)') +parser.add_argument('-n', type=str, help='Number of dataset sntries to use (default all of them)') args = parser.parse_args() -print("start"); dataset = LibriSpeech(args.test_name) -print("dataset") -loader = torch.utils.data.DataLoader(dataset, batch_size=16) -print("loader") - -# # Running inference on the dataset using a base Whisper model -# -# The following will take a few minutes to transcribe all utterances in the dataset. - -# In[5]: - +if args.n: + dataset = torch.utils.data.Subset(dataset,list(range(0,int(args.n)))) +print("dataset length:", dataset.__len__()) +loader = torch.utils.data.DataLoader(dataset, batch_size=16) model = whisper.load_model("base.en") print( f"Model is {'multilingual' if model.is_multilingual else 'English-only'} " f"and has {sum(np.prod(p.shape) for p in model.parameters()):,} parameters." ) - -# In[6]: - - # predict without timestamps for short-form transcription options = whisper.DecodingOptions(language="en", without_timestamps=True) - -# In[7]: - - hypotheses = [] references = [] @@ -107,38 +66,22 @@ def __getitem__(self, item): hypotheses.extend([result.text for result in results]) references.extend(texts) - -# In[8]: - - data = pd.DataFrame(dict(hypothesis=hypotheses, reference=references)) -data # # Calculating the word error rate # # Now, we use our English normalizer implementation to standardize the transcription and calculate the WER. -# In[9]: - - import jiwer from whisper.normalizers import EnglishTextNormalizer normalizer = EnglishTextNormalizer() - -# In[10]: - - data["hypothesis_clean"] = [normalizer(text) for text in data["hypothesis"]] data["reference_clean"] = [normalizer(text) for text in data["reference"]] print(data) - -# In[11]: - - wer = jiwer.wer(list(data["reference_clean"]), list(data["hypothesis_clean"])) print(f"WER: {wer * 100:.2f} %") From eb887d1cb8949d0f8f345d1410640b112d179e3f Mon Sep 17 00:00:00 2001 From: David Date: Fri, 13 Dec 2024 13:28:16 +1030 Subject: [PATCH 03/20] agc and hilbert compression --- asr_test.sh | 36 ++++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/asr_test.sh b/asr_test.sh index a858dd1..53f227a 100755 --- a/asr_test.sh +++ b/asr_test.sh @@ -17,23 +17,26 @@ function print_help { echo "Automated Speech Recognition (ASR) dataset processing for Radio Autoencoder testing" echo echo " usage ./asr_test.sh path/to/source dest [test option below]" - echo " usage ./ota_test.sh ~/.cache/LibriSpeech/test-clean ~/.cache/LibriSpeech/test-awgn-2dB --awgn 2" + echo " usage ./ota_test.sh ~/.cache/LibriSpeech/test-clean ~/.cache/LibriSpeech/test-awgn-2dB --No -30" echo - echo " --awgn SNRdB AWGN channel simulation" + echo " --No NodB ch channel simulation No value (experiment to get desired SNR)" echo " -n numSamples number of dataset samples to process (default all)" echo " -d verbose debug information" exit } n_samples=0 +No=-100 +setpoint_rms=2048 +comp_gain=6 POSITIONAL=() while [[ $# -gt 0 ]] do key="$1" case $key in - --awgn) - awgn_snr_dB="$2" + --No) + No="$2" shift shift ;; @@ -63,6 +66,7 @@ fi source=$1 dest=$2 +rm -Rf $dest # cp translation files to new dataset directory function cp_translation_files { @@ -85,14 +89,34 @@ function process { n=$(echo "$flac" | wc -l) printf "Processing %d samples in dataset\n" $n + in=t.raw + comp=comp.raw + ch_log=ch_log.txt + snr_log=snr_log.txt + rm -f ${snr_log} for f in $flac do d=$(dirname $f) mkdir -p ${dest}/${d} - sox ${source}/${f} -t .s16 -r 8000 - | ch - - --No -30 | sox -t .s16 -r 8000 -c 1 - -r 16000 ${dest}/${f} + sox ${source}/${f} -t .s16 -r 8000 ${in} + # AGC and Hilbert compression + set_rms ${in} $setpoint_rms + analog_compressor ${in} ${comp} ${comp_gain} + papr=$(measure_cpapr ${comp}) + ch ${comp} - --No ${No} 2>${ch_log} | sox -t .s16 -r 8000 -c 1 - -r 16000 ${dest}/${f} + snr=$(cat $ch_log | grep "SNR3k" | tr -s ' ' | cut -d' ' -f3) + snr=$(cat $ch_log | grep "SNR3k" | tr -s ' ' | cut -d' ' -f3) + echo $snr >> ${snr_log} + echo ${dest}/${f} ${snr} ${papr} done + python3 < Date: Sun, 15 Dec 2024 09:48:00 +1030 Subject: [PATCH 04/20] first pass at RADE prcoessing, a bit slow --- asr_test.sh | 46 ++++++++++++++++++++++++++++++---------------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/asr_test.sh b/asr_test.sh index 53f227a..e5cf30b 100755 --- a/asr_test.sh +++ b/asr_test.sh @@ -29,6 +29,7 @@ n_samples=0 No=-100 setpoint_rms=2048 comp_gain=6 +mode="rade_inf" POSITIONAL=() while [[ $# -gt 0 ]] @@ -89,7 +90,7 @@ function process { n=$(echo "$flac" | wc -l) printf "Processing %d samples in dataset\n" $n - in=t.raw + in=in.raw comp=comp.raw ch_log=ch_log.txt snr_log=snr_log.txt @@ -98,22 +99,35 @@ function process { do d=$(dirname $f) mkdir -p ${dest}/${d} - sox ${source}/${f} -t .s16 -r 8000 ${in} - # AGC and Hilbert compression - set_rms ${in} $setpoint_rms - analog_compressor ${in} ${comp} ${comp_gain} - papr=$(measure_cpapr ${comp}) - ch ${comp} - --No ${No} 2>${ch_log} | sox -t .s16 -r 8000 -c 1 - -r 16000 ${dest}/${f} - snr=$(cat $ch_log | grep "SNR3k" | tr -s ' ' | cut -d' ' -f3) - snr=$(cat $ch_log | grep "SNR3k" | tr -s ' ' | cut -d' ' -f3) - echo $snr >> ${snr_log} - echo ${dest}/${f} ${snr} ${papr} + if [ $mode == "ssb" ]; then + sox ${source}/${f} -t .s16 -r 8000 ${in} + # AGC and Hilbert compression + set_rms ${in} $setpoint_rms + analog_compressor ${in} ${comp} ${comp_gain} + papr=$(measure_cpapr ${comp}) + ch ${comp} - --No ${No} 2>${ch_log} | sox -t .s16 -r 8000 -c 1 - -r 16000 ${dest}/${f} + snr=$(cat $ch_log | grep "SNR3k" | tr -s ' ' | cut -d' ' -f3) + echo $snr >> ${snr_log} + echo ${dest}/${f} ${snr} ${papr} +# python3 < Date: Mon, 16 Dec 2024 09:35:35 +1030 Subject: [PATCH 05/20] WIP single file rade processing, basic framework OK --- asr_test.sh | 97 ++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 66 insertions(+), 31 deletions(-) diff --git a/asr_test.sh b/asr_test.sh index e5cf30b..cb08d54 100755 --- a/asr_test.sh +++ b/asr_test.sh @@ -80,6 +80,15 @@ function cp_translation_files { done } +function mean_text_file { + file_name=#1 + python3 <${ch_log} | sox -t .s16 -r 8000 -c 1 - -r 16000 ${dest}/${f} - snr=$(cat $ch_log | grep "SNR3k" | tr -s ' ' | cut -d' ' -f3) - echo $snr >> ${snr_log} - echo ${dest}/${f} ${snr} ${papr} -# python3 <${ch_log} | sox -t .s16 -r 8000 -c 1 - -r 16000 ${dest}/${f} + snr=$(cat $ch_log | grep "SNR3k" | tr -s ' ' | cut -d' ' -f3) + echo $snr >> ${snr_log} + echo ${dest}/${f} ${snr} ${papr} + print_mean_text_file ${snr_log} + done + fi + + if [ $mode == "rade_inf" ]; then + # find length of each file + duration_log="" + flac_full="" + pushd $source; + for f in $flac + do + duration_log+=$(sox --info -D ${f}) + duration_log+=" " + flac_full+=${source}/${f} + flac_full+=" " + done + popd; + + # cat samples into one long input file + sox $flac_full -t .s16 ${in} + + # process all samples as one file to save time + ./inference.sh model19_check3/checkpoints/checkpoint_epoch_100.pth ${in} out.wav \ + --rate_Fs --pilots --pilot_eq --eq_ls --cp 0.004 --bottleneck 3 --auxdata + + # extract individual output files + duration_array=( ${duration_log} ) + i=0 + st=0 + for f in $flac + do + dur=${duration_array[i]} + printf "%4d %s %5.2f %5.2f\n" $i $f $st $dur + ((i++)) + if [ $i -eq ${#duration_array[@]} ]; then + sox out.wav ${dest}/${f} trim $st + else + sox out.wav ${dest}/${f} trim $st $dur + fi + st=$(python3 -c "print($st + $dur)") + done + fi } From 465b347227fa7b7027a23f67d080082459d93cc5 Mon Sep 17 00:00:00 2001 From: David Date: Wed, 18 Dec 2024 09:54:00 +1030 Subject: [PATCH 06/20] plotting AWGN and MPP curves --- asr_test.sh | 112 ++++++++++++++++++++++++++++++++++---------- asr_test_awgn.sh | 30 ++++++++++++ multipath_samples.m | 14 +++--- radae_plots.m | 32 +++++++++++++ 4 files changed, 156 insertions(+), 32 deletions(-) create mode 100755 asr_test_awgn.sh diff --git a/asr_test.sh b/asr_test.sh index cb08d54..d9d5f15 100755 --- a/asr_test.sh +++ b/asr_test.sh @@ -16,31 +16,59 @@ function print_help { echo echo "Automated Speech Recognition (ASR) dataset processing for Radio Autoencoder testing" echo - echo " usage ./asr_test.sh path/to/source dest [test option below]" - echo " usage ./ota_test.sh ~/.cache/LibriSpeech/test-clean ~/.cache/LibriSpeech/test-awgn-2dB --No -30" + echo " usage ./asr_test.sh ssb|rade [test option below]" + echo " usage ./ota_test.sh ssb --No -30" + echo " usage ./ota_test.sh rade --EbNodB 10" echo + echo " --EbNodB EbNodB inference.py simulation noise level (experiment to get desired SNR)" echo " --No NodB ch channel simulation No value (experiment to get desired SNR)" echo " -n numSamples number of dataset samples to process (default all)" + echo " --results resultsFile name of results file (deafult results.txt)" echo " -d verbose debug information" exit } n_samples=0 No=-100 +EbNodB=100 setpoint_rms=2048 comp_gain=6 -mode="rade_inf" +results=asr_results.txt +inference_args="" +ch_args="" POSITIONAL=() while [[ $# -gt 0 ]] do key="$1" case $key in + --EbNodB) + EbNodB="$2" + shift + shift + ;; + --g_file) + g_file="$2" + if [ ! -f $2 ]; then + echo "can't find $2" + exit 1 + fi + inference_args="${inference_args} --g_file ${2}" + cp ${2} fast_fading_samples.float + ch_args="${ch_args} --fading_dir . --mpp --gain 0.5" + shift + shift + ;; --No) No="$2" shift shift ;; + --results) + results="$2" + shift + shift + ;; -n) n_samples="$2" shift @@ -61,17 +89,23 @@ esac done set -- "${POSITIONAL[@]}" # restore positional parameters -if [ $# -lt 2 ]; then +if [ $# -lt 1 ]; then print_help fi +mode=$1 -source=$1 -dest=$2 +source=~/.cache/LibriSpeech/test-clean +if [ ! -d $source ]; then + echo "cant find Librispeech source directory" $source + exit 1 +fi +# results must be written to a directory known by Librispeech package (can't be any name) +dest=~/.cache/LibriSpeech/test-other rm -Rf $dest # cp translation files to new dataset directory function cp_translation_files { - pushd $source; trans=$(find . -name '*.txt'); popd + pushd $source > /dev/null; trans=$(find . -name '*.txt'); popd > /dev/null for f in $trans do d=$(dirname $f) @@ -80,18 +114,18 @@ function cp_translation_files { done } -function mean_text_file { - file_name=#1 - python3 < /dev/null; flac=$(find . -name '*.flac'); popd > /dev/null if [ $n_samples -ne 0 ]; then flac=$(echo "$flac" | head -n $n_samples) fi @@ -102,11 +136,16 @@ function process { in=in.raw comp=comp.raw ch_log=ch_log.txt + rade_log=rade_log.txt snr_log=snr_log.txt + asr_log=asr.txt rm -f ${snr_log} + CNo_log=CNo_log.txt + rm -f ${CNo_log} if [ $mode == "ssb" ]; then - + + fading_adv=0 for f in $flac do d=$(dirname $f) @@ -114,17 +153,27 @@ function process { sox ${source}/${f} -t .s16 -r 8000 ${in} # AGC and Hilbert compression set_rms ${in} $setpoint_rms - analog_compressor ${in} ${comp} ${comp_gain} - papr=$(measure_cpapr ${comp}) - ch ${comp} - --No ${No} 2>${ch_log} | sox -t .s16 -r 8000 -c 1 - -r 16000 ${dest}/${f} + analog_compressor ${in} ${comp} ${comp_gain} 2>/dev/null + ch ${comp} - --No ${No} ${ch_args} --fading_adv ${fading_adv} 2>${ch_log} | sox -t .s16 -r 8000 -c 1 - -r 16000 ${dest}/${f} + grep "Fading file finished" $ch_log + if [ $? -eq 0 ]; then + echo "Error - fading file too short after" $fading_adv " seconds" + exit 1 + fi snr=$(cat $ch_log | grep "SNR3k" | tr -s ' ' | cut -d' ' -f3) + CNo=$(cat $ch_log | grep "SNR3k" | tr -s ' ' | cut -d' ' -f5) echo $snr >> ${snr_log} - echo ${dest}/${f} ${snr} ${papr} - print_mean_text_file ${snr_log} + echo $CNo >> ${CNo_log} + + # advance through fading simulation file + dur=$(sox --info -D ${source}/${f}) + fading_adv=$(python3 -c "print(${fading_adv} + ${dur})") done - fi + SNR_mean=$(print_mean_text_file ${snr_log}) + CNo_mean=$(print_mean_text_file ${CNo_log}) + fi - if [ $mode == "rade_inf" ]; then + if [ $mode == "rade" ]; then # find length of each file duration_log="" flac_full="" @@ -143,7 +192,16 @@ function process { # process all samples as one file to save time ./inference.sh model19_check3/checkpoints/checkpoint_epoch_100.pth ${in} out.wav \ - --rate_Fs --pilots --pilot_eq --eq_ls --cp 0.004 --bottleneck 3 --auxdata + --rate_Fs --pilots --pilot_eq --eq_ls --cp 0.004 --bottleneck 3 --auxdata --time_offset -16 \ + --EbNodB $EbNodB ${inference_args} | tee ${rade_log} + grep "Multipath Doppler spread file too short" $rade_log + if [ $? -eq 0 ]; then + echo "Error - fading file too short" + exit 1 + fi + + SNR_mean=$(cat $rade_log | grep "Measured" | tr -s ' ' | cut -d' ' -f4) + CNo_mean=$(cat $rade_log | grep "Measured" | tr -s ' ' | cut -d' ' -f3) # extract individual output files duration_array=( ${duration_log} ) @@ -152,7 +210,7 @@ function process { for f in $flac do dur=${duration_array[i]} - printf "%4d %s %5.2f %5.2f\n" $i $f $st $dur + #printf "%4d %s %5.2f %5.2f\n" $i $f $st $dur ((i++)) if [ $i -eq ${#duration_array[@]} ]; then sox out.wav ${dest}/${f} trim $st @@ -163,7 +221,9 @@ function process { done fi - + python3 asr_wer.py test-other -n $n_samples | tee > $asr_log + wer=$(tail -n1 $asr_log | tr -s ' ' | cut -d' ' -f2) + printf "%-4s %5.2f %5.2f %5.2f\n" $mode $SNR_mean $CNo_mean $wer | tee -a $results } cp_translation_files diff --git a/asr_test_awgn.sh b/asr_test_awgn.sh new file mode 100755 index 0000000..64779cb --- /dev/null +++ b/asr_test_awgn.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +# asr_test_awgn.sh +# +# Top level ASR test script for AWGN channels + +results_file=241217_asr_awgn +n=100 + +function ssb_awgn { + No_range="-100 -39 -36 -33 -30 -25 -20" + for No in $No_range + do + ./asr_test.sh ssb --No $No -n $n --results ${results_file}_ssb.txt + done + cat ${results_file}_ssb.txt | grep ssb | sed -e "s/ssb//" > tmp.txt + mv tmp.txt ${results_file}_ssb.txt +} + +function rade_awgn { + EbNodB_range="100 15 10 5 2.5 0 -2.5" + for EbNodB in $EbNodB_range + do + ./asr_test.sh rade --EbNodB $EbNodB -n $n --results ${results_file}_rade.txt + done + cat ${results_file}_rade.txt | grep rade | sed -e "s/rade//" > tmp.txt + mv tmp.txt ${results_file}_rade.txt +} + +ssb_awgn +rade_awgn diff --git a/multipath_samples.m b/multipath_samples.m index 208f711..6646340 100644 --- a/multipath_samples.m +++ b/multipath_samples.m @@ -61,12 +61,14 @@ function multipath_samples(ch, Fs, Rs, Nc, Nseconds, H_fn, G_fn="") LCR_meas = LC/Nseconds subplot(211); hold on; stem(LC_log,sqrt(P)*ones(length(LC_log))); hold off; axis([0 Nsecplot*Rs 0 3]); end - printf("H file size is Nseconds*Rs*Nc*(4 bytes/sample) = %d*%d*%d*4 = %d bytes\n", Nseconds,Rs,Nc,Nseconds*Rs*Nc*4) - f=fopen(H_fn,"wb"); - [r c] = size(H); - Hflat = reshape(H', 1, r*c); - fwrite(f, Hflat, 'float32'); - fclose(f); + if length(H_fn) + printf("H file size is Nseconds*Rs*Nc*(4 bytes/sample) = %d*%d*%d*4 = %d bytes\n", Nseconds,Rs,Nc,Nseconds*Rs*Nc*4) + f=fopen(H_fn,"wb"); + [r c] = size(H); + Hflat = reshape(H', 1, r*c); + fwrite(f, Hflat, 'float32'); + fclose(f); + end if length(G_fn) % G matrix cols are G1 G2, rows timesteps, with hf_gain the first row, diff --git a/radae_plots.m b/radae_plots.m index 3925862..b122989 100644 --- a/radae_plots.m +++ b/radae_plots.m @@ -483,3 +483,35 @@ function plot_sample_spec(wav_fn,png_spec_fn="") print("-dpng",png_spec_fn,"-S800,600"); end end + +function plot_wer(ssb_awgn_fn,rade_awgn_fn, ssb_mpp_fn="",rade_mpp_fn="") + ssb_awgn = load(ssb_awgn_fn); + rade_awgn = load(rade_awgn_fn); + if length(ssb_mpp_fn) + ssb_mpp = load(ssb_mpp_fn); + rade_mpp = load(rade_mpp_fn); + end + + figure(1); clf; + plot(ssb_awgn(:,2),ssb_awgn(:,3),'b+-;SSB AWGN;'); + hold on; + plot(rade_awgn(:,2),rade_awgn(:,3),'g+-;RADE AWGN;'); + if length(ssb_mpp_fn) + plot(ssb_mpp(:,2),ssb_mpp(:,3),'bo-;SSB MPP;'); + plot(rade_mpp(:,2),rade_mpp(:,3),'go-;RADE MPP;'); + end + hold off; + axis([30,60,0,60]); grid; ylabel('WER %'); xlabel("C/No (dB)"); + + figure(2); clf; + plot(ssb_awgn(:,1),ssb_awgn(:,3),'b+-;SSB AWGN;'); + hold on; + plot(rade_awgn(:,1),rade_awgn(:,3),'g+-;RADE AWGN;'); + if length(ssb_mpp_fn) + plot(ssb_mpp(:,1),ssb_mpp(:,3),'bo-;SSB MPP;'); + plot(rade_mpp(:,1),rade_mpp(:,3),'go-;RADE MPP;'); + end + hold off; + axis([-5,20,0,60]); grid; ylabel('WER %'); xlabel("SNR3k (dB)"); + print("-dpng","asr_test","-S800,600"); +end From 265bdcd6017e990a6495cf70056f31955e4bac20 Mon Sep 17 00:00:00 2001 From: David Date: Thu, 19 Dec 2024 08:36:19 +1030 Subject: [PATCH 07/20] support for large/turbo whisper models, clean up asr_test top level script --- asr_test.sh | 6 +++--- asr_test_awgn.sh | 30 ------------------------------ asr_test_top.sh | 37 +++++++++++++++++++++++++++++++++++++ asr_wer.py | 25 ++++++++++++++----------- radae_plots.m | 8 ++++---- 5 files changed, 58 insertions(+), 48 deletions(-) delete mode 100755 asr_test_awgn.sh create mode 100755 asr_test_top.sh diff --git a/asr_test.sh b/asr_test.sh index d9d5f15..346bd8e 100755 --- a/asr_test.sh +++ b/asr_test.sh @@ -177,7 +177,7 @@ function process { # find length of each file duration_log="" flac_full="" - pushd $source; + pushd $source > /dev/null; for f in $flac do duration_log+=$(sox --info -D ${f}) @@ -185,7 +185,7 @@ function process { flac_full+=${source}/${f} flac_full+=" " done - popd; + popd > /dev/null; # cat samples into one long input file sox $flac_full -t .s16 ${in} @@ -221,7 +221,7 @@ function process { done fi - python3 asr_wer.py test-other -n $n_samples | tee > $asr_log + python3 asr_wer.py test-other -n $n_samples --model turbo | tee > $asr_log wer=$(tail -n1 $asr_log | tr -s ' ' | cut -d' ' -f2) printf "%-4s %5.2f %5.2f %5.2f\n" $mode $SNR_mean $CNo_mean $wer | tee -a $results } diff --git a/asr_test_awgn.sh b/asr_test_awgn.sh deleted file mode 100755 index 64779cb..0000000 --- a/asr_test_awgn.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/env bash -# asr_test_awgn.sh -# -# Top level ASR test script for AWGN channels - -results_file=241217_asr_awgn -n=100 - -function ssb_awgn { - No_range="-100 -39 -36 -33 -30 -25 -20" - for No in $No_range - do - ./asr_test.sh ssb --No $No -n $n --results ${results_file}_ssb.txt - done - cat ${results_file}_ssb.txt | grep ssb | sed -e "s/ssb//" > tmp.txt - mv tmp.txt ${results_file}_ssb.txt -} - -function rade_awgn { - EbNodB_range="100 15 10 5 2.5 0 -2.5" - for EbNodB in $EbNodB_range - do - ./asr_test.sh rade --EbNodB $EbNodB -n $n --results ${results_file}_rade.txt - done - cat ${results_file}_rade.txt | grep rade | sed -e "s/rade//" > tmp.txt - mv tmp.txt ${results_file}_rade.txt -} - -ssb_awgn -rade_awgn diff --git a/asr_test_top.sh b/asr_test_top.sh new file mode 100755 index 0000000..37ac80c --- /dev/null +++ b/asr_test_top.sh @@ -0,0 +1,37 @@ +#!/usr/bin/env bash +# asr_test_awgn.sh +# +# Top level ASR test script for AWGN and MPP channels +set -x +results_file=241219_asr +n=100 + +function ssb { + local results_file=$1 + No_range=$2 + for No in $No_range + do + ./asr_test.sh ssb --No $No -n $n --results ${results_file} $3 + done + cat ${results_file} | grep ssb | sed -e "s/ssb//" > tmp.txt + mv tmp.txt ${results_file} +} + +function rade { + local results_file=$1 + EbNodB_range=$2 + for EbNodB in $EbNodB_range + do + ./asr_test.sh rade --EbNodB $EbNodB -n $n --results ${results_file} $3 + done + cat ${results_file} | grep rade | sed -e "s/rade//" > tmp.txt + mv tmp.txt ${results_file} +} + +#ssb ${results_file}_awgn_ssb.txt "-100 -39" +#ssb ${results_file}_mpp_ssb.txt "-100 -44" "--g_file g_mpp.f32" + +ssb ${results_file}_awgn_ssb.txt "-100 -39 -36 -33 -30 -25 -20" +rade ${results_file}_awgn_rade.txt "100 15 10 5 2.5 0 -2.5" +ssb ${results_file}_mpp_ssb.txt "-100 -44 -41 -39 -36 -33 -30" "--g_file g_mpp.f32" +rade ${results_file}_mpp_rade.txt "100 15 10 5 2.5 0" "--g_file g_mpp.f32" diff --git a/asr_wer.py b/asr_wer.py index 7b5b26b..68b441a 100644 --- a/asr_wer.py +++ b/asr_wer.py @@ -18,13 +18,15 @@ class LibriSpeech(torch.utils.data.Dataset): A simple class to wrap LibriSpeech and trim/pad the audio to 30 seconds. It will drop the last few seconds of a very small portion of the utterances. """ - def __init__(self, split="test-clean", device=DEVICE): + def __init__(self, n_mels, split="test-clean", device=DEVICE): self.dataset = torchaudio.datasets.LIBRISPEECH( root=os.path.expanduser("~/.cache"), url=split, download=True, ) self.device = device + self.n_mels = n_mels + print(n_mels) def __len__(self): return len(self.dataset) @@ -33,31 +35,32 @@ def __getitem__(self, item): audio, sample_rate, text, _, _, _ = self.dataset[item] assert sample_rate == 16000 audio = whisper.pad_or_trim(audio.flatten()).to(self.device) - mel = whisper.log_mel_spectrogram(audio) + mel = whisper.log_mel_spectrogram(audio,n_mels=self.n_mels) return (mel, text) parser = argparse.ArgumentParser() parser.add_argument('test_name', type=str, help='Librispeech dataset name (e.g. test-clean)') -parser.add_argument('-n', type=str, help='Number of dataset sntries to use (default all of them)') +parser.add_argument('-n', type=str, help='Number of dataset entries to use (default all of them)') +parser.add_argument('--model', default='base.en',type=str, help='Whisper model') args = parser.parse_args() -dataset = LibriSpeech(args.test_name) -if args.n: - dataset = torch.utils.data.Subset(dataset,list(range(0,int(args.n)))) -print("dataset length:", dataset.__len__()) - -loader = torch.utils.data.DataLoader(dataset, batch_size=16) -model = whisper.load_model("base.en") +model = whisper.load_model(args.model) print( f"Model is {'multilingual' if model.is_multilingual else 'English-only'} " f"and has {sum(np.prod(p.shape) for p in model.parameters()):,} parameters." ) - # predict without timestamps for short-form transcription options = whisper.DecodingOptions(language="en", without_timestamps=True) +dataset = LibriSpeech(model.dims.n_mels, args.test_name) +if args.n: + dataset = torch.utils.data.Subset(dataset,list(range(0,int(args.n)))) +print("dataset length:", dataset.__len__()) +loader = torch.utils.data.DataLoader(dataset, batch_size=16) + + hypotheses = [] references = [] diff --git a/radae_plots.m b/radae_plots.m index b122989..4af7b51 100644 --- a/radae_plots.m +++ b/radae_plots.m @@ -497,8 +497,8 @@ function plot_wer(ssb_awgn_fn,rade_awgn_fn, ssb_mpp_fn="",rade_mpp_fn="") hold on; plot(rade_awgn(:,2),rade_awgn(:,3),'g+-;RADE AWGN;'); if length(ssb_mpp_fn) - plot(ssb_mpp(:,2),ssb_mpp(:,3),'bo-;SSB MPP;'); - plot(rade_mpp(:,2),rade_mpp(:,3),'go-;RADE MPP;'); + plot(ssb_mpp(:,2),ssb_mpp(:,3),'bo--;SSB MPP;'); + plot(rade_mpp(:,2),rade_mpp(:,3),'go--;RADE MPP;'); end hold off; axis([30,60,0,60]); grid; ylabel('WER %'); xlabel("C/No (dB)"); @@ -508,8 +508,8 @@ function plot_wer(ssb_awgn_fn,rade_awgn_fn, ssb_mpp_fn="",rade_mpp_fn="") hold on; plot(rade_awgn(:,1),rade_awgn(:,3),'g+-;RADE AWGN;'); if length(ssb_mpp_fn) - plot(ssb_mpp(:,1),ssb_mpp(:,3),'bo-;SSB MPP;'); - plot(rade_mpp(:,1),rade_mpp(:,3),'go-;RADE MPP;'); + plot(ssb_mpp(:,1),ssb_mpp(:,3),'bo--;SSB MPP;'); + plot(rade_mpp(:,1),rade_mpp(:,3),'go--;RADE MPP;'); end hold off; axis([-5,20,0,60]); grid; ylabel('WER %'); xlabel("SNR3k (dB)"); From 225bba7963f5184157bd8644c5ccbd6104e41887 Mon Sep 17 00:00:00 2001 From: David Date: Sat, 21 Dec 2024 11:35:55 +1030 Subject: [PATCH 08/20] added some test modes to asr_test.sh, adding 500ms silence after samples to allow for any prcoessing delay --- asr_test.sh | 102 ++++++++++++++++++++++++++++---------------- asr_test_top.sh | 9 ++-- radae/radae_base.py | 8 ++-- radae_plots.m | 51 +++++++++++++++------- 4 files changed, 109 insertions(+), 61 deletions(-) diff --git a/asr_test.sh b/asr_test.sh index 346bd8e..876dfef 100755 --- a/asr_test.sh +++ b/asr_test.sh @@ -16,7 +16,7 @@ function print_help { echo echo "Automated Speech Recognition (ASR) dataset processing for Radio Autoencoder testing" echo - echo " usage ./asr_test.sh ssb|rade [test option below]" + echo " usage ./asr_test.sh ssb|rade|fargan|4kHz [test option below]" echo " usage ./ota_test.sh ssb --No -30" echo " usage ./ota_test.sh rade --EbNodB 10" echo @@ -143,37 +143,45 @@ function process { CNo_log=CNo_log.txt rm -f ${CNo_log} - if [ $mode == "ssb" ]; then + if [ $mode == "ssb" ] || [ $mode == "4kHz" ]; then fading_adv=0 for f in $flac do d=$(dirname $f) mkdir -p ${dest}/${d} - sox ${source}/${f} -t .s16 -r 8000 ${in} - # AGC and Hilbert compression - set_rms ${in} $setpoint_rms - analog_compressor ${in} ${comp} ${comp_gain} 2>/dev/null - ch ${comp} - --No ${No} ${ch_args} --fading_adv ${fading_adv} 2>${ch_log} | sox -t .s16 -r 8000 -c 1 - -r 16000 ${dest}/${f} - grep "Fading file finished" $ch_log - if [ $? -eq 0 ]; then - echo "Error - fading file too short after" $fading_adv " seconds" - exit 1 + + if [ $mode == "ssb" ]; then + sox ${source}/${f} -t .s16 -r 8000 ${in} + # AGC and Hilbert compression + set_rms ${in} $setpoint_rms + analog_compressor ${in} ${comp} ${comp_gain} 2>/dev/null + ch ${comp} - --No ${No} ${ch_args} --fading_adv ${fading_adv} 2>${ch_log} | sox -t .s16 -r 8000 -c 1 - -r 16000 ${dest}/${f} + grep "Fading file finished" $ch_log + if [ $? -eq 0 ]; then + echo "Error - fading file too short after" $fading_adv " seconds" + exit 1 + fi + snr=$(cat $ch_log | grep "SNR3k" | tr -s ' ' | cut -d' ' -f3) + CNo=$(cat $ch_log | grep "SNR3k" | tr -s ' ' | cut -d' ' -f5) + echo $snr >> ${snr_log} + echo $CNo >> ${CNo_log} + + # advance through fading simulation file + dur=$(sox --info -D ${source}/${f}) + fading_adv=$(python3 -c "print(${fading_adv} + ${dur})") + else + # $mode == "4kHz" (4kHz bandwidth, representing ideal Fs=8kHz vocoder) + sox ${source}/${f} -r 8000 -t .s16 -c 1 - | sox -r 8000 -t .s16 -c 1 - -r 16000 ${dest}/${f} fi - snr=$(cat $ch_log | grep "SNR3k" | tr -s ' ' | cut -d' ' -f3) - CNo=$(cat $ch_log | grep "SNR3k" | tr -s ' ' | cut -d' ' -f5) - echo $snr >> ${snr_log} - echo $CNo >> ${CNo_log} - - # advance through fading simulation file - dur=$(sox --info -D ${source}/${f}) - fading_adv=$(python3 -c "print(${fading_adv} + ${dur})") done - SNR_mean=$(print_mean_text_file ${snr_log}) - CNo_mean=$(print_mean_text_file ${CNo_log}) + if [ $mode == "ssb" ]; then + SNR_mean=$(print_mean_text_file ${snr_log}) + CNo_mean=$(print_mean_text_file ${CNo_log}) + fi fi - if [ $mode == "rade" ]; then + if [ $mode == "rade" ] || [ $mode == "fargan" ]; then # find length of each file duration_log="" flac_full="" @@ -182,26 +190,34 @@ function process { do duration_log+=$(sox --info -D ${f}) duration_log+=" " - flac_full+=${source}/${f} - flac_full+=" " + flac_full+="${source}/${f} /tmp/silence.wav " done popd > /dev/null; - # cat samples into one long input file + # cat samples into one long input file, insert 500ms at end of sample to allow for processing at output + sil=0.5 + sox -n -r 16000 -c 1 /tmp/silence.wav trim 0.0 ${sil} sox $flac_full -t .s16 ${in} # process all samples as one file to save time - ./inference.sh model19_check3/checkpoints/checkpoint_epoch_100.pth ${in} out.wav \ - --rate_Fs --pilots --pilot_eq --eq_ls --cp 0.004 --bottleneck 3 --auxdata --time_offset -16 \ - --EbNodB $EbNodB ${inference_args} | tee ${rade_log} - grep "Multipath Doppler spread file too short" $rade_log - if [ $? -eq 0 ]; then - echo "Error - fading file too short" - exit 1 - fi - SNR_mean=$(cat $rade_log | grep "Measured" | tr -s ' ' | cut -d' ' -f4) - CNo_mean=$(cat $rade_log | grep "Measured" | tr -s ' ' | cut -d' ' -f3) + if [ $mode == "rade" ]; then + ./inference.sh model19_check3/checkpoints/checkpoint_epoch_100.pth ${in} out.wav \ + --rate_Fs --pilots --pilot_eq --eq_ls --cp 0.004 --bottleneck 3 --auxdata --time_offset -16 \ + --EbNodB $EbNodB ${inference_args} | tee ${rade_log} + grep "Multipath Doppler spread file too short" $rade_log + if [ $? -eq 0 ]; then + echo "Error - fading file too short" + exit 1 + fi + + SNR_mean=$(cat $rade_log | grep "Measured" | tr -s ' ' | cut -d' ' -f4) + CNo_mean=$(cat $rade_log | grep "Measured" | tr -s ' ' | cut -d' ' -f3) + else + # $mode == "fargan" + ./inference.sh model19_check3/checkpoints/checkpoint_epoch_100.pth ${in} out.wav --auxdata --passthru + #sox -t .s16 -r 16000 -c 1 ${in} out.wav + fi # extract individual output files duration_array=( ${duration_log} ) @@ -210,6 +226,7 @@ function process { for f in $flac do dur=${duration_array[i]} + dur=$(python3 -c "print($dur + ${sil})") #printf "%4d %s %5.2f %5.2f\n" $i $f $st $dur ((i++)) if [ $i -eq ${#duration_array[@]} ]; then @@ -221,9 +238,22 @@ function process { done fi + # test mode that just copies files + if [ $mode == "clean" ]; then + for f in $flac + do + cp ${source}/${f} ${dest}/${f} + done + + fi + python3 asr_wer.py test-other -n $n_samples --model turbo | tee > $asr_log wer=$(tail -n1 $asr_log | tr -s ' ' | cut -d' ' -f2) - printf "%-4s %5.2f %5.2f %5.2f\n" $mode $SNR_mean $CNo_mean $wer | tee -a $results + if [ $mode == "ssb" ] || [ $mode == "rade" ]; then + printf "%-4s %5.2f %5.2f %5.2f\n" $mode $SNR_mean $CNo_mean $wer | tee -a $results + else + printf "%-4s %5.2f\n" $mode $wer | tee -a $results + fi } cp_translation_files diff --git a/asr_test_top.sh b/asr_test_top.sh index 37ac80c..1f87c30 100755 --- a/asr_test_top.sh +++ b/asr_test_top.sh @@ -4,7 +4,7 @@ # Top level ASR test script for AWGN and MPP channels set -x results_file=241219_asr -n=100 +n=500 function ssb { local results_file=$1 @@ -28,10 +28,7 @@ function rade { mv tmp.txt ${results_file} } -#ssb ${results_file}_awgn_ssb.txt "-100 -39" -#ssb ${results_file}_mpp_ssb.txt "-100 -44" "--g_file g_mpp.f32" - -ssb ${results_file}_awgn_ssb.txt "-100 -39 -36 -33 -30 -25 -20" +ssb ${results_file}_awgn_ssb.txt "-100 -38 -35 -32 -29 -26 -23 -20 -17" rade ${results_file}_awgn_rade.txt "100 15 10 5 2.5 0 -2.5" -ssb ${results_file}_mpp_ssb.txt "-100 -44 -41 -39 -36 -33 -30" "--g_file g_mpp.f32" +ssb ${results_file}_mpp_ssb.txt "-100 -44 -39 -36 -33 -30 -27" "--g_file g_mpp.f32" rade ${results_file}_mpp_rade.txt "100 15 10 5 2.5 0" "--g_file g_mpp.f32" diff --git a/radae/radae_base.py b/radae/radae_base.py index 95d8790..bab6236 100644 --- a/radae/radae_base.py +++ b/radae/radae_base.py @@ -185,7 +185,7 @@ def __init__(self, feature_dim, output_dim, bottleneck = 1): self.z_dense = nn.Linear(864, self.output_dim) nb_params = sum(p.numel() for p in self.parameters()) - print(f"encoder: {nb_params} weights", file=sys.stderr) + #print(f"encoder: {nb_params} weights", file=sys.stderr) # initialize weights self.apply(init_weights) @@ -251,7 +251,7 @@ def __init__(self, feature_dim, output_dim, bottleneck = 1): self.z_dense = nn.Linear(864, self.output_dim) nb_params = sum(p.numel() for p in self.parameters()) - print(f"encoder: {nb_params} weights", file=sys.stderr) + #print(f"encoder: {nb_params} weights", file=sys.stderr) # initialize weights self.apply(init_weights) @@ -326,7 +326,7 @@ def __init__(self, input_dim, output_dim): self.glu5 = GLU(96) nb_params = sum(p.numel() for p in self.parameters()) - print(f"decoder: {nb_params} weights", file=sys.stderr) + #print(f"decoder: {nb_params} weights", file=sys.stderr) # initialize weights self.apply(init_weights) @@ -393,7 +393,7 @@ def __init__(self, input_dim, output_dim): self.glu5 = GLU(96) nb_params = sum(p.numel() for p in self.parameters()) - print(f"decoder: {nb_params} weights", file=sys.stderr) + #print(f"decoder: {nb_params} weights", file=sys.stderr) # initialize weights self.apply(init_weights) diff --git a/radae_plots.m b/radae_plots.m index 4af7b51..d121f32 100644 --- a/radae_plots.m +++ b/radae_plots.m @@ -440,6 +440,7 @@ function plot_SNR_CNR(epslatex="") plot(theta, phi, "r-;phi;") hold off endfunction + function compare_pitch_corr(wav_fn,feat1_fn,feat2_fn,png_feat_fn="") Fs=16000; s=load_raw(wav_fn); @@ -484,34 +485,54 @@ function plot_sample_spec(wav_fn,png_spec_fn="") end end -function plot_wer(ssb_awgn_fn,rade_awgn_fn, ssb_mpp_fn="",rade_mpp_fn="") +function plot_wer(prefix_fn, png_fn="", epslatex="") + ssb_awgn_fn = sprintf("%s_asr_awgn_ssb.txt",prefix_fn); + rade_awgn_fn = sprintf("%s_asr_awgn_rade.txt",prefix_fn); + ssb_mpp_fn = sprintf("%s_asr_mpp_ssb.txt",prefix_fn); + rade_mpp_fn = sprintf("%s_asr_mpp_rade.txt",prefix_fn); + ssb_awgn = load(ssb_awgn_fn); rade_awgn = load(rade_awgn_fn); - if length(ssb_mpp_fn) - ssb_mpp = load(ssb_mpp_fn); - rade_mpp = load(rade_mpp_fn); + ssb_mpp = load(ssb_mpp_fn); + rade_mpp = load(rade_mpp_fn); + + if length(epslatex) + [textfontsize linewidth] = set_fonts(); end + # WER v C/No plot figure(1); clf; plot(ssb_awgn(:,2),ssb_awgn(:,3),'b+-;SSB AWGN;'); hold on; plot(rade_awgn(:,2),rade_awgn(:,3),'g+-;RADE AWGN;'); - if length(ssb_mpp_fn) - plot(ssb_mpp(:,2),ssb_mpp(:,3),'bo--;SSB MPP;'); - plot(rade_mpp(:,2),rade_mpp(:,3),'go--;RADE MPP;'); - end + plot(ssb_mpp(:,2),ssb_mpp(:,3),'bo--;SSB MPP;'); + plot(rade_mpp(:,2),rade_mpp(:,3),'go--;RADE MPP;'); + xmin=30; xmax=60; + plot([xmin xmax],[2.83 2.83],'r--;FARGAN;') + plot([xmin xmax],[3.11 3.11],'k-.;4kHz;') + plot([xmin xmax],[1.39 1.39],'c-;clean;') hold off; - axis([30,60,0,60]); grid; ylabel('WER %'); xlabel("C/No (dB)"); + axis([xmin,xmax,0,40]); grid; ylabel('WER %'); xlabel("C/No (dB)"); + # WER v SNR plot figure(2); clf; plot(ssb_awgn(:,1),ssb_awgn(:,3),'b+-;SSB AWGN;'); hold on; plot(rade_awgn(:,1),rade_awgn(:,3),'g+-;RADE AWGN;'); - if length(ssb_mpp_fn) - plot(ssb_mpp(:,1),ssb_mpp(:,3),'bo--;SSB MPP;'); - plot(rade_mpp(:,1),rade_mpp(:,3),'go--;RADE MPP;'); - end + plot(ssb_mpp(:,1),ssb_mpp(:,3),'bo--;SSB MPP;'); + plot(rade_mpp(:,1),rade_mpp(:,3),'go--;RADE MPP;'); + xmin=-5; xmax=20; + plot([xmin xmax],[2.83 2.83],'r--;FARGAN;') + plot([xmin xmax],[3.11 3.11],'k-.;4kHz;') + plot([xmin xmax],[1.39 1.39],'c-;clean;') hold off; - axis([-5,20,0,60]); grid; ylabel('WER %'); xlabel("SNR3k (dB)"); - print("-dpng","asr_test","-S800,600"); + axis([xmin,xmax,0,40]); grid; ylabel('WER %'); xlabel("SNR3k (dB)"); + + + if length(png_fn) + print("-dpng",png_fn,"-S800,600"); + end + if length(epslatex) + print_eps_restore(epslatex,"-S300,300",textfontsize,linewidth); + end end From cf8d63272a17e087301bc94d83bfe87e1e397054 Mon Sep 17 00:00:00 2001 From: David Date: Sat, 21 Dec 2024 15:40:55 +1030 Subject: [PATCH 09/20] automated gen & plotting of controls --- asr_test.sh | 8 ++++---- asr_test_top.sh | 11 ++++++++++- radae_plots.m | 18 ++++++++++-------- 3 files changed, 24 insertions(+), 13 deletions(-) diff --git a/asr_test.sh b/asr_test.sh index 876dfef..2447b7d 100755 --- a/asr_test.sh +++ b/asr_test.sh @@ -36,6 +36,7 @@ comp_gain=6 results=asr_results.txt inference_args="" ch_args="" +sil=0.5 POSITIONAL=() while [[ $# -gt 0 ]] @@ -142,6 +143,7 @@ function process { rm -f ${snr_log} CNo_log=CNo_log.txt rm -f ${CNo_log} + sox -n -r 16000 -c 1 /tmp/silence.wav trim 0.0 ${sil} if [ $mode == "ssb" ] || [ $mode == "4kHz" ]; then @@ -195,8 +197,6 @@ function process { popd > /dev/null; # cat samples into one long input file, insert 500ms at end of sample to allow for processing at output - sil=0.5 - sox -n -r 16000 -c 1 /tmp/silence.wav trim 0.0 ${sil} sox $flac_full -t .s16 ${in} # process all samples as one file to save time @@ -250,9 +250,9 @@ function process { python3 asr_wer.py test-other -n $n_samples --model turbo | tee > $asr_log wer=$(tail -n1 $asr_log | tr -s ' ' | cut -d' ' -f2) if [ $mode == "ssb" ] || [ $mode == "rade" ]; then - printf "%-4s %5.2f %5.2f %5.2f\n" $mode $SNR_mean $CNo_mean $wer | tee -a $results + printf "%-6s %5.2f %5.2f %5.2f\n" $mode $SNR_mean $CNo_mean $wer | tee -a $results else - printf "%-4s %5.2f\n" $mode $wer | tee -a $results + printf "%-6s %5.2f\n" $mode $wer | tee -a $results fi } diff --git a/asr_test_top.sh b/asr_test_top.sh index 1f87c30..00000c9 100755 --- a/asr_test_top.sh +++ b/asr_test_top.sh @@ -3,7 +3,7 @@ # # Top level ASR test script for AWGN and MPP channels set -x -results_file=241219_asr +results_file=241221_asr n=500 function ssb { @@ -28,6 +28,15 @@ function rade { mv tmp.txt ${results_file} } +# run the controls +controls_file=${results_file}_controls.txt +rm -f ${controls_file} +./asr_test.sh clean -n $n --results ${controls_file} +./asr_test.sh fargan -n $n --results ${controls_file} +./asr_test.sh 4kHz -n $n --results ${controls_file} +./asr_test.sh ssb -n $n --results ${controls_file} +./asr_test.sh rade -n $n --results ${controls_file} + ssb ${results_file}_awgn_ssb.txt "-100 -38 -35 -32 -29 -26 -23 -20 -17" rade ${results_file}_awgn_rade.txt "100 15 10 5 2.5 0 -2.5" ssb ${results_file}_mpp_ssb.txt "-100 -44 -39 -36 -33 -30 -27" "--g_file g_mpp.f32" diff --git a/radae_plots.m b/radae_plots.m index d121f32..dfc7a03 100644 --- a/radae_plots.m +++ b/radae_plots.m @@ -490,12 +490,14 @@ function plot_wer(prefix_fn, png_fn="", epslatex="") rade_awgn_fn = sprintf("%s_asr_awgn_rade.txt",prefix_fn); ssb_mpp_fn = sprintf("%s_asr_mpp_ssb.txt",prefix_fn); rade_mpp_fn = sprintf("%s_asr_mpp_rade.txt",prefix_fn); - + controls_fn = sprintf("%s_asr_c.txt",prefix_fn); + ssb_awgn = load(ssb_awgn_fn); rade_awgn = load(rade_awgn_fn); ssb_mpp = load(ssb_mpp_fn); rade_mpp = load(rade_mpp_fn); - + c = load(controls_fn); + if length(epslatex) [textfontsize linewidth] = set_fonts(); end @@ -508,9 +510,9 @@ function plot_wer(prefix_fn, png_fn="", epslatex="") plot(ssb_mpp(:,2),ssb_mpp(:,3),'bo--;SSB MPP;'); plot(rade_mpp(:,2),rade_mpp(:,3),'go--;RADE MPP;'); xmin=30; xmax=60; - plot([xmin xmax],[2.83 2.83],'r--;FARGAN;') - plot([xmin xmax],[3.11 3.11],'k-.;4kHz;') - plot([xmin xmax],[1.39 1.39],'c-;clean;') + plot(xmax-5,c(1),'cx;clean;') + plot(xmax-5,c(2),'ro;FARGAN;') + plot(xmax-5,c(3),'k+;4kHz;') hold off; axis([xmin,xmax,0,40]); grid; ylabel('WER %'); xlabel("C/No (dB)"); @@ -522,9 +524,9 @@ function plot_wer(prefix_fn, png_fn="", epslatex="") plot(ssb_mpp(:,1),ssb_mpp(:,3),'bo--;SSB MPP;'); plot(rade_mpp(:,1),rade_mpp(:,3),'go--;RADE MPP;'); xmin=-5; xmax=20; - plot([xmin xmax],[2.83 2.83],'r--;FARGAN;') - plot([xmin xmax],[3.11 3.11],'k-.;4kHz;') - plot([xmin xmax],[1.39 1.39],'c-;clean;') + plot(xmax-5,c(1),'cx;clean;') + plot(xmax-5,c(2),'ro;FARGAN;') + plot(xmax-5,c(3),'k+;4kHz;') hold off; axis([xmin,xmax,0,40]); grid; ylabel('WER %'); xlabel("SNR3k (dB)"); From 22af4f05b5d7c885e37fc122111015aec2e3b120 Mon Sep 17 00:00:00 2001 From: David Date: Sun, 22 Dec 2024 09:05:58 +1030 Subject: [PATCH 10/20] refining Latex plot for paper --- radae_plots.m | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/radae_plots.m b/radae_plots.m index dfc7a03..989d2ef 100644 --- a/radae_plots.m +++ b/radae_plots.m @@ -514,7 +514,7 @@ function plot_wer(prefix_fn, png_fn="", epslatex="") plot(xmax-5,c(2),'ro;FARGAN;') plot(xmax-5,c(3),'k+;4kHz;') hold off; - axis([xmin,xmax,0,40]); grid; ylabel('WER %'); xlabel("C/No (dB)"); + axis([xmin,xmax,0,40]); grid; ylabel('WER \%'); xlabel("C/No (dB)"); # WER v SNR plot figure(2); clf; @@ -528,13 +528,13 @@ function plot_wer(prefix_fn, png_fn="", epslatex="") plot(xmax-5,c(2),'ro;FARGAN;') plot(xmax-5,c(3),'k+;4kHz;') hold off; - axis([xmin,xmax,0,40]); grid; ylabel('WER %'); xlabel("SNR3k (dB)"); + axis([xmin,xmax,0,40]); grid; ylabel('WER \%'); xlabel("SNR3k (dB)"); if length(png_fn) print("-dpng",png_fn,"-S800,600"); end if length(epslatex) - print_eps_restore(epslatex,"-S300,300",textfontsize,linewidth); + print_eps_restore(epslatex,"-S275,300",textfontsize,linewidth); end end From 70967973892af15882b8c64873d3382f57e8018a Mon Sep 17 00:00:00 2001 From: David Date: Sun, 22 Dec 2024 16:53:03 +1030 Subject: [PATCH 11/20] typos --- asr_test_top.sh | 2 ++ radae_plots.m | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/asr_test_top.sh b/asr_test_top.sh index 00000c9..13e80ae 100755 --- a/asr_test_top.sh +++ b/asr_test_top.sh @@ -36,6 +36,8 @@ rm -f ${controls_file} ./asr_test.sh 4kHz -n $n --results ${controls_file} ./asr_test.sh ssb -n $n --results ${controls_file} ./asr_test.sh rade -n $n --results ${controls_file} +# strip off all but last column for Octave plotting +cat ${controls_file} | awk '{print $NF}' > ${results_file}_c.txt ssb ${results_file}_awgn_ssb.txt "-100 -38 -35 -32 -29 -26 -23 -20 -17" rade ${results_file}_awgn_rade.txt "100 15 10 5 2.5 0 -2.5" diff --git a/radae_plots.m b/radae_plots.m index 989d2ef..b4a8f72 100644 --- a/radae_plots.m +++ b/radae_plots.m @@ -530,7 +530,6 @@ function plot_wer(prefix_fn, png_fn="", epslatex="") hold off; axis([xmin,xmax,0,40]); grid; ylabel('WER \%'); xlabel("SNR3k (dB)"); - if length(png_fn) print("-dpng",png_fn,"-S800,600"); end From a17dc519b0a71778369134800a42add9ae7a11b7 Mon Sep 17 00:00:00 2001 From: David Date: Sat, 25 Jan 2025 07:16:19 +1030 Subject: [PATCH 12/20] increased font size for Latex plot --- radae_plots.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radae_plots.m b/radae_plots.m index b4a8f72..e0a9a49 100644 --- a/radae_plots.m +++ b/radae_plots.m @@ -499,7 +499,7 @@ function plot_wer(prefix_fn, png_fn="", epslatex="") c = load(controls_fn); if length(epslatex) - [textfontsize linewidth] = set_fonts(); + [textfontsize linewidth] = set_fonts(20); end # WER v C/No plot From 8279aaad73c78d4129b4b319a3e9ce20c9602fd9 Mon Sep 17 00:00:00 2001 From: David Date: Wed, 5 Feb 2025 18:12:24 +1030 Subject: [PATCH 13/20] lower vertical size of ASR plot --- radae_plots.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/radae_plots.m b/radae_plots.m index e0a9a49..0213c72 100644 --- a/radae_plots.m +++ b/radae_plots.m @@ -499,7 +499,7 @@ function plot_wer(prefix_fn, png_fn="", epslatex="") c = load(controls_fn); if length(epslatex) - [textfontsize linewidth] = set_fonts(20); + [textfontsize linewidth] = set_fonts(15); end # WER v C/No plot @@ -534,6 +534,6 @@ function plot_wer(prefix_fn, png_fn="", epslatex="") print("-dpng",png_fn,"-S800,600"); end if length(epslatex) - print_eps_restore(epslatex,"-S275,300",textfontsize,linewidth); + print_eps_restore(epslatex,"-S275,200",textfontsize,linewidth); end end From 7ed00285b45df714e4e459ef50fc53c1553d2823 Mon Sep 17 00:00:00 2001 From: David Date: Tue, 11 Feb 2025 16:32:29 +1030 Subject: [PATCH 14/20] another 10% lower vertical, () around % --- radae_plots.m | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/radae_plots.m b/radae_plots.m index 0213c72..37163bc 100644 --- a/radae_plots.m +++ b/radae_plots.m @@ -499,7 +499,7 @@ function plot_wer(prefix_fn, png_fn="", epslatex="") c = load(controls_fn); if length(epslatex) - [textfontsize linewidth] = set_fonts(15); + [textfontsize linewidth] = set_fonts(20); end # WER v C/No plot @@ -528,12 +528,12 @@ function plot_wer(prefix_fn, png_fn="", epslatex="") plot(xmax-5,c(2),'ro;FARGAN;') plot(xmax-5,c(3),'k+;4kHz;') hold off; - axis([xmin,xmax,0,40]); grid; ylabel('WER \%'); xlabel("SNR3k (dB)"); + axis([xmin,xmax,0,40]); grid; ylabel('WER (\%)'); xlabel("SNR3k (dB)"); if length(png_fn) print("-dpng",png_fn,"-S800,600"); end if length(epslatex) - print_eps_restore(epslatex,"-S275,200",textfontsize,linewidth); + print_eps_restore(epslatex,"-S250,200",textfontsize,linewidth); end end From cd430fec2a4c3403343071f1d10f3538db63471c Mon Sep 17 00:00:00 2001 From: David Date: Tue, 18 Mar 2025 12:22:12 +1030 Subject: [PATCH 15/20] 700D added to WER/ASR curves for 2024 HF RADE paper --- asr_test.sh | 42 +++++++++++++++++++++++++++++++++++++++--- asr_test_top.sh | 19 +++++++++++++++++++ radae_plots.m | 20 ++++++++++++++------ 3 files changed, 72 insertions(+), 9 deletions(-) diff --git a/asr_test.sh b/asr_test.sh index 2447b7d..89e486c 100755 --- a/asr_test.sh +++ b/asr_test.sh @@ -16,7 +16,7 @@ function print_help { echo echo "Automated Speech Recognition (ASR) dataset processing for Radio Autoencoder testing" echo - echo " usage ./asr_test.sh ssb|rade|fargan|4kHz [test option below]" + echo " usage ./asr_test.sh ssb|rade|700D|fargan|4kHz [test option below]" echo " usage ./ota_test.sh ssb --No -30" echo " usage ./ota_test.sh rade --EbNodB 10" echo @@ -128,7 +128,7 @@ END function process { pushd $source > /dev/null; flac=$(find . -name '*.flac'); popd > /dev/null if [ $n_samples -ne 0 ]; then - flac=$(echo "$flac" | head -n $n_samples) + flac=$(echo "$flac" | shuf --random-source=<(yes 42) | head -n $n_samples) fi n=$(echo "$flac" | wc -l) @@ -183,6 +183,42 @@ function process { fi fi + if [ $mode == "700D" ]; then + + fading_adv=0 + for f in $flac + do + d=$(dirname $f) + mkdir -p ${dest}/${d} + + # silence either side of sample to allow time for acquisition and latency + sox /tmp/silence.wav /tmp/silence.wav ${source}/${f} /tmp/silence.wav -t .s16 -r 8000 ${in} + + # trim start to remove acquisition noise + freedv_tx 700D ${in} - | \ + ch - - --No ${No} ${ch_args} --fading_adv ${fading_adv} 2>${ch_log} | \ + freedv_rx 700D - out.raw 2>/dev/null + cat out.raw | sox -t .s16 -r 8000 -c 1 - -r 16000 ${dest}/${f} trim 0.5 + # error check + grep "Fading file finished" $ch_log + if [ $? -eq 0 ]; then + echo "Error - fading file too short after" $fading_adv " seconds" + exit 1 + fi + snr=$(cat $ch_log | grep "SNR3k" | tr -s ' ' | cut -d' ' -f3) + CNo=$(cat $ch_log | grep "SNR3k" | tr -s ' ' | cut -d' ' -f5) + echo $snr >> ${snr_log} + echo $CNo >> ${CNo_log} + + # advance through fading simulation file + dur=$(sox --info -D ${source}/${f}) + fading_adv=$(python3 -c "print(${fading_adv} + ${dur})") + + done + SNR_mean=$(print_mean_text_file ${snr_log}) + CNo_mean=$(print_mean_text_file ${CNo_log}) + fi + if [ $mode == "rade" ] || [ $mode == "fargan" ]; then # find length of each file duration_log="" @@ -249,7 +285,7 @@ function process { python3 asr_wer.py test-other -n $n_samples --model turbo | tee > $asr_log wer=$(tail -n1 $asr_log | tr -s ' ' | cut -d' ' -f2) - if [ $mode == "ssb" ] || [ $mode == "rade" ]; then + if [ $mode == "ssb" ] || [ $mode == "rade" ] || [ $mode == "700D" ]; then printf "%-6s %5.2f %5.2f %5.2f\n" $mode $SNR_mean $CNo_mean $wer | tee -a $results else printf "%-6s %5.2f\n" $mode $wer | tee -a $results diff --git a/asr_test_top.sh b/asr_test_top.sh index 13e80ae..41631e0 100755 --- a/asr_test_top.sh +++ b/asr_test_top.sh @@ -28,6 +28,23 @@ function rade { mv tmp.txt ${results_file} } +function freedv_700D { + local results_file=$1 + No_range=$2 + for No in $No_range + do + ./asr_test.sh 700D --No $No -n $n --results ${results_file} $3 + done + cat ${results_file} | grep 700D | sed -e "s/700D//" > tmp.txt + mv tmp.txt ${results_file} +} + +freedv_700D ${results_file}_awgn_700D.txt "-100 -30 -26 -23 -20 -17 -15 -13" +freedv_700D ${results_file}_mpp_700D.txt "-100 -39 -36 -33 -30 -27" "--g_file g_mpp.f32" +#freedv_700D ${results_file}_awgn_700D.txt "-100 -38 -35 -32 -29 -26 -23 -20 -17" +#freedv_700D ${results_file}_mpp_700D.txt "-100 -44 -39 -36 -33 -30 -27" "--g_file g_mpp.f32" +exit 0 + # run the controls controls_file=${results_file}_controls.txt rm -f ${controls_file} @@ -39,7 +56,9 @@ rm -f ${controls_file} # strip off all but last column for Octave plotting cat ${controls_file} | awk '{print $NF}' > ${results_file}_c.txt + ssb ${results_file}_awgn_ssb.txt "-100 -38 -35 -32 -29 -26 -23 -20 -17" rade ${results_file}_awgn_rade.txt "100 15 10 5 2.5 0 -2.5" ssb ${results_file}_mpp_ssb.txt "-100 -44 -39 -36 -33 -30 -27" "--g_file g_mpp.f32" rade ${results_file}_mpp_rade.txt "100 15 10 5 2.5 0" "--g_file g_mpp.f32" + diff --git a/radae_plots.m b/radae_plots.m index 37163bc..596f351 100644 --- a/radae_plots.m +++ b/radae_plots.m @@ -488,14 +488,18 @@ function plot_sample_spec(wav_fn,png_spec_fn="") function plot_wer(prefix_fn, png_fn="", epslatex="") ssb_awgn_fn = sprintf("%s_asr_awgn_ssb.txt",prefix_fn); rade_awgn_fn = sprintf("%s_asr_awgn_rade.txt",prefix_fn); + freedv_700D_awgn_fn = sprintf("%s_asr_awgn_700D.txt",prefix_fn); ssb_mpp_fn = sprintf("%s_asr_mpp_ssb.txt",prefix_fn); rade_mpp_fn = sprintf("%s_asr_mpp_rade.txt",prefix_fn); + freedv_700D_mpp_fn = sprintf("%s_asr_mpp_700D.txt",prefix_fn); controls_fn = sprintf("%s_asr_c.txt",prefix_fn); ssb_awgn = load(ssb_awgn_fn); rade_awgn = load(rade_awgn_fn); + freedv_700D_awgn = load(freedv_700D_awgn_fn); ssb_mpp = load(ssb_mpp_fn); rade_mpp = load(rade_mpp_fn); + freedv_700D_mpp = load(freedv_700D_mpp_fn); c = load(controls_fn); if length(epslatex) @@ -507,11 +511,13 @@ function plot_wer(prefix_fn, png_fn="", epslatex="") plot(ssb_awgn(:,2),ssb_awgn(:,3),'b+-;SSB AWGN;'); hold on; plot(rade_awgn(:,2),rade_awgn(:,3),'g+-;RADE AWGN;'); + plot(freedv_700D_awgn(:,2),freedv_700D_awgn(:,3),'r+-;700D AWGN;'); plot(ssb_mpp(:,2),ssb_mpp(:,3),'bo--;SSB MPP;'); plot(rade_mpp(:,2),rade_mpp(:,3),'go--;RADE MPP;'); + plot(freedv_700D_mpp(:,2),freedv_700D_mpp(:,3),'ro--;700D MPP;'); xmin=30; xmax=60; plot(xmax-5,c(1),'cx;clean;') - plot(xmax-5,c(2),'ro;FARGAN;') + plot(xmax-5,c(2),'mo;FARGAN;') plot(xmax-5,c(3),'k+;4kHz;') hold off; axis([xmin,xmax,0,40]); grid; ylabel('WER \%'); xlabel("C/No (dB)"); @@ -520,20 +526,22 @@ function plot_wer(prefix_fn, png_fn="", epslatex="") figure(2); clf; plot(ssb_awgn(:,1),ssb_awgn(:,3),'b+-;SSB AWGN;'); hold on; - plot(rade_awgn(:,1),rade_awgn(:,3),'g+-;RADE AWGN;'); + plot(rade_awgn(:,1),rade_awgn(:,3),'r+-;RADE AWGN;'); + plot(freedv_700D_awgn(:,1),freedv_700D_awgn(:,3),'g+-;700D AWGN;'); plot(ssb_mpp(:,1),ssb_mpp(:,3),'bo--;SSB MPP;'); - plot(rade_mpp(:,1),rade_mpp(:,3),'go--;RADE MPP;'); + plot(rade_mpp(:,1),rade_mpp(:,3),'ro--;RADE MPP;'); + plot(freedv_700D_mpp(:,1),freedv_700D_mpp(:,3),'go--;700D MPP;'); xmin=-5; xmax=20; + plot(xmax-5,c(2),'mo;FARGAN;') plot(xmax-5,c(1),'cx;clean;') - plot(xmax-5,c(2),'ro;FARGAN;') - plot(xmax-5,c(3),'k+;4kHz;') hold off; axis([xmin,xmax,0,40]); grid; ylabel('WER (\%)'); xlabel("SNR3k (dB)"); + legend('boxoff'); legend("left"); if length(png_fn) print("-dpng",png_fn,"-S800,600"); end if length(epslatex) - print_eps_restore(epslatex,"-S250,200",textfontsize,linewidth); + print_eps_restore(epslatex,"-S250,250",textfontsize,linewidth); end end From c6803185dd08cb7b105f1507a91ece394ef853b9 Mon Sep 17 00:00:00 2001 From: David Date: Mon, 14 Apr 2025 11:22:32 +0930 Subject: [PATCH 16/20] trying solid line on FARGAN and clean control points --- radae_plots.m | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/radae_plots.m b/radae_plots.m index 596f351..92cfff7 100644 --- a/radae_plots.m +++ b/radae_plots.m @@ -532,8 +532,10 @@ function plot_wer(prefix_fn, png_fn="", epslatex="") plot(rade_mpp(:,1),rade_mpp(:,3),'ro--;RADE MPP;'); plot(freedv_700D_mpp(:,1),freedv_700D_mpp(:,3),'go--;700D MPP;'); xmin=-5; xmax=20; - plot(xmax-5,c(2),'mo;FARGAN;') - plot(xmax-5,c(1),'cx;clean;') + # plot(xmax-5,c(2),'mo;FARGAN;') + # plot(xmax-5,c(1),'cx;clean;') + plot([xmin xmax],[c(2) c(2)],'m-;FARGAN;') + plot([xmin xmax],[c(1) c(1)],'c-;clean;') hold off; axis([xmin,xmax,0,40]); grid; ylabel('WER (\%)'); xlabel("SNR3k (dB)"); legend('boxoff'); legend("left"); From c168ca43df57078805d27af5d9b3ec578587be9f Mon Sep 17 00:00:00 2001 From: David Date: Fri, 2 May 2025 11:05:25 +0930 Subject: [PATCH 17/20] --print_frame option to dump OFDM modem frame --- inference.py | 4 +++- radae/radae.py | 21 +++++++++++++++++++-- train.py | 4 +++- 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/inference.py b/inference.py index f66a18c..62e580f 100644 --- a/inference.py +++ b/inference.py @@ -75,6 +75,7 @@ parser.add_argument('--sine_amp', type=float, default=0.0, help='single freq interferer level (default zero)') parser.add_argument('--sine_freq', type=float, default=1000.0, help='single freq interferer freq (default 1000Hz)') parser.add_argument('--auxdata', action='store_true', help='inject auxillary data symbol') +parser.add_argument('--print_frame', action='store_true', help='print OFDM modem frame and exit') args = parser.parse_args() if len(args.h_file): @@ -101,7 +102,8 @@ phase_offset=args.phase_offset, freq_offset=args.freq_offset, df_dt=args.df_dt, gain=args.gain, pilots=args.pilots, pilot_eq=args.pilot_eq, eq_mean6 = not args.eq_ls, cyclic_prefix = args.cp, time_offset=args.time_offset, coarse_mag=args.coarse_mag, - bottleneck=args.bottleneck, correct_freq_offset=args.correct_freq_offset) + bottleneck=args.bottleneck, correct_freq_offset=args.correct_freq_offset, + print_frame=args.print_frame) checkpoint = torch.load(args.model_name, map_location='cpu',weights_only=True) model.load_state_dict(checkpoint['state_dict'], strict=False) checkpoint['state_dict'] = model.state_dict() diff --git a/radae/radae.py b/radae/radae.py index 09fbe27..ff007a7 100644 --- a/radae/radae.py +++ b/radae/radae.py @@ -80,7 +80,8 @@ def __init__(self, time_offset = 0, coarse_mag = False, correct_freq_offset = False, - stateful_decoder = False + stateful_decoder = False, + print_frame = False ): super(RADAE, self).__init__() @@ -110,6 +111,7 @@ def __init__(self, self.coarse_mag = coarse_mag self.correct_freq_offset = correct_freq_offset self.stateful_decoder = stateful_decoder + self.print_frame = print_frame # TODO: nn.DataParallel() shouldn't be needed self.core_encoder = nn.DataParallel(radae_base.CoreEncoder(feature_dim, latent_dim, bottleneck=bottleneck)) @@ -455,8 +457,12 @@ def forward(self, features, H, G=None): # run encoder, outputs sequence of latents that each describe 40ms of speech z = self.core_encoder(features) + if self.ber_test: z = torch.sign(torch.rand_like(z)-0.5) + if self.print_frame: + # replace z with element indexes + z[:,:,:] = torch.arange(0,self.latent_dim, dtype=torch.float32) # map z to QPSK symbols, note Es = var(tx_sym) = 2 var(z) = 2 # assuming |z| ~ 1 after training @@ -469,7 +475,7 @@ def forward(self, features, H, G=None): # reshape into sequence of OFDM modem frames tx_sym = torch.reshape(tx_sym,(num_batches,num_timesteps_at_rate_Rs,self.Nc)) - + # optionally insert pilot symbols, at the start of each modem frame if self.pilots: num_modem_frames = num_timesteps_at_rate_Rs // self.Ns @@ -480,6 +486,17 @@ def forward(self, features, H, G=None): num_timesteps_at_rate_Rs = num_timesteps_at_rate_Rs + num_modem_frames tx_sym = torch.reshape(tx_sym_pilots,(num_batches, num_timesteps_at_rate_Rs, self.Nc)) + # optionally print modem frame + if self.print_frame: + Ns = self.Ns + if self.pilots: + Ns += 1 + for c in range(self.Nc): + for t in range(Ns): + print(f"{tx_sym[0,t,c]:5.0f}\t", end='', file=sys.stderr) + print(file=sys.stderr) + quit() + tx_before_channel = None rx = None if self.rate_Fs: diff --git a/train.py b/train.py index 2c934a9..1b24ad3 100644 --- a/train.py +++ b/train.py @@ -71,6 +71,7 @@ training_group.add_argument('--plot_loss', action='store_true', help='plot loss versus epoch as we train') training_group.add_argument('--plot_EqNo', type=str, default="", help='plot loss versus Eq/No for final epoch') training_group.add_argument('--auxdata', action='store_true', help='inject auxillary data symbol') +training_group.add_argument('--print_frame', action='store_true', help='print OFDM modem frame and exit') args = parser.parse_args() @@ -123,7 +124,8 @@ rate_Fs = args.rate_Fs, range_EbNo_start=args.range_EbNo_start, freq_rand=args.freq_rand,gain_rand=args.gain_rand, bottleneck=args.bottleneck, - pilots=args.pilots, pilot_eq=args.pilot_eq, eq_mean6 = not args.eq_ls, cyclic_prefix = args.cp) + pilots=args.pilots, pilot_eq=args.pilot_eq, eq_mean6 = not args.eq_ls, + cyclic_prefix = args.cp, print_frame=args.print_frame) if type(args.initial_checkpoint) != type(None): print(f"Loading from checkpoint: {args.initial_checkpoint}") From 70bd4f4f989ab59a241f6359555787a9784b5f5f Mon Sep 17 00:00:00 2001 From: David Date: Sat, 3 May 2025 07:14:59 +0930 Subject: [PATCH 18/20] first pass at candidate2 waveform design where symbol mapping is same for train and run time --- ...-032 Radio Autoencoder Waveform Design.ods | Bin 26204 -> 27031 bytes radae/radae.py | 8 +++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/doc/FreeDV-032 Radio Autoencoder Waveform Design.ods b/doc/FreeDV-032 Radio Autoencoder Waveform Design.ods index d4c52edf3c7d4abb24c0cf2c53a9bab5ea26ac19..fdfb04772675fe60063c670add14a6d7f7864cc7 100644 GIT binary patch delta 10120 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 delta 9231 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 diff --git a/radae/radae.py b/radae/radae.py index ff007a7..594142c 100644 --- a/radae/radae.py +++ b/radae/radae.py @@ -136,10 +136,11 @@ def __init__(self, # wide in frequency and Ns symbols in duration bps = 2 # BPSK symbols per QPSK symbol + # TODO: need a better way to set this up that is backwards compatable with RADE V1 if self.pilots: - Ts = 0.03 # OFDM QPSK symbol period (without pilots or CP) + Ts = 0.04 # OFDM QPSK symbol period else: - Ts = 0.02 + Ts = 0.04 Rs = 1/Ts # OFDM QPSK symbol rate Nzmf = 3 # number of latent vectors in a modem frame Nsmf = Nzmf*self.latent_dim // bps # total number of QPSK symbols in a modem frame across all carriers @@ -220,7 +221,7 @@ def __init__(self, eoo = torch.tanh(torch.abs(eoo)) * torch.exp(1j*torch.angle(eoo)) self.eoo = eoo - print(f"Rs: {Rs:5.2f} Rs': {Rs_dash:5.2f} Ts': {Ts_dash:5.3f} Nsmf: {Nsmf:3d} Ns: {Ns:3d} Nc: {Nc:3d} M: {self.M:d} Ncp: {self.Ncp:d}", file=sys.stderr) + print(f"d: {latent_dim:3d} Rs: {Rs:5.2f} Rs': {Rs_dash:5.2f} Ts': {Ts_dash:5.3f} Nsmf: {Nsmf:3d} Ns: {Ns:3d} Nc: {Nc:3d} M: {self.M:d} Ncp: {self.Ncp:d}", file=sys.stderr) self.Tmf = Tmf self.bps = bps @@ -491,6 +492,7 @@ def forward(self, features, H, G=None): Ns = self.Ns if self.pilots: Ns += 1 + print(file=sys.stderr) for c in range(self.Nc): for t in range(Ns): print(f"{tx_sym[0,t,c]:5.0f}\t", end='', file=sys.stderr) From 5516122fed87e095405341d0f5d223d98f8feb01 Mon Sep 17 00:00:00 2001 From: David Date: Mon, 5 May 2025 06:34:46 +0930 Subject: [PATCH 19/20] symbol-carrier mapping test plots - no improvement found --- compare_models_inf.sh | 253 ++++++++++++++++++++++++++++++++++++++++++ radae_plots.m | 39 +++++++ 2 files changed, 292 insertions(+) create mode 100755 compare_models_inf.sh diff --git a/compare_models_inf.sh b/compare_models_inf.sh new file mode 100755 index 0000000..9f6fe7c --- /dev/null +++ b/compare_models_inf.sh @@ -0,0 +1,253 @@ +#!/bin/bash -x +# +# Compare models by plotting loss v SNR/PSNR curves from data generated by inference.py +# Similar to compare_models.sh, but uses time domain path in forward() that is closer to the +# real world configuration, e.g. time domain multipath model, cyclic prefix, ISI, pilot +# insertion and DSP EQ (if used). + +# Build an input test file from Librispeech +function build_input_file_from_librispeech() { + n_samples=$1 + input_file=$2 + source=~/.cache/LibriSpeech/test-clean + if [ ! -d $source ]; then + echo "cant find Librispeech source directory" $source + exit 1 + fi + flac=$(find ${source} -name '*.flac') + # randomise selection of files so we don't get them all from one speaker, + # --random-source makes it the same repeatable random sequence so we get the + # same results on each run + flac=$(echo "$flac" | shuf --random-source=<(yes 42) | head -n $n_samples) + n=$(echo "$flac" | wc -l) + printf "Collecting %d samples from Librispeech\n" $n + sox ${flac} -c 1 -r 16000 ${input_file} + dur=$(sox --info -D ${input_file}) + printf "%s duration %d\n" $input_file $dur +} + +# Run inference on a range of SNRs to compute loss +function run_model() { + model=$1 + dim=$2 + epoch=$3 + chan=$4 + freq_offset=$5 + shift + shift + shift + shift + shift + EbNodB_list='-3 0 3 6 9 12 15 18 21' + results=${model}_${chan}_${freq_offset}Hz_loss_SNR3k.txt + + # return if results file already exists + if [ $rebuild -eq 0 ]; then + if [ -f $results ]; then + return + fi + fi + + rm -f $results + for aEbNodB in $EbNodB_list + do + log=$(./inference.sh ${model}/checkpoints/checkpoint_epoch_${epoch}.pth ${input_file} /dev/null --bottleneck 3 --rate_Fs \ + --latent-dim ${dim} $@ --EbNodB ${aEbNodB} --freq_offset ${freq_offset}) + SNR3k=$(echo "$log" | grep "Measured:" | tr -s ' ' | cut -d' ' -f4) + PAPR=$(echo "$log" | grep "Measured:" | tr -s ' ' | cut -d' ' -f5) + loss=$(echo "$log" | grep "loss:" | tr -s ' ' | cut -d' ' -f2) + printf "%f\t%f\t%f\n" $SNR3k $loss $PAPR >> $results + done +} + +function print_help { + echo + echo " Compare models by plotting loss v SNR/PSNR curves from time domain inference" + echo + echo " usage ./compare_models_inf.sh [-n NumberSpeechSamples] [-p plotName] [-r]" + echo + echo " -n NumberSpeechSamples Use a low number (e.g. 10) when testing plots" + echo " -p Name of plot (see source)" + echo " -r Rebuild results files even if they already exists (e.g. if -n has changed)" + echo + exit +} + +# default is about 10 minutes long +n_samples=80 +input_file="wav/librispeech.wav" +plot="250413_inf" +rebuild=0 + +POSITIONAL=() +while [[ $# -gt 0 ]] +do +key="$1" +case $key in + -n) + n_samples="$2" + shift + shift + ;; + -p) + plot="$2" + shift + shift + ;; + -r) + rebuild=1 + shift + ;; + -h) + print_help + ;; + *) + POSITIONAL+=("$1") # save it in an array for later + shift + ;; +esac +done +set -- "${POSITIONAL[@]}" # restore positional parameters + +build_input_file_from_librispeech ${n_samples} ${input_file} + + +# compare RADE V1 to 250227b +if [ $plot == "250227b_inf" ]; then + #run_model model19_check3 80 100 awgn --tanh_clipper --cp 0.004 --time_offset -16 --auxdata --pilots --pilot_eq --eq_ls + #run_model model19_check3 80 100 mpp --tanh_clipper --cp 0.004 --time_offset -16 --auxdata --pilots --pilot_eq --eq_ls --g_file g_mpp.f32 + #run_model 250227b_test 40 200 awgn --cp 0.004 --time_offset -16 --correct_time_offset -32 + #run_model 250227b_test 40 200 mpp --cp 0.004 --time_offset -16 --correct_time_offset -32 --g_file g_mpp.f32 + + model_list='model19_check3_awgn model19_check3_mpp 250227b_test_awgn 250227b_test_mpp' + declare -a model_legend=("model19_check3 AWGN Nc=30" "model19_check3 MPP Nc=30" "250227b_test AWGN Nc=10" "250227b_test MPP Nc=10") +fi + +# comparsion of models trained with and without --freq_rand and --auxdata, 0 Hz freq offset +if [ $plot == "250412_inf" ]; then + #run_model model19_check3 80 100 awgn 0 --tanh_clipper --cp 0.004 --time_offset -16 --auxdata --pilots --pilot_eq --eq_ls + #run_model 2504227b_test 40 200 awgn 0 --cp 0.004 --time_offset -16 --correct_time_offset -32 + #run_model 250411 40 200 awgn 0 --cp 0.004 --time_offset -16 --correct_time_offset -32 --auxdata + #run_model 250411 40 200 awgn 1 --cp 0.004 --time_offset -16 --correct_time_offset -32 --auxdata + #run_model 250411 40 200 awgn 2 --cp 0.004 --time_offset -16 --correct_time_offset -32 --auxdata + #run_model 250411 40 200 awgn 3 --cp 0.004 --time_offset -16 --correct_time_offset -32 --auxdata + #run_model 250411b 40 200 awgn 0 --cp 0.004 --time_offset -16 --correct_time_offset -32 + #run_model 250412 40 200 awgn 0 --cp 0.004 --time_offset -16 --correct_time_offset -32 --auxdata + + model_list='model19_check3_awgn_0Hz 250227b_test_awgn_0Hz 250411_awgn_0Hz 250411b_awgn_0Hz 250412_awgn_0Hz' + declare -a model_legend=("model19_check3" "250227b_test" "250411 --freq_rand --auxdata" "250411b --freq_rand" "250412 repeat of 250227b") +fi + +# compare RADE V1 to 250411 which can handle +/-2 Hz, although all curves tested here at 0 Hz offset +if [ $plot == "250413_inf" ]; then + run_model model19_check3 80 100 awgn 0 --tanh_clipper --cp 0.004 --time_offset -16 --auxdata --pilots --pilot_eq --eq_ls + run_model model19_check3 80 100 mpp 0 --tanh_clipper --cp 0.004 --time_offset -16 --auxdata --pilots --pilot_eq --eq_ls --g_file g_mpp.f32 + run_model 250411 40 200 awgn 0 --cp 0.004 --time_offset -16 --correct_time_offset -32 --auxdata + run_model 250411 40 200 mpp 0 --cp 0.004 --time_offset -16 --correct_time_offset -32 --g_file g_mpp.f32 --auxdata + + model_list='model19_check3_awgn_0Hz model19_check3_mpp_0Hz 250411_awgn_0Hz 250411_mpp_0Hz' + declare -a model_legend=("model19_check3 AWGN Nc=30" "model19_check3 MPP Nc=30" "250411 AWGN Nc=10" "250411 MPP Nc=10") +fi + +# 250411 at different freq offsets +if [ $plot == "250413a_inf" ]; then + run_model 250411 40 200 awgn 0 --cp 0.004 --time_offset -16 --correct_time_offset -32 --auxdata + run_model 250411 40 200 awgn 2 --cp 0.004 --time_offset -16 --correct_time_offset -32 --auxdata + run_model 250411 40 200 awgn -2 --cp 0.004 --time_offset -16 --correct_time_offset -32 --auxdata + run_model 250411 40 200 mpp 0 --cp 0.004 --time_offset -16 --correct_time_offset -32 --g_file g_mpp.f32 --auxdata + run_model 250411 40 200 mpp 2 --cp 0.004 --time_offset -16 --correct_time_offset -32 --g_file g_mpp.f32 --auxdata + run_model 250411 40 200 mpp -2 --cp 0.004 --time_offset -16 --correct_time_offset -32 --g_file g_mpp.f32 --auxdata + + model_list='250411_awgn_0Hz 250411_awgn_2Hz 250411_awgn_-2Hz 250411_mpp_0Hz 250411_mpp_2Hz 250411_mpp_-2Hz' + declare -a model_legend=("250411 AWGN 0 Hz" "250411 AWGN 2 Hz" "250411 AWGN -2 Hz" "250411 MPP 0 Hz" "250411 MPP 2 Hz" "250411 MPP -2 Hz") +fi + +# Basic AWGN test of frame step 2 models +if [ $plot == "250416_inf" ]; then + run_model model19_check3 80 100 awgn 0 --tanh_clipper --cp 0.004 --time_offset -16 --auxdata --pilots --pilot_eq --eq_ls + run_model 250411 40 200 awgn 0 --cp 0.004 --time_offset -16 --correct_time_offset -32 --auxdata + run_model 250413_test 20 200 awgn 0 --cp 0.004 --time_offset -16 --correct_time_offset -32 --auxdata --frames_per_step 2 + run_model 250416_test 40 200 awgn 0 --cp 0.004 --time_offset -16 --correct_time_offset -32 --auxdata --frames_per_step 2 + + model_list='model19_check3_awgn_0Hz 250411_awgn_0Hz 250413_test_awgn_0Hz 250416_test_awgn_0Hz' + declare -a model_legend=("model19_check3 AWGN fs=4 d=80 Nc=30" "250411 AWGN fs=4 d=40 Nc=10" "250413_test AWGN fs=2 d=20 Nc=10" \ + "250416_test AWGN fs=2 d=40 Nc=20 start=-3dB") +fi + +# compare RADE V1 to 250415, poor MPP results as expected as not trained correctly. +if [ $plot == "250416b_inf" ]; then + run_model model19_check3 80 100 awgn 0 --tanh_clipper --cp 0.004 --time_offset -16 --auxdata --pilots --pilot_eq --eq_ls + run_model model19_check3 80 100 mpp 0 --tanh_clipper --cp 0.004 --time_offset -16 --auxdata --pilots --pilot_eq --eq_ls --g_file g_mpp.f32 + run_model 250415_test 40 200 awgn 0 --cp 0.004 --time_offset -16 --correct_time_offset -32 --auxdata --frames_per_step 2 + run_model 250415_test 40 200 mpp 0 --cp 0.004 --time_offset -16 --correct_time_offset -32 --auxdata --frames_per_step 2 --g_file g_mpp.f32 + + model_list='model19_check3_awgn_0Hz model19_check3_mpp_0Hz 250415_test_awgn_0Hz 250415_test_mpp_0Hz' + declare -a model_legend=("model19_check3 AWGN fs=4 d=80 Nc=30" "model19_check3 MPP fs=4 d=80 Nc=30" "250415 AWGN fs=2 d=40 Nc=20" "250415 MPP fs=2 d=40 Nc=20") +fi + +# compare RADE V1 to framestep 2 250416, MPP results a bit worse than RADE V1 at high SNR. Perhaps this is a result of the lower frame step +if [ $plot == "250416a_inf" ]; then + run_model model19_check3 80 100 awgn 0 --tanh_clipper --cp 0.004 --time_offset -16 --auxdata --pilots --pilot_eq --eq_ls + run_model model19_check3 80 100 mpp 0 --tanh_clipper --cp 0.004 --time_offset -16 --auxdata --pilots --pilot_eq --eq_ls --g_file g_mpp.f32 + run_model 250416_test 40 200 awgn 0 --cp 0.004 --time_offset -16 --correct_time_offset -32 --auxdata --frames_per_step 2 + run_model 250416a_test 80 200 awgn 0 --cp 0.004 --time_offset -16 --correct_time_offset -32 --auxdata --frames_per_step 8 + run_model 250416_test 40 200 mpp 0 --cp 0.004 --time_offset -16 --correct_time_offset -32 --auxdata --frames_per_step 2 --g_file g_mpp.f32 + run_model 250416a_test 80 200 mpp 0 --cp 0.004 --time_offset -16 --correct_time_offset -32 --auxdata --frames_per_step 8 --g_file g_mpp.f32 + + model_list='model19_check3_awgn_0Hz model19_check3_mpp_0Hz 250416_test_awgn_0Hz 250416_test_mpp_0Hz 250416a_test_awgn_0Hz 250416a_test_mpp_0Hz' + declare -a model_legend=("model19_check3 AWGN fs=4 d=80 Nc=30" "model19_check3 MPP fs=4 d=80 Nc=30" "250416 AWGN fs=2 d=40 Nc=20" \ + "250416 MPP fs=2 d=40 Nc=20" "250416a AWGN fs=8 d=80 Nc=10" "250416a MPP fs=8 d=80 Nc=10" ) +fi + +# compare RADE V1 to framestep 2,d=20,Nc=10 250413 +if [ $plot == "250417_inf" ]; then + run_model model19_check3 80 100 awgn 0 --tanh_clipper --cp 0.004 --time_offset -16 --auxdata --pilots --pilot_eq --eq_ls + run_model model19_check3 80 100 mpp 0 --tanh_clipper --cp 0.004 --time_offset -16 --auxdata --pilots --pilot_eq --eq_ls --g_file g_mpp.f32 + run_model 250413_test 20 200 awgn 0 --cp 0.004 --time_offset -16 --correct_time_offset -32 --auxdata --frames_per_step 2 + run_model 250413_test 20 200 mpp 0 --cp 0.004 --time_offset -16 --correct_time_offset -32 --auxdata --frames_per_step 2 --g_file g_mpp.f32 + + model_list='model19_check3_awgn_0Hz model19_check3_mpp_0Hz 250413_test_awgn_0Hz 250413_test_mpp_0Hz' + declare -a model_legend=("model19_check3 AWGN fs=4 d=80 Nc=30" "model19_check3 MPP fs=4 d=80 Nc=30" \ + "250413 AWGN fs=2 d=20 Nc=10" "250413 MPP fs=2 d=20 Nc=10" ) +fi + +# compare RADE V1 to framestep 4,d=10,Nc=10 250417 (240411 repeat with frame step arg) +if [ $plot == "250417a_inf" ]; then + run_model model19_check3 80 100 awgn 0 --tanh_clipper --cp 0.004 --time_offset -16 --auxdata --pilots --pilot_eq --eq_ls + run_model model19_check3 80 100 mpp 0 --tanh_clipper --cp 0.004 --time_offset -16 --auxdata --pilots --pilot_eq --eq_ls --g_file g_mpp.f32 + run_model 250417_test 40 200 awgn 0 --cp 0.004 --time_offset -16 --correct_time_offset -32 --auxdata + run_model 250417_test 40 200 mpp 0 --cp 0.004 --time_offset -16 --correct_time_offset -32 --auxdata --g_file g_mpp.f32 + run_model 250417a_test 40 200 awgn 0 --cp 0.004 --time_offset -16 --correct_time_offset -32 --auxdata + run_model 250417a_test 40 200 mpp 0 --cp 0.004 --time_offset -16 --correct_time_offset -32 --auxdata --g_file g_mpp.f32 + + model_list='model19_check3_awgn_0Hz model19_check3_mpp_0Hz 250417_test_awgn_0Hz 250417_test_mpp_0Hz 250417a_test_awgn_0Hz 250417a_test_mpp_0Hz' + declare -a model_legend=("model19_check3 AWGN fs=4 d=80 Nc=30" "model19_check3 MPP fs=4 d=80 Nc=30" \ + "250417 AWGN fs=4 d=40 Nc=10" "250417 MPP fs=4 d=40 Nc=10" \ + "250417a AWGN fs=4 d=40 Nc=10" "250417a MPP fs=4 d=40 Nc=10") +fi + +# compare RADE V1 candidate2 to RADE V1 candidate3 (prototyped in dr-asr branch). Tracking down training issue +# discovered in HF RADE paper review, mapping of z elements to symbols. +if [ $plot == "250503_inf" ]; then + run_model model19_check3 80 100 awgn 0 --cp 0.004 --time_offset -16 --auxdata --pilots --pilot_eq --eq_ls + run_model model19_check3 80 100 mpp 0 --cp 0.004 --time_offset -16 --auxdata --pilots --pilot_eq --eq_ls --g_file g_mpp.f32 + run_model 250502 60 200 awgn 0 --cp 0.004 --time_offset -16 --auxdata --pilots --pilot_eq --eq_ls + run_model 250502 60 200 mpp 0 --cp 0.004 --time_offset -16 --auxdata --pilots --pilot_eq --eq_ls --g_file g_mpp.f32 + + model_list='model19_check3_awgn_0Hz model19_check3_mpp_0Hz 250502_awgn_0Hz 250502_mpp_0Hz' + declare -a model_legend=("cand2 model19_check3 AWGN d=80 Nc=30" "cand2 model19_check3 MPP d=80 Nc=30" \ + "cand3 250502 AWGN d=60 Nc=30" "cand3 250502 MPP d=60 Nc=30") +fi + +# Generate the plots in PNG and EPS form, file names have suffix of ${plot} +vargs="" +i=0 +for model in $model_list + do + vargs="${vargs},'${model}_loss_SNR3k.txt','${model_legend[i]}'" + ((i++)) + done +echo "radae_plots; loss_SNR3k_plot(psnr=0,'${plot}_loss_SNR3k_models',''${vargs}); quit" | octave-cli -qf # PNG +echo "radae_plots; loss_SNR3k_plot(psnr=1,'${plot}_loss_PNR3k_models',''${vargs}); quit" | octave-cli -qf # PNG +echo "radae_plots; loss_SNR3k_plot(psnr=1,'','${plot}_loss_PNR3k_models'${vargs}); quit" | octave-cli -qf # EPS + diff --git a/radae_plots.m b/radae_plots.m index 92cfff7..84e446d 100644 --- a/radae_plots.m +++ b/radae_plots.m @@ -176,6 +176,45 @@ function loss_CNo_plot(png_fn, Rs, B, varargin) end endfunction +% Plots loss v SNR3k curves from text files dumped by inference.py, see compare_models_inf.py +% pnsr flag optionally includes PAPR +function loss_SNR3k_plot(pnsr=0,png_fn, epslatex, varargin) + if length(epslatex) + [textfontsize linewidth] = set_fonts(20); + end + figure(1); clf; hold on; + i = 1; + mn = 100; + while i <= length(varargin) + fn = varargin{i}; + data = load(fn); + i++; leg = varargin{i}; leg = strrep (leg, "_", " "); + SNR3k = data(:,1); + if pnsr + SNR3k += data(:,3); + end + mn = min([mn; SNR3k]); + plot(SNR3k,data(:,2),sprintf("+-;%s;",leg)) + i++; + end + hold off; grid('minor'); + if pnsr + xlabel('PNR (dB)'); + else + xlabel('SNR (dB)'); + end + ylabel('loss'); + mn = floor(mn); + axis([-5 20 0.05 0.35]) + legend('boxoff'); + if length(png_fn) + print("-dpng",png_fn); + end + if length(epslatex) + print_eps_restore(epslatex,"-S300,300",textfontsize,linewidth); + end +endfunction + % usage: % radae_plots; ofdm_sync_plots("","ofdm_sync.txt","go-;genie;","ofdm_sync_pilot_eq.txt","r+-;mean6;","ofdm_sync_pilot_eq_f2.txt","bx-;mean6 2 Hz;","ofdm_sync_pilot_eq_g0.1.txt","gx-;mean6 gain 0.1;","ofdm_sync_pilot_eq_ls.txt","ro-;LS;","ofdm_sync_pilot_eq_ls_f2.txt","bo-;LS 2 Hz;") From 9389497f366499e8250f41246dff14212e6c58d2 Mon Sep 17 00:00:00 2001 From: David Date: Mon, 5 May 2025 06:39:26 +0930 Subject: [PATCH 20/20] restore hard coded Ts changes back to RADE V1 (candidate2) --- radae/radae.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/radae/radae.py b/radae/radae.py index 594142c..7e08bc1 100644 --- a/radae/radae.py +++ b/radae/radae.py @@ -136,11 +136,11 @@ def __init__(self, # wide in frequency and Ns symbols in duration bps = 2 # BPSK symbols per QPSK symbol - # TODO: need a better way to set this up that is backwards compatable with RADE V1 + # TODO: consider a better way to set this up, e.g. so we can handle candidate2/3 without code changes (if useful in future) if self.pilots: - Ts = 0.04 # OFDM QPSK symbol period + Ts = 0.03 # OFDM QPSK symbol period else: - Ts = 0.04 + Ts = 0.02 Rs = 1/Ts # OFDM QPSK symbol rate Nzmf = 3 # number of latent vectors in a modem frame Nsmf = Nzmf*self.latent_dim // bps # total number of QPSK symbols in a modem frame across all carriers