diff options
| author | Author Name <[email protected]> | 2023-07-07 12:20:59 +0930 |
|---|---|---|
| committer | David Rowe <[email protected]> | 2023-07-07 12:29:06 +0930 |
| commit | ac7c48b4dee99d4c772f133d70d8d1b38262fcd2 (patch) | |
| tree | a2d0ace57a9c0e2e5b611c4987f6fed1b38b81e7 /unittest | |
shallow zip-file copy from codec2 e9d726bf20
Diffstat (limited to 'unittest')
67 files changed, 22967 insertions, 0 deletions
diff --git a/unittest/CMakeLists.txt b/unittest/CMakeLists.txt new file mode 100644 index 0000000..080ebc5 --- /dev/null +++ b/unittest/CMakeLists.txt @@ -0,0 +1,143 @@ +add_definitions(-DFLOATING_POINT -DVAR_ARRAYS) +include_directories(../src) + +add_executable(tfdmdv tfdmdv.c ../src/fdmdv.c ../src/kiss_fft.c ../src/octave.c) +target_link_libraries(tfdmdv codec2) + +add_executable(tcohpsk tcohpsk.c ../src/cohpsk.c ../src/octave.c) +target_link_libraries(tcohpsk codec2) + +add_executable(tfsk tfsk.c ../src/kiss_fft.c ../src/kiss_fftr.c ../src/octave.c ../src/modem_probe.c) +target_link_libraries(tfsk m) + +add_executable(tfreedv_data_channel tfreedv_data_channel.c ../src/freedv_data_channel.c) + +add_executable(tfmfsk tfmfsk.c ../src/octave.c ../src/modem_probe.c) +target_link_libraries(tfmfsk m) + +add_executable(tdeframer tdeframer.c) +target_link_libraries(tdeframer m codec2) + +add_definitions(-DMODEMPROBE_ENABLE -DXXXXX) + +add_executable(tofdm tofdm.c ../src/octave.c) +target_link_libraries(tofdm m codec2) + +add_executable(tofdm_acq tofdm_acq.c ../src/octave.c) +target_link_libraries(tofdm_acq m codec2) + +add_executable(tesno_est tesno_est.c) +target_link_libraries(tesno_est m codec2) + +if(UNIX) # Uses pthreads +add_executable(tfifo tfifo.c ../src/codec2_fifo.c) +target_link_libraries(tfifo codec2 ${CMAKE_THREAD_LIBS_INIT}) +endif() + +add_executable(fdmdv_mem fdmdv_mem.c) + +add_executable(ofdm_mem ofdm_mem.c ../src/ofdm.c ../src/octave.c ../src/kiss_fft.c ../src/modem_probe.c ../src/mpdecode_core.c ../src/phi0.c ../src/filter.c) +target_link_libraries(ofdm_mem m) + +add_library(function_trace STATIC ../unittest/function_trace.c) + +add_executable(ofdm_stack ofdm_stack.c ../src/ofdm.c ../src/octave.c ../src/kiss_fft.c ../src/modem_probe.c ../src/mpdecode_core.c ../src/phi0.c ../src/filter.c) +if (CMAKE_C_COMPILER MATCHES "gcc$") + target_link_libraries(ofdm_stack function_trace m -no-pie "-Wl,-Map=ofdm_stack.map") + target_compile_options(ofdm_stack PUBLIC -fstack-usage -finstrument-functions) +else() + target_link_libraries(ofdm_stack function_trace m -no-pie) + target_compile_options(ofdm_stack PUBLIC -finstrument-functions) +endif() +add_definitions(-D__UNITTEST__) + +add_executable(tnewamp1 tnewamp1.c ../src/quantise.c ../src/newamp1.c ../src/mbest.c ../src/kiss_fft.c ../src/sine.c ../src/nlp.c ../src/dump.c ../src/octave.c ${CODEBOOKS}) +target_link_libraries(tnewamp1 codec2) + +add_executable(compare_ints compare_ints.c) + +add_executable(compare_floats compare_floats.c) + +add_executable(test_phi0 test_phi0.c ../src/phi0.c) +target_link_libraries(test_phi0 m) + +add_executable(tst_codec2_fft_init tst_codec2_fft_init.c) +target_link_libraries(tst_codec2_fft_init m codec2) + +add_executable(tvq_mbest tvq_mbest.c) + +add_executable(tfreedv_800XA_rawdata tfreedv_800XA_rawdata.c) +target_link_libraries(tfreedv_800XA_rawdata codec2) + +add_executable(tfreedv_2400A_rawdata tfreedv_2400A_rawdata.c) +target_link_libraries(tfreedv_2400A_rawdata codec2) + +add_executable(tfreedv_2400B_rawdata tfreedv_2400B_rawdata.c) +target_link_libraries(tfreedv_2400B_rawdata codec2) + +add_executable(tfsk_llr tfsk_llr.c) +target_link_libraries(tfsk_llr codec2 m) + +add_executable(thash thash.c) +target_link_libraries(thash codec2 m) + +add_executable(tqam16 tqam16.c) +target_link_libraries(tqam16 codec2 m) + +add_executable(t16_8 t16_8.c ../src/fdmdv.c ../src/kiss_fft.c) +target_link_libraries(t16_8 codec2) + +add_executable(t16_8_short t16_8_short.c ../src/fdmdv.c ../src/kiss_fft.c) +target_link_libraries(t16_8_short codec2) + +add_executable(t48_8 t48_8.c ../src/fdmdv.c ../src/kiss_fft.c) +target_link_libraries(t48_8 codec2) + +add_executable(t48_8_short t48_8_short.c ../src/fdmdv.c ../src/kiss_fft.c) +target_link_libraries(t48_8_short codec2) + +add_executable(tquisk_filter tquisk_filter.c) +target_link_libraries(tquisk_filter codec2) + +# Build CML as part of unit test setup +find_program(OCTAVE_CMD octave-cli REQUIRED) +message("Octave command: ${OCTAVE_CMD}") + +include(ExternalProject) +set(CML_PATH ${CMAKE_CURRENT_BINARY_DIR}/../cml) +ExternalProject_Add(cml + GIT_REPOSITORY https://github.com/drowe67/cml.git + SOURCE_DIR ${CML_PATH} + BUILD_IN_SOURCE 1 + CONFIGURE_COMMAND true # No configuration required + BUILD_COMMAND cd ${CMAKE_CURRENT_BINARY_DIR}/../cml && make + INSTALL_COMMAND true # No installation required +) + +# Create fading files (used for channel simulation) as part of unit test setup +add_custom_target(fading_files ALL + DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/fast_fading_samples.float + DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/faster_fading_samples.float + ) +add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/fast_fading_samples.float + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/faster_fading_samples.float + COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ./fading_files.sh ${CMAKE_CURRENT_BINARY_DIR} +) + +add_executable(freedv_700d_comptx freedv_700d_comptx.c) +add_executable(freedv_700d_comprx freedv_700d_comprx.c) + +if(LPCNET AND lpcnetfreedv_FOUND) + target_link_libraries(freedv_700d_comptx m codec2 lpcnetfreedv) + target_link_libraries(freedv_700d_comprx m codec2 lpcnetfreedv) +else() + target_link_libraries(freedv_700d_comptx m codec2) + target_link_libraries(freedv_700d_comprx m codec2) +endif() + +add_executable(golay23 ../src/golay23.c) +target_compile_options(golay23 PUBLIC -DGOLAY23_UNITTEST) + +add_executable(golay23_runtime_tables ../src/golay23.c) +target_compile_options(golay23_runtime_tables PUBLIC -DGOLAY23_UNITTEST -DRUN_TIME_TABLES) diff --git a/unittest/check_comp.sh b/unittest/check_comp.sh new file mode 100755 index 0000000..b559a2e --- /dev/null +++ b/unittest/check_comp.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash +# +# Check Octave and C raw data mode waveforms have about the same +# compression - sanity check for C port of raw data modes. +# +# For manual run outside of ctest: +# cd codec/build_linux +# ../unittest/check_comp.sh ${CODEC2} ${PATH}:${CODEC2}/build_linux/src + +CODEC2=$1 +PATH=$2:$PATH +set -x +octave_log=$(mktemp) +ch_log=$(mktemp) + +echo "warning ('off', 'Octave:data-file-in-path'); + ofdm_ldpc_tx('test_datac0.raw','datac0',1,100,'awgn','bursts',10,'txclip'); + quit" | DISPLAY="" octave-cli -p ${CODEC2}/octave 1>${octave_log} +oct_rms=$(cat ${octave_log} | tr -s ' ' | grep 'RMS:' | cut -d' ' -f4) +oct_cpapr=$(cat ${octave_log} | grep 'RMS:' | tr -s ' ' | cut -d' ' -f6) + +freedv_data_raw_tx datac0 /dev/zero - --delay 1000 --testframes 10 --bursts 10 --clip 1 --txbpf 1 | \ +ch - /dev/null 2>${ch_log} +ch_rms=$(cat ${ch_log} | grep RMS | tr -s ' ' | cut -d' ' -f5) +ch_cpapr=$(cat ${ch_log} | grep RMS | tr -s ' ' | cut -d' ' -f7) + +# Allow 5% difference +python3 -c "import sys; sys.exit(0) if abs((${oct_rms} - ${ch_rms})/${oct_rms}) < 0.05 else sys.exit(1)" +python3 -c "import sys; sys.exit(0) if abs((${oct_cpapr} - ${ch_cpapr})/${oct_cpapr}) < 0.05 else sys.exit(1)" diff --git a/unittest/check_peak.sh b/unittest/check_peak.sh new file mode 100755 index 0000000..6462d04 --- /dev/null +++ b/unittest/check_peak.sh @@ -0,0 +1,58 @@ +#!/usr/bin/env bash +# +# Check peak level of each FreeDV waveform is about the same to present +# consistent drive to transmitters. +# +# For manual run outside of ctest: +# cd codec/build_linux +# PATH=${PATH}:${HOME}/codec2/build_linux/src +# ./unittest/check_peak.sh +# OR: +# + +voice_test() { + mode=$1 + echo -n "$mode " + f=$(mktemp) + freedv_tx $mode ../raw/ve9qrp_10s.raw $f --clip 1 + octave_cmd="cd ../octave; + t=load_raw('${f}'); + mx=max(t); printf('%d ',max(t)); + if (mx > 16000) && (mx < 17000) printf('PASS\n') else printf('FAIL\n') end" + octave-cli -qf --eval "$octave_cmd" +} + +data_test() { + mode=$1 + echo -n "$mode " + f=$(mktemp) + freedv_data_raw_tx --framesperburst 2 --bursts 3 --testframes 6 $mode /dev/zero $f 2>/dev/null + octave_cmd="cd ../octave; + t=load_raw('${f}'); + mx=max(t); printf('%d ',max(t)); + if (mx > 16000) && (mx < 17000) printf('PASS\n') else printf('FAIL\n') end" + octave-cli -qf --eval "$octave_cmd" +} + +if [ "$1" == "LPCNet" ]; then + # these don't get run unless we build with LPCNet + voice_test "2020" + voice_test "2020B" + voice_test "2020C" + else + voice_test "1600" + voice_test "700C" + voice_test "700D" + voice_test "700E" + voice_test "800XA" + voice_test "2400A" + voice_test "2400B" + data_test "datac0" + data_test "datac1" + data_test "datac3" + data_test "datac4" + data_test "datac13" +fi + +exit 0 + diff --git a/unittest/check_real_comp.sh b/unittest/check_real_comp.sh new file mode 100755 index 0000000..a7b57db --- /dev/null +++ b/unittest/check_real_comp.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash +# check_real_comp.sh +# Check the output of freedv_tx() and the real part of freedv_comptx() match, +# as they use different code paths. Run from codec2/unittest, set path to +# include codec2/build/misc and codec2/build/unittest + +set -x +cat ../raw/ve9qrp_10s.raw | freedv_700d_tx > tx_700d.int16 +cat ../raw/ve9qrp_10s.raw | freedv_700d_comptx > tx_700d.iq16 + +echo "tx_real=load_raw('tx_700d.int16'); tx_comp=load_raw('tx_700d.iq16'); \ + tx_comp=tx_comp(1:2:end)+j*tx_comp(2:2:end); \ + diff = sum(real(tx_comp)-tx_real); printf('diff: %f\n', diff); \ + if diff < 1, quit(0), end; \ + quit(1)" | octave-cli -p ../octave -qf diff --git a/unittest/compare_floats.c b/unittest/compare_floats.c new file mode 100644 index 0000000..572ce16 --- /dev/null +++ b/unittest/compare_floats.c @@ -0,0 +1,89 @@ +/* compare floats - a test utility */ + +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <getopt.h> +#include <math.h> +#include <errno.h> + +/* Declarations */ + +/* Globals */ + +/* Main */ + +int main(int argc, char *argv[]) { + + char usage[] = "Usage: %s [-t tolerance] file1 file2\n"; + + float tol = .001; + + int opt; + while ((opt = getopt(argc, argv, "t:")) != -1) { + switch (opt) { + case 't': + tol = atof(optarg); + break; + default: + fprintf(stderr, usage, argv[0]); + exit(1); + } + } + + if ((optind + 2) > argc) { + fprintf(stderr, usage, argv[0]); + exit(1); + } + char *fname1 = argv[optind++]; + char *fname2 = argv[optind++]; + + FILE *f1 = fopen(fname1, "rb"); + if (f1 == NULL) { + fprintf(stderr, "Error opening file1 \"%s\": ", fname1); + perror(NULL); + exit(1); + } + + FILE *f2 = fopen(fname2, "rb"); + if (f2 == NULL) { + fprintf(stderr, "Error opening file2 \"%s\": ", fname2); + perror(NULL); + exit(1); + } + + float data1, data2; + int count = 0; + int errors = 0; + double rms_sum = 0; + + while (fread(&data1, sizeof(float), 1, f1)) { + if (!fread(&data2, sizeof(float), 1, f2)) { + fprintf(stderr, "Error: file2 is shorter!"); + exit(1); + } + float err = fabsf((data1 - data2) / data1); + if (err > tol) { + errors ++; + printf("%d %g %g %g\n", count, data1, data2, err); + } + rms_sum += (err * err); + count ++; + } + if (fread(&data2, sizeof(float), 1, f2)) { + fprintf(stderr, "Error: file1 is shorter\n"); + exit(1); + } + + if (errors) { + printf("Fail: %d errors\n", errors); + printf(" rms error = %g\n", ((double)rms_sum/count)); + exit(1); + } + else printf("Pass\n"); + exit(0); + + } // main + + +/* vi:set ts=4 et sts=4: */ diff --git a/unittest/compare_ints.c b/unittest/compare_ints.c new file mode 100644 index 0000000..ef68a06 --- /dev/null +++ b/unittest/compare_ints.c @@ -0,0 +1,159 @@ +/* compare ints - a test utility */ + +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <getopt.h> +#include <errno.h> +#include <inttypes.h> + +/* Declarations */ + +/* Globals */ + +/* Functions */ +int get_data(FILE *f, int64_t *dd, int signed_flag, int bytes) { + int res; + int8_t d_8; + int16_t d_16; + uint8_t d_u8; + uint16_t d_u16; + // TODO Loop on reads until, but catch EOF!! + if (signed_flag) { + switch (bytes) { + case 1: + res = fread(&d_8, bytes, 1, f); + *dd = d_8; + break; + case 2: + res = fread(&d_16, bytes, 1, f); + *dd = d_16; + break; + default: + fprintf(stderr, "Error: unsupported size %d bytes\n", bytes); + exit(1); + } + } + else { // unsigned + switch (bytes) { + case 1: + res = fread(&d_u8, bytes, 1, f); + *dd = d_u8; + break; + case 2: + res = fread(&d_u16, bytes, 1, f); + *dd = d_u16; + break; + default: + fprintf(stderr, "Error: unsupported size %d bytes\n", bytes); + exit(1); + } + } + + if (res != 1) return(0); + else return(1); + } + + +/* Main */ + +int main(int argc, char *argv[]) { + + char usage[] = "Usage: %s [-b size_in_bytes] [-c] [-s] [-t tolerance] [-n numerrorstoexit] file1 file2\n"; + + int bytes = 1; + int count_errors = 0; + int signed_flag = 0; + int tol = 1; + int numerrorstoexit = -1; + + int opt; + while ((opt = getopt(argc, argv, "b:cst:n:")) != -1) { + switch (opt) { + case 'b': + bytes = atoi(optarg); + break; + case 'c': + count_errors = 1; + break; + case 's': + signed_flag = 1; + break; + case 'n': + numerrorstoexit = atoi(optarg); + break; + case 't': + tol = atof(optarg); + break; + default: + fprintf(stderr, usage, argv[0]); + exit(1); + } + } + + if ((optind + 2) > argc) { + fprintf(stderr, usage, argv[0]); + exit(1); + } + char *fname1 = argv[optind++]; + char *fname2 = argv[optind++]; + + FILE *f1 = fopen(fname1, "rb"); + if (f1 == NULL) { + fprintf(stderr, "Error opening file1 \"%s\": ", fname1); + perror(NULL); + exit(1); + } + + FILE *f2 = fopen(fname2, "rb"); + if (f2 == NULL) { + fprintf(stderr, "Error opening file2 \"%s\": ", fname2); + perror(NULL); + exit(1); + } + + // Convert inputs to SIGNED long values + int64_t data1, data2; + + int count = 0; + int errors = 0; + int rms_sum = 0; + + while (get_data(f1, &data1, signed_flag, bytes)) { + if (!get_data(f2, &data2, signed_flag, bytes)) { + fprintf(stderr, "Error: file2 is shorter\n"); + exit(1); + } + uint64_t err = llabs(data1 - data2); + if (err > tol) { + errors ++; + printf("%d %" PRId64 " %" PRId64 "\n", count, data1, data2); + if (numerrorstoexit != -1) + if (errors > numerrorstoexit) { + printf("reached errors: %d, bailing!", numerrorstoexit); + exit(1); + } + } + rms_sum += (err * err); + count ++; + } + if (get_data(f2, &data2, signed_flag, bytes)) { + fprintf(stderr, "Error: file1 is shorter\n"); + exit(1); + } + + if (count_errors) exit(errors); + else { + if (errors) { + printf("Fail: %d errors\n", errors); + printf(" rms error = %f\n", ((double)rms_sum/count)); + exit(1); + } + else printf("Pass\n"); + exit(0); + } + + } // main + + +/* vi:set ts=4 et sts=4: */ diff --git a/unittest/fading_files.sh b/unittest/fading_files.sh new file mode 100755 index 0000000..9fe17f3 --- /dev/null +++ b/unittest/fading_files.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +# +# Generate fading files used for channel simulation + +output_path=$1 +echo "Generating fading files ......" +cmd='cd ../octave; pkg load signal; ch_fading("'${output_path}'/fast_fading_samples.float", 8000, 1.0, 8000*60)' +octave --no-gui -qf --eval "$cmd" +[ ! $? -eq 0 ] && { echo "octave failed to run correctly .... exiting"; exit 1; } +cmd='cd ../octave; pkg load signal; ch_fading("'${output_path}'/faster_fading_samples.float", 8000, 2.0, 8000*60)' +octave --no-gui -qf --eval "$cmd" +[ ! $? -eq 0 ] && { echo "octave failed to run correctly .... exiting"; exit 1; } +exit 0 + diff --git a/unittest/fdmdv_mem.c b/unittest/fdmdv_mem.c new file mode 100644 index 0000000..6bcc6bc --- /dev/null +++ b/unittest/fdmdv_mem.c @@ -0,0 +1,63 @@ +/*---------------------------------------------------------------------------*\ + + FILE........: fdmdv_mem.c + AUTHOR......: David Rowe + DATE CREATED: 25 June 2014 + + Prints out the memory used by the FDMDV modem states. Used to optimise + memory use for the STM32F4 port. + +\*---------------------------------------------------------------------------*/ + +/* + Copyright (C) 2014 David Rowe + + All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 2.1, as + published by the Free Software Foundation. This program is + distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, see <http://www.gnu.org/licenses/>. +*/ + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <math.h> + +#include "fdmdv_internal.h" + +extern float pilot_coeff[]; + +int main(int argc, char *argv[]) +{ + struct FDMDV *fdmdv; + + printf("struct FDMDV..........: %ld\n", sizeof(struct FDMDV)); + printf("prev_tx_symbols.......: %ld\n", sizeof(fdmdv->prev_tx_symbols)); + printf("tx_filter_memory......: %ld\n", sizeof(fdmdv->tx_filter_memory)); + printf("phase_tx..............: %ld\n", sizeof(fdmdv->phase_tx)); + printf("freq..................: %ld\n", sizeof(fdmdv->freq)); + printf("pilot_lut.............: %ld\n", sizeof(fdmdv->pilot_lut)); + printf("pilot_baseband1.......: %ld\n", sizeof(fdmdv->pilot_baseband1)); + printf("pilot_baseband2.......: %ld\n", sizeof(fdmdv->pilot_baseband2)); + printf("pilot_lpf1............: %ld\n", sizeof(fdmdv->pilot_lpf1)); + printf("pilot_lpf2............: %ld\n", sizeof(fdmdv->pilot_lpf2)); + printf("S1....................: %ld\n", sizeof(fdmdv->S1)); + printf("S2....................: %ld\n", sizeof(fdmdv->S2)); + printf("phase_rx..............: %ld\n", sizeof(fdmdv->phase_rx)); + printf("rx_fdm_mem............: %ld\n", sizeof(fdmdv->rx_fdm_mem)); + printf("rx_filter_mem_timing..: %ld\n", sizeof(fdmdv->rx_filter_mem_timing)); + printf("phase_difference......: %ld\n", sizeof(fdmdv->phase_difference)); + printf("prev_rx_symbols.......: %ld\n", sizeof(fdmdv->prev_rx_symbols)); + + return 0; +} + diff --git a/unittest/freedv_700d_comprx.c b/unittest/freedv_700d_comprx.c new file mode 100644 index 0000000..9fdf43b --- /dev/null +++ b/unittest/freedv_700d_comprx.c @@ -0,0 +1,126 @@ +/*---------------------------------------------------------------------------*\ + + FILE........: freedv_700d_comprx.c + AUTHOR......: David Rowe + DATE CREATED: July 2022 + + Complex valued rx to support ctests. Includes a few operations that will + only work if complex Tx and Rx signals are being handled correctly. + +\*---------------------------------------------------------------------------*/ + +#include <assert.h> +#include <stdlib.h> +#include <stdio.h> + +#include "freedv_api.h" +#include "freedv_api_internal.h" +#include "ofdm_internal.h" +#include "codec2_cohpsk.h" +#include "comp_prim.h" + +int main(int argc, char *argv[]) { + + /* with no arguments then run with no test code */ + int test_num = 0; + if (argc == 2) { + if (strcmp(argv[1],"tx") == 0) { + test_num = 1; + } + if (strcmp(argv[1],"rx") == 0) { + test_num = 2; + } + } + fprintf(stderr,"%d\n", test_num); + + struct freedv *freedv; + freedv = freedv_open(FREEDV_MODE_700D); + assert(freedv != NULL); + + /* note API functions to tell us how big our buffers need to be */ + short speech_out[freedv_get_n_max_speech_samples(freedv)]; + short demod_in[2*freedv_get_n_max_modem_samples(freedv)]; + COMP demod_in_comp[2*freedv_get_n_max_modem_samples(freedv)]; + + /* set up small freq offset */ + float foff_hz = 25; + COMP phase_ch; phase_ch.real = 1.0; phase_ch.imag = 0.0; + + /* set complex sine wave interferer at -fc */ + COMP interferer_phase = {1.0,0.0}; + COMP interferer_freq; + interferer_freq.real = cos(2.0*M_PI*freedv->ofdm->tx_centre/FREEDV_FS_8000); + interferer_freq.imag = sin(2.0*M_PI*freedv->ofdm->tx_centre/FREEDV_FS_8000); + interferer_freq = cconj(interferer_freq); + + /* log a file of demod input samples for plotting in Octave */ + FILE *fdemod = fopen("demod.f32","wb"); assert(fdemod != NULL); + + /* measure demod input power, interferer input power */ + float power_d = 0.0; float power_interferer = 0.0; + + int frames = 0, sum_sync = 0, frames_snr = 0; float sum_snr = 0.0; + size_t nin, nout; + nin = freedv_nin(freedv); + + while(fread(demod_in, sizeof(short), 2*nin, stdin) == 2*nin) { + for(int i=0; i<nin; i++) { + demod_in_comp[i].real = (float)demod_in[2*i]; + demod_in_comp[i].imag = (float)demod_in[2*i+1]; + //demod_in_comp[i].imag = 0; + } + + if (test_num == 1) { + /* So Tx is a complex OFDM signal centered at +fc. A small + shift fd followed by Re{} will only work if Tx is complex. + If Tx is real, neg freq components at -fc+fd will be + aliased on top of fc+fd wanted signal by Re{} operation. + This can be tested by setting demod_in_comp[i].imag = 0 + above */ + fdmdv_freq_shift_coh(demod_in_comp, demod_in_comp, foff_hz, FREEDV_FS_8000, &phase_ch, nin); + for(int i=0; i<nin; i++) + demod_in_comp[i].imag = 0.0; + } + + if (test_num == 2) { + /* a complex sinewave (carrier) at -fc will only be ignored if + Rx is treating signal as complex, otherwise if real a +fc + alias will appear in the middle of our wanted signal at + +fc, this can be tested by setting demod_in_comp[i].imag = + 0 below */ + for(int i=0; i<nin; i++) { + COMP a = fcmult(2E4,interferer_phase); + interferer_phase = cmult(interferer_phase, interferer_freq); + power_interferer += a.real*a.real + a.imag*a.imag; + COMP d = demod_in_comp[i]; + power_d += d.real*d.real + d.imag*d.imag; + demod_in_comp[i] = cadd(d,a); + //demod_in_comp[i].imag = 0; + } + } + + /* useful to take a look at this with Octave */ + fwrite(demod_in_comp, sizeof(COMP), nin, fdemod); + + nout = freedv_comprx(freedv, speech_out, demod_in_comp); + nin = freedv_nin(freedv); /* call me on every loop! */ + fwrite(speech_out, sizeof(short), nout, stdout); + int sync; float snr_est; + freedv_get_modem_stats(freedv, &sync, &snr_est); + fprintf(stderr, "sync: %d snr_est: %f\n", sync, snr_est); + frames++; sum_sync += sync; if (sync) { sum_snr += snr_est; frames_snr++; } + } + + fclose(fdemod); + freedv_close(freedv); + + if (test_num == 2) + fprintf(stderr, "Demod/Interferer power ratio: %3.2f dB\n", 10*log10(power_d/power_interferer)); + float snr_av = sum_snr/frames_snr; + fprintf(stderr, "frames: %d sum_sync: %d snr_av: %3.2f dB\n", frames, sum_sync, snr_av); + + if (snr_av > 8.0) + return 0; + else + return 1; +} diff --git a/unittest/freedv_700d_comptx.c b/unittest/freedv_700d_comptx.c new file mode 100644 index 0000000..0e5350e --- /dev/null +++ b/unittest/freedv_700d_comptx.c @@ -0,0 +1,43 @@ +/*---------------------------------------------------------------------------*\ + + freedv_comptx.c + + Complex valued Tx to support ctests. + +\*---------------------------------------------------------------------------*/ + +#include <assert.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "freedv_api.h" + +int main(int argc, char *argv[]) { + struct freedv *freedv; + + freedv = freedv_open(FREEDV_MODE_700D); + assert(freedv != NULL); + + /* handy functions to set buffer sizes */ + int n_speech_samples = freedv_get_n_speech_samples(freedv); + short speech_in[n_speech_samples]; + int n_nom_modem_samples = freedv_get_n_nom_modem_samples(freedv); + COMP mod_out[n_nom_modem_samples]; + short mod_out_short[2*n_nom_modem_samples]; + + /* OK main loop --------------------------------------- */ + + while(fread(speech_in, sizeof(short), n_speech_samples, stdin) == n_speech_samples) { + freedv_comptx(freedv, mod_out, speech_in); + for(int i=0; i<n_nom_modem_samples; i++) { + mod_out_short[2*i] = mod_out[i].real; + mod_out_short[2*i+1] = mod_out[i].imag; + } + fwrite(mod_out_short, sizeof(short), 2*n_nom_modem_samples, stdout); + } + + freedv_close(freedv); + + return 0; +} diff --git a/unittest/function_trace.c b/unittest/function_trace.c new file mode 100644 index 0000000..5ed47a9 --- /dev/null +++ b/unittest/function_trace.c @@ -0,0 +1,36 @@ +#include <stdio.h> + +static FILE *fp_trace; + +void +__attribute__ ((constructor)) +trace_begin (void) +{ + fp_trace = fopen("function_trace.out", "w"); +} + +void +__attribute__ ((destructor)) +trace_end (void) +{ + if(fp_trace != NULL) { + fclose(fp_trace); + } +} + + +void +__cyg_profile_func_enter (void *func, void *caller) +{ + if(fp_trace != NULL) { + fprintf(fp_trace, "e %p %p\n", func, caller); + } +} + +void +__cyg_profile_func_exit (void *func, void *caller) +{ + if(fp_trace != NULL) { + fprintf(fp_trace, "x %p %p\n", func, caller); + } +} diff --git a/unittest/hts1a.h b/unittest/hts1a.h new file mode 100644 index 0000000..2e40397 --- /dev/null +++ b/unittest/hts1a.h @@ -0,0 +1,8002 @@ +short hts1a_raw[] = { +-14, +-14, +-8, +-7, +-11, +-15, +-14, +-16, +-24, +-26, +-25, +-26, +-22, +-22, +-24, +-19, +-19, +-19, +-26, +-28, +-28, +-21, +-16, +-14, +-19, +-19, +-18, +-18, +-16, +-18, +-26, +-28, +-35, +-28, +-19, +-12, +-12, +-14, +-15, +-21, +-16, +-12, +-9, +-11, +-5, +-8, +-7, +-5, +-8, +-8, +-7, +3, +3, +-1, +-2, +-5, +-1, +-7, +-5, +-4, +-4, +-7, +-5, +-9, +-8, +-12, +-21, +-21, +-28, +-28, +-24, +-25, +-29, +-29, +-31, +-32, +-28, +-31, +-35, +-26, +-35, +-31, +-28, +-32, +-26, +-21, +-22, +-16, +-15, +-14, +-18, +-12, +-19, +-12, +-12, +-16, +-15, +-16, +-16, +-16, +-12, +-15, +-12, +-18, +-18, +-15, +-19, +-18, +-16, +-14, +-15, +-16, +-16, +-16, +-14, +-16, +-11, +-4, +-8, +-8, +-9, +-8, +-15, +-12, +-11, +-12, +-9, +-11, +-8, +-11, +-14, +-11, +-18, +-16, +-14, +-14, +-9, +-5, +-8, +-15, +-16, +-14, +-16, +-18, +-15, +-31, +-32, +-19, +-15, +-12, +-16, +-15, +-18, +-14, +-12, +-12, +-16, +-24, +-25, +-19, +-18, +-22, +-21, +-19, +-16, +-16, +-14, +-16, +-24, +-24, +-19, +-24, +-24, +-19, +-21, +-24, +-25, +-28, +-25, +-25, +-26, +-18, +-12, +-22, +-25, +-26, +-25, +-24, +-24, +-22, +-21, +-19, +-16, +-15, +-12, +-12, +-14, +-8, +-12, +-11, +-5, +-1, +0, +0, +-1, +-4, +-4, +-5, +-7, +-7, +-11, +-8, +-11, +-5, +-4, +-2, +-8, +-5, +-12, +-14, +-14, +-14, +-12, +-12, +-7, +-16, +-21, +-22, +-22, +-25, +-28, +-24, +-31, +-32, +-33, +-33, +-35, +-31, +-29, +-32, +-36, +-36, +-35, +-35, +-32, +-26, +-35, +-29, +-22, +-18, +-11, +-16, +-14, +-14, +-11, +-8, +-8, +-12, +-11, +-24, +-25, +-12, +-8, +-7, +-9, +-5, +-9, +-8, +-4, +-7, +-5, +-11, +-11, +-4, +-5, +-5, +-8, +-4, +-8, +-4, +-8, +-14, +-8, +-9, +-12, +-11, +-15, +-22, +-21, +-19, +-22, +-15, +-22, +-18, +-15, +-16, +-18, +-12, +-14, +-21, +-19, +-16, +-19, +-21, +-22, +-21, +-25, +-19, +-26, +-21, +-19, +-21, +-19, +-16, +-15, +-18, +-9, +-8, +-9, +-8, +-14, +-19, +-15, +-16, +-16, +-16, +-12, +-11, +-12, +-11, +-11, +-7, +-19, +-18, +-14, +-28, +-28, +-26, +-28, +-31, +-31, +-24, +-25, +-26, +-29, +-28, +-31, +-28, +-24, +-21, +-19, +-21, +-16, +-24, +-24, +-18, +-16, +-19, +-12, +-9, +-12, +-14, +-12, +-7, +-9, +-9, +-5, +-5, +-9, +-7, +-5, +-5, +-4, +-21, +-25, +-12, +-5, +5, +10, +10, +-1, +-2, +3, +-4, +-7, +-8, +-5, +-11, +-12, +-25, +-26, +-24, +-33, +-32, +-29, +-35, +-36, +-33, +-38, +-42, +-38, +-38, +-36, +-36, +-33, +-32, +-38, +-32, +-28, +-22, +-18, +-14, +-9, +-15, +-9, +-8, +-11, +-2, +-2, +-5, +-2, +3, +-2, +-1, +-4, +-7, +-12, +-12, +-12, +-16, +-15, +-11, +-11, +-8, +-7, +-5, +-9, +-12, +-12, +-18, +-19, +-26, +-26, +-22, +-22, +-24, +-21, +-15, +-12, +-14, +-18, +-16, +-15, +-26, +-26, +-28, +-26, +-26, +-25, +-28, +-25, +-14, +-12, +-14, +-18, +-24, +-14, +-9, +-5, +-7, +-9, +-7, +-8, +-14, +-11, +-8, +-15, +-8, +-7, +-5, +-2, +-5, +-5, +-8, +-15, +-15, +-16, +-33, +-35, +-25, +-15, +-14, +-18, +-22, +-18, +-22, +-24, +-29, +-31, +-32, +-33, +-31, +-36, +-31, +-25, +-31, +-33, +-28, +-26, +-22, +-25, +-25, +-16, +-18, +-16, +-15, +-15, +-14, +-11, +-7, +-5, +-4, +-8, +-4, +-5, +-8, +-4, +0, +-7, +-1, +-1, +-9, +-11, +-12, +-14, +-15, +-9, +-8, +-7, +-5, +-11, +-12, +-12, +-19, +-15, +-16, +-16, +-12, +-18, +-19, +-19, +-18, +-15, +-19, +-24, +-19, +-25, +-28, +-26, +-26, +-26, +-28, +-26, +-22, +-21, +-25, +-19, +-16, +-16, +-9, +-9, +-12, +-7, +-11, +-7, +-12, +-15, +-18, +-24, +-16, +-16, +-12, +-19, +-18, +-16, +-25, +-24, +-22, +-25, +-21, +-25, +-24, +-24, +-19, +-15, +-26, +-19, +-14, +-19, +-16, +-18, +-26, +-28, +-16, +-4, +10, +15, +12, +13, +10, +8, +8, +-7, +-11, +-19, +-29, +-31, +-33, +-29, +-26, +-26, +-26, +-22, +-15, +-16, +-15, +-19, +-18, +-19, +-12, +-15, +-21, +-21, +-24, +-22, +-19, +-19, +-15, +-16, +-16, +-24, +-18, +-25, +-24, +-24, +-26, +-21, +-22, +-22, +-18, +-16, +-19, +-22, +-22, +-19, +-24, +-19, +-16, +-19, +-15, +-16, +-12, +-4, +-9, +-19, +-12, +-15, +-19, +-16, +-16, +-15, +-14, +-12, +-12, +-11, +-8, +-9, +-9, +-12, +-11, +-11, +-8, +-9, +-5, +5, +-1, +-1, +-4, +-8, +-8, +-8, +-9, +-8, +-7, +-11, +-19, +-19, +-18, +-26, +-21, +-24, +-24, +-26, +-35, +-32, +-36, +-31, +-26, +-28, +-26, +-25, +-22, +-26, +-35, +-35, +-36, +-45, +-45, +-33, +-28, +-19, +-16, +-9, +-14, +-12, +-5, +-11, +-8, +-7, +-5, +-5, +2, +-2, +-5, +0, +-7, +-5, +-11, +-14, +-14, +-8, +-8, +-7, +-9, +-11, +-11, +-15, +-15, +-14, +-12, +-14, +-18, +-16, +-9, +-12, +-12, +-14, +-16, +-22, +-25, +-26, +-31, +-29, +-26, +-29, +-25, +-22, +-19, +-18, +-24, +-21, +-24, +-19, +-12, +-15, +-15, +-16, +-15, +-14, +-16, +-15, +-16, +-24, +-19, +-22, +-26, +-24, +-19, +-18, +-19, +-15, +-11, +-5, +-2, +-2, +-4, +-7, +-4, +-8, +-8, +-11, +-15, +-11, +-9, +-7, +-9, +-4, +2, +-8, +-1, +-2, +-12, +-9, +-15, +-21, +-31, +-38, +-32, +-32, +-35, +-31, +-28, +-33, +-32, +-35, +-33, +-33, +-36, +-36, +-42, +-45, +-43, +-33, +-29, +-25, +-21, +-14, +-12, +-11, +-16, +-15, +-12, +-14, +-15, +-15, +-9, +-2, +-2, +0, +-5, +-4, +-2, +0, +8, +9, +10, +12, +3, +6, +5, +9, +3, +-7, +-9, +-25, +-32, +-25, +-11, +-1, +2, +-1, +-9, +-15, +-12, +-15, +-21, +-29, +-35, +-39, +-39, +-31, +-33, +-33, +-26, +-28, +-29, +-31, +-33, +-26, +-24, +-22, +-24, +-21, +-18, +-15, +-18, +-26, +-25, +-22, +-18, +-21, +-24, +-26, +-35, +-28, +-26, +-26, +-24, +-22, +-16, +-18, +-22, +-15, +-22, +-24, +-16, +-14, +-11, +-4, +3, +5, +3, +8, +8, +6, +0, +6, +3, +-5, +0, +6, +-5, +-5, +-8, +-11, +-14, +-19, +-21, +-24, +-25, +-28, +-28, +-22, +-28, +-38, +-38, +-26, +-26, +-22, +-32, +-31, +-26, +-18, +-12, +-11, +-9, +-16, +-21, +-19, +-16, +-16, +-18, +-12, +-12, +-8, +-7, +-15, +-16, +-16, +-19, +-21, +-22, +-22, +-22, +-25, +-32, +-29, +-35, +-32, +-33, +-33, +-29, +-25, +-18, +-15, +-16, +-12, +-12, +-8, +-2, +0, +6, +2, +0, +5, +2, +-5, +-1, +-5, +-8, +-8, +-8, +-9, +-15, +-12, +-11, +-5, +-9, +-18, +-19, +-22, +-16, +-14, +-11, +-2, +-2, +-8, +-16, +-21, +-22, +-19, +-24, +-24, +-16, +-16, +-12, +-7, +-8, +-5, +-8, +-16, +-24, +-29, +-35, +-36, +-36, +-38, +-41, +-38, +-43, +-41, +-39, +-35, +-32, +-31, +-32, +-32, +-28, +-24, +-24, +-19, +-16, +-18, +-12, +-2, +-1, +5, +5, +-9, +-25, +-21, +-15, +-14, +-15, +-14, +-18, +-16, +-12, +-11, +-5, +-2, +-7, +-11, +-7, +-18, +-11, +-5, +-2, +0, +-2, +0, +0, +-5, +-11, +-14, +-12, +-14, +-14, +-16, +-18, +-18, +-29, +-38, +-38, +-42, +-46, +-38, +-35, +-32, +-31, +-16, +-5, +0, +12, +19, +20, +17, +20, +20, +16, +16, +12, +5, +-7, +-15, +-14, +-22, +-25, +-26, +-26, +-32, +-38, +-43, +-45, +-49, +-55, +-56, +-52, +-56, +-48, +-39, +-33, +-28, +-31, +-24, +-29, +-32, +-26, +-16, +-7, +-14, +-11, +-11, +-16, +-18, +-29, +-28, +-24, +-14, +-11, +-15, +-4, +-11, +-12, +-5, +-2, +3, +5, +2, +10, +5, +6, +2, +-1, +-7, +-7, +-12, +-14, +-16, +-22, +-21, +-18, +-19, +-22, +-11, +-1, +3, +2, +2, +3, +6, +-2, +-7, +-15, +-18, +-24, +-26, +-31, +-38, +-41, +-39, +-36, +-39, +-33, +-26, +-24, +-18, +-19, +-21, +-19, +-18, +-16, +-21, +-21, +-15, +-14, +-18, +-24, +-25, +-31, +-38, +-43, +-45, +-46, +-43, +-39, +-33, +-28, +-19, +-11, +-8, +-4, +5, +12, +12, +17, +16, +9, +9, +10, +6, +8, +-4, +3, +0, +-5, +-11, +-14, +-28, +-26, +-15, +-24, +-32, +-32, +-28, +-32, +-28, +-18, +-22, +-22, +-15, +-15, +-24, +-25, +-26, +-25, +-16, +-16, +-18, +-22, +-21, +-26, +-29, +-25, +-22, +-19, +-16, +-9, +-4, +2, +6, +10, +3, +2, +0, +-7, +-7, +-14, +-16, +-15, +-22, +-26, +-29, +-25, +-25, +-22, +-29, +-35, +-25, +-19, +-14, +-15, +-12, +-15, +-26, +-24, +-29, +-28, +-26, +-26, +-29, +-32, +-38, +-42, +-38, +-33, +-29, +-25, +-25, +-21, +-14, +-5, +5, +6, +8, +6, +2, +0, +-1, +-9, +-16, +-18, +-19, +-22, +-22, +-21, +-15, +-18, +-22, +-11, +-8, +-11, +-7, +5, +2, +-2, +-4, +-5, +-7, +-5, +6, +-7, +-9, +-8, +-19, +-22, +-24, +-26, +-29, +-33, +-29, +-25, +-24, +-21, +-21, +-24, +-29, +-28, +-26, +-25, +-21, +-26, +-26, +-25, +-31, +-31, +-38, +-39, +-38, +-33, +-21, +-9, +-5, +-4, +5, +3, +-2, +-7, +-8, +-9, +-15, +-16, +-16, +-22, +-21, +-24, +-24, +-16, +-22, +-21, +-24, +-26, +-22, +-16, +-9, +-11, +-2, +6, +2, +-4, +-9, +-16, +-21, +-21, +-21, +-24, +-22, +-22, +-19, +-18, +-16, +-12, +-14, +-12, +-4, +-2, +-4, +-8, +-5, +-5, +-11, +0, +2, +-7, +-8, +-12, +-14, +-19, +-24, +-25, +-28, +-9, +-5, +-29, +-33, +-22, +-22, +-21, +-15, +-18, +-24, +-21, +-26, +-29, +-25, +-33, +-29, +-29, +-29, +-31, +-28, +-28, +-25, +-24, +-21, +-25, +-14, +-7, +-5, +-16, +-19, +-21, +-28, +-33, +-38, +-36, +-26, +-25, +-22, +-8, +-5, +0, +5, +10, +16, +13, +10, +8, +5, +-4, +-1, +-7, +-11, +-18, +-28, +-31, +-42, +-43, +-38, +-38, +-22, +-11, +-8, +-7, +6, +6, +3, +13, +8, +-7, +-2, +-9, +-16, +-11, +-15, +-18, +-24, +-28, +-24, +-25, +-22, +-25, +-28, +-25, +-38, +-39, +-35, +-36, +-14, +-25, +-42, +-16, +-9, +-29, +-28, +-26, +-31, +-29, +-39, +-29, +-12, +-7, +-1, +-2, +-4, +0, +2, +-5, +-15, +-21, +-35, +-32, +-22, +-19, +-18, +-19, +-5, +6, +0, +-14, +-26, +-11, +0, +-19, +-24, +-24, +-25, +-25, +-31, +-35, +-32, +-19, +-5, +-7, +-1, +0, +6, +8, +0, +-8, +-5, +9, +9, +6, +2, +9, +13, +-7, +-26, +-36, +-35, +-42, +-56, +-49, +-42, +-42, +-36, +-28, +-12, +-12, +-21, +-18, +-24, +-19, +-22, +-25, +-24, +-21, +-18, +-15, +-15, +-8, +-7, +3, +-4, +-11, +-22, +-22, +-16, +-24, +-21, +-7, +-22, +-31, +-16, +-21, +-11, +-12, +-21, +-26, +-28, +-19, +-28, +-31, +-25, +-38, +-38, +-29, +-33, +-38, +-33, +-9, +10, +19, +5, +-4, +-4, +-1, +-12, +-21, +-18, +-16, +-16, +-19, +-8, +-5, +-7, +-2, +0, +12, +16, +15, +2, +-14, +-12, +-22, +-29, +-42, +-36, +-25, +-16, +-18, +-19, +-12, +6, +2, +2, +9, +-4, +-11, +-19, +-25, +-24, +-28, +-35, +-43, +-35, +-25, +-41, +-45, +-42, +-39, +-35, +-41, +-33, +-29, +-18, +-2, +-19, +-32, +-12, +3, +-8, +-11, +-26, +-35, +-29, +-29, +-24, +-15, +-9, +-8, +-1, +2, +0, +-1, +-2, +2, +-1, +-8, +-9, +-18, +-21, +-26, +-32, +-35, +-26, +-15, +-9, +-7, +-2, +-2, +2, +13, +12, +-2, +-11, +-15, +-18, +-28, +-29, +-24, +-28, +-32, +-31, +-28, +-15, +-9, +-21, +-4, +10, +-7, +-5, +-14, +-18, +-9, +-21, +-25, +-29, +-32, +-36, +-31, +-16, +-24, +-18, +-18, +-22, +-25, +-32, +-31, +-12, +-8, +-8, +2, +-2, +15, +9, +-14, +-9, +-9, +0, +-1, +-22, +-24, +-18, +-32, +-29, +-29, +-39, +-33, +-24, +-41, +-33, +-18, +-33, +-28, +-32, +-28, +-24, +-35, +-32, +-24, +-21, +-14, +-21, +-25, +-18, +-9, +-8, +-11, +0, +3, +5, +-5, +-14, +-19, +-15, +-9, +2, +-2, +0, +12, +10, +19, +22, +8, +-2, +-5, +-9, +-22, +-16, +-5, +-16, +-21, +-19, +-16, +-16, +-26, +-29, +-28, +-25, +-1, +-2, +-15, +-19, +-29, +-35, +-35, +-38, +-36, +-38, +-29, +-46, +-56, +-38, +-45, +-62, +-55, +-48, +-33, +-42, +-79, +-93, +33, +207, +203, +112, +30, +-29, +-28, +-70, +-148, +-176, +-96, +-69, +-87, +-32, +-45, +-24, +46, +64, +76, +46, +43, +60, +39, +20, +12, +6, +-35, +-26, +-28, +-67, +-8, +-18, +-55, +6, +-38, +-75, +-24, +-5, +-5, +-25, +-25, +-35, +22, +93, +23, +-28, +-55, +-83, +-42, +-83, +-103, +-56, +-22, +44, +3, +-33, +6, +8, +12, +17, +-5, +-131, +-189, +100, +265, +40, +49, +135, +-59, +12, +51, +-123, +-87, +-182, +-298, +-226, +-192, +-205, +-198, +-49, +30, +39, +183, +238, +183, +141, +187, +132, +83, +176, +16, +-116, +-90, +-118, +-138, +-189, +-123, +-137, +-147, +-9, +-28, +49, +125, +66, +29, +43, +46, +-70, +-100, +-75, +-121, +-117, +-109, +-58, +-28, +29, +77, +74, +128, +118, +63, +12, +56, +158, +90, +0, +-29, +-127, +-103, +-33, +-137, +-140, +-18, +-43, +-66, +-7, +-45, +-73, +6, +42, +-35, +-111, +-104, +-92, +-147, +-526, +-1264, +-1575, +-765, +438, +997, +1207, +1339, +1320, +1748, +2310, +2055, +1176, +299, +-434, +-1009, +-1257, +-1632, +-2439, +-2960, +-2928, +-2615, +-2054, +-1308, +-736, +-303, +445, +1377, +2112, +2587, +2728, +2464, +2061, +1838, +1525, +888, +182, +-511, +-1203, +-1516, +-1425, +-1447, +-1556, +-1386, +-1076, +-642, +-16, +437, +547, +662, +915, +1092, +1055, +883, +519, +53, +-259, +-472, +-771, +-1037, +-1070, +-981, +-814, +-511, +-203, +2, +251, +519, +616, +631, +648, +582, +476, +391, +271, +-18, +-305, +-348, +-399, +-478, +-389, +-348, +-351, +-222, +-121, +-386, +-1735, +-3442, +-2533, +573, +2049, +2601, +3434, +2708, +2736, +4175, +4209, +2551, +296, +-1119, +-2477, +-3494, +-3361, +-4179, +-5485, +-5636, +-4580, +-3316, +-2031, +-22, +1181, +1763, +3271, +4874, +5510, +5224, +4612, +3186, +1425, +652, +-147, +-1598, +-2834, +-3484, +-3818, +-3613, +-2537, +-1594, +-1278, +-574, +734, +1997, +2818, +3281, +3233, +2488, +1960, +1561, +520, +-672, +-1616, +-2288, +-2651, +-2450, +-2115, +-2137, +-1772, +-998, +-341, +312, +891, +1208, +1322, +1574, +1687, +1285, +810, +427, +-18, +-404, +-664, +-1030, +-1306, +-1064, +-656, +-397, +-182, +-16, +170, +514, +922, +1023, +956, +915, +626, +-42, +-1701, +-4508, +-5074, +-1033, +2559, +2488, +3256, +3525, +2940, +5224, +6208, +3992, +-50, +-2480, +-3034, +-4628, +-4714, +-5427, +-7560, +-7667, +-5645, +-2855, +-1414, +621, +2572, +3066, +5038, +7469, +7951, +6155, +4438, +2841, +544, +-373, +-1233, +-3376, +-5177, +-5271, +-4414, +-3653, +-2121, +-788, +-392, +720, +2701, +3972, +4022, +3914, +3451, +2206, +1426, +796, +-632, +-2163, +-3381, +-4159, +-4113, +-3045, +-1776, +-1312, +-743, +275, +1098, +1851, +2335, +2131, +1527, +1259, +1200, +713, +73, +-516, +-1176, +-1677, +-1755, +-1672, +-1670, +-1336, +-625, +37, +718, +1435, +1620, +1412, +1445, +1137, +438, +388, +645, +505, +194, +-907, +-3965, +-7195, +-5524, +1111, +4509, +3145, +3535, +3294, +3846, +7424, +7315, +3111, +-1601, +-3095, +-3366, +-4972, +-5281, +-7332, +-9647, +-8219, +-4862, +-1956, +-808, +1125, +2677, +3750, +7120, +9202, +7949, +5515, +3979, +2538, +716, +-4, +-1881, +-5029, +-6072, +-5264, +-4448, +-3573, +-2254, +-1472, +-802, +1513, +3981, +4536, +4289, +4083, +3336, +2484, +2141, +963, +-1417, +-3088, +-3937, +-4523, +-3991, +-2694, +-2023, +-1694, +-593, +580, +1431, +2310, +2417, +1777, +1433, +1598, +1241, +374, +-69, +-753, +-1490, +-1449, +-1357, +-1625, +-1628, +-1176, +-845, +-356, +723, +1418, +1370, +1530, +1663, +1222, +1057, +1140, +345, +-576, +-628, +-1435, +-4343, +-6010, +-2139, +2712, +2651, +2832, +3744, +2781, +5433, +7295, +4184, +117, +-2377, +-2990, +-4553, +-4853, +-5022, +-7143, +-6439, +-4074, +-2314, +-1340, +-260, +822, +720, +2570, +4741, +4967, +4932, +4619, +4026, +2856, +2222, +1221, +-911, +-1898, +-2510, +-2962, +-2593, +-1870, +-1447, +-1553, +-1085, +-648, +-342, +240, +418, +505, +623, +1054, +1416, +1337, +1193, +544, +-101, +-348, +-407, +-426, +-604, +-573, +-519, +-297, +9, +-235, +-589, +-927, +-952, +-563, +-310, +9, +168, +219, +369, +245, +40, +-235, +-451, +-287, +46, +415, +683, +836, +842, +670, +489, +316, +166, +54, +-36, +-65, +36, +-96, +-883, +-1693, +-1570, +-756, +-117, +340, +975, +1918, +2596, +2086, +1068, +199, +-509, +-625, +-662, +-529, +71, +-33, +-597, +-1067, +-1512, +-1670, +-1870, +-1901, +-1547, +-974, +-269, +142, +260, +272, +56, +-164, +-24, +509, +1019, +1292, +1496, +1616, +1660, +1613, +1357, +966, +660, +605, +597, +480, +275, +-134, +-671, +-1134, +-1376, +-1473, +-1534, +-1564, +-1496, +-1237, +-812, +-402, +-128, +64, +267, +431, +522, +479, +332, +320, +468, +565, +548, +407, +180, +-63, +-264, +-455, +-638, +-685, +-574, +-358, +-32, +238, +328, +329, +371, +427, +438, +448, +424, +383, +441, +482, +29, +-1189, +-2337, +-1898, +-396, +261, +599, +1708, +2529, +2347, +1500, +312, +-430, +-148, +278, +227, +432, +471, +-532, +-1710, +-2265, +-2405, +-2323, +-2014, +-1597, +-1182, +-690, +-406, +-567, +-617, +-358, +-89, +343, +1088, +1780, +1952, +1833, +1659, +1418, +1391, +1493, +1446, +1302, +1180, +911, +415, +-45, +-492, +-962, +-1186, +-1142, +-1095, +-1100, +-1196, +-1422, +-1556, +-1353, +-995, +-586, +-59, +257, +287, +287, +197, +3, +-36, +156, +418, +737, +932, +759, +427, +165, +-84, +-252, +-225, +-148, +-84, +34, +39, +-96, +-169, +-168, +-72, +63, +179, +360, +539, +485, +-67, +-1439, +-2707, +-1793, +379, +1014, +1030, +2144, +2711, +2106, +1211, +-4, +-525, +364, +955, +628, +584, +170, +-1432, +-2769, +-2892, +-2525, +-2075, +-1551, +-1390, +-1363, +-1025, +-944, +-1087, +-615, +145, +594, +1099, +1758, +1969, +1712, +1453, +1289, +1384, +1843, +2083, +1800, +1387, +975, +400, +-86, +-260, +-446, +-681, +-781, +-918, +-1183, +-1481, +-1768, +-1877, +-1558, +-1020, +-515, +-28, +192, +9, +-261, +-322, +-178, +98, +444, +706, +805, +761, +492, +145, +-5, +6, +20, +53, +81, +27, +-103, +-249, +-325, +-219, +-9, +139, +291, +473, +547, +400, +42, +-904, +-2367, +-2330, +-46, +1336, +924, +1518, +2371, +1895, +1372, +564, +-271, +454, +1397, +1020, +578, +328, +-986, +-2542, +-2821, +-2398, +-1994, +-1564, +-1564, +-1956, +-1922, +-1598, +-1434, +-879, +175, +771, +983, +1429, +1726, +1615, +1534, +1616, +1721, +2049, +2369, +2073, +1426, +888, +304, +-192, +-219, +-94, +-284, +-630, +-961, +-1359, +-1618, +-1696, +-1748, +-1454, +-778, +-324, +-128, +-18, +-195, +-423, +-382, +-226, +70, +483, +701, +628, +428, +210, +10, +-22, +114, +227, +272, +257, +169, +25, +-75, +-138, +-118, +102, +328, +287, +159, +135, +-135, +-1224, +-2588, +-2139, +243, +1339, +861, +1548, +2238, +1746, +1333, +575, +20, +917, +1538, +977, +662, +405, +-964, +-2463, +-2640, +-2228, +-1890, +-1544, +-1616, +-1970, +-1980, +-1750, +-1567, +-867, +258, +771, +834, +1153, +1445, +1448, +1465, +1600, +1732, +2025, +2283, +1981, +1387, +883, +305, +-131, +-60, +112, +-138, +-472, +-713, +-1204, +-1632, +-1686, +-1616, +-1274, +-676, +-513, +-574, +-314, +-246, +-516, +-545, +-349, +-28, +512, +853, +680, +427, +319, +192, +159, +289, +343, +264, +254, +193, +-29, +-143, +-148, +-107, +152, +371, +271, +-62, +-913, +-2303, +-2531, +-572, +992, +788, +1179, +2054, +1888, +1608, +1079, +285, +625, +1391, +1211, +911, +815, +-239, +-1829, +-2473, +-2303, +-1946, +-1557, +-1548, +-1949, +-2143, +-1976, +-1775, +-1202, +-178, +458, +614, +910, +1272, +1374, +1395, +1466, +1518, +1745, +2114, +2090, +1680, +1224, +657, +53, +-175, +-52, +-45, +-203, +-454, +-892, +-1329, +-1492, +-1447, +-1204, +-814, +-655, +-631, +-406, +-247, +-334, +-468, +-526, +-358, +83, +466, +547, +455, +343, +228, +230, +309, +335, +371, +394, +352, +231, +64, +-60, +-48, +104, +202, +-11, +-747, +-2004, +-2576, +-1166, +550, +643, +737, +1650, +1868, +1642, +1375, +674, +546, +1071, +1077, +943, +1115, +595, +-841, +-1963, +-2249, +-2154, +-1810, +-1444, +-1523, +-1822, +-1890, +-1813, +-1512, +-726, +32, +333, +650, +1095, +1306, +1343, +1429, +1450, +1429, +1627, +1802, +1692, +1440, +1016, +360, +-106, +-130, +-84, +-157, +-290, +-645, +-1068, +-1270, +-1301, +-1210, +-984, +-715, +-532, +-436, +-353, +-370, +-471, +-464, +-363, +-165, +206, +496, +517, +400, +306, +192, +166, +284, +373, +383, +386, +383, +319, +224, +135, +127, +115, +-491, +-1799, +-2388, +-1161, +363, +522, +420, +1024, +1442, +1559, +1517, +1026, +750, +863, +628, +452, +800, +684, +-348, +-1405, +-1942, +-2143, +-2044, +-1704, +-1476, +-1461, +-1526, +-1621, +-1422, +-716, +5, +325, +585, +1013, +1288, +1380, +1476, +1487, +1409, +1382, +1354, +1255, +1174, +968, +519, +129, +-52, +-240, +-438, +-441, +-399, +-604, +-941, +-1258, +-1284, +-315, +394, +-628, +-1085, +-532, +-675, +-126, +-277, +-1573, +-522, +598, +237, +407, +805, +1075, +653, +318, +362, +-59, +-56, +-240, +-233, +312, +-489, +-2514, +-4669, +-4202, +-712, +2020, +3805, +4136, +2529, +3440, +5718, +5515, +3867, +1626, +-647, +-2193, +-2176, +-1803, +-3276, +-5465, +-7012, +-7661, +-6402, +-3818, +-2200, +-1338, +105, +1535, +3292, +5837, +7231, +6633, +5443, +4310, +3120, +2167, +863, +-1674, +-4124, +-5139, +-5305, +-4852, +-3473, +-1936, +-1080, +-52, +1583, +2812, +3833, +4309, +3278, +2191, +1920, +1330, +168, +-1132, +-2559, +-3548, +-3413, +-3045, +-3000, +-2440, +-1105, +424, +1562, +2003, +1718, +1409, +1794, +1922, +959, +-191, +-649, +-329, +-22, +-2232, +-7587, +-10133, +-6123, +3815, +11010, +10048, +7655, +4465, +7798, +10768, +5766, +-1615, +-9642, +-12287, +-10166, +-6666, +-6229, +-9606, +-9109, +-5832, +-1326, +4475, +7162, +6468, +5722, +6975, +8255, +7576, +5333, +919, +-3576, +-5125, +-4751, +-4305, +-4140, +-4257, +-4346, +-2541, +1238, +4948, +7386, +6832, +4496, +3434, +2708, +1527, +-206, +-3454, +-6091, +-5990, +-4237, +-2710, +-1963, +-1253, +-324, +1191, +2740, +2787, +2061, +1796, +1911, +1408, +-65, +-1632, +-2642, +-2612, +-2111, +-2132, +-2119, +-703, +1824, +2113, +-3052, +-10217, +-9285, +2474, +15080, +14696, +9168, +3569, +1871, +8840, +7733, +-863, +-10981, +-16373, +-12835, +-7172, +-2606, +-4832, +-7242, +-3728, +1232, +7185, +9862, +7265, +2730, +1101, +4013, +4560, +2412, +-866, +-5642, +-6610, +-3630, +-702, +380, +20, +-726, +-825, +2400, +6645, +7202, +4332, +728, +-1582, +-1799, +-535, +-1312, +-4539, +-5701, +-4256, +-1013, +2246, +2495, +1214, +859, +1630, +1894, +788, +-715, +-1510, +-1008, +-797, +-1376, +-2139, +-2347, +-1166, +64, +874, +1826, +2927, +1167, +-7529, +-13865, +-7119, +8693, +17405, +11376, +5806, +-706, +3910, +12384, +4898, +-5841, +-15567, +-15268, +-9199, +-2915, +-2, +-6828, +-7298, +-645, +4931, +9593, +8510, +3404, +-1029, +1545, +5927, +3179, +704, +-3058, +-7267, +-5092, +-1456, +523, +578, +803, +476, +128, +4503, +7132, +4843, +2484, +-314, +-2661, +-1629, +-312, +-2667, +-4845, +-4645, +-3556, +-106, +3033, +2307, +815, +1327, +2202, +1608, +238, +-1347, +-2299, +-1676, +-1349, +-2026, +-2123, +-993, +185, +601, +1363, +2536, +1356, +-6385, +-14216, +-8118, +9341, +18113, +10028, +5714, +-662, +2201, +13868, +5555, +-6998, +-16122, +-13932, +-7484, +-3936, +-511, +-7031, +-7875, +1569, +7356, +9852, +7233, +2382, +-812, +2168, +7258, +2096, +-2579, +-3745, +-6101, +-3546, +-123, +70, +-1707, +-134, +2587, +1895, +4233, +5028, +2090, +1861, +1389, +-720, +-1444, +-1278, +-3112, +-4277, +-3164, +-2898, +-1520, +861, +1640, +1528, +1967, +2726, +1695, +329, +-492, +-1697, +-1969, +-1929, +-2346, +-2041, +-600, +480, +449, +1232, +2001, +-2082, +-11654, +-11999, +3894, +17853, +11683, +5279, +1994, +-1245, +12112, +9934, +-5184, +-14616, +-13918, +-5837, +-3437, +-2238, +-7345, +-9704, +1457, +9286, +8837, +4980, +1010, +1222, +4271, +7312, +1819, +-5336, +-4002, +-3308, +-1866, +-985, +-3300, +-3466, +-199, +5187, +4104, +1751, +2997, +2126, +3600, +3670, +-678, +-3106, +-1891, +-1261, +-2368, +-2264, +-3919, +-3476, +176, +1251, +1354, +400, +691, +2138, +1896, +1695, +-373, +-2231, +-1989, +-1816, +-1622, +-1393, +-557, +-261, +839, +1234, +-6099, +-13830, +-6704, +12207, +17650, +5219, +4873, +2652, +5438, +14840, +2038, +-11781, +-14722, +-7010, +-2482, +-5104, +-8182, +-11393, +-3796, +9197, +8387, +2298, +752, +2771, +7659, +8564, +3986, +-5080, +-5073, +893, +-749, +-2984, +-6554, +-6884, +-950, +4891, +4912, +-866, +561, +5333, +7261, +5833, +-1037, +-4326, +-1758, +1554, +-393, +-5394, +-5454, +-3454, +179, +2665, +-1061, +-2457, +677, +3635, +4058, +1347, +-451, +-846, +-346, +-403, +-2221, +-2535, +-1778, +-671, +454, +-2159, +-10071, +-12945, +-179, +17116, +12061, +403, +7550, +7610, +11287, +11279, +-5843, +-14361, +-7034, +-1442, +-6719, +-11613, +-12495, +-8241, +4100, +9619, +-682, +-1891, +5146, +9786, +11057, +5758, +-974, +-3229, +3852, +2712, +-6304, +-8209, +-8290, +-4368, +1412, +1119, +-2455, +-1243, +5528, +8521, +7421, +3537, +-1505, +772, +3590, +-341, +-4257, +-4940, +-4165, +-2414, +-583, +-3011, +-4147, +39, +2313, +2528, +2055, +1207, +1947, +2715, +1007, +-2408, +-2377, +-414, +-1230, +-1867, +-3633, +-9724, +-12863, +-3041, +14045, +10335, +-2909, +9310, +13964, +12254, +12191, +-3180, +-10105, +-345, +-192, +-11763, +-16662, +-13357, +-7749, +648, +2417, +-6327, +-892, +9776, +11498, +8643, +4306, +4143, +5477, +7138, +935, +-8335, +-6130, +-4015, +-5890, +-6088, +-6166, +-3580, +1133, +4766, +3210, +4129, +8211, +5810, +3999, +3155, +502, +-186, +-1683, +-4503, +-5169, +-3667, +-3607, +-4424, +-3364, +-1626, +496, +2096, +1967, +2167, +3503, +3642, +1586, +-283, +-332, +-32, +-1544, +-4229, +-7899, +-12648, +-8414, +7338, +10181, +-4368, +4177, +17458, +13517, +13236, +4243, +-5162, +1964, +2892, +-11114, +-17844, +-12323, +-7832, +-6496, +-4604, +-7565, +-3331, +7284, +7605, +3611, +5653, +10209, +9934, +7845, +4885, +-679, +-230, +-329, +-7610, +-9785, +-6780, +-5146, +-4804, +-3114, +-808, +1753, +6140, +6461, +4152, +5323, +6386, +4387, +857, +-710, +-1095, +-2162, +-3546, +-6282, +-6035, +-3066, +-2787, +-2956, +-1881, +510, +2940, +3258, +2654, +2028, +2429, +2474, +1033, +-233, +-2762, +-7034, +-11056, +-7771, +4646, +5433, +-5715, +3990, +16374, +11856, +10648, +5494, +-18, +5462, +1544, +-10835, +-12677, +-6704, +-7160, +-10246, +-8169, +-7359, +-3156, +2089, +-420, +260, +6647, +8892, +6911, +7202, +8552, +5729, +4090, +1688, +-3204, +-3246, +-3532, +-7259, +-7538, +-4539, +-3069, +-2792, +-884, +932, +3421, +5464, +3526, +3536, +5345, +4133, +2168, +584, +-281, +-1044, +-2500, +-4101, +-4951, +-3804, +-3109, +-3549, +-2503, +-910, +410, +1643, +2037, +1937, +2188, +2788, +2315, +-572, +-6503, +-9657, +-1302, +7699, +-1541, +-6680, +9781, +13307, +6279, +7430, +3430, +3365, +5749, +-4342, +-10037, +-4328, +-4039, +-10940, +-9452, +-4717, +-5445, +-3381, +-2392, +-2448, +2723, +4173, +2037, +4710, +9437, +7904, +4223, +5554, +4404, +1385, +-43, +-3303, +-4253, +-3654, +-5194, +-5691, +-3290, +-1456, +-2081, +-498, +1909, +2226, +3394, +3325, +2560, +4033, +3472, +1280, +1159, +354, +-1428, +-2003, +-2912, +-3889, +-3756, +-3522, +-3245, +-2163, +-801, +-58, +556, +1671, +2327, +2137, +1292, +-3214, +-7879, +-365, +8792, +-2058, +-6971, +10764, +11376, +2080, +7475, +5194, +2926, +4953, +-5039, +-7315, +-715, +-5646, +-12680, +-6937, +-1755, +-6613, +-5901, +-2174, +-1343, +2238, +575, +-682, +5661, +9086, +4503, +2478, +7910, +6936, +1933, +1266, +-19, +-835, +-2541, +-5805, +-4897, +-2034, +-3317, +-5455, +-1761, +1654, +-167, +-301, +2165, +3287, +3091, +2318, +1799, +3063, +3081, +-334, +-481, +970, +-1720, +-3691, +-3239, +-2820, +-2991, +-3206, +-2470, +-811, +529, +173, +716, +2623, +1564, +-3845, +-6581, +2529, +8041, +-5370, +-4499, +13582, +7503, +-109, +8820, +5684, +4438, +3673, +-6724, +-2925, +1272, +-9126, +-12206, +-2956, +-2350, +-9067, +-5194, +-1653, +-1288, +390, +-2551, +530, +6921, +4657, +798, +5477, +10107, +4739, +2049, +4361, +2961, +561, +-2843, +-4325, +-1612, +-2273, +-6381, +-5260, +-109, +-1162, +-3773, +-522, +1928, +1069, +328, +1702, +3025, +2946, +2046, +955, +2895, +2338, +-1672, +-1136, +-243, +-2378, +-3644, +-2902, +-1987, +-2112, +-1495, +-920, +270, +1683, +595, +-992, +-4240, +-4096, +5163, +3365, +-8693, +2644, +13871, +721, +2637, +10991, +4279, +5176, +1215, +-4254, +1188, +-1917, +-10538, +-7706, +-284, +-6409, +-9875, +-1970, +-2217, +-3228, +-2424, +-2575, +1869, +3423, +1156, +1937, +7458, +7611, +2627, +5239, +6736, +2968, +897, +-186, +-48, +-1270, +-3511, +-4529, +-2943, +-1507, +-4284, +-3616, +-532, +-870, +-1332, +-877, +1520, +2161, +605, +2045, +3158, +2852, +1636, +554, +1527, +479, +-1720, +-2091, +-1287, +-1420, +-2853, +-1994, +-642, +-702, +-651, +-305, +809, +-1228, +-6310, +-3284, +6444, +57, +-9694, +6692, +12322, +-2264, +4655, +10553, +3995, +5217, +-66, +-1294, +2509, +-3061, +-9247, +-4914, +871, +-7968, +-9271, +-1035, +-3443, +-5114, +-3791, +-1539, +1162, +829, +660, +2385, +7135, +5285, +1660, +6675, +7080, +2594, +1368, +2909, +2358, +-1581, +-2121, +-1548, +-1810, +-3079, +-4944, +-2309, +-1422, +-3854, +-2656, +-14, +115, +-634, +735, +2188, +2383, +1821, +1231, +2298, +1998, +6, +-378, +210, +-244, +-1924, +-1735, +-317, +-1118, +-1969, +-886, +5, +-368, +-410, +-812, +-3948, +-5540, +1489, +4735, +-7002, +-3001, +13628, +3594, +-3100, +10227, +7590, +2375, +2916, +1211, +1479, +-631, +-3134, +-6350, +-1802, +-978, +-10207, +-6177, +-1046, +-4499, +-6925, +-3423, +1443, +-1421, +-584, +2444, +2882, +5009, +3294, +3499, +5559, +5411, +3077, +1096, +4451, +2750, +-1703, +-631, +383, +-1183, +-4035, +-2959, +-1350, +-3069, +-3804, +-2694, +-236, +-904, +-1864, +513, +1545, +1463, +455, +1019, +2475, +970, +230, +551, +798, +211, +-916, +-468, +-216, +-352, +-1036, +-1298, +-138, +-232, +-750, +-580, +40, +-1001, +-5521, +-4688, +5193, +3492, +-9220, +1094, +13917, +1137, +-3168, +7172, +9020, +2400, +-2739, +3367, +3104, +-2930, +-2350, +-4632, +-2215, +-2432, +-6993, +-6593, +-3793, +228, +-5689, +-6176, +2457, +1840, +-1003, +-1275, +4305, +5439, +333, +2841, +4258, +4558, +3512, +1238, +3254, +2559, +1884, +-421, +-1639, +1150, +-1170, +-3158, +-3000, +-1500, +-1025, +-3848, +-2200, +-277, +-804, +-1328, +-773, +1228, +162, +-96, +884, +812, +1234, +386, +645, +708, +381, +633, +-624, +-126, +146, +-872, +-957, +-586, +223, +-698, +-781, +544, +261, +200, +-328, +-2742, +-4169, +1605, +6106, +-3718, +-5298, +7649, +6832, +-1261, +-1390, +4067, +6767, +-470, +-1764, +1864, +-38, +257, +-2297, +-3900, +-2558, +-1958, +-1561, +-6583, +-4334, +516, +-2266, +-3226, +-2200, +2206, +1794, +-1454, +1915, +2658, +3157, +2576, +1949, +3410, +1896, +3450, +2310, +117, +1588, +1002, +917, +-1493, +-1639, +129, +-2106, +-2237, +-2421, +-1808, +-1254, +-2307, +-727, +-923, +-651, +449, +46, +471, +268, +1098, +710, +-117, +1031, +362, +-192, +-90, +398, +471, +-775, +-349, +271, +125, +-277, +-403, +-31, +162, +682, +-104, +-477, +441, +534, +682, +136, +-219, +114, +-447, +-2159, +-2436, +1852, +1779, +-2721, +-567, +1960, +1799, +1184, +-179, +1457, +1266, +629, +1852, +-82, +-866, +-345, +61, +-696, +-3194, +-1676, +-726, +-1898, +-1857, +-1936, +-516, +-494, +-253, +808, +-301, +554, +1218, +1193, +1286, +398, +1408, +1364, +843, +1215, +506, +400, +94, +496, +632, +-447, +-192, +-387, +-386, +-505, +-1087, +-390, +-591, +-458, +-196, +-703, +-257, +-225, +87, +90, +-470, +210, +258, +-121, +-270, +-150, +267, +-203, +-43, +302, +-101, +47, +244, +209, +63, +142, +353, +64, +-158, +-79, +312, +-121, +-985, +-253, +224, +-389, +-603, +-351, +-28, +56, +376, +220, +-222, +353, +635, +478, +441, +85, +-219, +-768, +-570, +876, +771, +-478, +-189, +482, +529, +329, +211, +618, +210, +-94, +575, +101, +-511, +-508, +-454, +-437, +-1064, +-996, +-845, +-1071, +-617, +-593, +-579, +-382, +-58, +471, +253, +135, +289, +926, +1221, +374, +432, +626, +645, +834, +325, +203, +84, +-69, +-36, +-344, +-247, +-287, +-308, +-257, +-498, +-158, +-109, +-198, +51, +-60, +-96, +-138, +-56, +98, +-11, +54, +-73, +-188, +-216, +-209, +-5, +-219, +-244, +-28, +-33, +68, +-84, +-84, +80, +73, +119, +-16, +66, +277, +234, +102, +-55, +-16, +47, +2, +-93, +-193, +-113, +-55, +50, +-53, +-307, +-24, +213, +98, +37, +-31, +-7, +158, +335, +243, +22, +-138, +-114, +100, +-46, +-12, +59, +-209, +-89, +-387, +-869, +-338, +349, +214, +-232, +57, +417, +335, +312, +414, +604, +151, +63, +478, +22, +-329, +-402, +-315, +-355, +-754, +-553, +-526, +-538, +-332, +-424, +-199, +-28, +5, +-42, +94, +415, +271, +316, +136, +90, +485, +233, +202, +189, +-7, +119, +61, +238, +200, +-237, +-254, +-49, +136, +64, +-79, +20, +129, +93, +9, +163, +95, +-203, +-49, +-24, +-295, +-223, +-110, +-223, +-396, +-499, +-400, +-240, +-33, +98, +-1, +54, +179, +134, +97, +78, +77, +83, +197, +296, +85, +70, +180, +-124, +-99, +192, +-128, +-218, +57, +-193, +-181, +74, +-113, +-140, +-164, +-338, +-179, +243, +366, +-42, +-277, +-104, +326, +618, +-14, +-451, +-82, +427, +529, +-158, +-205, +119, +80, +131, +-334, +-213, +29, +-440, +-203, +-304, +-836, +-890, +-169, +646, +-67, +-447, +172, +636, +871, +527, +643, +608, +262, +642, +401, +-199, +-569, +-523, +23, +-393, +-1005, +-848, +-468, +-303, +-376, +-237, +-406, +-287, +127, +-24, +32, +107, +294, +410, +155, +369, +735, +1157, +894, +228, +386, +77, +-355, +-358, +-547, +-555, +-441, +-222, +-250, +-179, +108, +136, +345, +287, +162, +-33, +-328, +-152, +-376, +-598, +-557, +-479, +-116, +-18, +-29, +-179, +-58, +306, +129, +206, +138, +67, +337, +284, +462, +255, +127, +401, +97, +33, +-124, +-90, +194, +-120, +-174, +-185, +-107, +-46, +-546, +-587, +-11, +101, +-488, +-414, +-205, +-223, +604, +237, +-778, +-128, +197, +-56, +346, +-42, +-471, +543, +625, +42, +398, +270, +352, +612, +226, +-97, +-545, +-986, +-945, +180, +328, +-916, +-487, +557, +1254, +725, +-351, +751, +1211, +679, +478, +-325, +-431, +-253, +-48, +-569, +-1721, +-1248, +-523, +-457, +-1384, +-1587, +255, +138, +-553, +-114, +-66, +592, +657, +497, +609, +570, +1263, +1060, +490, +405, +734, +1057, +-32, +-239, +210, +68, +-104, +-819, +-495, +-219, +-666, +-557, +-737, +-389, +-352, +-536, +-222, +-397, +50, +74, +-90, +59, +-155, +548, +417, +-70, +325, +411, +649, +248, +204, +645, +186, +219, +112, +-70, +-109, +-446, +-278, +-542, +-702, +-586, +-596, +-352, +-617, +-462, +-216, +-222, +25, +-247, +-18, +456, +500, +543, +379, +531, +935, +1053, +335, +25, +672, +318, +-28, +-816, +-2456, +-1516, +306, +-121, +-1684, +-1292, +1694, +2342, +236, +60, +2252, +3274, +1020, +-188, +1232, +1227, +-252, +-1636, +-1516, +-904, +-1924, +-2779, +-2848, +-2019, +-1439, +-2150, +-1854, +-683, +309, +118, +-339, +1142, +2092, +1617, +1234, +1834, +2791, +2007, +1289, +1677, +1789, +1272, +289, +217, +270, +-317, +-828, +-1357, +-1162, +-1102, +-1626, +-1775, +-1529, +-903, +-1022, +-1267, +-600, +-113, +-67, +-274, +162, +766, +571, +349, +359, +1031, +1385, +534, +396, +1299, +1418, +144, +-242, +669, +265, +-870, +-965, +-404, +-662, +-1479, +-1228, +-777, +-889, +-1008, +-862, +-344, +42, +110, +54, +404, +1167, +1098, +571, +942, +1528, +1010, +363, +766, +584, +-897, +-1931, +-1206, +-25, +-637, +-1718, +-533, +1422, +900, +-67, +1286, +2403, +1732, +659, +1009, +1558, +228, +-802, +-877, +-925, +-1503, +-2632, +-2514, +-2009, +-2026, +-2343, +-2281, +-906, +-525, +-892, +-334, +718, +1351, +805, +1249, +2375, +2283, +1903, +1872, +2457, +2290, +1439, +1279, +1129, +860, +43, +-607, +-550, +-942, +-1455, +-1851, +-1708, +-1513, +-1901, +-1768, +-1313, +-951, +-920, +-816, +-144, +112, +93, +227, +495, +816, +757, +554, +582, +1324, +1474, +458, +711, +1433, +798, +-86, +145, +626, +-513, +-1102, +-339, +-651, +-1347, +-1325, +-907, +-1033, +-1323, +-828, +-642, +-444, +-113, +-33, +411, +789, +969, +827, +980, +1337, +1112, +1075, +727, +374, +27, +-933, +-1200, +-696, +-79, +-913, +-1343, +905, +997, +-110, +963, +1732, +1617, +723, +866, +1238, +71, +-341, +-839, +-1040, +-1214, +-2238, +-2186, +-1978, +-1808, +-2140, +-2121, +-819, +-845, +-894, +-189, +489, +830, +605, +1397, +1894, +1789, +1928, +1903, +2286, +2038, +1530, +1394, +1204, +891, +-12, +-249, +-227, +-934, +-1386, +-1439, +-1292, +-1607, +-1796, +-1363, +-1213, +-1135, +-1015, +-678, +-307, +-260, +-90, +165, +466, +520, +431, +665, +667, +502, +727, +1115, +806, +367, +1021, +1166, +173, +83, +558, +-42, +-816, +-562, +-464, +-1091, +-1190, +-917, +-975, +-1025, +-924, +-692, +-450, +-291, +-193, +142, +595, +626, +686, +970, +1082, +990, +805, +867, +843, +319, +17, +-195, +-859, +-952, +-270, +-365, +-1005, +-184, +815, +199, +328, +1211, +1085, +894, +789, +633, +384, +-89, +-576, +-976, +-924, +-1469, +-2074, +-1662, +-1558, +-1801, +-1713, +-1090, +-713, +-758, +-223, +244, +524, +791, +986, +1443, +1626, +1612, +1636, +1802, +1817, +1365, +1225, +1156, +713, +267, +23, +-169, +-603, +-894, +-988, +-1146, +-1236, +-1325, +-1250, +-1073, +-1022, +-940, +-717, +-448, +-366, +-295, +-28, +160, +192, +305, +522, +591, +553, +554, +502, +492, +703, +751, +373, +354, +735, +388, +-164, +102, +87, +-564, +-675, +-444, +-706, +-992, +-816, +-678, +-750, +-689, +-505, +-259, +-150, +-116, +176, +438, +465, +560, +815, +880, +638, +615, +851, +466, +-58, +207, +19, +-777, +-959, +-577, +-66, +-379, +-678, +444, +939, +363, +565, +1241, +1237, +565, +407, +598, +155, +-564, +-972, +-849, +-1078, +-1833, +-1883, +-1422, +-1428, +-1737, +-1373, +-620, +-553, +-545, +46, +621, +725, +734, +1186, +1600, +1530, +1380, +1579, +1777, +1428, +1053, +1111, +992, +452, +61, +49, +-175, +-716, +-967, +-907, +-1025, +-1335, +-1370, +-1115, +-1063, +-1161, +-979, +-622, +-477, +-475, +-219, +156, +240, +243, +503, +785, +734, +650, +795, +826, +557, +315, +452, +605, +60, +-329, +190, +153, +-550, +-494, +-135, +-389, +-811, +-593, +-314, +-597, +-669, +-386, +-167, +-210, +-266, +78, +299, +187, +206, +471, +554, +353, +455, +582, +393, +261, +217, +119, +-53, +-186, +-361, +-647, +-732, +-604, +-236, +-86, +-325, +388, +1004, +599, +871, +1273, +1200, +945, +645, +653, +224, +-325, +-740, +-1025, +-1149, +-1776, +-1976, +-1733, +-1752, +-1781, +-1622, +-985, +-698, +-570, +27, +500, +829, +990, +1336, +1726, +1749, +1748, +1770, +1867, +1667, +1258, +1130, +910, +447, +-38, +-286, +-509, +-991, +-1326, +-1383, +-1461, +-1615, +-1615, +-1384, +-1196, +-1080, +-838, +-482, +-175, +-14, +216, +546, +721, +771, +874, +1017, +977, +800, +710, +674, +476, +139, +50, +37, +-290, +-491, +-485, +-547, +-501, +-539, +-647, +-450, +-303, +-443, +-355, +5, +6, +-121, +158, +337, +213, +237, +445, +488, +373, +371, +309, +216, +257, +166, +-93, +-67, +-25, +-487, +-577, +-237, +-757, +-1373, +-832, +-334, +-368, +-174, +226, +1135, +1470, +1102, +1724, +2099, +1709, +1214, +900, +893, +-62, +-961, +-1193, +-1544, +-2098, +-2837, +-2650, +-2327, +-2490, +-2272, +-1748, +-890, +-508, +-179, +742, +1418, +1785, +1945, +2451, +2852, +2549, +2342, +2263, +2071, +1439, +723, +422, +-70, +-780, +-1397, +-1652, +-1795, +-2228, +-2373, +-2126, +-1836, +-1672, +-1380, +-736, +-179, +146, +496, +999, +1399, +1449, +1470, +1602, +1565, +1249, +881, +663, +364, +-144, +-546, +-764, +-1006, +-1224, +-1274, +-1323, +-1236, +-962, +-746, +-407, +-120, +80, +463, +721, +747, +843, +993, +900, +669, +618, +533, +236, +-48, +-107, +-201, +-458, +-556, +-542, +-529, +-465, +-437, +-307, +-188, +-438, +-662, +-305, +-39, +-849, +-1609, +-263, +1428, +228, +-375, +2323, +3098, +1664, +1745, +2553, +2535, +1112, +77, +-77, +-756, +-1720, +-3222, +-3587, +-2878, +-3439, +-4100, +-3459, +-1755, +-1227, +-1445, +83, +1728, +2314, +2379, +2965, +3941, +3821, +3266, +2732, +2490, +2154, +755, +-389, +-760, +-1169, +-2230, +-3181, +-2864, +-2619, +-2872, +-2717, +-1946, +-927, +-441, +68, +922, +1777, +2357, +2318, +2405, +2664, +2408, +1643, +895, +553, +-104, +-1131, +-1816, +-2166, +-2399, +-2609, +-2615, +-2416, +-1697, +-736, +-598, +40, +1632, +2103, +1854, +2137, +2549, +2345, +1456, +902, +564, +-128, +-821, +-1510, +-1762, +-1629, +-1827, +-1897, +-1407, +-639, +-298, +-256, +342, +1237, +1516, +948, +881, +1300, +285, +-601, +-106, +-1005, +-2152, +-765, +366, +-225, +105, +1800, +2667, +2263, +2358, +2609, +1902, +1309, +383, +-1178, +-2028, +-2431, +-3415, +-4550, +-4205, +-3344, +-3426, +-2845, +-1330, +27, +949, +1939, +3073, +3622, +4112, +4210, +3486, +2987, +2535, +1477, +2, +-942, +-1447, +-2432, +-3129, +-3225, +-3139, +-2816, +-2288, +-1557, +-826, +190, +1229, +1697, +2171, +2713, +2848, +2494, +1998, +1574, +810, +-38, +-777, +-1573, +-2133, +-2414, +-2554, +-2640, +-2453, +-1750, +-916, +-403, +165, +1489, +2218, +1984, +2950, +3358, +1847, +1268, +1173, +3, +-1163, +-1755, +-2176, +-2690, +-2518, +-1972, +-1908, +-1192, +22, +618, +1019, +1695, +2107, +1666, +1636, +2010, +1092, +94, +-263, +-1376, +-1768, +-457, +-1731, +-5151, +-3981, +955, +2337, +-400, +750, +5303, +6160, +5173, +3503, +1711, +2310, +1724, +-1812, +-6147, +-6323, +-3861, +-5816, +-7464, +-5990, +-3493, +-1001, +97, +1540, +2770, +5367, +7786, +5898, +4386, +4606, +4302, +1937, +-1267, +-2217, +-3112, +-3790, +-4215, +-5298, +-4526, +-2551, +-773, +-546, +-32, +2599, +3965, +3955, +3518, +3064, +2937, +2131, +893, +-1169, +-2477, +-2268, +-2844, +-3763, +-3940, +-2899, +-1616, +-1023, +-181, +607, +1586, +2617, +3023, +3070, +2914, +2991, +2172, +623, +-310, +-1374, +-2183, +-2752, +-3338, +-3214, +-2667, +-1629, +-816, +-278, +799, +1765, +2420, +2415, +2095, +1811, +1020, +509, +175, +-542, +-1258, +-1676, +-1622, +-1721, +-1740, +-1068, +-852, +-2054, +-2617, +929, +5290, +4343, +2568, +4316, +5511, +5146, +3002, +-412, +-2276, +-2717, +-3204, +-6159, +-8896, +-7253, +-4846, +-3602, +-2969, +-1694, +1159, +3995, +6267, +6021, +4739, +5447, +5599, +3839, +720, +-1621, +-2390, +-3349, +-4039, +-4975, +-5170, +-3395, +-1315, +139, +701, +2083, +4056, +4738, +4561, +3559, +2429, +1602, +507, +-1149, +-3232, +-4042, +-3810, +-3787, +-3580, +-3000, +-1677, +-99, +1293, +2238, +2416, +2580, +2706, +3382, +4128, +2082, +-743, +-502, +-239, +-2169, +-3426, +-3827, +-3585, +-2181, +-999, +-751, +-658, +1255, +3304, +2794, +1816, +1619, +1680, +1241, +8, +-1322, +-2153, +-1180, +-321, +-1526, +-2302, +-1478, +-72, +363, +-230, +-426, +-82, +-103, +-77, +2541, +4581, +3326, +3631, +4197, +3161, +2512, +785, +-1567, +-3641, +-4417, +-4491, +-6115, +-6640, +-5357, +-3878, +-2181, +-630, +1004, +2187, +3811, +5725, +5413, +4276, +3600, +2869, +1625, +-298, +-1819, +-3206, +-3817, +-3270, +-3022, +-2734, +-1844, +-175, +1408, +2225, +2944, +3158, +3145, +3144, +2440, +1057, +-421, +-1236, +-1857, +-2701, +-3165, +-3225, +-2780, +-1898, +-937, +-298, +-206, +87, +1419, +3719, +4816, +3523, +2068, +1482, +881, +-351, +-2295, +-3896, +-4219, +-3195, +-2016, +-1842, +-1410, +9, +1300, +1732, +1636, +1591, +1442, +1292, +1060, +905, +951, +40, +-693, +-286, +-671, +-1437, +-1264, +-383, +371, +148, +623, +1538, +-1352, +-5744, +-4090, +1312, +2921, +1799, +1479, +2488, +5347, +7009, +4782, +210, +-1976, +-191, +-607, +-4050, +-6603, +-6961, +-5451, +-3585, +-2599, +-3395, +-3123, +320, +3569, +4271, +3628, +3813, +4892, +5331, +4548, +2148, +-569, +-1301, +-1040, +-1894, +-3494, +-4162, +-3511, +-2160, +-805, +-235, +-93, +737, +2313, +3162, +2711, +2027, +1466, +942, +786, +684, +-712, +-2331, +-1677, +-1330, +-2395, +-1867, +-539, +-52, +-62, +434, +820, +233, +735, +1092, +-206, +-628, +-8, +124, +-546, +-681, +-390, +-814, +-535, +90, +-5, +-80, +381, +1054, +919, +810, +759, +221, +373, +445, +-358, +-1278, +-1213, +15, +517, +240, +469, +618, +117, +-366, +-1994, +-3771, +-679, +3229, +1528, +-90, +1057, +1545, +2429, +2774, +1183, +-1064, +-1624, +94, +-409, +-2670, +-2848, +-2292, +-1846, +-1057, +-812, +-1316, +-1070, +454, +1003, +468, +747, +1275, +1268, +1598, +1792, +975, +808, +942, +410, +441, +-32, +-622, +-261, +-436, +-603, +-474, +-709, +-641, +-368, +-305, +-310, +-242, +-165, +-29, +114, +107, +193, +187, +158, +287, +194, +100, +51, +-28, +-31, +-70, +-19, +-33, +-143, +-165, +-225, +-240, +-244, +-273, +-283, +-269, +-118, +-7, +34, +68, +94, +196, +267, +258, +202, +136, +102, +29, +-72, +-179, +-281, +-298, +-274, +-227, +-161, +-15, +162, +227, +236, +268, +332, +335, +237, +132, +8, +-77, +-130, +-179, +-220, +-267, +-270, +-218, +-159, +-65, +46, +111, +144, +187, +226, +197, +129, +56, +-14, +-93, +-179, +-259, +-280, +-256, +-236, +-198, +-168, +-134, +-41, +51, +66, +42, +50, +76, +107, +156, +168, +144, +124, +110, +76, +-5, +-50, +-72, +-130, +-219, +-284, +-253, +-181, +-118, +-75, +-56, +-9, +77, +144, +175, +176, +206, +247, +240, +187, +85, +-15, +-79, +-158, +-240, +-274, +-283, +-257, +-178, +-96, +-36, +44, +131, +196, +206, +180, +175, +134, +61, +-26, +-116, +-179, +-195, +-188, +-215, +-250, +-209, +-137, +-97, +-62, +-22, +-5, +32, +84, +114, +93, +100, +142, +135, +84, +33, +3, +-8, +-9, +-22, +-60, +-79, +-50, +-5, +17, +2, +-1, +6, +23, +40, +43, +25, +26, +42, +29, +-15, +-55, +-104, +-134, +-147, +-203, +-244, +-220, +-175, +-118, +-58, +-5, +44, +95, +136, +135, +134, +132, +115, +60, +-4, +-46, +-72, +-110, +-124, +-135, +-159, +-145, +-93, +-46, +-21, +44, +107, +141, +151, +132, +111, +110, +100, +44, +-15, +-43, +-67, +-92, +-107, +-110, +-96, +-80, +-58, +-15, +13, +44, +67, +68, +42, +23, +19, +-15, +-49, +-86, +-101, +-121, +-131, +-141, +-159, +-165, +-152, +-120, +-92, +-52, +13, +67, +107, +139, +151, +141, +117, +76, +36, +10, +-16, +-35, +-73, +-75, +-56, +-46, +-49, +-58, +-46, +-15, +17, +40, +43, +53, +59, +49, +34, +12, +-9, +-19, +-43, +-79, +-116, +-130, +-134, +-123, +-100, +-96, +-77, +-48, +-12, +30, +44, +59, +60, +47, +40, +25, +20, +22, +15, +6, +-14, +-24, +-29, +-35, +-36, +-43, +-45, +-41, +-33, +-18, +-9, +3, +6, +-2, +-4, +3, +9, +17, +20, +17, +10, +3, +5, +3, +-12, +-24, +-31, +-36, +-45, +-52, +-48, +-55, +-59, +-60, +-55, +-53, +-53, +-63, +-65, +-60, +-50, +-43, +-48, +-35, +-24, +-25, +-35, +-41, +-39, +-33, +-16, +-2, +12, +27, +42, +57, +68, +66, +61, +51, +44, +25, +0, +-9, +-31, +-49, +-53, +-52, +-36, +-39, +-41, +-7, +27, +25, +15, +27, +32, +23, +20, +5, +-18, +-26, +-36, +-39, +-50, +-62, +-63, +-62, +-55, +-50, +-41, +-33, +-25, +-14, +-1, +5, +9, +15, +5, +-7, +-18, +-26, +-33, +-46, +-56, +-73, +-82, +-66, +-67, +-50, +-38, +-22, +-7, +17, +30, +37, +36, +29, +19, +13, +12, +-7, +-25, +-41, +-42, +-38, +-49, +-49, +-35, +-26, +-8, +6, +0, +0, +-2, +-5, +-1, +-4, +-2, +-12, +-12, +-15, +-16, +-19, +-24, +-35, +-39, +-48, +-46, +-35, +-25, +-4, +13, +25, +27, +3, +2, +30, +27, +-1, +-1, +-16, +-25, +-18, +-36, +-42, +-48, +-62, +-56, +-48, +-53, +-46, +-36, +-26, +-15, +-8, +12, +23, +29, +25, +13, +5, +5, +0, +-16, +-12, +-19, +-25, +-24, +-29, +-29, +-39, +-33, +-25, +-33, +-19, +-5, +-11, +-22, +-24, +-18, +-25, +-32, +-39, +-39, +-32, +-41, +-36, +-43, +-39, +-18, +-11, +-7, +-12, +-8, +-4, +-7, +0, +5, +-4, +2, +6, +-2, +-5, +-19, +-25, +-29, +-33, +-39, +-36, +-25, +-24, +-12, +-7, +5, +13, +16, +34, +29, +3, +-1, +-1, +-14, +-16, +-18, +-19, +-19, +-26, +-22, +-32, +-26, +-26, +-28, +-14, +-11, +-5, +-2, +-4, +0, +-9, +-15, +-16, +-28, +-28, +-32, +-48, +-56, +-52, +-55, +-56, +-56, +-48, +-41, +-31, +-22, +-8, +-2, +-1, +9, +10, +-1, +3, +-7, +-1, +0, +2, +-1, +-11, +-16, +-28, +-38, +-45, +-41, +-42, +-24, +-15, +5, +9, +-9, +-19, +-62, +-192, +-328, +125, +1057, +514, +-579, +277, +-138, +-1499, +-18, +595, +-276, +67, +-332, +-853, +761, +1310, +-301, +-237, +513, +762, +540, +-164, +57, +-1481, +-1500, +396, +-1350, +-365, +1445, +-933, +136, +1200, +-686, +876, +1901, +166, +-349, +-244, +-198, +-1073, +-1085, +187, +-481, +294, +893, +-877, +-4, +659, +-7, +-7, +-178, +73, +-62, +463, +223, +-523, +500, +209, +318, +199, +-818, +398, +-28, +-508, +320, +-635, +-127, +226, +-688, +83, +-65, +-426, +214, +-318, +-297, +425, +134, +29, +311, +-21, +115, +316, +-417, +-84, +127, +-294, +407, +219, +-470, +-110, +221, +-148, +-176, +231, +-189, +180, +194, +-562, +172, +71, +110, +90, +-645, +107, +-58, +267, +646, +-467, +349, +628, +-366, +-205, +-16, +-235, +-386, +-322, +-31, +22, +54, +316, +-226, +219, +689, +-186, +272, +-288, +-679, +37, +-351, +17, +-284, +-130, +95, +-312, +347, +-15, +563, +439, +-696, +54, +-113, +-233, +-212, +-246, +190, +-33, +91, +-76, +-106, +192, +88, +117, +196, +432, +56, +-100, +279, +0, +-65, +-243, +-412, +-49, +-193, +-361, +121, +104, +-280, +102, +-89, +-120, +488, +-7, +-145, +194, +261, +-270, +-314, +396, +-185, +-93, +271, +81, +134, +-317, +13, +-158, +-549, +445, +388, +-53, +-2, +-550, +-332, +88, +-237, +-131, +-45, +36, +386, +-128, +-243, +90, +145, +686, +-259, +-511, +217, +-213, +422, +-318, +-277, +809, +-339, +131, +-73, +-577, +354, +296, +527, +-158, +-488, +-89, +-196, +203, +-252, +-86, +146, +-242, +264, +-39, +56, +287, +-470, +-624, +-666, +71, +632, +221, +292, +189, +390, +39, +-600, +-80, +-182, +-79, +182, +44, +-33, +-700, +101, +319, +-417, +895, +459, +-155, +550, +-325, +-645, +-325, +-94, +-491, +-460, +284, +-193, +182, +669, +325, +-58, +122, +520, +-504, +-423, +169, +169, +360, +-165, +-195, +-404, +-161, +42, +-1156, +-581, +449, +434, +301, +-41, +405, +544, +298, +-87, +-716, +46, +322, +-460, +-590, +-164, +-28, +-530, +-140, +141, +138, +776, +514, +-121, +-461, +380, +1170, +257, +-32, +10, +-174, +-114, +-369, +-771, +-925, +148, +571, +-413, +-89, +539, +17, +-87, +6, +-716, +-392, +683, +124, +-436, +233, +473, +66, +-198, +-329, +-478, +-26, +588, +0, +-475, +362, +483, +-189, +-145, +-310, +-516, +-4, +63, +-461, +-269, +413, +360, +507, +805, +291, +447, +580, +-383, +-723, +-312, +-141, +-270, +-638, +-824, +-416, +-89, +-277, +-126, +476, +1006, +1010, +898, +1040, +320, +-185, +-148, +-794, +-989, +-1019, +-972, +-450, +-210, +17, +-31, +83, +90, +-138, +415, +219, +-162, +597, +1156, +1003, +108, +-376, +-202, +-291, +-216, +-644, +-1095, +-404, +282, +248, +-150, +145, +371, +-104, +305, +261, +-512, +-267, +-101, +-549, +-553, +59, +260, +350, +557, +-114, +-223, +495, +388, +278, +414, +177, +22, +23, +29, +-12, +-65, +90, +-250, +-756, +-60, +533, +142, +-76, +-157, +-181, +193, +-77, +-1185, +-1054, +54, +333, +-124, +-750, +-431, +700, +1449, +1017, +-877, +-1236, +415, +277, +-586, +-126, +373, +326, +85, +410, +132, +-361, +185, +-293, +-440, +915, +1344, +155, +-698, +262, +519, +-270, +-431, +-678, +-693, +-556, +-294, +78, +-182, +-325, +-478, +-675, +-55, +248, +-404, +-395, +415, +907, +614, +-206, +-14, +699, +387, +-135, +-276, +192, +759, +456, +-263, +-420, +340, +585, +-128, +-208, +-114, +111, +781, +93, +-675, +-99, +-7, +-560, +-835, +-647, +-638, +-199, +853, +291, +-198, +666, +439, +-127, +-266, +-457, +-229, +-134, +-103, +-678, +-1277, +-120, +782, +461, +-101, +124, +985, +421, +-76, +-128, +-329, +493, +806, +653, +363, +349, +687, +-593, +-945, +-67, +-495, +-1020, +-1158, +-869, +-436, +359, +757, +-753, +-1234, +42, +680, +217, +-504, +-137, +609, +934, +333, +-750, +-252, +867, +632, +-327, +-104, +951, +1184, +548, +-529, +-618, +565, +969, +-179, +-967, +-184, +680, +735, +-87, +-1227, +-985, +125, +-7, +-1102, +-794, +676, +669, +-28, +-332, +-1333, +-1237, +513, +1204, +335, +318, +1758, +420, +-2489, +-1776, +76, +-359, +-1987, +-1489, +925, +2204, +2995, +2332, +306, +1254, +3049, +1171, +-1367, +-1189, +-468, +-1397, +-2357, +-2116, +-1536, +-1060, +-750, +-790, +-788, +-75, +638, +683, +699, +383, +91, +642, +911, +258, +-376, +-362, +-271, +-167, +241, +296, +308, +650, +945, +999, +883, +953, +946, +418, +-453, +-808, +-461, +-706, +-1258, +-988, +-434, +-52, +66, +-123, +151, +679, +428, +-481, +-509, +771, +714, +-512, +-1035, +-1486, +-726, +-31, +-1281, +-1212, +1309, +2854, +-717, +-5541, +-2528, +4411, +4364, +-965, +-2135, +1605, +6128, +6542, +901, +-2670, +955, +3056, +-1970, +-6739, +-4304, +-1117, +-2714, +-3398, +-2501, +-995, +1508, +2242, +360, +-608, +1734, +2558, +452, +-246, +119, +-35, +-26, +448, +260, +60, +1385, +1602, +292, +541, +1344, +673, +-386, +-482, +-556, +-744, +-250, +-352, +-906, +-236, +1023, +238, +-1847, +-1223, +598, +466, +107, +156, +44, +587, +1174, +431, +-1296, +-1827, +-647, +-169, +-780, +-1298, +-1319, +-43, +2335, +3246, +1074, +-2402, +-6047, +-7079, +-1059, +4745, +1480, +-734, +4822, +9405, +9224, +5118, +-67, +-1939, +-822, +-1629, +-6357, +-8638, +-5218, +-2163, +-2139, +-2847, +-1910, +727, +2801, +3025, +1442, +682, +1915, +2640, +1630, +-328, +-1203, +-831, +-587, +-253, +102, +145, +388, +1416, +1997, +1157, +750, +902, +285, +-634, +-968, +-935, +-1489, +-1441, +-559, +-307, +98, +255, +-79, +393, +757, +383, +-92, +224, +977, +612, +-250, +-768, +-1037, +-1196, +-1434, +-1257, +-528, +53, +-83, +128, +1382, +1759, +1323, +-341, +-8125, +-13054, +-157, +15976, +6087, +-6341, +5951, +14103, +11032, +4159, +-6743, +-10446, +-3027, +1775, +-9133, +-16626, +-6476, +3326, +1674, +-3781, +-1209, +4156, +6635, +7114, +2682, +-1791, +1685, +5147, +898, +-4427, +-3907, +-1369, +-1268, +-375, +-530, +-1009, +1666, +3818, +2716, +1439, +2971, +2807, +-334, +-836, +548, +-1022, +-3429, +-2739, +-1867, +-1887, +-494, +346, +44, +972, +2167, +1438, +-239, +-396, +-202, +-971, +-1043, +-826, +-696, +13, +401, +-335, +-805, +129, +226, +-515, +410, +1208, +717, +88, +-5350, +-13627, +-8527, +10641, +17424, +-276, +-2734, +12987, +14423, +8109, +-1717, +-11484, +-8209, +-128, +-4410, +-16396, +-14255, +-1867, +5154, +1136, +-2316, +3394, +8584, +10376, +6841, +-603, +-2497, +2138, +1684, +-4646, +-7354, +-4663, +-2188, +-675, +945, +795, +1892, +5103, +6024, +3291, +1198, +1634, +500, +-1851, +-2889, +-3691, +-3739, +-1530, +228, +-833, +-215, +2780, +3205, +1946, +1246, +642, +-103, +-849, +-1823, +-3171, +-2957, +-1587, +-1554, +-1490, +-322, +710, +1122, +1530, +1998, +1508, +689, +476, +669, +754, +-720, +-4025, +-6837, +-7325, +-6367, +-2698, +6174, +12964, +9699, +7442, +11835, +10703, +3935, +-2704, +-7836, +-10023, +-10369, +-10922, +-12144, +-10017, +-3153, +3001, +3870, +4798, +9021, +10308, +7988, +4810, +1285, +-1649 +}; diff --git a/unittest/hts1a_1300.h b/unittest/hts1a_1300.h new file mode 100644 index 0000000..a254ecc --- /dev/null +++ b/unittest/hts1a_1300.h @@ -0,0 +1,8002 @@ +short hts1a_1300[] = { +0, +1, +1, +2, +2, +3, +2, +2, +2, +3, +2, +4, +2, +2, +-1, +0, +-2, +0, +2, +2, +-3, +-2, +-5, +-2, +-4, +-1, +-5, +-5, +-10, +-11, +-15, +-9, +-8, +0, +-4, +-5, +-10, +-5, +0, +1, +-3, +7, +3, +2, +0, +6, +10, +12, +5, +8, +9, +7, +13, +13, +5, +19, +23, +14, +3, +0, +7, +10, +9, +0, +-11, +-18, +-15, +-16, +-15, +-27, +-37, +-46, +-40, +-37, +-49, +-80, +-73, +-65, +-43, +-10, +39, +72, +88, +82, +82, +66, +57, +34, +38, +32, +28, +17, +28, +7, +1, +-12, +-1, +-13, +-2, +-9, +-11, +-24, +-16, +-26, +-16, +-24, +-11, +-17, +-11, +-21, +-14, +-26, +-11, +-9, +6, +-11, +-3, +-9, +2, +-5, +10, +11, +23, +2, +8, +5, +24, +14, +29, +9, +21, +14, +21, +11, +12, +9, +22, +4, +16, +0, +5, +-2, +-1, +-12, +2, +-6, +-13, +-29, +-22, +-37, +-25, +-32, +-29, +-42, +-45, +-60, +-51, +-59, +-45, +-19, +23, +57, +80, +69, +65, +49, +40, +34, +38, +20, +10, +5, +5, +-6, +-5, +-10, +-3, +-7, +-15, +-20, +-21, +-16, +-9, +-17, +-18, +-21, +-20, +-9, +-5, +-5, +-7, +-19, +-15, +-4, +2, +6, +-3, +-3, +1, +8, +7, +16, +12, +7, +3, +5, +15, +22, +15, +17, +12, +16, +6, +13, +-3, +8, +12, +9, +1, +7, +-1, +6, +-5, +-1, +-7, +-9, +-20, +-17, +-20, +-3, +-8, +-1, +-27, +-35, +-46, +-36, +-34, +-21, +-23, +2, +20, +43, +41, +52, +41, +37, +36, +41, +24, +16, +7, +1, +-18, +-14, +-22, +-6, +-4, +-5, +-8, +-7, +-15, +-14, +-20, +-10, +-21, +-13, +-3, +-2, +-7, +0, +1, +-4, +-10, +-3, +-1, +-1, +-4, +-2, +4, +13, +15, +11, +3, +-4, +3, +4, +2, +7, +13, +9, +9, +9, +6, +0, +1, +-1, +-1, +3, +5, +1, +0, +-5, +-2, +-12, +4, +-7, +-3, +-11, +-8, +-6, +-10, +-14, +-8, +-16, +-12, +-19, +-19, +-20, +-3, +0, +10, +23, +25, +27, +45, +30, +28, +19, +24, +13, +0, +-15, +-20, +-17, +-4, +-9, +-6, +-5, +-10, +-9, +0, +-1, +-7, +-14, +-14, +-6, +-6, +4, +0, +-5, +1, +4, +6, +4, +-1, +3, +-8, +-4, +1, +11, +9, +12, +6, +1, +1, +10, +6, +-2, +-3, +9, +7, +4, +4, +0, +-8, +0, +-3, +-1, +-2, +-1, +-6, +-19, +-10, +-18, +-13, +-15, +-14, +-11, +-6, +-4, +0, +7, +18, +16, +38, +34, +32, +23, +17, +7, +-6, +-24, +-17, +-16, +-9, +-6, +-5, +-12, +-3, +-12, +-5, +-6, +-5, +-15, +-6, +-3, +2, +1, +13, +0, +4, +0, +0, +-1, +7, +6, +11, +4, +2, +4, +9, +5, +7, +-4, +-1, +0, +7, +-1, +-7, +1, +1, +1, +0, +-5, +-10, +-9, +-9, +-6, +-13, +-13, +-9, +-7, +-10, +-5, +-3, +10, +12, +27, +21, +26, +17, +13, +9, +2, +-4, +5, +-10, +-4, +-18, +-11, +-15, +-5, +-6, +2, +-9, +-5, +-17, +-15, +-4, +13, +0, +0, +4, +15, +7, +2, +-4, +0, +-7, +10, +5, +13, +11, +8, +4, +8, +0, +-1, +0, +3, +-2, +1, +-3, +0, +0, +-12, +-14, +-18, +-15, +-14, +-15, +-16, +-7, +3, +9, +7, +12, +17, +24, +25, +27, +16, +10, +-8, +-2, +-8, +-8, +-14, +-10, +-13, +-2, +-7, +2, +-12, +-4, +-9, +3, +-2, +4, +-2, +13, +1, +7, +-9, +1, +0, +13, +6, +11, +0, +9, +-2, +6, +-9, +3, +-9, +10, +-5, +-7, +-4, +5, +-22, +-8, +-28, +-14, +-23, +-11, +-16, +7, +13, +31, +24, +35, +27, +33, +16, +20, +5, +3, +-20, +-6, +-13, +-2, +-17, +-10, +-18, +-2, +-7, +1, +-10, +-5, +-12, +-2, +-6, +4, +-5, +6, +-3, +-2, +-9, +-1, +-5, +1, +0, +3, +-8, +0, +-5, +0, +-6, +-13, +-11, +3, +-4, +-10, +-18, +-4, +0, +6, +4, +17, +26, +35, +38, +35, +34, +24, +15, +6, +-2, +0, +0, +5, +0, +-6, +-10, +-13, +-12, +-6, +-10, +-6, +-2, +-3, +-13, +-13, +-8, +-9, +-9, +-4, +-10, +-10, +-8, +-7, +-15, +-8, +-8, +-14, +-11, +-8, +-9, +-4, +-20, +-13, +-3, +-4, +-11, +-19, +-16, +1, +4, +15, +21, +39, +45, +56, +49, +48, +30, +26, +8, +4, +-4, +-5, +-3, +1, +-5, +-7, +-17, +-8, +-4, +-2, +-11, +-1, +-5, +-4, +-9, +-10, +-7, +-9, +-13, +-7, +-2, +-8, +-10, +-14, +-19, +-12, +-4, +-6, +-10, +-11, +-11, +-14, +-9, +-11, +-4, +-8, +-5, +-11, +-17, +-16, +-6, +-3, +15, +15, +38, +45, +56, +53, +49, +32, +31, +11, +14, +-7, +-5, +-10, +1, +-10, +-8, +-22, +-5, +-10, +-7, +-21, +-2, +-12, +2, +-13, +-1, +-11, +-4, +-17, +4, +0, +1, +-7, +0, +-6, +11, +5, +4, +-7, +2, +-10, +1, +-8, +-3, +-7, +1, +-11, +-13, +-26, +-16, +-29, +-11, +-12, +2, +11, +27, +32, +37, +32, +43, +27, +25, +12, +9, +-4, +-11, +-25, +-20, +-18, +-11, +-13, +-8, +-8, +-8, +-16, +-14, +-5, +4, +6, +10, +5, +8, +7, +4, +16, +8, +2, +4, +9, +13, +15, +4, +0, +0, +-3, +-1, +-2, +-7, +-6, +-1, +-6, +-12, +-20, +-21, +-29, +-29, +-16, +-14, +-3, +9, +16, +26, +31, +43, +37, +31, +23, +15, +7, +-5, +-22, +-23, +-23, +-18, +-18, +-16, +-18, +-5, +-10, +-9, +-16, +-7, +-6, +7, +1, +12, +7, +18, +9, +11, +0, +5, +0, +15, +8, +23, +8, +7, +-6, +3, +-8, +10, +-4, +6, +-10, +2, +-18, +-1, +-12, +-13, +-32, +-24, +-28, +-15, +-29, +1, +-6, +15, +21, +45, +41, +47, +32, +31, +14, +12, +-11, +-9, +-19, +-15, +-23, +-16, +-28, +-10, +-15, +-5, +-19, +-12, +-11, +3, +-4, +4, +1, +7, +7, +1, +-1, +4, +6, +16, +10, +14, +13, +11, +8, +7, +5, +7, +5, +3, +4, +1, +-9, +-7, +-8, +-15, +-11, +-20, +-22, +-26, +-26, +-28, +-23, +-17, +-2, +5, +30, +39, +56, +47, +46, +29, +23, +3, +7, +-7, +-1, +-22, +-22, +-33, +-25, +-30, +-19, +-17, +-11, +-22, +-2, +-5, +0, +-6, +12, +2, +19, +2, +11, +8, +16, +13, +16, +4, +14, +5, +13, +0, +7, +-1, +5, +-3, +7, +-1, +-2, +-6, +-4, +-16, +-6, +-12, +-7, +-13, +-12, +-23, +-22, +-30, +-20, +-23, +-6, +-1, +15, +18, +29, +33, +42, +36, +44, +36, +34, +21, +7, +-7, +-11, +-19, +-28, +-32, +-23, +-25, +-16, +-25, +-23, +-26, +-25, +-12, +-4, +-1, +0, +3, +7, +4, +7, +7, +12, +14, +4, +15, +16, +15, +15, +14, +5, +8, +4, +6, +4, +6, +11, +4, +6, +5, +-4, +-10, +-9, +-4, +-5, +-8, +-12, +-17, +-24, +-30, +-28, +-30, +-32, +-28, +-22, +-16, +-5, +6, +15, +27, +44, +52, +61, +50, +46, +36, +31, +17, +1, +-5, +-9, +-15, +-18, +-28, +-26, +-34, +-34, +-32, +-25, +-24, +-19, +-17, +-12, +-9, +0, +-6, +7, +0, +13, +9, +15, +12, +16, +10, +14, +13, +22, +14, +17, +0, +9, +1, +7, +7, +16, +4, +9, +-7, +-2, +-7, +4, +-2, +3, +-3, +-4, +-22, +-9, +-18, +-11, +-19, +-4, +-16, +-22, +-33, +-31, +-40, +-29, +-32, +-14, +-8, +17, +26, +51, +45, +57, +53, +60, +53, +53, +36, +31, +16, +12, +-7, +-18, +-32, +-36, +-44, +-39, +-45, +-36, +-41, +-35, +-31, +-24, +-28, +-17, +-14, +-2, +-6, +0, +8, +18, +18, +19, +24, +18, +11, +19, +12, +15, +18, +20, +19, +18, +13, +9, +4, +8, +6, +5, +0, +1, +-5, +0, +3, +4, +6, +4, +6, +3, +-5, +-7, +-16, +-18, +-19, +-12, +-4, +-7, +-14, +-24, +-32, +-42, +-39, +-43, +-39, +-30, +-29, +-25, +-20, +-5, +3, +16, +33, +49, +68, +79, +81, +84, +77, +70, +58, +41, +26, +9, +-1, +-16, +-30, +-39, +-49, +-53, +-50, +-52, +-51, +-46, +-40, +-41, +-36, +-33, +-27, +-18, +-10, +3, +9, +9, +16, +13, +14, +10, +14, +14, +11, +7, +10, +21, +23, +19, +16, +12, +7, +2, +3, +7, +8, +7, +10, +10, +15, +14, +6, +2, +-5, +-1, +-4, +-3, +-3, +-3, +1, +-1, +-2, +-3, +-12, +-16, +-18, +-12, +-11, +-10, +-6, +-3, +-12, +-19, +-19, +-23, +-24, +-22, +-22, +-34, +-39, +-33, +-31, +-18, +-2, +21, +51, +69, +82, +92, +87, +86, +77, +61, +42, +19, +0, +-11, +-21, +-33, +-43, +-46, +-50, +-54, +-59, +-53, +-47, +-42, +-34, +-26, +-16, +-10, +-1, +0, +-2, +-1, +9, +10, +15, +22, +27, +16, +3, +7, +2, +11, +16, +15, +16, +11, +10, +4, +3, +8, +4, +10, +6, +6, +8, +3, +0, +-1, +2, +-1, +-2, +-4, +-4, +-3, +0, +-3, +-6, +-7, +-8, +-13, +-15, +-13, +-12, +-14, +-12, +-15, +-17, +-24, +-32, +-36, +-41, +-40, +-26, +-12, +7, +25, +41, +63, +79, +80, +82, +70, +63, +50, +24, +7, +-6, +-17, +-35, +-38, +-33, +-38, +-40, +-45, +-45, +-31, +-30, +-24, +-17, +-18, +-13, +-5, +3, +3, +6, +5, +8, +7, +10, +11, +9, +11, +9, +8, +6, +5, +10, +11, +10, +10, +10, +4, +6, +7, +6, +5, +7, +11, +9, +9, +11, +9, +4, +1, +-2, +-3, +3, +4, +0, +-6, +-13, +-15, +-22, +-26, +-25, +-25, +-19, +-18, +-22, +-27, +-28, +-35, +-36, +-35, +-19, +-1, +21, +39, +64, +69, +73, +70, +64, +47, +34, +15, +1, +-13, +-16, +-31, +-30, +-39, +-32, +-29, +-17, +-19, +-21, +-17, +-3, +-16, +-7, +-8, +3, +-2, +4, +-4, +0, +-12, +0, +6, +5, +0, +7, +4, +9, +0, +17, +6, +8, +7, +12, +8, +17, +2, +5, +1, +6, +5, +7, +5, +6, +-2, +-3, +-3, +0, +-11, +0, +1, +0, +-6, +-9, +-18, +-6, +-8, +-9, +-14, +-4, +-14, +-19, +-29, +-28, +-29, +-23, +-22, +-7, +3, +25, +39, +59, +66, +68, +59, +51, +30, +16, +-2, +-14, +-25, +-26, +-40, +-24, +-25, +-12, +-16, +-13, +-17, +-10, +-21, +-10, +-14, +-1, +-9, +3, +0, +1, +-8, +5, +-8, +-2, +-3, +14, +6, +17, +1, +11, +5, +4, +-10, +15, +3, +12, +7, +14, +8, +15, +2, +8, +-4, +9, +0, +5, +-5, +8, +-6, +0, +-6, +0, +-15, +-10, +-23, +-15, +-15, +-6, +-20, +-12, +-20, +-17, +-27, +-18, +-14, +7, +13, +32, +42, +56, +43, +45, +34, +26, +10, +-2, +-14, +-10, +-25, +-21, +-22, +-12, +-18, +-11, +-9, +-17, +-12, +-7, +-15, +-1, +0, +-1, +-1, +1, +-12, +-8, +2, +8, +12, +7, +9, +4, +4, +3, +7, +4, +12, +6, +10, +15, +15, +3, +-3, +-5, +3, +6, +6, +0, +1, +-4, +-3, +-9, +-13, +-10, +-9, +-9, +-4, +-9, +-12, +-20, +-23, +-26, +-27, +-26, +-12, +2, +19, +38, +45, +48, +54, +44, +26, +15, +6, +-5, +-10, +-17, +-21, +-18, +-18, +-21, +-16, +-12, +-14, +-9, +-9, +-16, +-7, +3, +2, +0, +1, +-3, +9, +12, +1, +-3, +0, +3, +16, +10, +8, +10, +13, +4, +2, +7, +15, +5, +5, +3, +1, +-6, +1, +-4, +-6, +-7, +-3, +-20, +-7, +-17, +-12, +-18, +-14, +-21, +-25, +-34, +-20, +-22, +-2, +18, +44, +52, +70, +50, +47, +22, +4, +-6, +-12, +-21, +-17, +-20, +-10, +-18, +-16, +-15, +-10, +-11, +-9, +-5, +0, +-8, +-2, +0, +-4, +-3, +-1, +4, +7, +13, +12, +8, +0, +7, +5, +10, +5, +9, +9, +6, +-9, +-1, +0, +7, +-1, +7, +-5, +0, +-18, +-14, +-14, +2, +-2, +-9, +-34, +-27, +-34, +-18, +-22, +4, +9, +47, +47, +67, +53, +50, +20, +11, +-18, +-2, +-33, +-14, +-16, +-5, +-14, +2, +-21, +0, +-11, +0, +-21, +0, +-11, +-1, +-10, +5, +-17, +5, +-8, +13, +-3, +9, +-1, +17, +-1, +18, +2, +16, +2, +21, +1, +18, +-5, +10, +-7, +9, +-12, +8, +-17, +-5, +-29, +-14, +-31, +-3, +-27, +-16, +-35, +-11, +-9, +32, +16, +54, +43, +46, +14, +20, +-19, +3, +-11, +5, +-25, +-2, +-17, +-4, +-25, +7, +-17, +2, +-19, +1, +-6, +11, +-17, +0, +-9, +28, +1, +16, +-9, +9, +2, +23, +7, +20, +0, +13, +-3, +14, +-7, +9, +-4, +3, +-14, +0, +-21, +3, +-11, +-12, +-23, +-17, +-42, +-20, +-45, +-46, +-39, +-24, +-22, +11, +-2, +39, +89, +134, +120, +101, +57, +24, +3, +1, +-50, +-71, +-68, +-47, +-53, +-38, +-30, +-33, +-19, +-9, +-27, +-3, +-3, +-14, +-9, +11, +12, +13, +0, +19, +29, +13, +16, +25, +19, +18, +18, +24, +15, +23, +26, +13, +16, +18, +11, +3, +-4, +-2, +-3, +-28, +-30, +-22, +-42, +-41, +-34, +-50, +-35, +-34, +-69, +-66, +-76, +-101, +-94, +-109, +-80, +16, +103, +192, +318, +337, +259, +220, +156, +64, +6, +-55, +-127, +-185, +-195, +-185, +-182, +-146, +-143, +-137, +-71, +-18, +3, +22, +42, +46, +58, +66, +57, +53, +44, +30, +21, +11, +8, +-1, +6, +7, +15, +24, +30, +33, +39, +42, +40, +47, +63, +59, +47, +31, +14, +-5, +-19, +-28, +-45, +-46, +-55, +-55, +-65, +-72, +-87, +-100, +-97, +-100, +-99, +-127, +-147, +-166, +-184, +-204, +-177, +-157, +-17, +398, +737, +863, +867, +696, +510, +337, +155, +-43, +-288, +-535, +-694, +-732, +-710, +-658, +-558, +-493, +-392, +-178, +40, +186, +298, +359, +366, +397, +439, +372, +242, +150, +30, +-79, +-127, +-173, +-207, +-219, +-151, +-86, +-12, +69, +136, +170, +198, +223, +227, +212, +183, +145, +68, +8, +-23, +-56, +-76, +-84, +-104, +-137, +-199, +-222, +-219, +-193, +-154, +-109, +-85, +-48, +-34, +-45, +-79, +-106, +-163, +-189, +-284, +-388, +-491, +-520, +-528, +-492, +-334, +-181, +337, +1588, +2517, +2790, +2658, +2100, +1398, +804, +193, +-514, +-1413, +-2214, +-2762, +-2872, +-2689, +-2341, +-1805, +-1254, +-671, +197, +1134, +1850, +2271, +2443, +2238, +1863, +1564, +1131, +361, +-457, +-1179, +-1843, +-2236, +-2234, +-2109, +-1937, +-1485, +-755, +-24, +787, +1557, +2039, +2226, +2373, +2324, +1994, +1476, +844, +37, +-724, +-1258, +-1640, +-1935, +-1977, +-1798, +-1435, +-968, +-344, +216, +675, +1089, +1400, +1518, +1429, +1249, +876, +412, +-33, +-430, +-785, +-1070, +-1170, +-1277, +-1314, +-1269, +-1110, +-978, +-804, +-609, +-641, +-697, +-651, +-552, +-494, +-325, +135, +500, +2215, +5495, +6796, +6052, +5042, +3147, +1215, +116, +-1002, +-3144, +-5589, +-6520, +-6317, +-5735, +-4288, +-2890, +-2091, +-759, +1408, +3363, +4471, +4948, +4826, +3928, +3044, +2389, +1098, +-649, +-2099, +-3161, +-3899, +-4013, +-3612, +-3199, +-2637, +-1418, +9, +1187, +2276, +3095, +3361, +3341, +3278, +2863, +1960, +1001, +31, +-1000, +-1789, +-2155, +-2455, +-2552, +-2271, +-1698, +-1070, +-305, +507, +1062, +1471, +1806, +1930, +1752, +1427, +964, +339, +-303, +-834, +-1274, +-1591, +-1681, +-1594, +-1359, +-1020, +-649, +-326, +-41, +192, +413, +493, +346, +-75, +-535, +-949, +-1245, +-1428, +-1321, +-980, +-317, +2528, +6139, +7000, +6364, +5218, +3303, +1706, +628, +-795, +-3546, +-5920, +-6498, +-6545, +-6103, +-4835, +-3917, +-3066, +-1293, +1199, +3039, +3927, +4666, +4768, +4350, +4274, +3789, +2220, +377, +-1035, +-2202, +-3198, +-3586, +-3753, +-3980, +-3515, +-2339, +-1137, +-45, +1100, +2067, +2672, +3234, +3600, +3294, +2586, +1853, +987, +2, +-850, +-1499, +-2112, +-2413, +-2256, +-1928, +-1532, +-945, +-262, +366, +972, +1480, +1667, +1590, +1400, +1114, +714, +239, +-184, +-534, +-877, +-1107, +-1193, +-1275, +-1284, +-1066, +-770, +-586, +-439, +-277, +-199, +-166, +-58, +-137, +-537, +-864, +-1068, +-1254, +-1350, +-1073, +-581, +291, +3441, +7266, +8090, +7061, +5406, +3345, +1467, +10, +-1669, +-4631, +-6887, +-6956, +-6475, +-5812, +-4678, +-3607, +-2441, +-531, +2202, +3894, +4422, +4836, +4817, +4194, +3508, +2578, +947, +-906, +-1956, +-2704, +-3506, +-3806, +-3708, +-3506, +-2837, +-1548, +-261, +710, +1659, +2504, +2934, +3179, +3249, +2844, +2061, +1358, +616, +-247, +-975, +-1414, +-1790, +-1969, +-1781, +-1391, +-1000, +-548, +140, +701, +1053, +1344, +1397, +1199, +923, +652, +241, +-257, +-572, +-828, +-1058, +-1161, +-1162, +-1141, +-996, +-631, +-296, +-10, +279, +423, +307, +137, +-64, +-363, +-676, +-897, +-1239, +-1631, +-1853, +-1864, +-1797, +-1355, +-681, +342, +2668, +6898, +9692, +9193, +7285, +4881, +2444, +230, +-1644, +-4223, +-7340, +-8228, +-7415, +-6573, +-5662, +-4441, +-2955, +-1304, +1388, +3742, +4299, +4524, +4932, +4858, +4111, +3152, +1788, +-254, +-1512, +-2051, +-2934, +-3781, +-3890, +-3579, +-3057, +-1982, +-708, +-11, +683, +1632, +2286, +2472, +2621, +2660, +2229, +1872, +1752, +1188, +366, +-264, +-774, +-1311, +-1553, +-1552, +-1748, +-1830, +-1453, +-964, +-523, +-8, +474, +765, +1050, +1356, +1343, +1042, +833, +623, +350, +90, +-155, +-527, +-898, +-1037, +-1049, +-1037, +-957, +-808, +-603, +-439, +-291, +-303, +-458, +-601, +-511, +-491, +-695, +-960, +-1106, +-1124, +-1013, +-616, +-129, +1213, +5183, +8228, +7688, +5918, +3998, +2165, +464, +-1063, +-2909, +-5096, +-5447, +-4841, +-4942, +-5007, +-4437, +-3402, +-1980, +-204, +1295, +1952, +2731, +3668, +3893, +3588, +3107, +2463, +1659, +952, +248, +-785, +-1655, +-2032, +-2130, +-2199, +-2108, +-1876, +-1482, +-1012, +-360, +102, +458, +793, +1145, +1295, +1284, +1138, +1002, +843, +739, +622, +451, +377, +481, +375, +155, +-17, +-117, +-238, +-274, +-402, +-593, +-772, +-748, +-776, +-836, +-809, +-643, +-500, +-319, +-144, +7, +123, +323, +472, +579, +574, +458, +214, +-122, +-464, +-744, +-859, +-976, +-1160, +-1370, +-1517, +-1518, +-1444, +-982, +-524, +1029, +4637, +6859, +5961, +4565, +3378, +2129, +1090, +-15, +-1187, +-2183, +-2343, +-2444, +-3464, +-4397, +-4442, +-4000, +-3218, +-2264, +-1375, +-581, +369, +1273, +1793, +2064, +2506, +2839, +2890, +2653, +2186, +1589, +1011, +404, +-166, +-700, +-1027, +-1311, +-1499, +-1686, +-1744, +-1678, +-1455, +-1118, +-694, +-282, +102, +418, +670, +812, +971, +1153, +1304, +1331, +1250, +1065, +836, +544, +266, +-3, +-156, +-180, +-268, +-519, +-707, +-805, +-809, +-840, +-840, +-795, +-698, +-521, +-314, +-146, +-32, +10, +49, +-12, +-107, +-245, +-376, +-365, +-344, +-348, +-490, +-668, +-736, +-795, +-556, +-286, +348, +2825, +5387, +5002, +3315, +2393, +1505, +702, +211, +-402, +-751, +-684, +-948, +-2080, +-3432, +-3953, +-3680, +-3103, +-2450, +-1818, +-1127, +-419, +-9, +145, +370, +965, +1734, +2356, +2572, +2423, +2145, +1741, +1238, +753, +441, +293, +89, +-239, +-724, +-1212, +-1515, +-1602, +-1528, +-1360, +-1120, +-814, +-541, +-344, +-138, +109, +394, +709, +981, +1141, +1192, +1141, +1008, +851, +720, +599, +454, +287, +95, +-114, +-311, +-473, +-583, +-621, +-636, +-712, +-775, +-779, +-784, +-771, +-761, +-763, +-783, +-811, +-779, +-618, +-374, +-158, +-116, +-126, +-142, +-180, +-17, +183, +843, +2996, +4968, +4288, +2646, +1929, +1208, +464, +249, +54, +97, +424, +-99, +-1685, +-3049, +-3497, +-3422, +-3049, +-2480, +-1883, +-1313, +-971, +-995, +-1134, +-862, +-44, +911, +1655, +2068, +2191, +2109, +1843, +1522, +1281, +1239, +1303, +1226, +867, +312, +-232, +-667, +-949, +-1075, +-1019, +-885, +-784, +-781, +-840, +-844, +-719, +-468, +-182, +102, +358, +515, +586, +572, +552, +521, +510, +525, +508, +444, +316, +152, +-43, +-233, +-378, +-474, +-520, +-567, +-643, +-757, +-884, +-985, +-1056, +-1022, +-850, +-637, +-464, +-411, +-391, +-371, +-257, +90, +368, +1460, +3843, +4975, +3682, +2294, +1470, +670, +290, +146, +-168, +-205, +-298, +-1151, +-2520, +-3381, +-3395, +-2867, +-2241, +-1657, +-1224, +-844, +-576, +-415, +-281, +229, +991, +1680, +2012, +2055, +1855, +1640, +1354, +1090, +820, +679, +499, +236, +-195, +-618, +-944, +-1077, +-1123, +-1024, +-908, +-695, +-525, +-369, +-278, +-117, +80, +334, +530, +688, +723, +751, +652, +579, +443, +379, +294, +223, +63, +-111, +-338, +-485, +-601, +-615, +-630, +-594, +-629, +-644, +-725, +-794, +-872, +-788, +-650, +-486, +-471, +-540, +-571, +-526, +-364, +-37, +309, +1862, +4190, +4600, +3152, +2067, +1274, +715, +616, +523, +145, +-155, +-547, +-1396, +-2551, +-3151, +-3128, +-2715, +-2181, +-1744, +-1572, +-1324, +-954, +-541, +-102, +497, +1104, +1662, +1956, +2016, +1834, +1620, +1420, +1287, +1066, +814, +492, +208, +-116, +-426, +-722, +-859, +-916, +-843, +-804, +-724, +-686, +-553, +-410, +-212, +-34, +169, +325, +453, +488, +492, +437, +464, +415, +396, +282, +183, +3, +-131, +-280, +-386, +-526, +-568, +-646, +-701, +-839, +-898, +-980, +-910, +-809, +-687, +-610, +-516, +-430, +-315, +-150, +167, +562, +2306, +4506, +4563, +3127, +2021, +1106, +548, +416, +226, +-298, +-721, +-1105, +-1838, +-2796, +-3160, +-2969, +-2491, +-1878, +-1259, +-868, +-516, +-136, +260, +592, +1021, +1463, +1763, +1817, +1633, +1225, +923, +715, +559, +317, +100, +-186, +-453, +-747, +-894, +-966, +-885, +-728, +-518, +-383, +-221, +-79, +98, +282, +440, +522, +588, +604, +577, +468, +399, +310, +258, +162, +39, +-141, +-279, +-357, +-398, +-430, +-458, +-513, +-578, +-673, +-753, +-848, +-811, +-683, +-575, +-540, +-541, +-543, +-532, +-355, +-14, +501, +2415, +4789, +4874, +3247, +1809, +813, +340, +298, +5, +-896, +-1698, +-2031, +-2557, +-3148, +-3246, +-2873, +-2215, +-1395, +-706, +-313, +16, +617, +1239, +1719, +2007, +2086, +1953, +1662, +1308, +898, +448, +0, +-343, +-637, +-864, +-1045, +-1159, +-1080, +-895, +-587, +-272, +58, +315, +587, +663, +658, +714, +798, +799, +685, +474, +191, +-66, +-284, +-456, +-592, +-671, +-709, +-692, +-605, +-521, +-430, +-361, +-321, +-307, +-343, +-402, +-452, +-385, +-431, +-538, +-653, +-767, +-798, +-722, +-333, +74, +2319, +6417, +7237, +4578, +1893, +80, +-534, +-305, +-518, +-2023, +-3525, +-3760, +-3773, +-3936, +-3578, +-2753, +-1490, +63, +1233, +1601, +1711, +2132, +2565, +2695, +2518, +1948, +1219, +528, +-152, +-810, +-1410, +-1721, +-1920, +-1947, +-1724, +-1397, +-867, +-186, +379, +817, +1180, +1475, +1649, +1664, +1540, +1277, +916, +479, +9, +-409, +-756, +-979, +-1131, +-1198, +-1176, +-971, +-620, +-254, +26, +163, +125, +128, +93, +-31, +-292, +-478, +-635, +-953, +-1380, +-1583, +-1644, +-1419, +-1152, +-399, +251, +4499, +10959, +10640, +5605, +940, +-1797, +-1900, +-912, +-1437, +-4530, +-6791, +-6083, +-4884, +-3823, +-2752, +-1802, +-493, +1411, +3196, +3629, +3332, +3298, +3084, +2571, +1731, +518, +-743, +-1456, +-1563, +-1716, +-1912, +-1821, +-1440, +-688, +139, +720, +993, +1170, +1393, +1542, +1498, +1215, +727, +218, +-303, +-680, +-897, +-986, +-982, +-935, +-801, +-570, +-336, +-50, +215, +398, +461, +412, +177, +-178, +-514, +-799, +-1120, +-1238, +-1446, +-1914, +-2206, +-2069, +-1724, +-1218, +-675, +403, +1587, +10391, +17960, +12996, +4106, +-3204, +-5756, +-3983, +-2049, +-4025, +-10017, +-11376, +-7444, +-3275, +-60, +751, +750, +1933, +5097, +7442, +6069, +3469, +1246, +87, +103, +-526, +-2235, +-4086, +-4202, +-2776, +-1238, +-34, +354, +956, +2050, +2908, +3065, +2242, +1311, +557, +-49, +-425, +-1171, +-1757, +-1790, +-1290, +-594, +-357, +-284, +-212, +122, +809, +1042, +818, +168, +-69, +474, +421, +-452, +-1605, +-2380, +-2274, +-1574, +-1230, +-2216, +-2752, +-2511, +-1407, +-640, +312, +676, +4389, +19943, +23784, +11922, +-2128, +-11491, +-10505, +-5286, +-2405, +-8670, +-16588, +-12890, +-4652, +2789, +5901, +4161, +3109, +5802, +10495, +9957, +4443, +-427, +-3507, +-3885, +-3529, +-4689, +-6813, +-7109, +-4011, +-349, +1936, +2980, +3045, +3496, +4643, +5230, +4065, +1849, +-118, +-1173, +-1445, +-2042, +-2893, +-3375, +-2894, +-1254, +274, +1038, +1307, +2290, +2781, +2132, +1267, +106, +-630, +-884, +-1637, +-2480, +-3193, +-3014, +-2220, +-1334, +-981, +-1771, +-2146, +-1551, +-431, +200, +527, +742, +3176, +17053, +21910, +10382, +-1289, +-7789, +-6810, +-3716, +-4452, +-11521, +-16921, +-10358, +-1918, +2356, +3087, +1882, +3618, +8258, +11358, +8237, +2385, +-273, +-1011, +-759, +-2180, +-5560, +-7378, +-5592, +-2185, +-744, +-524, +8, +1316, +3802, +5109, +4298, +2707, +1756, +1418, +649, +-450, +-1740, +-2189, +-1271, +-1100, +-1679, +-1844, +-1360, +-592, +-325, +-144, +133, +745, +1554, +1549, +852, +513, +308, +-135, +-795, +-1430, +-1921, +-2655, +-3667, +-3859, +-3421, +-2082, +-855, +743, +1367, +11088, +21800, +14144, +3001, +-2687, +-3697, +-1984, +-3637, +-10176, +-15992, +-11894, +-4093, +-1757, +-1223, +-662, +2685, +8279, +10945, +8109, +3635, +2661, +2783, +892, +-2400, +-6211, +-7154, +-4982, +-2746, +-2283, +-2438, +-971, +1750, +3884, +4493, +3457, +2951, +3329, +2881, +1281, +-590, +-1789, +-2085, +-2185, +-2296, +-2621, +-2183, +-1219, +-538, +-59, +510, +1243, +1925, +1929, +1515, +982, +511, +-433, +-1173, +-1845, +-2172, +-2228, +-2871, +-3663, +-3279, +-2056, +-670, +256, +1334, +2850, +15158, +19429, +7039, +-384, +-1970, +-1342, +-1607, +-6796, +-13500, +-14145, +-6652, +-2427, +-3188, +-2202, +996, +6164, +9815, +8518, +5061, +3964, +4637, +2691, +-1290, +-4764, +-6237, +-5122, +-3867, +-4382, +-4340, +-2171, +954, +2885, +3430, +3650, +4302, +5176, +4711, +2169, +3, +-958, +-1485, +-2344, +-3327, +-3815, +-3159, +-1939, +-888, +-331, +405, +1484, +2494, +2693, +2133, +1396, +838, +-47, +-874, +-1956, +-2849, +-2975, +-3277, +-3840, +-3478, +-2275, +-834, +246, +1425, +2537, +15180, +18376, +5188, +410, +802, +554, +-1236, +-7656, +-13700, +-12256, +-5313, +-3930, +-5878, +-3184, +1356, +6062, +8660, +6908, +5229, +6261, +6295, +2669, +-1615, +-3864, +-4683, +-4685, +-5191, +-6164, +-4786, +-1257, +803, +1647, +2717, +4277, +5751, +5444, +3534, +1693, +788, +240, +-1209, +-2868, +-3856, +-3683, +-2945, +-2398, +-2127, +-1237, +221, +1625, +2252, +2363, +2177, +2139, +1646, +747, +-682, +-1592, +-1973, +-2640, +-4154, +-4457, +-3691, +-2213, +-1609, +358, +9, +8796, +19973, +8801, +323, +3725, +4003, +1287, +-4158, +-11215, +-12123, +-6560, +-4893, +-8680, +-7000, +-1262, +2843, +5584, +5846, +5210, +7061, +8286, +5502, +1735, +34, +-1179, +-2711, +-4369, +-6004, +-5869, +-4326, +-3324, +-2591, +-387, +2387, +3383, +4066, +4520, +4303, +4319, +3839, +1928, +-212, +-929, +-1713, +-3166, +-3950, +-4068, +-3727, +-2636, +-1732, +-858, +429, +1712, +2271, +2567, +2489, +2242, +1508, +525, +-534, +-1480, +-3190, +-4176, +-4399, +-3711, +-3319, +-1318, +-1529, +5824, +18263, +8980, +1129, +6565, +5402, +1529, +-2393, +-8897, +-9751, +-5403, +-5862, +-10492, +-7777, +-1518, +366, +2076, +3696, +4427, +7644, +8718, +5220, +2988, +3238, +1518, +-1549, +-3359, +-4583, +-5273, +-4773, +-4814, +-4600, +-2655, +-692, +471, +2320, +4827, +5642, +5031, +4951, +4328, +3095, +1691, +-433, +-2141, +-2810, +-3720, +-4751, +-4605, +-3658, +-2743, +-1551, +-321, +614, +1792, +2448, +2382, +2240, +1606, +743, +87, +-1379, +-3091, +-3718, +-3446, +-3437, +-2448, +-1598, +213, +13754, +13943, +1573, +6057, +9324, +2801, +-23, +-4927, +-8617, +-5726, +-5704, +-11215, +-10704, +-3695, +-2220, +-2605, +1165, +3401, +5816, +8504, +6316, +4585, +6307, +4747, +594, +-1023, +-1896, +-3709, +-4527, +-5028, +-5585, +-4224, +-2220, +-1274, +293, +1942, +2965, +3680, +4734, +4718, +3363, +2892, +2290, +475, +-716, +-1845, +-2946, +-3263, +-3337, +-3567, +-3026, +-1758, +-982, +-436, +702, +1028, +1410, +1741, +899, +309, +291, +-996, +-2445, +-3047, +-2952, +-3078, +-1523, +-2030, +2577, +15559, +9454, +1329, +10378, +8861, +812, +-178, +-5162, +-7416, +-4607, +-8367, +-13138, +-8360, +-2930, +-5048, +-4060, +1923, +3453, +5460, +7466, +5185, +6214, +8049, +3818, +813, +1270, +-554, +-3504, +-4259, +-4876, +-4876, +-3386, +-3386, +-3113, +-156, +1431, +1404, +2648, +3661, +3690, +3786, +3546, +2712, +1647, +1033, +-233, +-1897, +-2276, +-2634, +-3387, +-2877, +-2404, +-2222, +-1388, +-525, +-72, +513, +771, +515, +361, +372, +-1054, +-2250, +-2155, +-2433, +-2983, +-1100, +-1463, +1833, +14674, +9493, +994, +11938, +10503, +-113, +849, +-2621, +-6603, +-4210, +-8873, +-13590, +-7446, +-3923, +-8350, +-5775, +1855, +2051, +2889, +6569, +5468, +6885, +9044, +3845, +1981, +4585, +840, +-3160, +-2258, +-3339, +-4931, +-3915, +-4280, +-4171, +-1115, +-336, +-844, +1792, +3382, +2470, +3089, +3456, +2413, +2294, +1580, +-213, +-464, +-689, +-2154, +-2373, +-1735, +-1924, +-1759, +-1042, +-263, +-271, +-470, +311, +122, +-736, +-585, +-908, +-1820, +-2145, +-2413, +-2727, +-1706, +-627, +-619, +4874, +12214, +7923, +4151, +10217, +8801, +1832, +-249, +-2252, +-3941, +-5356, +-8809, +-10597, +-8087, +-4985, +-7232, +-7340, +-455, +1766, +511, +3545, +5644, +6035, +7060, +5689, +3666, +4528, +4075, +-346, +-2183, +-702, +-2923, +-5332, +-3956, +-3938, +-3891, +-1722, +-1618, +-1007, +2233, +3124, +2092, +3473, +4677, +3158, +2354, +2344, +896, +13, +-353, +-1920, +-2500, +-1876, +-2429, +-2904, +-1931, +-1548, +-1647, +-1143, +-884, +-587, +-356, +-508, +-603, +-570, +-880, +-1604, +-1523, +-1037, +-233, +392, +246, +7721, +12282, +3714, +4320, +10768, +5362, +-326, +-905, +-1597, +-3500, +-6473, +-8522, +-9139, +-6295, +-5259, +-8560, +-4871, +1286, +254, +-49, +3842, +6419, +5774, +5068, +5505, +4977, +4533, +3102, +-292, +-514, +212, +-2991, +-5293, +-3514, +-2846, +-4594, +-4048, +-1929, +-1453, +-380, +842, +1019, +2564, +4031, +2872, +2666, +4105, +3307, +1387, +1469, +1130, +-486, +-1134, +-1432, +-2295, +-2313, +-2289, +-2707, +-2354, +-1718, +-2015, +-1659, +-910, +-1114, +-1046, +-671, +-781, +-696, +-1066, +-920, +-494, +630, +-299, +2946, +11838, +6153, +756, +9967, +9690, +956, +-307, +1845, +-422, +-4869, +-5636, +-7078, +-7135, +-4922, +-8414, +-8790, +-2391, +-1148, +-3767, +-1233, +4544, +4553, +2606, +5319, +6810, +5740, +4969, +3580, +2641, +2352, +620, +-2410, +-2839, +-1672, +-3703, +-5513, +-3556, +-2440, +-3208, +-2349, +-432, +345, +1085, +2191, +2337, +2745, +3717, +2847, +1849, +2422, +2079, +335, +-214, +-200, +-1094, +-1711, +-1872, +-2344, +-2092, +-1723, +-2328, +-2458, +-1317, +-1102, +-1879, +-1257, +-123, +-629, +-738, +-453, +-109, +594, +1436, +277, +2464, +11940, +6632, +-1004, +8264, +10307, +345, +-2750, +2361, +611, +-7160, +-6461, +-5327, +-7288, +-6340, +-7520, +-7649, +-3079, +-742, +-3018, +-1572, +5171, +5207, +1733, +5350, +8233, +5364, +3687, +4600, +3696, +1606, +583, +-1252, +-2373, +-1724, +-3315, +-5337, +-3644, +-2214, +-3828, +-3650, +-742, +30, +-493, +992, +2447, +2577, +2805, +3105, +2993, +3033, +2781, +1472, +929, +1016, +-326, +-1571, +-1491, +-1549, +-2321, +-2736, +-2195, +-1921, +-2231, +-2236, +-1424, +-1151, +-1565, +-1131, +-567, +-966, +-660, +-647, +-512, +129, +1163, +-39, +2185, +9519, +3970, +1406, +8300, +7218, +904, +922, +4135, +-220, +-3955, +-2012, +-3738, +-5164, +-4935, +-5977, +-5533, +-3731, +-3711, +-4401, +-1222, +1074, +-614, +576, +3421, +3648, +2696, +3423, +4231, +3550, +2987, +2061, +1263, +1521, +290, +-1497, +-1411, +-887, +-2235, +-3156, +-2033, +-1655, +-2231, +-1780, +-914, +-406, +-37, +398, +721, +1364, +1844, +1608, +1558, +1991, +1915, +1263, +920, +1012, +455, +-182, +-375, +-600, +-1079, +-1270, +-1304, +-1347, +-1242, +-1019, +-991, +-693, +-437, +-596, +-480, +54, +-152, +-430, +-113, +13, +-468, +-500, +-521, +-375, +-321, +46, +-615, +1857, +5276, +1047, +1304, +5364, +3720, +427, +969, +2866, +60, +-1980, +-856, +-1994, +-2713, +-2620, +-3138, +-3432, +-2515, +-1677, +-2598, +-1889, +111, +16, +-190, +914, +1912, +1649, +1546, +2185, +1917, +1525, +1621, +1180, +585, +440, +444, +-330, +-841, +-637, +-867, +-1331, +-1271, +-1064, +-1109, +-1007, +-648, +-539, +-378, +38, +310, +399, +644, +932, +1043, +1017, +1087, +1034, +896, +724, +493, +249, +71, +-7, +-260, +-565, +-549, +-526, +-607, +-669, +-547, +-426, +-399, +-283, +-164, +-120, +-9, +41, +53, +-42, +-73, +-112, +-179, +-342, +-426, +-469, +-497, +-554, +-574, +-701, +-484, +-266, +-66, +-354, +757, +2832, +1417, +142, +2007, +3958, +2079, +28, +1105, +1712, +464, +-647, +-819, +-992, +-1518, +-1511, +-1828, +-2259, +-1935, +-1445, +-1378, +-1419, +-869, +-216, +-39, +212, +606, +932, +966, +1093, +1317, +1159, +923, +869, +781, +471, +155, +48, +-163, +-431, +-536, +-624, +-717, +-739, +-679, +-574, +-466, +-297, +-136, +0, +162, +282, +383, +443, +478, +521, +500, +418, +335, +257, +194, +91, +7, +-59, +-114, +-170, +-186, +-240, +-312, +-257, +-154, +-145, +-165, +-121, +-32, +-7, +24, +81, +90, +89, +174, +205, +111, +67, +89, +81, +-9, +-51, +-70, +-155, +-284, +-278, +-327, +-429, +-396, +-341, +-381, +-430, +-352, +-310, +-272, +-204, +-85, +-150, +80, +662, +335, +-1, +222, +524, +319, +344, +1942, +1990, +525, +363, +1053, +1318, +421, +50, +91, +-476, +-747, +-661, +-791, +-1278, +-1465, +-1063, +-965, +-1033, +-768, +-502, +-304, +-172, +154, +401, +463, +596, +731, +746, +683, +622, +562, +404, +269, +151, +-16, +-163, +-270, +-338, +-409, +-413, +-369, +-398, +-358, +-250, +-188, +-222, +-97, +60, +158, +174, +248, +307, +329, +325, +354, +295, +217, +162, +154, +78, +0, +-66, +-86, +-97, +-93, +-119, +-104, +-35, +-4, +-11, +-6, +-69, +-43, +38, +41, +-7, +-37, +0, +-13, +-85, +-68, +-68, +-80, +-107, +-81, +-82, +-74, +-86, +-66, +-92, +-61, +-61, +-33, +-59, +-71, +-122, +-126, +-181, +-217, +-254, +-236, +-270, +-342, +-315, +-291, +-285, +-153, +-35, +78, +89, +1837, +2562, +831, +424, +1316, +1760, +754, +-120, +26, +-317, +-593, +-716, +-1095, +-1379, +-1373, +-1018, +-1002, +-1129, +-762, +-415, +-179, +-2, +243, +478, +604, +784, +838, +751, +682, +608, +504, +280, +35, +-118, +-205, +-323, +-476, +-579, +-561, +-566, +-544, +-427, +-292, +-179, +-49, +98, +231, +332, +430, +482, +485, +478, +442, +392, +283, +178, +82, +18, +-77, +-143, +-208, +-211, +-224, +-225, +-227, +-167, +-112, +-45, +-15, +50, +114, +161, +178, +204, +219, +205, +162, +106, +66, +-23, +-155, +-194, +-192, +-254, +-307, +-314, +-257, +-247, +-184, +-157, +-118, +-48, +35, +34, +9, +0, +53, +-15, +-101, +-126, +-139, +-229, +-321, +-431, +-343, +-225, +-136, +-203, +155, +2249, +2043, +292, +680, +1780, +1723, +370, +-146, +104, +-212, +-411, +-968, +-1471, +-1388, +-1103, +-1045, +-1383, +-1195, +-536, +-254, +-126, +-31, +344, +710, +860, +881, +800, +822, +866, +599, +316, +112, +0, +-205, +-447, +-592, +-660, +-669, +-626, +-625, +-510, +-331, +-112, +20, +154, +307, +459, +557, +569, +515, +492, +464, +383, +189, +70, +5, +-88, +-218, +-281, +-289, +-290, +-312, +-254, +-188, +-103, +-18, +89, +160, +201, +248, +324, +334, +307, +255, +201, +125, +47, +-41, +-146, +-175, +-214, +-290, +-298, +-398, +-399, +-262, +-234, +-263, +-198, +-20, +69, +41, +17, +59, +80, +37, +-109, +-163, +-139, +-217, +-396, +-489, +-464, +-241, +-223, +-132, +-266, +1765, +2822, +428, +325, +1911, +2224, +731, +-466, +58, +90, +-363, +-1061, +-1848, +-1478, +-1048, +-1188, +-1626, +-1425, +-500, +-126, +-120, +29, +427, +983, +1016, +846, +890, +1003, +968, +621, +321, +168, +11, +-203, +-507, +-673, +-695, +-714, +-741, +-820, +-670, +-416, +-267, +-153, +14, +262, +468, +551, +629, +658, +705, +676, +571, +452, +333, +200, +49, +-106, +-193, +-336, +-439, +-444, +-440, +-429, +-382, +-296, +-193, +-87, +17, +78, +148, +237, +288, +278, +282, +280, +243, +162, +95, +67, +6, +-62, +-121, +-174, +-228, +-261, +-278, +-309, +-330, +-301, +-278, +-304, +-342, +-329, +-283, +-302, +-329, +-278, +-238, +-245, +-244, +-269, +-78, +41, +242, +97, +1291, +2890, +1483, +993, +1982, +2135, +1016, +98, +475, +68, +-777, +-1112, +-1511, +-1531, +-1717, +-1782, +-1761, +-1558, +-1042, +-930, +-723, +-176, +262, +536, +698, +1069, +1263, +1231, +1250, +1085, +901, +696, +423, +113, +-206, +-382, +-622, +-836, +-909, +-938, +-898, +-852, +-708, +-503, +-323, +-82, +121, +340, +520, +672, +780, +820, +838, +808, +714, +614, +452, +309, +124, +-30, +-158, +-280, +-378, +-437, +-465, +-459, +-439, +-386, +-330, +-247, +-164, +-94, +-30, +20, +75, +97, +72, +38, +66, +56, +10, +7, +24, +-3, +-44, +-31, +2, +-17, +-43, +-11, +-33, +-65, +-125, +-208, +-261, +-399, +-523, +-590, +-648, +-749, +-865, +-727, +-583, +-399, +-359, +937, +2246, +1545, +1816, +2823, +2789, +1815, +1425, +1819, +907, +22, +-344, +-736, +-1228, +-1916, +-2187, +-2283, +-2317, +-2281, +-2329, +-1779, +-1327, +-1049, +-609, +-9, +603, +799, +1139, +1566, +1672, +1679, +1543, +1538, +1309, +954, +658, +360, +66, +-293, +-625, +-752, +-945, +-1055, +-1136, +-1060, +-938, +-895, +-698, +-484, +-262, +-79, +125, +365, +533, +654, +779, +859, +933, +880, +852, +803, +690, +555, +394, +291, +92, +-95, +-230, +-373, +-524, +-625, +-691, +-729, +-764, +-719, +-680, +-583, +-469, +-351, +-222, +-73, +79, +170, +274, +392, +423, +419, +417, +393, +254, +117, +-36, +-207, +-402, +-607, +-734, +-940, +-1034, +-1216, +-1324, +-1241, +-1034, +-923, +-557, +1060, +1737, +1615, +2842, +3811, +3578, +2905, +3300, +3178, +1690, +955, +455, +-292, +-1483, +-2319, +-2502, +-3018, +-3353, +-3562, +-3301, +-2792, +-2659, +-2195, +-1426, +-640, +-142, +273, +1167, +1653, +1841, +2078, +2310, +2408, +2044, +1855, +1622, +1205, +717, +206, +-131, +-533, +-952, +-1217, +-1401, +-1429, +-1527, +-1467, +-1251, +-1021, +-781, +-507, +-138, +199, +419, +710, +910, +1082, +1144, +1180, +1166, +1090, +978, +809, +614, +444, +219, +4, +-204, +-352, +-515, +-653, +-746, +-776, +-825, +-810, +-804, +-699, +-617, +-494, +-373, +-190, +-34, +102, +214, +357, +438, +477, +469, +458, +394, +225, +28, +-148, +-353, +-602, +-785, +-934, +-1033, +-1142, +-1203, +-1058, +-874, +-612, +-427, +838, +1786, +1645, +2459, +3425, +3408, +2808, +2738, +2832, +1691, +673, +137, +-499, +-1435, +-2395, +-2637, +-2831, +-3168, +-3261, +-2988, +-2361, +-2064, +-1671, +-864, +-100, +419, +790, +1411, +1924, +1941, +1943, +2017, +1964, +1554, +1104, +872, +496, +-29, +-470, +-717, +-911, +-1231, +-1365, +-1308, +-1196, +-1146, +-992, +-667, +-350, +-150, +119, +446, +714, +840, +956, +1107, +1132, +1064, +940, +850, +690, +445, +212, +24, +-154, +-367, +-514, +-588, +-630, +-683, +-664, +-595, +-502, +-438, +-322, +-194, +-88, +-28, +67, +160, +192, +218, +246, +253, +204, +150, +101, +1, +-96, +-193, +-284, +-376, +-455, +-555, +-575, +-631, +-659, +-679, +-651, +-615, +-644, +-561, +-425, +-228, +-130, +276, +1386, +1613, +1695, +2525, +2972, +2674, +2279, +2336, +2011, +997, +342, +-163, +-758, +-1590, +-2268, +-2448, +-2639, +-2927, +-2968, +-2601, +-2099, +-1895, +-1414, +-663, +-21, +374, +827, +1410, +1787, +1850, +1929, +2002, +1916, +1555, +1215, +942, +563, +49, +-358, +-646, +-900, +-1218, +-1356, +-1331, +-1269, +-1215, +-1025, +-714, +-425, +-201, +114, +419, +671, +789, +948, +1040, +1053, +955, +865, +756, +580, +352, +185, +22, +-143, +-317, +-395, +-475, +-535, +-564, +-500, +-483, +-398, +-319, +-195, +-106, +-11, +86, +152, +199, +219, +243, +221, +140, +94, +32, +-51, +-104, +-140, +-202, +-240, +-263, +-294, +-345, +-390, +-409, +-483, +-545, +-577, +-602, +-626, +-699, +-643, +-557, +-392, +-292, +114, +1059, +1328, +1501, +2285, +2720, +2562, +2319, +2336, +2078, +1242, +591, +88, +-507, +-1320, +-2026, +-2257, +-2474, +-2759, +-2782, +-2435, +-1979, +-1679, +-1182, +-459, +126, +549, +972, +1433, +1694, +1699, +1649, +1588, +1338, +953, +553, +236, +-157, +-557, +-827, +-994, +-1099, +-1142, +-1070, +-878, +-648, +-389, +-78, +231, +532, +746, +930, +1044, +1084, +1028, +909, +732, +516, +277, +1, +-263, +-479, +-645, +-807, +-871, +-828, +-761, +-639, +-472, +-236, +-29, +175, +347, +507, +612, +652, +641, +584, +501, +346, +180, +22, +-131, +-285, +-416, +-501, +-560, +-574, +-595, +-565, +-495, +-437, +-411, +-363, +-258, +-217, +-226, +-211, +-226, +-253, +-363, +-472, +-483, +-424, +-340, +-341, +552, +1617, +1454, +1874, +2855, +3002, +2514, +2085, +2061, +1390, +268, +-528, +-1234, +-1750, +-2502, +-3167, +-3146, +-2930, +-2789, +-2584, +-1872, +-901, +-282, +308, +1085, +1794, +2213, +2289, +2307, +2303, +1969, +1401, +756, +267, +-269, +-938, +-1423, +-1683, +-1786, +-1828, +-1744, +-1412, +-955, +-474, +-38, +439, +953, +1308, +1472, +1559, +1581, +1441, +1132, +752, +391, +-3, +-410, +-793, +-1044, +-1157, +-1200, +-1179, +-984, +-695, +-328, +-29, +350, +698, +959, +1087, +1108, +1083, +927, +662, +339, +41, +-276, +-572, +-826, +-957, +-1012, +-1024, +-912, +-762, +-514, +-289, +-55, +187, +332, +428, +429, +397, +275, +14, +-231, +-469, +-727, +-1018, +-1174, +-1020, +-786, +-613, +-228, +1364, +2799, +2457, +2788, +3873, +3684, +2460, +1383, +960, +-70, +-1527, +-2538, +-3408, +-3453, +-3426, +-3769, +-3360, +-2146, +-960, +-415, +314, +1728, +2548, +2696, +2764, +2677, +2528, +1897, +899, +-2, +-669, +-1177, +-1992, +-2608, +-2448, +-2195, +-1980, +-1628, +-901, +1, +661, +1183, +1643, +2036, +2295, +2105, +1691, +1346, +916, +309, +-359, +-841, +-1115, +-1364, +-1512, +-1455, +-1159, +-790, +-458, +-64, +400, +781, +977, +1072, +1176, +1113, +902, +624, +333, +67, +-243, +-501, +-741, +-848, +-834, +-819, +-831, +-736, +-485, +-307, +-216, +-73, +114, +244, +355, +353, +266, +237, +286, +158, +-288, +-453, +-492, +-814, +-1143, +-1383, +-1324, +-1111, +-815, +-615, +442, +3114, +3953, +3029, +3919, +4941, +3971, +1878, +374, +-455, +-1974, +-3286, +-4552, +-5650, +-4570, +-3342, +-3483, +-2641, +-269, +1946, +2478, +2830, +4030, +4214, +3803, +2888, +1228, +136, +-491, +-1700, +-3203, +-3856, +-3201, +-2821, +-2817, +-1903, +-637, +581, +1606, +2167, +2553, +2896, +3040, +2593, +1551, +807, +272, +-605, +-1460, +-2042, +-2202, +-2015, +-1775, +-1386, +-876, +-132, +665, +1150, +1393, +1651, +1797, +1694, +1299, +803, +318, +-107, +-513, +-923, +-1298, +-1358, +-1289, +-1153, +-1084, +-838, +-447, +-41, +243, +441, +656, +958, +981, +839, +682, +511, +236, +-52, +-445, +-778, +-1174, +-1308, +-1352, +-1558, +-1649, +-1547, +-1278, +-879, +-382, +161, +644, +3348, +6104, +4816, +3648, +4808, +4552, +2078, +-784, +-2182, +-3265, +-4352, +-4702, +-5791, +-6030, +-3554, +-1612, +-1271, +-443, +1928, +3939, +4107, +4000, +4064, +3257, +2711, +1724, +-334, +-2045, +-2530, +-2818, +-3814, +-4286, +-3290, +-2251, +-1389, +-186, +846, +1714, +2703, +3331, +3093, +2464, +2180, +1666, +523, +-508, +-1187, +-1812, +-2159, +-2176, +-2015, +-1810, +-1193, +-271, +366, +829, +1396, +1903, +2099, +1994, +1626, +1124, +566, +139, +-409, +-1054, +-1436, +-1468, +-1292, +-1136, +-1026, +-684, +-200, +225, +501, +562, +625, +732, +705, +523, +230, +-65, +-282, +-465, +-752, +-1154, +-1418, +-1247, +-1238, +-1499, +-1622, +-1520, +-1164, +-695, +-34, +403, +1638, +5931, +7958, +5384, +4240, +4911, +3894, +836, +-2312, +-4068, +-5473, +-5637, +-5363, +-6650, +-6603, +-3512, +-852, +-104, +663, +2831, +4675, +5137, +5106, +4149, +2660, +2205, +1551, +-531, +-2750, +-3254, +-2848, +-3218, +-3660, +-3123, +-2255, +-1075, +296, +1020, +1242, +1997, +3202, +3380, +2502, +1924, +1596, +954, +119, +-773, +-1635, +-2079, +-1966, +-1815, +-1949, +-1750, +-932, +-103, +443, +899, +1332, +1814, +2147, +2079, +1638, +1083, +677, +232, +-448, +-1163, +-1623, +-1755, +-1731, +-1606, +-1448, +-1105, +-692, +-98, +367, +706, +901, +1041, +1012, +803, +544, +56, +-557, +-990, +-1031, +-1320, +-1806, +-2086, +-1988, +-1453, +-840, +-107, +286, +2824, +8161, +9177, +5513, +3632, +3567, +2327, +-794, +-3932, +-6200, +-7500, +-6227, +-4761, +-5838, +-5877, +-2578, +1103, +2615, +3068, +4186, +5094, +5568, +5778, +4142, +1250, +-132, +-349, +-1662, +-3998, +-5143, +-4834, +-4300, +-3396, +-2216, +-1395, +-256, +1793, +3508, +3851, +3570, +3620, +3451, +2651, +1641, +418, +-868, +-1653, +-1907, +-2339, +-2754, +-2446, +-1671, +-1065, +-478, +139, +677, +1196, +1665, +1890, +1696, +1486, +1340, +988, +343, +-254, +-685, +-994, +-1246, +-1391, +-1430, +-1328, +-1092, +-793, +-525, +-158, +205, +559, +744, +829, +829, +758, +439, +9, +-296, +-447, +-813, +-1334, +-1615, +-1731, +-1694, +-1559, +-1055, +-694, +360, +3442, +5109, +3607, +2189, +2785, +4015, +3152, +868, +-642, +-1512, +-1742, +-1879, +-2998, +-4294, +-4178, +-2538, +-1395, +-1552, +-1371, +-211, +1144, +1968, +2197, +2053, +1917, +2172, +2349, +1636, +410, +-250, +-406, +-763, +-1506, +-2096, +-2271, +-2112, +-1695, +-1288, +-1062, +-671, +160, +1038, +1398, +1487, +1588, +1643, +1513, +1173, +665, +72, +-358, +-595, +-820, +-1083, +-1209, +-1030, +-748, +-424, +-140, +137, +405, +691, +869, +882, +764, +636, +514, +322, +62, +-196, +-408, +-528, +-545, +-548, +-555, +-450, +-265, +-90, +79, +217, +343, +400, +391, +378, +302, +180, +41, +-105, +-226, +-375, +-435, +-459, +-428, +-381, +-321, +-222, +-149, +-79, +24, +158, +248, +183, +112, +63, +27, +-49, +-119, +-233, +-176, +64, +185, +-62, +-229, +-261, +-200, +-123, +-85, +126, +1087, +1992, +1942, +1470, +1327, +1169, +695, +-37, +-706, +-1358, +-1547, +-1428, +-1460, +-1608, +-1364, +-798, +-192, +143, +449, +713, +959, +1160, +1184, +857, +537, +313, +121, +-207, +-539, +-789, +-843, +-801, +-670, +-573, +-439, +-208, +123, +325, +448, +530, +617, +602, +526, +364, +173, +-17, +-133, +-269, +-377, +-447, +-423, +-353, +-257, +-159, +-41, +67, +202, +275, +321, +314, +294, +244, +180, +84, +-28, +-95, +-131, +-186, +-185, +-179, +-147, +-95, +-25, +31, +51, +111, +150, +135, +146, +122, +78, +61, +43, +-4, +-43, +-51, +-74, +-81, +-66, +-69, +-64, +-40, +-19, +-7, +4, +16, +29, +27, +30, +28, +8, +6, +6, +-7, +0, +12, +6, +-13, +-22, +-26, +-42, +-54, +-51, +-62, +-59, +-32, +-21, +-21, +-11, +4, +12, +7, +3, +-21, +-56, +-46, +-42, +-68, +-89, +-95, +-84, +-68, +-46, +-32, +-22, +67, +214, +276, +221, +144, +119, +94, +30, +-43, +-81, +-46, +51, +136, +177, +210, +244, +262, +213, +106, +-21, +-133, +-221, +-299, +-373, +-413, +-380, +-299, +-201, +-98, +23, +134, +230, +301, +309, +268, +224, +184, +103, +-21, +-121, +-166, +-189, +-204, +-211, +-207, +-174, +-96, +-10, +44, +72, +99, +126, +137, +126, +96, +48, +17, +-6, +-40, +-68, +-83, +-75, +-69, +-73, +-58, +-29, +-1, +14, +24, +41, +47, +41, +39, +38, +37, +23, +0, +-10, +-6, +-1, +-1, +-4, +-3, +-1, +5, +7, +7, +15, +11, +-1, +-3, +6, +-6, +-8, +0, +-6, +-5, +6, +17, +-1, +-7, +5, +4, +-3, +-6, +-10, +-26, +-43, +-37, +-28, +-21, +-18, +-28, +-29, +-15, +-3, +-2, +-17, +-22, +-13, +-31, +-36, +-27, +-33, +-57, +-43, +-15, +5, +20, +60, +107, +165, +197, +199, +172, +144, +102, +51, +-28, +-94, +-131, +-139, +-157, +-158, +-149, +-117, +-87, +-39, +-7, +22, +43, +67, +67, +56, +40, +44, +25, +13, +-12, +-28, +-31, +-23, +-25, +-18, +-12, +-8, +-6, +14, +12, +6, +19, +35, +25, +16, +5, +0, +9, +7, +2, +16, +18, +3, +-4, +8, +18, +3, +9, +5, +10, +-3, +-10, +-12, +-10, +-13, +-19, +-29, +-10, +-17, +-18, +-24, +-27, +-27, +-10, +-24, +-29, +-43, +-35, +-39, +-31, +-33, +-21, +-30, +-8, +21, +53, +65, +103, +117, +128, +120, +119, +68, +36, +2, +-23, +-55, +-71, +-94, +-88, +-81, +-67, +-63, +-48, +-34, +-8, +6, +11, +10, +30, +25, +15, +27, +26, +15, +-1, +0, +3, +-6, +-5, +9, +3, +7, +4, +8, +-2, +9, +12, +27, +13, +6, +-2, +6, +-17, +5, +-4, +0, +-9, +4, +-16, +-16, +-26, +-10, +-36, +-18, +-30, +-26, +-33, +-19, +-50, +-38, +-43, +-25, +-16, +24, +27, +71, +92, +108, +94, +97, +64, +52, +16, +-6, +-33, +-36, +-57, +-57, +-68, +-43, +-43, +-37, +-46, +-20, +-10, +3, +-7, +10, +10, +11, +13, +12, +-1, +19, +3, +4, +12, +20, +12, +10, +15, +14, +-2, +15, +19, +5, +2, +2, +1, +-5, +-16, +-1, +-8, +-16, +-8, +-13, +-29, +-24, +-27, +-28, +-28, +-37, +-30, +-24, +-8, +26, +58, +75, +81, +73, +59, +51, +30, +6, +3, +-5, +-24, +-22, +-15, +-23, +-34, +-38, +-30, +-21, +-10, +-8, +-4, +0, +4, +-12, +-15, +-6, +-9, +13, +22, +18, +28, +30, +15, +10, +5, +6, +-13, +-36, +-34, +-42, +-47, +-25, +-14, +-21, +-17, +22, +26, +-5, +4, +2, +-24, +-32, +-4, +5, +11, +25, +7, +6, +15, +23, +26, +6, +11, +43, +21, +8, +39, +4, +21, +40, +22, +60, +75, +83, +122, +95, +73, +26, +-23, +-27, +-92, +-119, +-104, +-92, +-41, +-4, +-4, +-6, +6, +15, +38, +34, +23, +31, +14, +9, +-15, +-37, +-33, +-83, +-120, +-100, +-81, +-97, +-64, +-11, +8, +10, +41, +15, +46, +174, +106, +55, +99, +1, +-18, +-30, +-61, +-61, +-67, +-39, +-33, +-12, +13, +-1, +5, +19, +3, +9, +-1, +-5, +-25, +-26, +-15, +-37, +-14, +-3, +-10, +5, +15, +22, +26, +39, +40, +33, +37, +30, +18, +22, +7, +7, +0, +-5, +-6, +-15, +-11, +-19, +-17, +-21, +-23, +-27, +-27, +-30, +-35, +-27, +-49, +-45, +-64, +-56, +-74, +-28, +-13, +15, +429, +417, +80, +188, +29, +-101, +-111, +-167, +-162, +-171, +-56, +-91, +-72, +42, +-18, +15, +45, +11, +31, +1, +23, +-7, +-5, +21, +-21, +28, +12, +5, +28, +7, +26, +7, +15, +25, +5, +25, +10, +10, +22, +-9, +10, +-17, +-5, +-16, +-17, +-9, +-8, +-21, +-11, +-39, +-23, +-46, +-30, +-46, +-26, +-51, +-67, +-76, +-90, +-80, +-63, +39, +-30, +635, +818, +46, +147, +-11, +-278, +-216, +-248, +-194, +-242, +-6, +-63, +-100, +131, +-11, +44, +91, +17, +-18, +-58, +-5, +-49, +21, +-33, +34, +160, +61, +42, +32, +-2, +-113, +-120, +-192, +-75, +174, +-143, +-100, +-10, +1, +79, +135, +266, +-1, +116, +120, +-89, +154, +-22, +10, +-58, +-38, +-202, +-307, +432, +240, +-80, +320, +-182, +-285, +-252, +-618, +-269, +-89, +401, +459, +347, +472, +26, +-18, +-47, +-376, +-223, +-199, +-46, +-93, +22, +219, +196, +46, +-326, +-160, +115, +-210, +144, +129, +-141, +-8, +140, +433, +-12, +-87, +20, +-10, +30, +-307, +-261, +-690, +-238, +822, +469, +415, +320, +-6, +185, +-219, +-145, +-246, +-420, +-115, +-157, +-84, +-87, +221, +0, +-23, +530, +4, +-15, +89, +9, +-58, +-197, +-96, +-421, +-165, +-13, +-452, +55, +171, +130, +110, +311, +464, +-39, +43, +10, +-65, +28, +-164, +62, +-60, +-209, +-362, +-272, +244, +-103, +209, +819, +167, +68, +-34, +-230, +-100, +-10, +105, +170, +423, +632, +119, +-199, +42, +-178, +-85, +-74, +-344, +-439, +-339, +-216, +311, +678, +582, +435, +-158, +-175, +73, +-80, +-464, +-815, +-203, +-323, +-664, +-21, +26, +433, +441, +246, +333, +142, +171, +-387, +-263, +148, +23, +149, +-126, +30, +51, +117, +188, +-539, +29, +205, +-123, +155, +131, +104, +19, +145, +-289, +-431, +0, +-420, +-218, +56, +91, +195, +-103, +87, +-3, +343, +207, +-281, +35, +-148, +138, +93, +-334, +63, +36, +78, +-302, +-526, +-427, +-660, +-238, +-298, +21, +790, +448, +472, +757, +856, +389, +-389, +69, +103, +-63, +-13, +-557, +-170, +125, +-4, +-352, +-410, +315, +-48, +-97, +-124, +-527, +246, +647, +71, +-235, +-228, +-451, +-306, +363, +382, +-74, +108, +28, +-341, +37, +162, +-66, +183, +454, +227, +187, +517, +471, +-139, +-228, +63, +-170, +-150, +244, +-64, +-456, +-54, +461, +195, +-309, +-326, +-89, +49, +50, +-323, +-210, +435, +988, +820, +218, +-165, +-192, +-175, +-579, +-567, +-365, +-146, +141, +-662, +-1223, +-704, +227, +269, +-448, +337, +931, +673, +437, +-205, +-176, +-156, +-299, +-386, +-496, +142, +116, +-154, +-355, +-491, +17, +-203, +-499, +-298, +546, +1489, +516, +-241, +67, +90, +-223, +-616, +-622, +-157, +616, +1075, +610, +288, +676, +659, +150, +-342, +-244, +-86, +-320, +-220, +-278, +-543, +-80, +488, +247, +-54, +-153, +-207, +-283, +-198, +-170, +-266, +60, +153, +-215, +-72, +120, +76, +56, +-87, +22, +64, +141, +73, +-37, +277, +136, +-169, +-203, +-127, +-17, +-145, +-140, +-241, +-175, +15, +-195, +-212, +-100, +-52, +-12, +-178, +-183, +-149, +-57, +-117, +-392, +-91, +48, +-216, +-271, +-469, +-397, +-181, +516, +777, +1001, +3479, +4115, +1347, +-222, +-177, +-240, +-1385, +-1635, +-1006, +-1541, +-520, +485, +-557, +-748, +-38, +644, +246, +-252, +468, +74, +-220, +342, +-5, +-595, +-393, +152, +8, +-347, +106, +-23, +-344, +218, +493, +39, +-291, +56, +296, +-170, +-123, +-127, +-256, +22, +115, +-152, +-373, +40, +424, +4, +-81, +71, +149, +196, +108, +-52, +-140, +178, +398, +119, +-134, +-9, +219, +92, +44, +-49, +5, +156, +240, +44, +-224, +-7, +198, +46, +-57, +-75, +82, +53, +94, +-147, +-214, +10, +142, +-60, +-192, +-55, +4, +13, +-20, +-153, +-107, +-17, +-1, +-264, +-153, +125, +109, +-43, +-215, +-368, +-192, +-77, +-270, +-476, +-241, +3, +-343, +-672, +-840, +-735, +-154, +520, +-118, +638, +5110, +5318, +313, +-1333, +-315, +-312, +-1728, +-2948, +-3283, +-1999, +2484, +2536, +-2013, +-1675, +2109, +2863, +441, +-940, +-163, +507, +1163, +406, +-2538, +-2285, +789, +1005, +-1405, +-2044, +21, +1110, +607, +143, +-524, +-70, +1636, +1329, +-636, +-1084, +522, +978, +-127, +-706, +-462, +-31, +667, +392, +-639, +-636, +527, +949, +183, +-322, +-7, +503, +716, +348, +-298, +-269, +355, +505, +-116, +-541, +-312, +179, +305, +-33, +-464, +-280, +277, +425, +-204, +-549, +-279, +129, +80, +-348, +-630, +-332, +214, +54, +-792, +-987, +-88, +-51, +-616, +-1227, +-1268, +-659, +-335, +-81, +-718, +4376, +11477, +5778, +-1970, +-272, +1642, +-973, +-5351, +-7281, +-6891, +-2672, +3193, +-7, +-4939, +911, +7202, +4919, +629, +214, +1744, +2296, +1588, +-2183, +-6087, +-2879, +1197, +-1255, +-4385, +-1998, +1583, +2244, +1509, +876, +599, +2229, +3512, +715, +-1625, +-209, +943, +-848, +-2156, +-1329, +-394, +36, +502, +56, +-46, +1476, +2086, +549, +-412, +521, +1004, +-234, +-1188, +-1288, +-1050, +-434, +-346, +-644, +-305, +722, +1109, +337, +-280, +-52, +5, +78, +-1079, +-2736, +-2774, +-1980, +-1476, +-1451, +-969, +245, +1501, +10820, +16134, +3358, +-4437, +1229, +2611, +-3693, +-10270, +-10079, +-5371, +-873, +1188, +-4267, +-5509, +6273, +12488, +6174, +732, +1904, +4303, +3430, +-849, +-7991, +-10784, +-3524, +1056, +-4652, +-7465 +}; diff --git a/unittest/ofdm_check b/unittest/ofdm_check new file mode 100755 index 0000000..a4b80ed --- /dev/null +++ b/unittest/ofdm_check @@ -0,0 +1,68 @@ +#! /bin/bash + +# ofdm_check +# +# A series of checks of the ofdm functions, mostly decode. +# +# This uses ofdm_mod to supply test data to ofdm_demod and mostly +# assumes that the encode function is correct. + +# Define macros to (later) allow testing alternate versions. +alias OFDM_MOD=ofdm_mod +alias OFDM_DEMOD=ofdm_demod +shopt -s expand_aliases + +# PATH +PATH=$PATH:../src + +PASS=1 + +############################### +echo +echo "Simple test, plain, ideal" +OFDM_MOD --in /dev/zero --testframes 100 | + OFDM_DEMOD --out /dev/null --testframes --verbose 1 2> tmp +cat tmp +p1=$(grep '^BER\.*: 0.000' tmp | wc -l) +p2=$(grep '^BER2\.*: 0.000' tmp | wc -l) +if [[ $p1 -eq 1 && $p2 -eq 1 ]]; then echo "OK"; else echo "BAD"; PASS=0; fi + +############################### +echo +echo "Simple test, plain, AWGN" +OFDM_MOD --in /dev/zero --testframes 100 | + cohpsk_ch - - -20 -Fs 8000 -f -5 | + OFDM_DEMOD --out /dev/null --testframes --verbose 1 2>tmp +cat tmp +n=$(grep '^BER\.*:' tmp | cut -d ' ' -f 2) +p1=$(echo $n '<=' 0.10 | bc) +n=$(grep '^BER2\.*:' tmp | cut -d ' ' -f 2) +p2=$(echo $n '<=' 0.10 | bc) +if [[ $p1 -eq 1 && $p2 -eq 1 ]]; then echo "OK"; else echo "BAD"; PASS=0; fi + +############################### +echo +echo "Simple test, LDPC, ideal" +OFDM_MOD --in /dev/zero --ldpc --testframes 100 | + OFDM_DEMOD --out /dev/null --ldpc --testframes --verbose 1 2>tmp +cat tmp +p1=$(grep '^BER\.*: 0.000' tmp | wc -l) +p2=$(grep '^Coded BER: 0.000' tmp | wc -l) +if [[ $p1 -eq 1 && $p2 -eq 1 ]]; then echo "OK"; else echo "BAD"; PASS=0; fi + +############################### +echo +echo "Simple test, LDPC, AWGN" +OFDM_MOD --in /dev/zero --ldpc --testframes 100 | + cohpsk_ch - - -20 -Fs 8000 -f -5 --fading_dir ../../build_linux/unittest | + OFDM_DEMOD --out /dev/null --ldpc --testframes --verbose 1 2>tmp +cat tmp +n=$(grep '^BER\.*:' tmp | cut -d ' ' -f 2) +p1=$(echo $n '<=' 0.10 | bc) +n=$(grep '^Coded.*BER\.*:' tmp | cut -d ' ' -f 3) +p2=$(echo $n '<=' 0.01 | bc) +if [[ $p1 -eq 1 && $p2 -eq 1 ]]; then echo "OK"; else echo "BAD"; PASS=0; fi + + +echo +if [[ $PASS == 1 ]]; then echo "PASSED"; else echo "FAILED"; fi diff --git a/unittest/ofdm_fade.sh b/unittest/ofdm_fade.sh new file mode 100755 index 0000000..03f290e --- /dev/null +++ b/unittest/ofdm_fade.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash +# +# David June 2019 +# Tests 700D OFDM modem fading channel performance, using a simulated channel + +results=$(mktemp) +fading_dir=$1 +# BER should be around 4% for this test (it's better for larger interleavers but no one uses interleaving in practice) +ofdm_mod --in /dev/zero --ldpc 1 --testframes 60 --txbpf | ch - - --No -24 -f -10 --mpp --fading_dir $fading_dir | ofdm_demod --out /dev/null --testframes --verbose 2 --ldpc 1 2> $results +cat $results +cber=$(cat $results | sed -n "s/^Coded BER.* \([0-9..]*\) Tbits.*/\1/p") +python3 -c "import sys; sys.exit(0) if $cber<=0.05 else sys.exit(1)" diff --git a/unittest/ofdm_fade_dpsk.sh b/unittest/ofdm_fade_dpsk.sh new file mode 100755 index 0000000..18e38bb --- /dev/null +++ b/unittest/ofdm_fade_dpsk.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash +# +# David Sep 2019 +# Tests 2020 OFDM modem fading channel performance in DPSK mode, using a simulated faster (2Hz) high SNR fading channel + +fading_dir=$1 +results=$(mktemp) + +# Coded BER should be < 1% for this test +ofdm_mod --in /dev/zero --testframes 300 --mode 2020 --ldpc --verbose 1 --dpsk | \ +ch - - --No -40 -f 10 --ssbfilt 1 --mpd --fading_dir $fading_dir --multipath_delay 2 | \ +ofdm_demod --out /dev/null --testframes --mode 2020 --verbose 1 --ldpc --dpsk 2> $results +cat $results +cber=$(cat $results | sed -n "s/^Coded BER.* \([0-9..]*\) Tbits.*/\1/p") +python3 -c "import sys; sys.exit(0) if $cber<=0.05 else sys.exit(1)" diff --git a/unittest/ofdm_mem.c b/unittest/ofdm_mem.c new file mode 100644 index 0000000..c87f067 --- /dev/null +++ b/unittest/ofdm_mem.c @@ -0,0 +1,107 @@ +/*---------------------------------------------------------------------------*\ + + FILE........: ofdm_mem.c + AUTHOR......: Don Reid + DATE CREATED: 11 June 2018 + + Prints out the memory used by the OFDM modem states. Used to optimise + memory use for the STM32F4 port. + +\*---------------------------------------------------------------------------*/ + +/* + Copyright (C) 2018 Don Reid + + All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 2.1, as + published by the Free Software Foundation. This program is + distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, see <http://www.gnu.org/licenses/>. +*/ + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <math.h> +#include <complex.h> + +#include "codec2_ofdm.h" +#include "ofdm_internal.h" + +int main() +{ + struct OFDM_CONFIG *ofdm_config; + struct OFDM *ofdm; + + ofdm = ofdm_create(NULL); + assert(ofdm != NULL); + + /* Get a copy of the actual modem config */ + ofdm_config = ofdm_get_config_param(ofdm); + + int ofdm_m = (int) (ofdm_config->fs / ofdm_config->rs); /* 144 */ + int ofdm_ncp = (int) (ofdm_config->tcp * ofdm_config->fs); /* 16 */ + int ofdm_bitsperframe = (ofdm_config->ns - 1) * (ofdm_config->nc * ofdm_config->bps); + int ofdm_rowsperframe = ofdm_bitsperframe / (ofdm_config->nc * ofdm_config->bps); + int ofdm_samplesperframe = ofdm_config->ns * (ofdm_m + ofdm_ncp); + int ofdm_rxbuf = 3 * ofdm_samplesperframe + 3 * (ofdm_m + ofdm_ncp); + int ofdm_nuwbits = (ofdm_config->ns - 1) * ofdm_config->bps - ofdm_config->txtbits; + + int used = 0; + + printf("struct OFDM.................: %zd\n", sizeof(struct OFDM)); + printf("config......................: %zd\n", sizeof(struct OFDM_CONFIG)); + used += sizeof(struct OFDM_CONFIG); + printf("pilot_samples...............: %zd\n", sizeof (complex float) * (ofdm_m + ofdm_ncp)); + used += sizeof (complex float) * (ofdm_m + ofdm_ncp); + printf("rxbuf.......................: %zd\n", sizeof (complex float) * ofdm_rxbuf); + used += sizeof (complex float) * ofdm_rxbuf; + printf("pilots......................: %zd\n", sizeof (complex float) * (ofdm_config->nc + 2)); + used += sizeof (complex float) * (ofdm_config->nc + 2); + + size_t rxsym_size = sizeof (complex float) * (ofdm_config->ns + 3) * (ofdm_config->nc + 2); + + printf("rx_sym......................: %zd\n", rxsym_size); + used += rxsym_size; + printf("rx_np.......................: %zd\n", sizeof (complex float) * (ofdm_rowsperframe * ofdm_config->nc)); + used += sizeof (complex float) * (ofdm_rowsperframe * ofdm_config->nc); + printf("rx_amp......................: %zd\n", sizeof (float) * (ofdm_rowsperframe * ofdm_config->nc)); + used += sizeof (float) * (ofdm_rowsperframe * ofdm_config->nc); + printf("aphase_est_pilot_log........: %zd\n", sizeof (float) * (ofdm_rowsperframe * ofdm_config->nc)); + used += sizeof (float) * (ofdm_rowsperframe * ofdm_config->nc); + printf("tx_uw.......................: %zd\n", sizeof (int) * ofdm_nuwbits); + used += sizeof (int) * ofdm_nuwbits; + printf("sync_state..................: %zd\n", sizeof (State)); + used += sizeof (State); + printf("last_sync_state.............: %zd\n", sizeof (State)); + used += sizeof (State); + printf("sync_state_interleaver......: %zd\n", sizeof (State)); + used += sizeof (State); + printf("last_sync_state_interleaver.: %zd\n", sizeof (State)); + used += sizeof (State); + + // add in non-array sizes + int single = 0; + single += 8 * sizeof(int); + single += 13 * sizeof(float); + single += 1 * sizeof(complex float); + single += 1 * sizeof(float *); + single += 4 * sizeof(bool); + printf("single values...............: %d\n", single); + used += single; + + printf("Total used .................: %zd\n", (size_t) used); + + ofdm_destroy(ofdm); + + return 0; +} + diff --git a/unittest/ofdm_phase_est_bw.sh b/unittest/ofdm_phase_est_bw.sh new file mode 100755 index 0000000..7eb826a --- /dev/null +++ b/unittest/ofdm_phase_est_bw.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +# +# ofdm_phase_est_bw.sh +# David August 2019 + +# Tests 2020 OFDM modem phase est bandwidth mode option. Locking the +# phase est bandwidth to "high" is useful for High SNR channels with +# fast fading or high phase noise. In this test we show that with +# high bandwidth phase est mode, the BER is < 5% for the "--mpp" (1 +# Hz fading) channel model on a fairly high SNR channel. +# +# To run manually outside of ctest: +# $ cd codec2/unittest +# $ PATH=$PATH:../build_linux/src ./ofdm_phase_est_bw.sh + +results=$(mktemp) +fading_dir=$1 +# BER should be < 5% for this test +ofdm_mod --in /dev/zero --testframes 300 --mode 2020 --ldpc --verbose 0 | \ +ch - - --No -40 -f 10 --ssbfilt 1 --mpp --fading_dir $fading_dir | \ +ofdm_demod --out /dev/null --testframes --mode 2020 --verbose 2 --ldpc --bandwidth 1 2> $results +cat $results +cber=$(cat $results | sed -n "s/^Coded BER.* \([0-9..]*\) Tbits.*/\1/p") +python3 -c "import sys; sys.exit(0) if $cber<=0.05 else sys.exit(1)" diff --git a/unittest/ofdm_stack.c b/unittest/ofdm_stack.c new file mode 100644 index 0000000..aef6f42 --- /dev/null +++ b/unittest/ofdm_stack.c @@ -0,0 +1,229 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <complex.h> +#include <unistd.h> +#include <assert.h> + +#include "comp.h" +#include "ofdm_internal.h" +#include "codec2_ofdm.h" +#include "test_bits_ofdm.h" /* payload_data_bits */ +#include "mpdecode_core.h" + +#define MAX_ERRORS 32 + +static int ofdm_bitsperframe; +static int ofdm_rowsperframe; +static int ofdm_nuwbits; +static int ofdm_ntxtbits; +static int ofdm_rx_offset; +static int ofdm_data_bitsperframe; +static int ofdm_samplesperframe; +static int ofdm_max_samplesperframe; +static int ofdm_rxbuf; +static int ofdm_m; +static int ofdm_ncp; + +// Forwards +void run_modem(struct OFDM *ofdm, int tx_bits[], int rx_bits[], COMP tx_rx[]); +void dummy_code(); + +///////////////////////////////////////////////////////////// +/// MAIN() +int main(int argc, char *argv[]) { + // Options + int f, i, opt; + int dummy = 0; // flag to use dummy code + int frames = 1; // how many frames + int print = 0; // flag to print all bits + struct OFDM *ofdm; + struct OFDM_CONFIG *ofdm_config; + + ofdm = ofdm_create(NULL); + assert(ofdm != NULL); + + /* Get a copy of the actual modem config */ + + ofdm_config = ofdm_get_config_param(ofdm); + + ofdm_m = (int) (ofdm_config->fs / ofdm_config->rs); + ofdm_ncp = (int) (ofdm_config->tcp * ofdm_config->fs); + ofdm_bitsperframe = ofdm_get_bits_per_frame(ofdm); + ofdm_rowsperframe = ofdm_bitsperframe / (ofdm_config->nc * ofdm_config->bps); + ofdm_samplesperframe = ofdm_get_samples_per_frame(ofdm); + ofdm_max_samplesperframe = ofdm_get_max_samples_per_frame(ofdm); + ofdm_rxbuf = 3 * ofdm_samplesperframe + 3 * (ofdm_m + ofdm_ncp); + ofdm_nuwbits = (ofdm_config->ns - 1) * ofdm_config->bps - ofdm_config->txtbits; + ofdm_ntxtbits = ofdm_config->txtbits; + ofdm_rx_offset = (ofdm_nuwbits + ofdm_ntxtbits); + ofdm_data_bitsperframe = (ofdm_bitsperframe - ofdm_rx_offset); + + int tx_bits[ofdm_data_bitsperframe]; + int rx_bits[ofdm_data_bitsperframe]; + COMP tx_rx[ofdm_samplesperframe]; + + while ((opt = getopt(argc, argv, "df:p")) != -1) { + switch (opt) { + case 'd': + dummy = 1; + break; + case 'f': + frames = atoi(optarg); + break; + case 'p': + print = 1; + break; + default: + fprintf(stderr, "Usage: %s [-e] [-f <frames>] [-p]\n", argv[0]); + } + } + + for (f = 0; f < frames; f++) { + //////// + // Prep inputs + + for(i=0; i<ofdm_data_bitsperframe; i++) { + tx_bits[i] = payload_data_bits[(i % (sizeof(payload_data_bits)/sizeof(payload_data_bits[0])))]; + } + + //////// + // Modem (or dummy) + + if (dummy) { + dummy_code(tx_bits, rx_bits); + } else { + run_modem(ofdm, tx_bits, rx_bits, tx_rx); + } + + //////// + // Compare results (or print) + int errors = 0; + + if (print) { + for(i=0; i<ofdm_data_bitsperframe; i++) { + fprintf(stderr, "bit %3d: tx = %1d, rx = %1d", + i, tx_bits[i], rx_bits[i + ofdm_rx_offset]); + + if (tx_bits[i] != rx_bits[i + ofdm_rx_offset]) { + fprintf(stderr, " Error"); + errors ++; + } + + fprintf(stderr, "\n"); + } + } else { + for(i=0; i<ofdm_data_bitsperframe; i++) { + if (tx_bits[i] != rx_bits[i + ofdm_rx_offset]) { + if (errors < MAX_ERRORS) { + fprintf(stderr, "Error in bit %3d: tx = %1d, rx = %1d\n", + i, tx_bits[i], rx_bits[i + ofdm_rx_offset]); + } + + errors++; + } + } + } + + fprintf(stderr, "%d Errors\n", errors); + + } // for (f<frames + + ofdm_destroy(ofdm); + +} // end main() + + +////////////////////////////////// +void run_modem(struct OFDM *ofdm, int tx_bits[], int rx_bits[], COMP tx_rx[]) { + int mod_bits[ofdm_samplesperframe]; + int i, j; + + /////////// + // Mod + /////////// + + for(i=0; i<ofdm_nuwbits; i++) { + mod_bits[i] = ofdm->tx_uw[i]; + } + + for(i=ofdm_nuwbits; i<ofdm_nuwbits+ofdm_ntxtbits; i++) { + mod_bits[i] = 0; + } + + for(j=0, i=ofdm_nuwbits+ofdm_ntxtbits; j<ofdm_data_bitsperframe; i++,j++) { + mod_bits[i] = tx_bits[j]; + } + + for(j=0; j<ofdm_data_bitsperframe; i++,j++) { + mod_bits[i] = tx_bits[j]; + } + + ofdm_mod(ofdm, tx_rx, mod_bits); + + /////////// + // DeMod + /////////// + + int Nsam = ofdm_samplesperframe; + int prx = 0; + int nin = ofdm_samplesperframe + 2 * (ofdm_m + ofdm_ncp); + + int lnew; + COMP rxbuf_in[ofdm_max_samplesperframe]; + + for (i=0; i<ofdm_samplesperframe ; i++,prx++) { + ofdm->rxbuf[ofdm_rxbuf-nin+i] = tx_rx[prx].real + tx_rx[prx].imag * I; + } + + for (i=ofdm_samplesperframe ; i<nin; i++) { + ofdm->rxbuf[ofdm_rxbuf-nin+i] = 0.0 + 0.0 * I; + } + + /* disable estimators for initial testing */ + ofdm_set_verbose(ofdm, false); + ofdm_set_timing_enable(ofdm, true); + ofdm_set_foff_est_enable(ofdm, true); + ofdm_set_phase_est_enable(ofdm, true); + + ofdm->mean_amp = 1.0; + + nin = ofdm_get_nin(ofdm); + + /* Insert samples at end of buffer, set to zero if no samples + available to disable phase estimation on future pilots on + last frame of simulation. */ + + if ((Nsam-prx) < nin) { + lnew = Nsam-prx; + } else { + lnew = nin; + } + for(i=0; i<nin; i++) { + rxbuf_in[i].real = 0.0; + rxbuf_in[i].imag = 0.0; + } + + if (lnew) { + for(i=0; i<lnew; i++, prx++) { + rxbuf_in[i] = tx_rx[prx]; + } + } + + ofdm_demod(ofdm, rx_bits, rxbuf_in); + + +} // end run_modem() + + +////////////////////////////////// +void dummy_code(int tx_bits[], int rx_bits[]) { + int i; + + for(i=0; i<ofdm_data_bitsperframe; i++) { + rx_bits[i] = tx_bits[i]; + } + +} // end dummy_code() + +/* vi:set ts=4 sts=4 et: */ diff --git a/unittest/ofdm_time_sync.sh b/unittest/ofdm_time_sync.sh new file mode 100755 index 0000000..fec51c9 --- /dev/null +++ b/unittest/ofdm_time_sync.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +# Shell script version of ofdm_time_sync() +# David June 2019 +# Tests ofdm modem sync time, using real, off air files + +onerun=$(mktemp) +results=$(mktemp) + +# bunch of runs at different time offsets into a sample off air file with low SNR and semi-staionary fading +for start_secs in `seq 0 29`; +do + if [ "$1" = "700D" ]; then + ofdm_demod --in ../wav/vk2tpm_004.wav --out /dev/null --verbose 2 --ldpc \ + --start_secs $start_secs --len_secs 5 2>/dev/null > $onerun + fi; + if [ "$1" = "2020" ]; then + ofdm_demod --mode 2020 --in ../wav/david4.wav --out /dev/null --verbose 2 --ldpc \ + --start_secs $start_secs --len_secs 5 2>/dev/null > $onerun + fi; + [ ! $? -eq 0 ] && { echo "error running ofdm_demod"; exit 1; } + cat $onerun | sed -n "s/time_to_sync: \([0-9..]*\)/\1/p" >> $results +done +# a pass is we never take longer than 5 secs to sync (mean is much smaller) +python3 -c " +import sys; import numpy as np +x=np.loadtxt(\"$results\") +fails=sum(x == -1) +print(\"fails: %d mean: %5.2f var: %5.2f \" % (fails, np.mean(x), np.var(x))) +sys.exit(0) if fails==0 else sys.exit(1) +" diff --git a/unittest/ota_auto.sh b/unittest/ota_auto.sh new file mode 100755 index 0000000..b6355ba --- /dev/null +++ b/unittest/ota_auto.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash +# ota_auto.sh +# +# Run a single automated test and log results + +timestamp=$(date +"%F-%T") +mkdir -p $timestamp +start_dir=$(pwd) +cd $timestamp +../ota_test.sh "$@" >> log.txt 2>&1 +cd $start_dir +kiwi_sdr=$(head -n 1 ${timestamp}/log.txt) +mode=$(head -n 2 ${timestamp}/log.txt | tail -n 1) +result=$(awk '/FrmGd/{getline; print}' ${timestamp}/log.txt) +printf "%s %-25s %s %s\n" $timestamp $kiwi_sdr $mode "$result" >> log.txt diff --git a/unittest/ota_last.sh b/unittest/ota_last.sh new file mode 100755 index 0000000..31efa1c --- /dev/null +++ b/unittest/ota_last.sh @@ -0,0 +1,70 @@ +#!/usr/bin/env bash +# +# Present summary info from the n-th latest OTA HF data test + +function print_help { + echo + echo "Summary of last automated Over The Air (OTA) test" + echo + echo " usage ./ota_last.sh [options]" + echo + echo " -a show scatter diagram" + echo " -p play the received wave file" + echo " -n N select N-th from last file" + echo " -s display spectrogram" +} + +show_spec=0 +show_scatter=0 +play_file=0 +N=1 + +while [[ $# -gt 0 ]] +do +key="$1" +case $key in + -n) + N="$2" + shift + shift + ;; + -a) + show_scatter=1 + shift + ;; + -s) + show_spec=1 + shift + ;; + -p) + play_file=1 + shift + ;; + -h) + print_help + ;; +esac +done + +# cat the log from the selected test +directory=$(ls -td 2021* | head -n ${N} | tail -n 1) +echo ${directory} +cat ${directory}/log.txt + +# optionally show a few plots + +if [ $show_spec -eq 1 ]; then + if [ -f ${directory}/spec.png ]; then + eog ${directory}/spec.png + else + eog ${directory}/spec.jpg + fi +fi + +if [ $show_scatter -eq 1 ]; then + eog ${directory}/scatter.png +fi + +if [ $play_file -eq 1 ]; then + play ${directory}/rx.wav +fi diff --git a/unittest/ota_summary.sh b/unittest/ota_summary.sh new file mode 100755 index 0000000..5e028e3 --- /dev/null +++ b/unittest/ota_summary.sh @@ -0,0 +1,55 @@ +#!/usr/bin/env bash +# +# Summarise tests to date + +CODEC2=${HOME}/codec2 + +function print_help { + echo + echo "Automated Over The Air (OTA) data test for FreeDV OFDM HF data modems" + echo + echo " usage ./ota_summary.sh [-t]" + echo + echo " -t create/update thumbnail directory" + exit 0 +} + +thumbnails=0 +while [[ $# -gt 0 ]] +do +key="$1" +case $key in + -t) + thumbnails=1 + shift + ;; + -h) + print_help + ;; +esac +done + +total_bytes=$(cat log.txt | tr -s ' ' | cut -f6 -d' ' | awk '$0==($0+0)' | paste -sd+ | bc) +printf "total bytes: %'d\n" ${total_bytes} + +# collect SNR averages from log.txt and generate a histogram +ota_snrs=mktemp +cat log.txt | tr -s ' ' | cut -f7 -d' ' | awk '$0==($0+0)'| sed '/-nan/d' > ${ota_snrs} +echo "warning('off', 'all'); \ + snr=load('${ota_snrs}'); \ + hist(snr); \ + print('snr_hist.png','-dpng'); \ + quit" | octave-cli -qf > /dev/null + +# option to put small versions of spec/scatter in one dir + +if [ $thumbnails -ne 0 ]; then + mkdir -p thumbnails + spec_files=$(find . -name spec.jpg -o -name spec.png) + for f in $spec_files + do + d=$(echo $f | sed -r 's/\.(.*)\//\1_/') + echo $f thumbnails${d} + cp $f thumbnails${d} + done +fi diff --git a/unittest/ota_test.sh b/unittest/ota_test.sh new file mode 100755 index 0000000..37b6ac0 --- /dev/null +++ b/unittest/ota_test.sh @@ -0,0 +1,161 @@ +#!/usr/bin/env bash +# ota_test.sh +# +# Automated Over The Air (OTA) data test for FreeDV OFDM HF data modems +# +# 1. Build codec2 +# 2. Install kiwclient: +# cd ~ && git clone [email protected]:jks-prv/kiwiclient.git +# 3. Install Hamlib cli tools + +PATH=${PATH}:${HOME}/codec2/build_linux/src:${HOME}/kiwiclient +CODEC2=${HOME}/codec2 + +kiwi_url="" +port=8073 +freq_kHz="7177" +tx_only=0 +Nbursts=5 +mode="datac0" +model=361 + +function print_help { + echo + echo "Automated Over The Air (OTA) data test for FreeDV OFDM HF data modems" + echo + echo " usage ./ota_test.sh [-d] [-f freq_kHz] [-t] [-n Nbursts] [-o model] [-p port] kiwi_url" + echo + echo " -d debug mode; trace script execution" + echo " -o model select radio model number ('rigctl -l' to list)" + echo " -m mode datac0|datac1|datac3" + echo " -t Tx only, useful for manually observing SDRs which block multiple sessions from one IP" + echo + exit +} + +function run_rigctl { + command=$1 + model=$2 + echo $command | rigctl -m $model -r /dev/ttyUSB0 > /dev/null + if [ $? -ne 0 ]; then + echo "Can't talk to Tx" + exit 1 + fi +} + +POSITIONAL=() +while [[ $# -gt 0 ]] +do +key="$1" +case $key in + -d) + set -x + shift + ;; + -f) + freq_kHz="$2" + shift + shift + ;; + -o) + model="$2" + shift + shift + ;; + -m) + mode="$2" + shift + shift + ;; + -n) + Nbursts="$2" + shift + shift + ;; + -p) + port="$2" + shift + shift + ;; + -t) + tx_only=1 + shift + ;; + -h) + print_help + ;; + *) + POSITIONAL+=("$1") # save it in an array for later + shift + ;; +esac +done +set -- "${POSITIONAL[@]}" # restore positional parameters + +if [ $tx_only -eq 0 ]; then + if [ $# -lt 1 ]; then + print_help + fi + kiwi_url="$1" + echo $kiwi_url +fi + +# create test Tx file +echo $mode +freedv_data_raw_tx -q --framesperburst 1 --bursts ${Nbursts} --testframes ${Nbursts} ${mode} /dev/zero test_datac0.raw + +usb_lsb=$(python3 -c "print('usb') if ${freq_kHz} >= 10000 else print('lsb')") + +if [ $tx_only -eq 0 ]; then + echo -n "waiting for KiwiSDR " + # start recording from remote kiwisdr + kiwi_stdout=$(mktemp) + kiwirecorder.py -s $kiwi_url -p ${port} -f $freq_kHz -m ${usb_lsb} -r 8000 --filename=rx --time-limit=300 >$kiwi_stdout & + kiwi_pid=$! + + # wait for kiwi to start recording + timeout_counter=0 + until grep -q -i 'Block: ' $kiwi_stdout + do + timeout_counter=$((timeout_counter+1)) + if [ $timeout_counter -eq 10 ]; then + echo "can't connect to ${kiwi_url}" + exit 1 + fi + echo -n "." + sleep 1 + done + echo +fi + +# transmit using local SSB radio +echo "Tx data signal" +freq_Hz=$((freq_kHz*1000)) +usb_lsb_upper=$(echo ${usb_lsb} | awk '{print toupper($0)}') +run_rigctl "\\set_mode PKT${usb_lsb_upper} 0" $model +run_rigctl "\\set_freq ${freq_Hz}" $model +run_rigctl "\\set_ptt 1" $model +aplay --device="plughw:CARD=CODEC,DEV=0" -f S16_LE test_datac0.raw 2>/dev/null +run_rigctl "\\set_ptt 0" $model + +if [ $tx_only -eq 0 ]; then + sleep 2 + echo "Stopping KiwiSDR" + kill ${kiwi_pid} + wait ${kiwi_pid} 2>/dev/null + + echo "Process receiver sample" + # generate spectrogram + echo "pkg load signal; warning('off', 'all'); \ + s=load_raw('rx.wav'); \ + plot_specgram(s, 8000, 500, 2500); print('spec.jpg', '-djpg'); \ + quit" | octave-cli -p ${CODEC2}/octave -qf > /dev/null + # attempt to demodulate + freedv_data_raw_rx -q --framesperburst 1 --testframes ${mode} -v --scatter scatter.txt --singleline rx.wav /dev/null + # render scatter plot (if we get any frames) + scatter_sz=$(ls -l scatter.txt | cut -f 5 -d' ') + if [ $scatter_sz -ne 0 ]; then + echo "pkg load signal; warning('off', 'all'); pl_scatter('scatter.txt'); quit" | octave-cli -p ${CODEC2}/octave -qf > /dev/null + fi +fi + diff --git a/unittest/ota_voice_auto.sh b/unittest/ota_voice_auto.sh new file mode 100755 index 0000000..c741f2d --- /dev/null +++ b/unittest/ota_voice_auto.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +# ota_voice_auto.sh +# +# Run a single automated voice test, files are put in a time stamped +# directory, and summarised in single line in the log file +# log_voice.txt. Designed to be used from cron. + +# use crontab -e to edit cron for current user, sample entry: + +# m h dom mon dow command +# */10 6-12 24 4 * cd codec2/unittest; ./ota_voice_auto.sh ~/your_speech_file.s16 your.kiwi.sdr + +timestamp=$(date +"%F-%T") +mkdir -p $timestamp +start_dir=$(pwd) +cd $timestamp +../ota_voice_test.sh "$@" > log.txt 2>&1 +cd $start_dir +kiwi_sdr=$(head -n 1 ${timestamp}/log.txt) +mode=$(head -n 2 ${timestamp}/log.txt | tail -n 1) +Nsync=$(cat ${timestamp}/log.txt | grep Nsync | tr -s ' ' | cut -d' ' -f2) +SNRav=$(cat ${timestamp}/log.txt | grep SNRav | tr -s ' ' | cut -d' ' -f2) +printf "%s %-25s %s %3d %5.2f\n" $timestamp $kiwi_sdr $mode $Nsync $SNRav >> log_voice.txt diff --git a/unittest/ota_voice_summary.sh b/unittest/ota_voice_summary.sh new file mode 100755 index 0000000..62014fd --- /dev/null +++ b/unittest/ota_voice_summary.sh @@ -0,0 +1,80 @@ +#!/usr/bin/env bash +# +# Summarise tests to date into one directory to allow easy browsing + +src=/home/david/Downloads/speech_orig_16k.wav +length_src=$(sox $src -n stat 2>&1 | grep Length | sed -r 's/.*\:\s*//') + +dir=voice_summary +mkdir -p ${dir} +time_snr_files=$(find . -name time_snr.jpg | sort) +p=$(pwd) +serial=0 + +echo "<table>" +echo "<tr>" +echo "<th>Serial</th><th>Mode</th><th>KiwiSDR</th><th>Rx</th><th>AnDV</th><th>DV</th><th>Spectrogram</th><th>SNR</th>" +echo "</tr>" + +for f in $time_snr_files +do + d=$(echo $f | sed -r 's/\.\/(.*)\/time_snr.jpg/\1/') + sdr_url=$(head ${d}/log.txt -n 1) + sdr="unk" + case $sdr_url in + "kiwisdr.areg.org.au") + sdr="areg" + ;; + "sdr-amradioantennas.com") + sdr="am" + ;; + "vk6qs.proxy.kiwisdr.com") + sdr="vk6qs" + ;; + "sdr.ironstonerange.com") + sdr="iron" + ;; + "kk6pr.ddns.net") + sdr="kk6pr" + ;; + "kiwisdr.owdjim.gen.nz") + sdr="marahau" + ;; + "kiwisdrzl1kfm.ddns.net") + sdr="zl1kfm" + ;; + *) + echo "Unknown Kiwi SDR" + ;; + esac + mode=$(head ${d}/log.txt -n 2 | tail -n 1) + serial_str=$(printf "%04d" $serial) + #echo $serial_str $d $sdr $mode + echo "<tr>" + echo "<td>$serial</td>" + echo "<td>$mode</td>" + echo "<td>$sdr</td>" + + f=${dir}/${serial_str}_${d}_${sdr}_${mode} + f1=${serial_str}_${d}_${sdr}_${mode} + + cp ${d}/rx.wav ${f}_rx.wav + echo "<td><a href=\"${f1}_rx.wav\">Rx</td>" + + cp ${d}/rx_freedv.wav ${f}_rx_freedv.wav + echo "<td><a href=\"${f1}_rx_freedv.wav\">AnDV</td>" + + length_f=$(sox ${f}_rx_freedv.wav -n stat 2>&1 | grep Length | sed -r 's/.*\:\s*//') + start_dv=$(python -c "print(${length_f}-${length_src}-2)") + sox ${d}/rx_freedv.wav ${f}_rx_freedv_dv.wav trim $start_dv $length_src + echo "<td><a href=\"${f1}_rx_freedv_dv.wav\">DV</td>" + + cp ${d}/spec.jpg ${f}_spec.jpg + echo "<td><img src=\"${f1}_spec.jpg\" width="200" height="200" /></td>" + cp ${d}/time_snr.jpg ${f}_time_snr.jpg + echo "<td><img src=\"${f1}_time_snr.jpg\" width="200" height="200" /></td>" + echo "</tr>" + serial=$((serial + 1)) +done + +echo "</table>" diff --git a/unittest/ota_voice_test.sh b/unittest/ota_voice_test.sh new file mode 100755 index 0000000..4f502ec --- /dev/null +++ b/unittest/ota_voice_test.sh @@ -0,0 +1,309 @@ +#!/usr/bin/env bash +# ota_voice_test.sh +# +# Automated Over The Air (OTA) voice test for FreeDV HF voice modes +# +# 1. Build codec2 +# 2. Install kiwclient: +# cd ~ && git clone [email protected]:jks-prv/kiwiclient.git +# 3. Install Hamlib cli tools, and add user to dialout group: +# sudo adduser david dialout +# 4. To test rigctl: +# echo "m" | rigctl -m 361 -r /dev/ttyUSB0 +# 5. Adjust Tx drive so ALC is just being tickled, set desired RF power: +# ../build_linux/src/freedv_tx 2020 ~/Downloads/speech_orig_16k.wav - | aplay -f S16_LE --device="plughw:CARD=CODEC,DEV=0" +# 6. Sample command line: +# ./ota_voice_test.sh ~/Downloads/speech_orig_16k.wav -m 700E -i ~/Downloads/vk5dgr_testing_8k.wav sdr.ironstonerange.com -p 8074 + +MY_PATH=`dirname $0` +BUILD_PATH=`echo $MY_PATH/../build_*/src` +PATH=${PATH}:${BUILD_PATH}:${HOME}/kiwiclient +CODEC2=${MY_PATH}/.. + +kiwi_url="" +port=8074 +freq_kHz="7177" +tx_only=0 +Nbursts=5 +mode="700D" +model=361 +gain=6 +serialPort="/dev/ttyUSB0" +rxwavefile=0 +soundDevice="plughw:CARD=CODEC,DEV=0" +txstats=0 +stationid="" + +function print_help { + echo + echo "Automated Over The Air (OTA) voice test for FreeDV HF voice modes" + echo + echo " usage ./ota_voice_test.sh [options] SpeechWaveFile [kiwi_url]" + echo " or:" + echo " usage ./ota_voice_test.sh -r rxWaveFile" + echo + echo " -c dev The sound device (in ALSA format on Linux, CoreAudio for macOS)" + echo " -d debug mode; trace script execution" + echo " -f Frequency (kHz)" + echo " -g SSB (analog) compressor gain" + echo " -i StationIDWaveFile Prepend this file to identify transmission (should be 8KHz mono)" + echo " -m mode 700c|700d|700e" + echo " -o model select radio model number ('rigctl -l' to list)" + echo " -p port kiwi_url port to use (default 8073)." + echo " -r Rx wave file mode: Rx process supplied rx wave file" + echo " -s SerialPort The serial port (or hostname:port) to control SSB radio," + echo " default /dev/ttyUSB0" + echo " -t Tx only, useful for manually observing SDRs" + echo " -x Generate tx.wav file and exit" + echo + exit +} + +# Approximation of Hilbert clipper type compressor. Could do with some HF boost +function analog_compressor { + input_file=$1 + output_file=$2 + gain=$3 + cat $input_file | ch - - 2>/dev/null | \ + ch - - --No -100 --clip 16384 --gain $gain 2>/dev/null | \ + # final line prints peak and CPAPR for SSB + ch - - --clip 16384 | + # manually adjusted to get similar peak levels for SSB and FreeDV + sox -t .s16 -r 8000 -c 1 -v 0.85 - -t .s16 $output_file +} + +function run_rigctl { + command=$1 + model=$2 + echo $command | rigctl -m $model -r $serialPort > /dev/null + if [ $? -ne 0 ]; then + echo "Can't talk to Tx" + clean_up + exit 1 + fi +} + +function clean_up { + echo "killing KiwiSDR process" + kill ${kiwi_pid} + wait ${kiwi_pid} 2>/dev/null + exit 1 +} + +function process_rx { + echo "Process receiver sample" + rx=$1 + # generate spectrogram + echo "pkg load signal; warning('off', 'all'); \ + s=load_raw('${rx}'); \ + plot_specgram(s, 8000, 200, 3000); print('spec.jpg', '-djpg'); \ + quit" | octave-cli -p ${CODEC2}/octave -qf > /dev/null + # attempt to decode + freedv_rx ${mode} ${rx} - -v --passthroughgain 1.0 2>rx_stats.txt | sox -t .s16 -r $speechFs -c 1 - rx_freedv.wav + cat rx_stats.txt | tr -s ' ' | cut -f5 -d' ' | awk '$0==($0+0)' > sync.txt + cat rx_stats.txt | tr -s ' ' | cut -f10 -d' ' | awk '$0==($0+0)' > snr.txt + # time domain plot of output speech, SNR, and sync + echo "pkg load signal; warning('off', 'all'); \ + s=load_raw('rx_freedv.wav'); snr=load('snr.txt'); sync=load('sync.txt'); \ + subplot(211); plot(s); subplot(212); x=1:length(sync); plotyy(x,snr,x,sync); \ + ylim([-5 15]); ylabel('SNR (dB)'); grid; \ + print('time_snr.jpg', '-djpg'); \ + printf('Nsync: %3d\n', sum(sync)); \ + snr_valid = snr(find(snr != -5.0)); \ + if length(snr_valid) printf('SNRav: %5.2f\n', mean(snr_valid)); else printf('SNRav: %5.2f\n', -5); end; + quit" | octave-cli -p ${CODEC2}/octave -qf +} + +POSITIONAL=() +while [[ $# -gt 0 ]] +do +key="$1" +case $key in + -d) + set -x + shift + ;; + -f) + freq_kHz="$2" + shift + shift + ;; + -g) + gain="$2" + shift + shift + ;; + -i) + stationid="$2" + shift + shift + ;; + -o) + model="$2" + shift + shift + ;; + -m) + mode="$2" + shift + shift + ;; + -p) + port="$2" + shift + shift + ;; + -t) + tx_only=1 + shift + ;; + -r) + rxwavefile=1 + shift + ;; + -x) + txstats=1 + shift + ;; + -c) + soundDevice="$2" + shift + shift + ;; + -s) + serialPort="$2" + shift + shift + ;; + -h) + print_help + ;; + *) + POSITIONAL+=("$1") # save it in an array for later + shift + ;; +esac +done +set -- "${POSITIONAL[@]}" # restore positional parameters + +# determine sample rate of freedv_tx/freedv_rx +speechFs=8000 +if [ "$mode" == "2020" ] || [ "$mode" == "2020B" ] || [ "$mode" == "2020C" ]; then + speechFs=16000 +fi + +if [ $rxwavefile -eq 1 ]; then + process_rx $1 + exit 0 +fi + +speechfile="$1" +if [ ! -f $speechfile ]; then + echo "Can't find input speech wave file: ${speechfile}!" + exit 1 +fi + +if [ $tx_only -eq 0 ]; then + if [ $# -lt 1 ]; then + print_help + fi + kiwi_url="$2" + echo $kiwi_url +fi + +# create Tx file ------------------------ +echo $mode + +# create compressed analog +speechfile_raw_8k=$(mktemp) +comp_in=$(mktemp) +speech_comp=$(mktemp) +speech_freedv=$(mktemp) +# If 16kHz input files for 2020x, we need an 8kHz version for SSB +sox $speechfile -r 8000 -t .s16 -c 1 $speechfile_raw_8k +if [ -z $stationid ]; then + cp $speechfile_raw_8k $comp_in +else + # append station ID and apply analog compression + stationid_raw_8k=$(mktemp) + sox $stationid -r 8000 -t .s16 -c 1 $stationid_raw_8k + cat $stationid_raw_8k $speechfile_raw_8k> $comp_in +fi +analog_compressor $comp_in $speech_comp $gain + +# create modulated FreeDV, with compressor enabled +sox $speechfile -t .s16 -r $speechFs - | freedv_tx $mode - $speech_freedv --clip 1 +cat $speech_comp $speech_freedv > tx.raw +sox -t .s16 -r 8000 -c 1 tx.raw tx.wav + +if [ $txstats -eq 1 ]; then + # ch just used to monitor observe peak and RMS level + ch $speech_freedv /dev/null + # time domain plot of tx signal + echo "pkg load signal; warning('off', 'all'); \ + s=load_raw('tx.raw'); plot(s); \ + print('tx.jpg', '-djpg'); \ + quit" | octave-cli -p ${CODEC2}/octave -qf > /dev/null + exit 0 +fi + +# kick off KiwiSDR ---------------------------- + +usb_lsb=$(python3 -c "print('usb') if ${freq_kHz} >= 10000 else print('lsb')") +if [ $tx_only -eq 0 ]; then + # clean up any kiwiSDR processes if we get a ctrl-C + trap clean_up SIGHUP SIGINT SIGTERM + + echo -n "waiting for KiwiSDR " + # start recording from remote kiwisdr + kiwi_stdout=$(mktemp) + kiwirecorder.py -s $kiwi_url -p ${port} -f $freq_kHz -m ${usb_lsb} -r 8000 --filename=rx --time-limit=300 >$kiwi_stdout & + kiwi_pid=$! + + # wait for kiwi to start recording + timeout_counter=0 + until grep -q -i 'Block: ' $kiwi_stdout + do + timeout_counter=$((timeout_counter+1)) + if [ $timeout_counter -eq 10 ]; then + echo "can't connect to ${kiwi_url}" + kill ${kiwi_pid} + wait ${kiwi_pid} 2>/dev/null + exit 1 + fi + echo -n "." + sleep 1 + done + echo +fi + +# transmit using local SSB radio +echo "Tx data signal" +freq_Hz=$((freq_kHz*1000)) +usb_lsb_upper=$(echo ${usb_lsb} | awk '{print toupper($0)}') +run_rigctl "\\set_mode PKT${usb_lsb_upper} 0" $model +run_rigctl "\\set_freq ${freq_Hz}" $model +run_rigctl "\\set_ptt 1" $model +if [ `uname` == "Darwin" ]; then + AUDIODEV="${soundDevice}" play -t raw -b 16 -c 1 -r 8000 -e signed-integer --endian little tx.raw +else + aplay --device="${soundDevice}" -f S16_LE tx.raw 2>/dev/null +fi +if [ $? -ne 0 ]; then + run_rigctl "\\set_ptt 0" $model + clean_up + echo "Problem running aplay!" + echo "Is ${soundDevice} configured as the default sound device in Settings-Sound?" + exit 1 +fi +run_rigctl "\\set_ptt 0" $model + +if [ $tx_only -eq 0 ]; then + sleep 2 + echo "Stopping KiwiSDR" + kill ${kiwi_pid} + wait ${kiwi_pid} 2>/dev/null + + process_rx rx.wav +fi + diff --git a/unittest/raw_data_curves/Makefile b/unittest/raw_data_curves/Makefile new file mode 100644 index 0000000..9f13d1f --- /dev/null +++ b/unittest/raw_data_curves/Makefile @@ -0,0 +1,132 @@ +# Makefile +# Dec 2022 +# +# Automates PER/BER curve generation for raw data mode: +# +# 1. Compare "ch" noise injection/SNR measurement against reference Octave Tx +# 2. Compare C Tx against Octave Tx (with and without compression) +# 3. Plot curves for SNR estimated from C Rx against actual SNR +# 4. Plot AWGN/PER C Tx curves for end user documentation + +SHELL := /bin/bash +CODEC2 := $(HOME)/codec2 + +all: test \ + octave_ch_noise_awgn.png octave_c_tx_awgn.png octave_c_tx_comp_awgn.png \ + octave_ch_noise_mpp.png octave_c_tx_mpp.png octave_c_tx_comp_mpp.png \ + snrest_snr_ctx.png snrest_snr_ctxc.png \ + c_tx_comp.png c_tx_comp_thruput.png + +clean: + rm -f *.txt *.png *.raw + +# run this first, traps common CML setup error +test: + source snr_curves.sh; test_ldpc + +# subset of files generated, but enough to set up Makefile dependencies +snr_oct = snr_oct_datac0_awgn.txt snr_oct_datac1_awgn.txt snr_oct_datac3_awgn.txt +snr_ch = snr_ch_datac0_awgn.txt snr_ch_datac1_awgn.txt snr_ch_datac3_awgn.txt +snr_ctx = snr_ctx_datac0_awgn.txt snr_ctx_datac1_awgn.txt snr_ctx_datac3_awgn.txt +snr_ctxc = snr_ctxc_datac0_awgn.txt snr_ctxc_datac3_awgn.txt + +snr_oct_mpp = snr_oct_datac0_mpp.txt snr_oct_datac1_mpp.txt snr_oct_datac3_mpp.txt +snr_ch_mpp = snr_ch_datac0_mpp.txt snr_ch_datac1_mpp.txt snr_ch_datac3_mpp.txt +snr_ctx_mpp = snr_ctx_datac0_mpp.txt snr_ctx_datac1_mpp.txt snr_ctx_datac3_mpp.txt +snr_ctxc_mpp = snr_ctxc_datac0_mpp.txt snr_ctxc_datac3_mpp.txt + +$(snr_oct): + source snr_curves.sh; generate_octave_tx_data datac0 awgn + source snr_curves.sh; generate_octave_tx_data datac1 awgn + source snr_curves.sh; generate_octave_tx_data datac3 awgn + +$(snr_oct_mpp): + source snr_curves.sh; generate_octave_tx_data datac0 mpp + source snr_curves.sh; generate_octave_tx_data datac1 mpp + source snr_curves.sh; generate_octave_tx_data datac3 mpp + +$(snr_ch): + source snr_curves.sh; generate_ch_data datac0 awgn + source snr_curves.sh; generate_ch_data datac1 awgn + source snr_curves.sh; generate_ch_data datac3 awgn + +$(snr_ch_mpp): + source snr_curves.sh; generate_ch_data datac0 mpp + source snr_curves.sh; generate_ch_data datac1 mpp + source snr_curves.sh; generate_ch_data datac3 mpp + +# C without compression + +$(snr_ctx): + source snr_curves.sh; generate_snrest_v_snr_data datac0 awgn + source snr_curves.sh; generate_snrest_v_snr_data datac1 awgn + source snr_curves.sh; generate_snrest_v_snr_data datac3 awgn + source snr_curves.sh; generate_snrest_v_snr_data datac4 awgn + source snr_curves.sh; generate_snrest_v_snr_data datac13 awgn + +$(snr_ctx_mpp): + source snr_curves.sh; generate_snrest_v_snr_data datac0 mpp + source snr_curves.sh; generate_snrest_v_snr_data datac1 mpp + source snr_curves.sh; generate_snrest_v_snr_data datac3 mpp + source snr_curves.sh; generate_snrest_v_snr_data datac4 mpp + source snr_curves.sh; generate_snrest_v_snr_data datac13 mpp + +# C with compression + +$(snr_ctxc): + source snr_curves.sh; generate_snrest_v_snr_data datac0 awgn 1 + source snr_curves.sh; generate_snrest_v_snr_data datac1 awgn 1 + source snr_curves.sh; generate_snrest_v_snr_data datac3 awgn 1 + source snr_curves.sh; generate_snrest_v_snr_data datac4 awgn 1 + source snr_curves.sh; generate_snrest_v_snr_data datac13 awgn 1 + +$(snr_ctxc_mpp): + source snr_curves.sh; generate_snrest_v_snr_data datac0 mpp 1 + source snr_curves.sh; generate_snrest_v_snr_data datac1 mpp 1 + source snr_curves.sh; generate_snrest_v_snr_data datac3 mpp 1 + source snr_curves.sh; generate_snrest_v_snr_data datac4 mpp 1 + source snr_curves.sh; generate_snrest_v_snr_data datac13 mpp 1 + +# Octave and C curves should be on top of each other, indicating Octave +# and ch noise injection/SNR measurement are equivalent +octave_ch_noise_awgn.png: $(snr_oct) $(snr_ch) + echo "snr_curves_plot; octave_ch_noise_print('awgn'); quit" | \ + octave-cli -p $(CODEC2)/octave +octave_ch_noise_mpp.png: $(snr_oct_mpp) $(snr_ch_mpp) + echo "snr_curves_plot; octave_ch_noise_print('mpp'); quit" | \ + octave-cli -p $(CODEC2)/octave + +# Octave Tx and C Tx curves should be on top of each other +octave_c_tx_awgn.png: $(snr_oct) $(snr_ctx) + echo "snr_curves_plot; octave_c_tx_print('awgn'); quit" | \ + octave-cli -p $(CODEC2)/octave +octave_c_tx_mpp.png: $(snr_oct_mpp) $(snr_ctx_mpp) + echo "snr_curves_plot; octave_c_tx_print('mpp'); quit" | \ + octave-cli -p $(CODEC2)/octave + +# Octave Tx and C Tx (compressed) curves should be close, but C may be 1dB +# poorer +octave_c_tx_comp_awgn.png: $(snr_oc) $(snr_ctxc) + echo "snr_curves_plot; octave_c_tx_comp_print('awgn'); quit" | \ + octave-cli -p $(CODEC2)/octave +octave_c_tx_comp_mpp.png: $(snr_oct_mpp) $(snr_ctxc_mpp) + echo "snr_curves_plot; octave_c_tx_comp_print('mpp'); quit" | \ + octave-cli -p $(CODEC2)/octave + +# combined AWGN and MPP from C Tx (compressed) - what end users would run +c_tx_comp.png: $(snr_ctxc) $(snr_ctxc_mpp) + echo "snr_curves_plot; c_tx_comp_print; quit" | \ + octave-cli -p $(CODEC2)/octave + +# Curves of SNR estimates from C Rx compared to actual SNR, useful for "gear shifting" +snrest_snr_ctx.png: $(snr_ctx) + echo "snr_curves_plot; snrest_snr_print('ctx', 'awgn'); quit" | \ + octave-cli -p $(CODEC2)/octave +snrest_snr_ctxc.png: $(snr_ctxc) + echo "snr_curves_plot; snrest_snr_print('ctxc', 'awgn'); quit" | \ + octave-cli -p $(CODEC2)/octave + +# Throughput of payload data in bits/s of modes against SNR +c_tx_comp_thruput.png: $(snr_ctxc) $(snr_ctxc_mpp) + echo "snr_curves_plot; c_tx_comp_thruput_print; quit" | \ + octave-cli -p $(CODEC2)/octave diff --git a/unittest/raw_data_curves/snr_curves.sh b/unittest/raw_data_curves/snr_curves.sh new file mode 100755 index 0000000..6da671c --- /dev/null +++ b/unittest/raw_data_curves/snr_curves.sh @@ -0,0 +1,184 @@ +# 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)) + 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" ]]; 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 +} diff --git a/unittest/reliable_text_fade.sh b/unittest/reliable_text_fade.sh new file mode 100755 index 0000000..8e86c2a --- /dev/null +++ b/unittest/reliable_text_fade.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +# +# Tests reliable_text fading channel performance, using a simulated channel + +results=$(mktemp -d) +mode=$1 +snr=$2 +min_text_packets=$3 +clip=$4 +build_folder=$5 +fading_dir=${build_folder}/../unittest +rx=$build_folder/freedv_rx +tx=$build_folder/freedv_tx + +if [ $clip -eq 1 ]; then + clip_args="--txbpf 1 --clip 1" +else + clip_args= +fi + +$tx $mode ../raw/ve9qrp.raw - --reliabletext AB1CDEF $clip_args | $build_folder/ch - - --No $snr --mpp -f -5 --fading_dir $fading_dir > $results/reliable_fade.raw +$rx $mode $results/reliable_fade.raw /dev/null --txtrx $results/reliable_fade.txt --reliabletext +if [ `cat $results/reliable_fade.txt | wc -l` -ge $min_text_packets ]; then + exit 0 +else + exit -1 +fi diff --git a/unittest/sd.c b/unittest/sd.c new file mode 100644 index 0000000..1d1a628 --- /dev/null +++ b/unittest/sd.c @@ -0,0 +1,84 @@ +/*--------------------------------------------------------------------------*\ + + FILE........: sd.c + AUTHOR......: David Rowe + DATE CREATED: 20/7/93 + + Function to determine spectral distortion between two sets of LPCs. + +\*--------------------------------------------------------------------------*/ + +/* + Copyright (C) 2009 David Rowe + + All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 2.1, as + published by the Free Software Foundation. This program is + distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, see <http://www.gnu.org/licenses/>. +*/ + +#define MAX_N 2048 /* maximum DFT size */ + +#include <math.h> +#include "four1.h" +#include "comp.h" +#include "sd.h" + +/*---------------------------------------------------------------------------*\ + + FUNCTION....: spectral_dist() + + AUTHOR......: David Rowe + DATE CREATED: 20/7/93 + + This function returns the soectral distoertion between two + sets of LPCs. + +\*---------------------------------------------------------------------------*/ + +float spectral_dist(float ak1[], float ak2[], int p, int n) +/* float ak1[]; unquantised set of p+1 LPCs */ +/* float ak2[]; quantised set of p+1 LPCs */ +/* int p; LP order */ +/* int n; DFT size to use for SD calculations (power of 2) */ +{ + COMP A1[MAX_N]; /* DFT of ak1[] */ + COMP A2[MAX_N]; /* DFT of ak2[] */ + float P1,P2; /* power of current bin */ + float sd; + int i; + + for(i=0; i<n; i++) { + A1[i].real = 0.0; + A1[i].imag = 0.0; + A2[i].real = 0.0; + A2[i].imag = 0.0; + } + + for(i=0; i<p+1; i++) { + A1[i].real = ak1[i]; + A2[i].real = ak2[i]; + } + + #warn Array index -1 is out of bounds + four1(&A1[-1].imag,n,-1); + four1(&A2[-1].imag,n,-1); + + sd = 0.0; + for(i=0; i<n; i++) { + P1 = A1[i].real*A1[i].real + A1[i].imag*A1[i].imag; + P2 = A2[i].real*A2[i].real + A2[i].imag*A2[i].imag; + sd += pow(log10(P2/P1),2.0); + } + sd = 10.0*sqrt(sd/n); /* sd in dB */ + + return(sd); +} diff --git a/unittest/sd.h b/unittest/sd.h new file mode 100644 index 0000000..866b95b --- /dev/null +++ b/unittest/sd.h @@ -0,0 +1,33 @@ +/*--------------------------------------------------------------------------*\ + + FILE........: sd.h + AUTHOR......: David Rowe + DATE CREATED: 22/7/93 + + Function to determine spectral distortion between two sets of LPCs. + +\*--------------------------------------------------------------------------*/ + +/* + Copyright (C) 2009 David Rowe + + All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 2.1, as + published by the Free Software Foundation. This program is + distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __SD__ +#define __SD__ + +float spectral_dist(float ak1[], float ak2[], int p, int n); + +#endif /* __SD__ */ diff --git a/unittest/spectrogram.sh b/unittest/spectrogram.sh new file mode 100755 index 0000000..87d3fa2 --- /dev/null +++ b/unittest/spectrogram.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +# spectrogram.sh +# +# Render a spectrogram from a wave file. + +PATH=${PATH}:${HOME}/codec2/build_linux/src +CODEC2=${HOME}/codec2 + +fullfile=$1 +filename=$(basename -- "$fullfile") +extension="${filename##*.}" +filename="${filename%.*}" + +echo "pkg load signal; warning('off', 'all'); \ + s=load_raw('${fullfile}'); \ + plot_specgram(s, 8000, 500, 2500); print('${filename}.jpg', '-djpg'); \ + quit" | octave-cli -p ${CODEC2}/octave -qf > /dev/null diff --git a/unittest/sum_debug_alloc b/unittest/sum_debug_alloc new file mode 100755 index 0000000..21e3137 --- /dev/null +++ b/unittest/sum_debug_alloc @@ -0,0 +1,79 @@ +#!/usr/bin/env python3 + +""" sum_debug_alloc + + Sum lines reported from codec2-dev/src/debug_alloc.h and report + + Lines like: + + CALLOC: run_ldpc_decoder(112, 32) + + + MALLOC: other_func(238) + + """ + +import fileinput + +by_func = {} +by_addr = {} + +def new_func(): + rec = {} + rec['cur'] = 0 + rec['max'] = 0 + return(rec) + +def new_addr(): + rec = {} + rec['func'] = 0 # Where allocated, may not be same as where free'd! + rec['size'] = 0 + return(rec) + +for line in fileinput.input(): + + if ((line.startswith("MALLOC:")) or (line.startswith("CALLOC:"))): + if (line.startswith("MALLOC:")): + words = line.translate( str.maketrans("()", " ") ).strip().split() + func = words[1] + addr = words[2] + size = int(words[3]) + + elif (line.startswith("CALLOC:")): + words = line.translate( str.maketrans("(,)", " ") ).strip().split() + func = words[1] + addr = words[2] + size = int(words[3]) * int(words[4]) + + #print("Alloc: {} to {} in {}".format(size, addr, func)) + if (not (func in by_func)): by_func[func] = new_func() + data = by_func[func] + data['cur'] += size + if (data['cur'] > data['max']): + data['max'] = data['cur'] + if (addr in by_addr): + print("Error: duplicate allocation to {} in {}".format(addr, func)) + else: + by_addr[addr] = new_addr() + by_addr[addr]['func'] = func + by_addr[addr]['size'] = size + + elif (line.startswith("FREE:")): + words = line.translate( str.maketrans("(,)", " ") ).strip().split() + func = words[1] + addr = words[2] + if (addr in by_addr): + func_a = by_addr[addr]['func'] + by_func[func_a]['cur'] -= by_addr[addr]['size'] + del(by_addr[addr]) + else: + print("Error: free of unallocated location {} in {}".format(addr, func)) + #print("Free: {}:{} in {} to {}".format(func_a, addr, func, by_func[func_a]['cur'])) + +##### +total = 0 +for func, sum in by_func.items(): + max = by_func[func]['max'] + print("{} = {}".format(func, max)) + total += max +print("Total = {}".format(total)) diff --git a/unittest/t16_8.c b/unittest/t16_8.c new file mode 100644 index 0000000..704eecd --- /dev/null +++ b/unittest/t16_8.c @@ -0,0 +1,105 @@ +/* + t16_8.c + David Rowe + May 10 2012 + + Unit test for 16 <-> 8 kHz sample rate conversion functions. I + + Evaluated output by plotting using Octave and looking for jaggies: + + pl("../unittest/out16.raw",1,3000) + pl("../unittest/out8.raw",1,3000) + + Listening to it also shows up anything nasty: + + $ play -s -2 -r 16000 out16.raw + $ play -s -2 -r 8000 out8.raw + + */ + +#include <assert.h> +#include <math.h> +#include <stdlib.h> +#include <stdio.h> +#include "codec2_fdmdv.h" + +#define N8 159 /* processing buffer size at 8 kHz (odd number deliberate) */ +#define N16 (N8*FDMDV_OS) +#define FRAMES 50 +#define TWO_PI 6.283185307 +#define FS 16000 + +#define SINE + +int main() { + float in8k[FDMDV_OS_TAPS_8K + N8]; + short in8k_short[N8]; + float out16k[N16]; + short out16k_short[N16]; + FILE *f16; + + float in16k[FDMDV_OS_TAPS_16K + N16]; + float out8k[N16]; + short out8k_short[N8]; + FILE *f8, *f8in; + + int i,f,t,t1; + float freq = 800.0; + + f16 = fopen("out16.raw", "wb"); + assert(f16 != NULL); + f8 = fopen("out8.raw", "wb"); + assert(f8 != NULL); + f8in = fopen("in8.raw", "wb"); + assert(f8in != NULL); + + /* clear filter memories */ + for(i=0; i<FDMDV_OS_TAPS_8K; i++) + in8k[i] = 0.0; + for(i=0; i<FDMDV_OS_TAPS_16K; i++) + in16k[i] = 0.0; + + t = t1 = 0; + for(f=0; f<FRAMES; f++) { + +#ifdef DC + for(i=0; i<N8; i++) + in8k[FDMDV_OS_TAPS_8K+i] = 16000.0; +#endif +#ifdef SINE + for(i=0; i<N8; i++,t++) + in8k[FDMDV_OS_TAPS_8K+i] = 16000.0*cos(TWO_PI*t*freq/(FS/FDMDV_OS)); +#endif + for(i=0; i<N8; i++) + in8k_short[i] = (short)in8k[i]; + fwrite(in8k_short, sizeof(short), N8, f8in); + + /* upsample */ + fdmdv_8_to_16(out16k, &in8k[FDMDV_OS_TAPS_8K], N8); + + /* save 16k to disk for plotting and check out */ + for(i=0; i<N16; i++) + out16k_short[i] = (short)out16k[i]; + fwrite(out16k_short, sizeof(short), N16, f16); + + /* add a 6 kHz spurious signal, down sampler should + knock this out */ + for(i=0; i<N16; i++,t1++) + in16k[i+FDMDV_OS_TAPS_16K] = out16k[i] + 16000.0*cos(TWO_PI*t1*6000.0/FS); + + /* downsample */ + fdmdv_16_to_8(out8k, &in16k[FDMDV_OS_TAPS_16K], N8); + + /* save 8k to disk for plotting and check out */ + for(i=0; i<N8; i++) + out8k_short[i] = (short)out8k[i]; + fwrite(out8k_short, sizeof(short), N8, f8); + + } + + fclose(f16); + fclose(f8); + fclose(f8in); + return 0; + +} diff --git a/unittest/t16_8_short.c b/unittest/t16_8_short.c new file mode 100644 index 0000000..6c10318 --- /dev/null +++ b/unittest/t16_8_short.c @@ -0,0 +1,94 @@ +/* + t16_8_short.c + David Rowe + 19 August 2014 + + Unit test for 16 <-> 8 kHz sample rate conversion functions. I + evaluated output by plotting using Octave and looking for jaggies: + + pl("../unittest/out16_short.raw",1,3000) + pl("../unittest/out8.raw",1,3000) + + Listening to it also shows up anything nasty: + + $ play -s -2 -r 16000 out16_short.raw + $ play -s -2 -r 8000 out8.raw + + */ + +#include <assert.h> +#include <math.h> +#include <stdlib.h> +#include <stdio.h> +#include "codec2_fdmdv.h" + +#define N8 159 /* procssing buffer size at 8 kHz */ +#define N16 (N8*FDMDV_OS) +#define FRAMES 100 +#define TWO_PI 6.283185307 +#define FS 16000 + +#define SINE + +int main() { + short in8k_short[FDMDV_OS_TAPS_8K + N8]; + short out16k_short[N16]; + FILE *f16; + + short in16k_short[FDMDV_OS_TAPS_16K + N16]; + short out8k_short[N16]; + FILE *f8, *f8in; + + int i,f,t,t1; + float freq = 800.0; + + f16 = fopen("out16_short.raw", "wb"); + assert(f16 != NULL); + f8 = fopen("out8_short.raw", "wb"); + assert(f8 != NULL); + f8in = fopen("in8_short.raw", "wb"); + assert(f8in != NULL); + + /* clear filter memories */ + for(i=0; i<FDMDV_OS_TAPS_8K; i++) + in8k_short[i] = 0; + for(i=0; i<FDMDV_OS_TAPS_16K; i++) + in16k_short[i] = 0; + + t = t1 = 0; + for(f=0; f<FRAMES; f++) { + +#ifdef DC + for(i=0; i<N8; i++) + in8k_short[FDMDV_OS_TAPS_8K+i] = 16000.0; +#endif +#ifdef SINE + for(i=0; i<N8; i++,t++) + in8k_short[FDMDV_OS_TAPS_8K+i] = 8000.0*cos(TWO_PI*t*freq/FS); +#endif + fwrite(in8k_short, sizeof(short), N8, f8in); + + /* upsample */ + fdmdv_8_to_16_short(out16k_short, &in8k_short[FDMDV_OS_TAPS_8K], N8); + + fwrite(out16k_short, sizeof(short), N16, f16); + + /* add a 6 kHz spurious signal for fun, we want down sampler to + knock this out */ + for(i=0; i<N16; i++,t1++) + in16k_short[i+FDMDV_OS_TAPS_16K] = out16k_short[i] + 8000.0*cos(TWO_PI*t1*6000.0/FS); + + /* downsample */ + fdmdv_16_to_8_short(out8k_short, &in16k_short[FDMDV_OS_TAPS_16K], N8); + + /* save 8k to disk for plotting and check out */ + fwrite(out8k_short, sizeof(short), N8, f8); + + } + + fclose(f16); + fclose(f8); + fclose(f8in); + return 0; + +} diff --git a/unittest/t48_8.c b/unittest/t48_8.c new file mode 100644 index 0000000..2e277a3 --- /dev/null +++ b/unittest/t48_8.c @@ -0,0 +1,111 @@ +/* + t48_8.c + David Rowe + May 10 2012 + + Unit test for 48 to 8 kHz sample rate conversion functions. I + evaluated output by plotting using Octave and looking for jaggies: + + pl("../unittest/out48.raw",1,3000) + pl("../unittest/out8.raw",1,3000) + + Listening to it also shows up anything nasty: + + $ play -s -2 -r 48000 out48.raw + $ play -s -2 -r 8000 out8.raw + + */ + +#include <assert.h> +#include <math.h> +#include <stdlib.h> +#include <stdio.h> +#include "codec2_fdmdv.h" + +#define N8 180 /* processing buffer size at 8 kHz */ +#define N48 (N8*FDMDV_OS_48) +#define MEM8 FDMDV_OS_TAPS_48_8K +#define FRAMES 50 +#define TWO_PI 6.283185307 +#define FS 48000 + +#define SINE + +int main() { + float in8k[MEM8 + N8]; + short in8k_short[N8]; + float out48k[N48]; + short out48k_short[N48]; + FILE *f48; + + float in48k[FDMDV_OS_TAPS_48K + N48]; + float out8k[N48]; + short out8k_short[N8]; + FILE *f8, *f8in; + + int i,f,t,t1; + float freq = 800.0; + + f48 = fopen("out48.raw", "wb"); + assert(f48 != NULL); + f8 = fopen("out8.raw", "wb"); + assert(f8 != NULL); + f8in = fopen("in8.raw", "wb"); + assert(f8in != NULL); + + /* clear filter memories */ + + for(i=0; i<MEM8; i++) + in8k[i] = 0.0; + for(i=0; i<FDMDV_OS_TAPS_48K; i++) + in48k[i] = 0.0; + + t = t1 = 0; + for(f=0; f<FRAMES; f++) { + +#ifdef DC + for(i=0; i<N8; i++) + in8k[MEM8+i] = 16000.0; +#endif +#ifdef SINE + for(i=0; i<N8; i++,t++) + in8k[MEM8+i] = 16000.0*cos(TWO_PI*t*freq/(FS/FDMDV_OS_48)); +#endif + for(i=0; i<N8; i++) + in8k_short[i] = (short)in8k[i]; + fwrite(in8k_short, sizeof(short), N8, f8in); + + /* upsample */ + + fdmdv_8_to_48(out48k, &in8k[MEM8], N8); + + /* save 48k to disk for plotting and check out */ + + for(i=0; i<N48; i++) + out48k_short[i] = (short)out48k[i]; + fwrite(out48k_short, sizeof(short), N48, f48); + + /* add a 10 kHz spurious signal for fun, we want down sampler to + knock this out */ + + for(i=0; i<N48; i++,t1++) + in48k[i+FDMDV_OS_TAPS_48K] = out48k[i] + 16000.0*cos(TWO_PI*t1*1E4/FS); + + /* downsample */ + + fdmdv_48_to_8(out8k, &in48k[FDMDV_OS_TAPS_48K], N8); + + /* save 8k to disk for plotting and check out */ + + for(i=0; i<N8; i++) + out8k_short[i] = (short)out8k[i]; + fwrite(out8k_short, sizeof(short), N8, f8); + + } + + fclose(f48); + fclose(f8); + fclose(f8in); + return 0; + +} diff --git a/unittest/t48_8_short.c b/unittest/t48_8_short.c new file mode 100644 index 0000000..31caa60 --- /dev/null +++ b/unittest/t48_8_short.c @@ -0,0 +1,84 @@ +/* + t48_8_short.c + David Rowe + Dec 2021 +*/ + +#include <assert.h> +#include <math.h> +#include <stdlib.h> +#include <stdio.h> +#include "codec2_fdmdv.h" + +#define N8 180 /* processing buffer size at 8 kHz */ +#define N48 (N8*FDMDV_OS_48) +#define MEM8 FDMDV_OS_TAPS_48_8K +#define FRAMES 50 +#define TWO_PI 6.283185307 +#define FS 48000 + +#define SINE + +int main() { + short in8k[MEM8+N8]; + short out48k[N48]; + FILE *f48; + + short in48k[FDMDV_OS_TAPS_48K + N48]; + short out8k[N48]; + FILE *f8, *f8in; + + int i,f,t,t1; + float freq = 800.0; + + f48 = fopen("out48.raw", "wb"); + assert(f48 != NULL); + f8 = fopen("out8.raw", "wb"); + assert(f8 != NULL); + f8in = fopen("in8.raw", "wb"); + assert(f8in != NULL); + + /* clear filter memories */ + + for(i=0; i<MEM8; i++) + in8k[i] = 0.0; + for(i=0; i<FDMDV_OS_TAPS_48K; i++) + in48k[i] = 0.0; + + t = t1 = 0; + for(f=0; f<FRAMES; f++) { + +#ifdef DC + for(i=0; i<N8; i++) + in8k[MEM8+i] = 16000.0; +#endif +#ifdef SINE + for(i=0; i<N8; i++,t++) + in8k[MEM8+i] = 16000.0*cos(TWO_PI*t*freq/(FS/FDMDV_OS_48)); +#endif + fwrite(&in8k[MEM8], sizeof(short), N8, f8in); + + /* upsample */ + fdmdv_8_to_48_short(out48k, &in8k[MEM8], N8); + + /* save 48k to disk for plotting and check out */ + fwrite(out48k, sizeof(short), N48, f48); + + /* add a 10 kHz spurious signal for fun, we want down sampler to + knock this out */ + for(i=0; i<N48; i++,t1++) + in48k[i+FDMDV_OS_TAPS_48K] = out48k[i] + 16000.0*cos(TWO_PI*t1*1E4/FS); + + /* downsample */ + fdmdv_48_to_8_short(out8k, &in48k[FDMDV_OS_TAPS_48K], N8); + + /* save 8k to disk for plotting and check out */ + fwrite(out8k, sizeof(short), N8, f8); + } + + fclose(f48); + fclose(f8); + fclose(f8in); + return 0; + +} diff --git a/unittest/t_helpers.c b/unittest/t_helpers.c new file mode 100644 index 0000000..4f20743 --- /dev/null +++ b/unittest/t_helpers.c @@ -0,0 +1,38 @@ +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "t_helpers.h" + +void test(char * tfn) +{ + fn = tfn; + printf("========================================\n"); + printf("test function: %s\n", fn); + printf("========================================\n"); +} + +void test_failed() +{ + printf("Failed to calculate %s.\n", fn); + exit(1); +} + +void test_failed_s(char * expected, char * res) +{ + + printf("Failed to calculate %s.\n", fn); + + printf("expected: %s\ngot: %s\n", expected, res); + exit(1); +} + +void test_failed_f(float expected, float res) +{ + + printf("Failed to calculate %s.\n", fn); + printf("expected: %f\ngot: %f\n", expected, res); + exit(1); +} + diff --git a/unittest/t_helpers.h b/unittest/t_helpers.h new file mode 100644 index 0000000..2dfcbef --- /dev/null +++ b/unittest/t_helpers.h @@ -0,0 +1,41 @@ +/*---------------------------------------------------------------------------*\ + + FILE........: t_helpers.c + AUTHOR......: Phil Ayres + DATE CREATED: July 2017 + + * Simple helper functions for unit tests + * +\*---------------------------------------------------------------------------*/ + +/* + Copyright David Rowe 2017 + + All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 2.1, as + published by the Free Software Foundation. This program is + distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, see <http://www.gnu.org/licenses/>. + + */ + +#ifndef T_HELPERS_H +#define T_HELPERS_H + +void test(char * tfn); +void test_failed(); +void test_failed_s(char * expected, char * res); +void test_failed_f(float expected, float res); + +char *fn; + + +#endif /* T_HELPERS_H */ + diff --git a/unittest/tcohpsk.c b/unittest/tcohpsk.c new file mode 100644 index 0000000..7b421ed --- /dev/null +++ b/unittest/tcohpsk.c @@ -0,0 +1,286 @@ +/*---------------------------------------------------------------------------*\ + + FILE........: tcohpsk.c + AUTHOR......: David Rowe + DATE CREATED: March 2015 + + Tests for the C version of the coherent PSK FDM modem. This program + outputs a file of Octave vectors that are loaded and automatically + tested against the Octave version of the modem by the Octave script + tcohpsk.m + +\*---------------------------------------------------------------------------*/ + +/* + Copyright (C) 2015 David Rowe + + All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 2.1, as + published by the Free Software Foundation. This program is + distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, see <http://www.gnu.org/licenses/>. +*/ + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <math.h> + +#include "fdmdv_internal.h" +#include "codec2_fdmdv.h" +#include "codec2_cohpsk.h" +#include "cohpsk_defs.h" +#include "cohpsk_internal.h" +#include "octave.h" +#include "comp_prim.h" +#include "noise_samples.h" + +#define FRAMES 30 /* LOG_FRAMES is #defined in cohpsk_internal.h */ +#define SYNC_FRAMES 12 /* sync state uses up extra log storage as we reprocess several times */ +#define FRAMESL (SYNC_FRAMES*FRAMES) /* worst case is every frame is out of sync */ + +#define FOFF 58.7 +#define DFOFF (-0.5/(float)COHPSK_FS) +#define ESNODB 8 +#define PPM -1500 + +extern float pilots_coh[][PILOTS_NC]; + +int main(int argc, char *argv[]) +{ + struct COHPSK *coh; + int tx_bits[COHPSK_BITS_PER_FRAME]; + COMP tx_symb[NSYMROWPILOT][COHPSK_NC*COHPSK_ND]; + COMP tx_fdm_frame[COHPSK_M*NSYMROWPILOT]; + COMP ch_fdm_frame[COHPSK_M*NSYMROWPILOT]; + //COMP rx_fdm_frame_bb[M*NSYMROWPILOT]; + //COMP ch_symb[NSYMROWPILOT][COHPSK_NC*COHPSK_ND]; + float rx_bits_sd[COHPSK_BITS_PER_FRAME]; + int rx_bits[COHPSK_BITS_PER_FRAME]; + + int tx_bits_log[COHPSK_BITS_PER_FRAME*FRAMES]; + COMP tx_symb_log[NSYMROWPILOT*FRAMES][COHPSK_NC*COHPSK_ND]; + COMP tx_fdm_frame_log[COHPSK_M*NSYMROWPILOT*FRAMES]; + COMP ch_fdm_frame_log[COHPSK_M*NSYMROWPILOT*FRAMES]; + COMP ch_fdm_frame_log_out[(COHPSK_M*NSYMROWPILOT+1)*FRAMES]; + //COMP rx_fdm_frame_bb_log[M*NSYMROWPILOT*FRAMES]; + //COMP ch_symb_log[NSYMROWPILOT*FRAMES][COHPSK_NC*COHPSK_ND]; + COMP ct_symb_ff_log[NSYMROWPILOT*FRAMES][COHPSK_NC*COHPSK_ND]; + float rx_amp_log[NSYMROW*FRAMES][COHPSK_NC*COHPSK_ND]; + float rx_phi_log[NSYMROW*FRAMES][COHPSK_NC*COHPSK_ND]; + COMP rx_symb_log[NSYMROW*FRAMES][COHPSK_NC*COHPSK_ND]; + int rx_bits_log[COHPSK_BITS_PER_FRAME*FRAMES]; + + FILE *fout; + int f, r, c, log_r, log_data_r, noise_r, ff_log_r, i; + double foff; + COMP foff_rect, phase_ch; + + struct FDMDV *fdmdv; + //COMP rx_filt[COHPSK_NC*COHPSK_ND][P+1]; + //int rx_filt_log_col_index = 0; + //float env[NT*P]; + //float __attribute__((unused)) rx_timing; + COMP tx_onesym[COHPSK_NC*COHPSK_ND]; + //COMP rx_onesym[COHPSK_NC*COHPSK_ND]; + //int rx_baseband_log_col_index = 0; + //COMP rx_baseband_log[COHPSK_NC*COHPSK_ND][(M+M/P)*NSYMROWPILOT*FRAMES]; + float f_est_log[FRAMES], sig_rms_log[FRAMES], noise_rms_log[FRAMES]; + int f_est_samples; + + int log_bits; + float EsNo, variance; + COMP scaled_noise; + int reliable_sync_bit; + int ch_fdm_frame_log_index, nin_frame, tmp, nout; + + coh = cohpsk_create(); + fdmdv = coh->fdmdv; + assert(coh != NULL); + cohpsk_set_verbose(coh, 1); + + /* these puppies are used for logging data in the bowels on the modem */ + + coh->rx_baseband_log_col_sz = (COHPSK_M+COHPSK_M/P)*NSYMROWPILOT*FRAMESL; + coh->rx_baseband_log = (COMP *)malloc(sizeof(COMP)*COHPSK_NC*COHPSK_ND*coh->rx_baseband_log_col_sz); + + coh->rx_filt_log_col_sz = (P+1)*NSYMROWPILOT*FRAMESL; + coh->rx_filt_log = (COMP *)malloc(sizeof(COMP)*COHPSK_NC*COHPSK_ND*coh->rx_filt_log_col_sz); + + coh->ch_symb_log_col_sz = COHPSK_NC*COHPSK_ND; + coh->ch_symb_log = (COMP *)malloc(sizeof(COMP)*NSYMROWPILOT*FRAMESL*coh->ch_symb_log_col_sz); + + coh->rx_timing_log = (float*)malloc(sizeof(float)*NSYMROWPILOT*FRAMESL); + + /* init stuff */ + + log_r = log_data_r = noise_r = log_bits = ff_log_r = f_est_samples = 0; + phase_ch.real = 1.0; phase_ch.imag = 0.0; + foff = FOFF; + + /* each carrier has power = 2, total power 2Nc, total symbol rate + NcRs, noise BW B=Fs Es/No = (C/Rs)/(N/B), N = var = + 2NcFs/NcRs(Es/No) = 2Fs/Rs(Es/No) */ + + EsNo = pow(10.0, ESNODB/10.0); + variance = 2.0*COHPSK_FS/(COHPSK_RS*EsNo); + //fprintf(stderr, "doff: %e\n", DFOFF); + + /* Main Loop ---------------------------------------------------------------------*/ + + for(f=0; f<FRAMES; f++) { + + /* --------------------------------------------------------*\ + Mod + \*---------------------------------------------------------*/ + + cohpsk_get_test_bits(coh, tx_bits); + bits_to_qpsk_symbols(tx_symb, (int*)tx_bits, COHPSK_BITS_PER_FRAME); + + for(r=0; r<NSYMROWPILOT; r++) { + for(c=0; c<COHPSK_NC*COHPSK_ND; c++) + tx_onesym[c] = tx_symb[r][c]; + tx_filter_and_upconvert_coh(&tx_fdm_frame[r*COHPSK_M], COHPSK_NC*COHPSK_ND , tx_onesym, fdmdv->tx_filter_memory, + fdmdv->phase_tx, fdmdv->freq, &fdmdv->fbb_phase_tx, fdmdv->fbb_rect); + } + cohpsk_clip(tx_fdm_frame, COHPSK_CLIP, NSYMROWPILOT*COHPSK_M); + + /* --------------------------------------------------------*\ + Channel + \*---------------------------------------------------------*/ + + for(r=0; r<NSYMROWPILOT*COHPSK_M; r++) { + foff_rect.real = cos(2.0*M_PI*foff/COHPSK_FS); foff_rect.imag = sin(2.0*M_PI*foff/COHPSK_FS); + foff += DFOFF; + phase_ch = cmult(phase_ch, foff_rect); + ch_fdm_frame[r] = cmult(tx_fdm_frame[r], phase_ch); + } + phase_ch.real /= cabsolute(phase_ch); + phase_ch.imag /= cabsolute(phase_ch); + //fprintf(stderr, "%f\n", foff); + for(r=0; r<NSYMROWPILOT*COHPSK_M; r++,noise_r++) { + scaled_noise = fcmult(sqrt(variance), noise[noise_r]); + ch_fdm_frame[r] = cadd(ch_fdm_frame[r], scaled_noise); + } + + /* optional gain to test level sensitivity */ + + for(r=0; r<NSYMROWPILOT*COHPSK_M; r++) { + ch_fdm_frame[r] = fcmult(1.0, ch_fdm_frame[r]); + } + + /* tx vector logging */ + + memcpy(&tx_bits_log[COHPSK_BITS_PER_FRAME*f], tx_bits, sizeof(int)*COHPSK_BITS_PER_FRAME); + memcpy(&tx_fdm_frame_log[COHPSK_M*NSYMROWPILOT*f], tx_fdm_frame, sizeof(COMP)*COHPSK_M*NSYMROWPILOT); + memcpy(&ch_fdm_frame_log[COHPSK_M*NSYMROWPILOT*f], ch_fdm_frame, sizeof(COMP)*COHPSK_M*NSYMROWPILOT); + + for(r=0; r<NSYMROWPILOT; r++, log_r++) { + for(c=0; c<COHPSK_NC*COHPSK_ND; c++) { + tx_symb_log[log_r][c] = tx_symb[r][c]; + } + } + } + + /* Fs offset simulation */ + + nout = cohpsk_fs_offset(ch_fdm_frame_log_out, ch_fdm_frame_log, COHPSK_M*NSYMROWPILOT*FRAMES, PPM); + assert(nout < (COHPSK_M*NSYMROWPILOT+1)*FRAMES); + + nin_frame = COHPSK_NOM_SAMPLES_PER_FRAME; + ch_fdm_frame_log_index = 0; + + /* --------------------------------------------------------*\ + Demod + \*---------------------------------------------------------*/ + + for(f=0; f<FRAMES; f++) { + coh->frame = f; + + //printf("nin_frame: %d\n", nin_frame); + + assert(ch_fdm_frame_log_index < COHPSK_M*NSYMROWPILOT*FRAMES); + tmp = nin_frame; + cohpsk_demod(coh, rx_bits_sd, &reliable_sync_bit, &ch_fdm_frame_log_out[ch_fdm_frame_log_index], &nin_frame); + for(i=0; i<COHPSK_BITS_PER_FRAME; i++) + rx_bits[i] = rx_bits_sd[i] < 0.0; + + ch_fdm_frame_log_index += tmp; + + /* --------------------------------------------------------*\ + Log each vector + \*---------------------------------------------------------*/ + + if (coh->sync == 1) { + + for(r=0; r<NSYMROWPILOT; r++, ff_log_r++) { + for(c=0; c<COHPSK_NC*COHPSK_ND; c++) { + ct_symb_ff_log[ff_log_r][c] = coh->ct_symb_ff_buf[r][c]; + } + } + + for(r=0; r<NSYMROW; r++, log_data_r++) { + for(c=0; c<COHPSK_NC*COHPSK_ND; c++) { + rx_amp_log[log_data_r][c] = coh->amp_[r][c]; + rx_phi_log[log_data_r][c] = coh->phi_[r][c]; + rx_symb_log[log_data_r][c] = coh->rx_symb[r][c]; + } + } + memcpy(&rx_bits_log[COHPSK_BITS_PER_FRAME*log_bits], rx_bits, sizeof(int)*COHPSK_BITS_PER_FRAME); + log_bits++; + f_est_log[f_est_samples] = coh->f_est; + sig_rms_log[f_est_samples] = coh->sig_rms; + noise_rms_log[f_est_samples] = coh->noise_rms; + f_est_samples++;; + } + + assert(log_r <= NSYMROWPILOT*FRAMES); + assert(noise_r <= NSYMROWPILOT*COHPSK_M*FRAMES); + assert(log_data_r <= NSYMROW*FRAMES); + + printf("\r [%d]", f+1); + } + printf("\n"); + + /*---------------------------------------------------------*\ + Dump logs to Octave file for evaluation + by tcohpsk.m Octave script + \*---------------------------------------------------------*/ + + fout = fopen("tcohpsk_out.txt","wt"); + assert(fout != NULL); + fprintf(fout, "# Created by tcohpsk.c\n"); + octave_save_int(fout, "tx_bits_log_c", tx_bits_log, 1, COHPSK_BITS_PER_FRAME*FRAMES); + octave_save_complex(fout, "tx_symb_log_c", (COMP*)tx_symb_log, NSYMROWPILOT*FRAMES, COHPSK_NC*COHPSK_ND, COHPSK_NC*COHPSK_ND); + octave_save_complex(fout, "tx_fdm_frame_log_c", (COMP*)tx_fdm_frame_log, 1, COHPSK_M*NSYMROWPILOT*FRAMES, COHPSK_M*NSYMROWPILOT*FRAMES); + octave_save_complex(fout, "ch_fdm_frame_log_c", (COMP*)ch_fdm_frame_log_out, 1, nout-1, nout-1); + //octave_save_complex(fout, "rx_fdm_frame_bb_log_c", (COMP*)rx_fdm_frame_bb_log, 1, M*NSYMROWPILOT*FRAMES, M*NSYMROWPILOT*FRAMES); + octave_save_complex(fout, "rx_baseband_log_c", (COMP*)coh->rx_baseband_log, COHPSK_NC*COHPSK_ND, coh->rx_baseband_log_col_index, coh->rx_baseband_log_col_sz); + octave_save_complex(fout, "rx_filt_log_c", (COMP*)coh->rx_filt_log, COHPSK_NC*COHPSK_ND, coh->rx_filt_log_col_index, coh->rx_filt_log_col_sz); + octave_save_complex(fout, "ch_symb_log_c", (COMP*)coh->ch_symb_log, coh->ch_symb_log_r, COHPSK_NC*COHPSK_ND, COHPSK_NC*COHPSK_ND); + octave_save_float(fout, "rx_timing_log_c", (float*)coh->rx_timing_log, 1, coh->rx_timing_log_index, coh->rx_timing_log_index); + octave_save_complex(fout, "ct_symb_ff_log_c", (COMP*)ct_symb_ff_log, NSYMROWPILOT*FRAMES, COHPSK_NC*COHPSK_ND, COHPSK_NC*COHPSK_ND); + octave_save_float(fout, "rx_amp_log_c", (float*)rx_amp_log, log_data_r, COHPSK_NC*COHPSK_ND, COHPSK_NC*COHPSK_ND); + octave_save_float(fout, "rx_phi_log_c", (float*)rx_phi_log, log_data_r, COHPSK_NC*COHPSK_ND, COHPSK_NC*COHPSK_ND); + octave_save_complex(fout, "rx_symb_log_c", (COMP*)rx_symb_log, log_data_r, COHPSK_NC*COHPSK_ND, COHPSK_NC*COHPSK_ND); + octave_save_int(fout, "rx_bits_log_c", rx_bits_log, 1, COHPSK_BITS_PER_FRAME*log_bits); + octave_save_float(fout, "f_est_log_c", &f_est_log[1], 1, f_est_samples-1, f_est_samples-1); + octave_save_float(fout, "sig_rms_log_c", sig_rms_log, 1, f_est_samples, f_est_samples-1); + octave_save_float(fout, "noise_rms_log_c", noise_rms_log, 1, f_est_samples, f_est_samples); +#ifdef XX +#endif + fclose(fout); + + cohpsk_destroy(coh); + + return 0; +} + diff --git a/unittest/tcontphase.c b/unittest/tcontphase.c new file mode 100644 index 0000000..02e51b2 --- /dev/null +++ b/unittest/tcontphase.c @@ -0,0 +1,186 @@ +/*---------------------------------------------------------------------------*\ + + FILE........: tcontphase.c + AUTHOR......: David Rowe + DATE CREATED: 11/9/09 + + Test program for developing continuous phase track synthesis algorithm. + However while developing this it was discovered that synthesis_mixed() + worked just as well. + +\*---------------------------------------------------------------------------*/ + +/* + Copyright (C) 2009 David Rowe + + All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 2.1, as + published by the Free Software Foundation. This program is + distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, see <http://www.gnu.org/licenses/>. +*/ + +#define N 80 /* frame size */ +#define F 160 /* frames to synthesis */ +#define P 10 /* LPC order */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <math.h> +#include "sine.h" +#include "dump.h" +#include "synth.h" +#include "phase.h" + +int frames; + +float ak[] = { + 1.000000, +-1.455836, + 1.361841, +-0.879267, + 0.915985, +-1.002202, + 0.944103, +-0.743094, + 1.053356, +-0.817491, + 0.431222 +}; + + +/*---------------------------------------------------------------------------*\ + + switch_present() + + Searches the command line arguments for a "switch". If the switch is + found, returns the command line argument where it ws found, else returns + NULL. + +\*---------------------------------------------------------------------------*/ + +int switch_present(sw,argc,argv) + char sw[]; /* switch in string form */ + int argc; /* number of command line arguments */ + char *argv[]; /* array of command line arguments in string form */ +{ + int i; /* loop variable */ + + for(i=1; i<argc; i++) + if (!strcmp(sw,argv[i])) + return(i); + + return 0; +} + +/*---------------------------------------------------------------------------*\ + + MAIN + +\*---------------------------------------------------------------------------*/ + +int main(argc,argv) +int argc; +char *argv[]; +{ + FILE *fout; + short buf[N]; + int i,j; + int dump; + float phi_prev[MAX_AMP]; + float Wo_prev, ex_phase, G; + //float ak[P+1]; + COMP H[MAX_AMP]; + float f0; + + if (argc < 3) { + printf("\nusage: %s OutputRawSpeechFile F0\n", argv[0]); + exit(1); + } + + /* Output file */ + + if ((fout = fopen(argv[1],"wb")) == NULL) { + printf("Error opening output speech file: %s\n",argv[1]); + exit(1); + } + + f0 = atof(argv[2]); + + dump = switch_present("--dump",argc,argv); + if (dump) + dump_on(argv[dump+1]); + + init_decoder(); + + for(i=0; i<MAX_AMP; i++) + phi_prev[i] = 0.0; + Wo_prev = 0.0; + + model.Wo = PI*(f0/4000.0); + G = 1000.0; + model.L = floor(PI/model.Wo); + + //aks_to_H(&model, ak, G , H, P); + //for(i=1; i<=model.L; i++) + model.A[i] = sqrt(H[i].real*H[i].real + H[i].imag*H[i].imag); + //printf("L = %d\n", model.L); + //model.L = 10; + for(i=1; i<=model.L; i++) { + model.A[i] = 1000/model.L; + model.phi[i] = 0; + H[i].real = 1.0; H[i].imag = 0.0; + } + + //ak[0] = 1.0; + //for(i=1; i<=P; i++) + // ak[i] = 0.0; + + frames = 0; + for(j=0; j<F; j++) { + frames++; + + #ifdef SWAP + /* lets make phases bounce around from frame to frame. This + could happen if H[m] is varying, for example due to frame + to frame Wo variations, or non-stationary speech. + Continuous model generally results in smooth phase track + under these circumstances. */ + if (j%2){ + H[1].real = 1.0; H[1].imag = 0.0; + model.phi[1] = 0.0; + } + else { + H[1].real = 0.0; H[1].imag = 1.0; + model.phi[1] = PI/2; + } + #endif + + //#define CONT + #ifdef CONT + synthesise_continuous_phase(Pn, &model, Sn_, 1, &Wo_prev, phi_prev); + #else + phase_synth_zero_order(5.0, H, &Wo_prev, &ex_phase); + synthesise_mixed(Pn,&model,Sn_,1); + #endif + + for(i=0; i<N; i++) + buf[i] = Sn_[i]; + fwrite(buf,sizeof(short),N,fout); + } + + fclose(fout); + if (dump) dump_off(); + + return 0; +} + + diff --git a/unittest/tdeframer.c b/unittest/tdeframer.c new file mode 100644 index 0000000..03e8bb1 --- /dev/null +++ b/unittest/tdeframer.c @@ -0,0 +1,148 @@ +/*---------------------------------------------------------------------------*\ + + FILE........: tdeframer.c + AUTHOR......: Brady O'Brien + DATE CREATED: 8 April 2016 + + C unit test for the VHF framer/deframer used by modes 2400A and 2400B. + The deframer should sync up within one frame at a BER of 10e-3 +\*---------------------------------------------------------------------------*/ + +/* + Copyright (C) 2016 David Rowe + + All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 2.1, as + published by the Free Software Foundation. This program is + distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, see <http://www.gnu.org/licenses/>. +*/ + +/* BER of test */ +#define TESTBER 0.01 + +/* Frame count */ +#define FRCNT 1500 + +/* Random bits leading frame */ +#define LRCNT 44 + +#include <stdio.h> +#include <math.h> +#include <stdint.h> +#include <freedv_vhf_framing.h> +#include <golay23.h> +#include <string.h> + +/* The main loop of the test driver */ +int main(int argc,char *argv[]){ + uint8_t * bit_buffer; + uint8_t c2_buffer[10]; + struct freedv_vhf_deframer * fvd; + int i,p,k; + int bitbufferlen; + int fsize; + int ftype; + int first_tol; + + if(argc<2){ + fprintf(stderr,"Usage: %s [A|B]\n",argv[0]); + exit(1); + } + + if(strcmp(argv[1],"A")==0){ + ftype = FREEDV_VHF_FRAME_A; + first_tol = 2; + }else if(strcmp(argv[1],"B")==0){ + ftype = FREEDV_HF_FRAME_B; + first_tol = 5; + }else{ + fprintf(stderr,"Usage: %s [A|B]\n",argv[0]); + exit(1); + } + + srand(1); + golay23_init(); + + /* Set up the deframer */ + fvd = fvhff_create_deframer(ftype,1); + + fsize = fvhff_get_frame_size(fvd); + bitbufferlen = (LRCNT+fsize*FRCNT); + + /* Allocate bit buffer */ + bit_buffer = (uint8_t *) malloc(sizeof(uint8_t)*bitbufferlen); + p = 0; + + /* Fill out front of buffer */ + for(i=0; i<LRCNT; i++){ + bit_buffer[p++] = rand()&0x1; + } + + /* Place frames */ + for(i=0; i<FRCNT; i++){ + /* Encode frame index into golay codeword to protect from test BER*/ + k = golay23_encode((i+1)&0x0FFF); + c2_buffer[5] = (k )&0xFF; + c2_buffer[1] = (k>>8 )&0xFF; + c2_buffer[0] = (k>>16)&0x7F; + /* Frame the bits */ + fvhff_frame_bits(ftype, &bit_buffer[p+(i*fsize)], c2_buffer,NULL,NULL); + } + + /* Flip bits */ + for(i=0; i<bitbufferlen; i++){ + if( (rand()&0xFFFFFF) < (int)(TESTBER*0xFFFFFF)){ + bit_buffer[i] = bit_buffer[i]?0:1; + } + } + + p=0; + int first_extract = 0; + int total_extract = 0; + int err_count = 0; + printf("\n"); + /* Deframe some bits */ + for(i=0; i<bitbufferlen; i+=fsize){ + if( fvhff_deframe_bits(fvd, c2_buffer, NULL, NULL, &bit_buffer[i])){ + /* Extract golay23 codeword */ + k = ((int)c2_buffer[5]) ; + k |= ((int)c2_buffer[1])<<8 ; + k |= ((int)c2_buffer[0])<<16; + k = k & 0x7FFFFF; + /* Decode frame index */ + p = golay23_decode(k); + err_count += golay23_count_errors(k,p); + p = p>>11; + + printf("%d,\t",p); + total_extract++; + if(first_extract==0) + first_extract=p; + } + } + printf("\n"); + float measured_ber = (float)err_count/(float)(23*total_extract); + + printf("First extracted frame %d\n",first_extract); + printf("Extracted %d frames of %d, %f hit rate\n",total_extract,FRCNT,((float)total_extract/(float)FRCNT)); + printf("Bit error rate %f measured from golay code\n",measured_ber); + printf("Bit error rate %f measured by deframer\n",fvd->ber_est); + printf("Bit error rate %f measured by deframer\n",(float)fvd->total_uw_err/(float)fvd->total_uw_bits); + /* Check test condition */ + if(first_extract<first_tol){ + printf("Test passed at test BER of %f!\n",TESTBER); + exit(0); + }else{ + printf("** Test failed at test BER of %f!\n",TESTBER); + exit(1); + } + fvhff_destroy_deframer(fvd); +} diff --git a/unittest/tesno_est.c b/unittest/tesno_est.c new file mode 100644 index 0000000..e198510 --- /dev/null +++ b/unittest/tesno_est.c @@ -0,0 +1,31 @@ +/*---------------------------------------------------------------------------*\ + + FILE........: tesno_est.c + AUTHORS.....: David Rowe + DATE CREATED: Mar 2021 + + Test for C port of Es/No estimator. + +\*---------------------------------------------------------------------------*/ + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <math.h> + +#include "ofdm_internal.h" + +int main(int argc, char *argv[]) +{ + FILE *fin = fopen(argv[1],"rb"); assert(fin != NULL); + size_t nsym = atoi(argv[2]); assert(nsym >= 0); + complex float rx_sym[nsym]; + size_t nread = fread(rx_sym, sizeof(complex float), nsym, fin); + assert(nread == nsym); + fclose(fin); + + float EsNodB = ofdm_esno_est_calc(rx_sym, nsym); + printf("%f\n",EsNodB); + + return 0; +} diff --git a/unittest/test_700c_eq.sh b/unittest/test_700c_eq.sh new file mode 100755 index 0000000..1a106f0 --- /dev/null +++ b/unittest/test_700c_eq.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash +# test_700c_eq.sh +# make sure 700C EQ is reducing VQ distortion + +results=$(mktemp) + +c2enc 700C ../raw/kristoff.raw /dev/null --var 2> $results +var=$(cat $results | sed -n "s/.*var: \([0-9..]*\) .*/\1/p") +c2enc 700C ../raw/kristoff.raw /dev/null --var --eq 2> $results +var_eq=$(cat $results | sed -n "s/.*var: \([0-9..]*\) .*/\1/p") +printf "var: %5.2f var_eq: %5.2f\n" $var $var_eq +python3 -c "import sys; sys.exit(0) if $var_eq<=$var else sys.exit(1)" diff --git a/unittest/test_phi0.c b/unittest/test_phi0.c new file mode 100644 index 0000000..8063843 --- /dev/null +++ b/unittest/test_phi0.c @@ -0,0 +1,78 @@ +/* + FILE...: test_phi0.c + AUTHOR.: Matthew C. Valenti, Rohit Iyer Seshadri, David Rowe, Don Reid + CREATED: Sep 2018 + + Compare new generated phi0 function to what was originally in mpdecode_core.c +*/ + +#include <math.h> +#include <stdlib.h> +#include <stdio.h> + +#include "phi0.h" + + +/* Original Phi function */ +static float phi0_orig( float x ) { + float z; + + if (x>10) + return( 0 ); + else if (x< 9.08e-5 ) + return( 10 ); + else if (x > 9) + return( 1.6881e-4 ); + /* return( 1.4970e-004 ); */ + else if (x > 8) + return( 4.5887e-4 ); + /* return( 4.0694e-004 ); */ + else if (x > 7) + return( 1.2473e-3 ); + /* return( 1.1062e-003 ); */ + else if (x > 6) + return( 3.3906e-3 ); + /* return( 3.0069e-003 ); */ + else if (x > 5) + return( 9.2168e-3 ); + /* return( 8.1736e-003 ); */ + else { + z = (float) exp(x); + return( (float) log( (z+1)/(z-1) ) ); + } +} + +//////////////////////////////////////////////////// +// Main +int main(void) { + + float xf; + float error; + int errsum = 0; + int errsum2 = 0; + int errcnt = 0; + + for (xf=10.5f; xf>5e-5f; xf = xf * 0.9) { + + float orig = phi0_orig(xf); + float new = phi0(xf); + + error = new - orig; + printf("%10.4f: %10.6f - %10.6f = %10.6f", xf, new, orig, error); + if ((error >= 0.001) && (error >= (orig * 0.1))) printf(" ****"); + printf("\n"); + + errsum += error; + errsum2 += error * error; + errcnt ++; + + } + + printf("Net error %f\n", (double)errsum); + printf("avg error %f\n", (double)errsum/errcnt); + printf("rms error %f\n", (double)sqrt(errsum2/errcnt)); + + return(0); +} + +/* vi:set ts=4 et sts=4: */ diff --git a/unittest/tfdmdv.c b/unittest/tfdmdv.c new file mode 100644 index 0000000..6806a5d --- /dev/null +++ b/unittest/tfdmdv.c @@ -0,0 +1,286 @@ +/*---------------------------------------------------------------------------*\ + + FILE........: tfdmdv.c + AUTHOR......: David Rowe + DATE CREATED: April 16 2012 + + Tests for the C version of the FDMDV modem. This program outputs a + file of Octave vectors that are loaded and automatically tested + against the Octave version of the modem by the Octave script + tfmddv.m + +\*---------------------------------------------------------------------------*/ + +/* + Copyright (C) 2012 David Rowe + + All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 2.1, as + published by the Free Software Foundation. This program is + distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, see <http://www.gnu.org/licenses/>. +*/ + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <math.h> + +#include "fdmdv_internal.h" +#include "codec2_fdmdv.h" +#include "octave.h" + +#define FRAMES 35 +#define CHANNEL_BUF_SIZE (10*M_FAC) + +extern float pilot_coeff[]; + +int main(int argc, char *argv[]) +{ + struct FDMDV *fdmdv; + int tx_bits[FDMDV_BITS_PER_FRAME]; + COMP tx_symbols[FDMDV_NC+1]; + COMP tx_fdm[M_FAC]; + float channel[CHANNEL_BUF_SIZE]; + int channel_count; + COMP rx_fdm[M_FAC+M_FAC/P]; + float foff_coarse; + int nin, next_nin; + COMP rx_fdm_fcorr[M_FAC+M_FAC/P]; + COMP rx_fdm_filter[M_FAC+M_FAC/P]; + COMP rx_filt[NC+1][P+1]; + float rx_timing; + float env[NT*P]; + COMP rx_symbols[FDMDV_NC+1]; + int rx_bits[FDMDV_BITS_PER_FRAME]; + float foff_fine; + int sync_bit, reliable_sync_bit; + + int tx_bits_log[FDMDV_BITS_PER_FRAME*FRAMES]; + COMP tx_symbols_log[(FDMDV_NC+1)*FRAMES]; + COMP tx_fdm_log[M_FAC*FRAMES]; + COMP pilot_baseband1_log[NPILOTBASEBAND*FRAMES]; + COMP pilot_baseband2_log[NPILOTBASEBAND*FRAMES]; + COMP pilot_lpf1_log[NPILOTLPF*FRAMES]; + COMP pilot_lpf2_log[NPILOTLPF*FRAMES]; + COMP S1_log[MPILOTFFT*FRAMES]; + COMP S2_log[MPILOTFFT*FRAMES]; + float foff_coarse_log[FRAMES]; + float foff_log[FRAMES]; + COMP rx_fdm_filter_log[(M_FAC+M_FAC/P)*FRAMES]; + int rx_fdm_filter_log_index; + COMP rx_filt_log[NC+1][(P+1)*FRAMES]; + int rx_filt_log_col_index; + float env_log[NT*P*FRAMES]; + float rx_timing_log[FRAMES]; + COMP rx_symbols_log[FDMDV_NC+1][FRAMES]; + COMP phase_difference_log[FDMDV_NC+1][FRAMES]; + float sig_est_log[FDMDV_NC+1][FRAMES]; + float noise_est_log[FDMDV_NC+1][FRAMES]; + int rx_bits_log[FDMDV_BITS_PER_FRAME*FRAMES]; + float foff_fine_log[FRAMES]; + int sync_bit_log[FRAMES]; + int sync_log[FRAMES]; + int nin_log[FRAMES]; + + FILE *fout; + int f,c,i,j; + + fdmdv = fdmdv_create(FDMDV_NC); + next_nin = M_FAC; + channel_count = 0; + + rx_fdm_filter_log_index = 0; + rx_filt_log_col_index = 0; + + printf("sizeof FDMDV states: %zd bytes\n", sizeof(struct FDMDV)); + + for(f=0; f<FRAMES; f++) { + + /* --------------------------------------------------------*\ + Modulator + \*---------------------------------------------------------*/ + + fdmdv_get_test_bits(fdmdv, tx_bits); + bits_to_dqpsk_symbols(tx_symbols, FDMDV_NC, fdmdv->prev_tx_symbols, tx_bits, &fdmdv->tx_pilot_bit, 0); + memcpy(fdmdv->prev_tx_symbols, tx_symbols, sizeof(COMP)*(FDMDV_NC+1)); + tx_filter_and_upconvert(tx_fdm, FDMDV_NC , tx_symbols, fdmdv->tx_filter_memory, + fdmdv->phase_tx, fdmdv->freq, &fdmdv->fbb_phase_tx, fdmdv->fbb_rect); + + /* --------------------------------------------------------*\ + Channel + \*---------------------------------------------------------*/ + + nin = next_nin; + + // nin = M_FAC; // when debugging good idea to uncomment this to "open loop" + + /* add M_FAC tx samples to end of buffer */ + + assert((channel_count + M_FAC) < CHANNEL_BUF_SIZE); + for(i=0; i<M_FAC; i++) + channel[channel_count+i] = tx_fdm[i].real; + channel_count += M_FAC; + + /* take nin samples from start of buffer */ + + for(i=0; i<nin; i++) { + rx_fdm[i].real = channel[i]; + rx_fdm[i].imag = 0; + } + + /* shift buffer back */ + + for(i=0,j=nin; j<channel_count; i++,j++) + channel[i] = channel[j]; + channel_count -= nin; + + /* --------------------------------------------------------*\ + Demodulator + \*---------------------------------------------------------*/ + + /* shift down to complex baseband */ + + fdmdv_freq_shift(rx_fdm, rx_fdm, -FDMDV_FCENTRE, &fdmdv->fbb_phase_rx, nin); + + /* freq offset estimation and correction */ + + // fdmdv->sync = 0; // when debugging good idea to uncomment this to "open loop" + + foff_coarse = rx_est_freq_offset(fdmdv, rx_fdm, nin, !fdmdv->sync); + + if (fdmdv->sync == 0) + fdmdv->foff = foff_coarse; + fdmdv_freq_shift(rx_fdm_fcorr, rx_fdm, -fdmdv->foff, &fdmdv->foff_phase_rect, nin); + + /* baseband processing */ + + rxdec_filter(rx_fdm_filter, rx_fdm_fcorr, fdmdv->rxdec_lpf_mem, nin); + down_convert_and_rx_filter(rx_filt, fdmdv->Nc, rx_fdm_filter, fdmdv->rx_fdm_mem, fdmdv->phase_rx, fdmdv->freq, + fdmdv->freq_pol, nin, M_FAC/Q); + rx_timing = rx_est_timing(rx_symbols, FDMDV_NC, rx_filt, fdmdv->rx_filter_mem_timing, env, nin, M_FAC); + foff_fine = qpsk_to_bits(rx_bits, &sync_bit, FDMDV_NC, fdmdv->phase_difference, fdmdv->prev_rx_symbols, rx_symbols, 0); + + //for(i=0; i<FDMDV_NC;i++) + // printf("rx_symbols: %f %f prev_rx_symbols: %f %f phase_difference: %f %f\n", rx_symbols[i].real, rx_symbols[i].imag, + // fdmdv->prev_rx_symbols[i].real, fdmdv->prev_rx_symbols[i].imag, fdmdv->phase_difference[i].real, fdmdv->phase_difference[i].imag); + //if (f==1) + // exit(0); + + snr_update(fdmdv->sig_est, fdmdv->noise_est, FDMDV_NC, fdmdv->phase_difference); + memcpy(fdmdv->prev_rx_symbols, rx_symbols, sizeof(COMP)*(FDMDV_NC+1)); + + next_nin = M_FAC; + + if (rx_timing > 2*M_FAC/P) + next_nin += M_FAC/P; + + if (rx_timing < 0) + next_nin -= M_FAC/P; + + fdmdv->sync = freq_state(&reliable_sync_bit, sync_bit, &fdmdv->fest_state, &fdmdv->timer, fdmdv->sync_mem); + fdmdv->foff -= TRACK_COEFF*foff_fine; + + /* --------------------------------------------------------*\ + Log each vector + \*---------------------------------------------------------*/ + + memcpy(&tx_bits_log[FDMDV_BITS_PER_FRAME*f], tx_bits, sizeof(int)*FDMDV_BITS_PER_FRAME); + memcpy(&tx_symbols_log[(FDMDV_NC+1)*f], tx_symbols, sizeof(COMP)*(FDMDV_NC+1)); + memcpy(&tx_fdm_log[M_FAC*f], tx_fdm, sizeof(COMP)*M_FAC); + + memcpy(&pilot_baseband1_log[f*NPILOTBASEBAND], fdmdv->pilot_baseband1, sizeof(COMP)*NPILOTBASEBAND); + memcpy(&pilot_baseband2_log[f*NPILOTBASEBAND], fdmdv->pilot_baseband2, sizeof(COMP)*NPILOTBASEBAND); + memcpy(&pilot_lpf1_log[f*NPILOTLPF], fdmdv->pilot_lpf1, sizeof(COMP)*NPILOTLPF); + memcpy(&pilot_lpf2_log[f*NPILOTLPF], fdmdv->pilot_lpf2, sizeof(COMP)*NPILOTLPF); + memcpy(&S1_log[f*MPILOTFFT], fdmdv->S1, sizeof(COMP)*MPILOTFFT); + memcpy(&S2_log[f*MPILOTFFT], fdmdv->S2, sizeof(COMP)*MPILOTFFT); + foff_coarse_log[f] = foff_coarse; + foff_log[f] = fdmdv->foff; + + /* rx filtering */ + + for(i=0; i<nin; i++) + rx_fdm_filter_log[rx_fdm_filter_log_index + i] = rx_fdm_filter[i]; + rx_fdm_filter_log_index += nin; + + for(c=0; c<NC+1; c++) { + for(i=0; i<(P*nin)/M_FAC; i++) + rx_filt_log[c][rx_filt_log_col_index + i] = rx_filt[c][i]; + } + rx_filt_log_col_index += (P*nin)/M_FAC; + + /* timing estimation */ + + memcpy(&env_log[NT*P*f], env, sizeof(float)*NT*P); + rx_timing_log[f] = rx_timing; + nin_log[f] = nin; + + for(c=0; c<FDMDV_NC+1; c++) { + rx_symbols_log[c][f] = rx_symbols[c]; + phase_difference_log[c][f] = fdmdv->phase_difference[c]; + } + + /* qpsk_to_bits() */ + + memcpy(&rx_bits_log[FDMDV_BITS_PER_FRAME*f], rx_bits, sizeof(int)*FDMDV_BITS_PER_FRAME); + for(c=0; c<FDMDV_NC+1; c++) { + sig_est_log[c][f] = fdmdv->sig_est[c]; + noise_est_log[c][f] = fdmdv->noise_est[c]; + } + foff_fine_log[f] = foff_fine; + sync_bit_log[f] = sync_bit; + + sync_log[f] = fdmdv->sync; + } + + + /*---------------------------------------------------------*\ + Dump logs to Octave file for evaluation + by tfdmdv.m Octave script + \*---------------------------------------------------------*/ + + fout = fopen("tfdmdv_out.txt","wt"); + assert(fout != NULL); + fprintf(fout, "# Created by tfdmdv.c\n"); + octave_save_int(fout, "tx_bits_log_c", tx_bits_log, 1, FDMDV_BITS_PER_FRAME*FRAMES); + octave_save_complex(fout, "tx_symbols_log_c", tx_symbols_log, 1, (FDMDV_NC+1)*FRAMES, (FDMDV_NC+1)*FRAMES); + octave_save_complex(fout, "tx_fdm_log_c", (COMP*)tx_fdm_log, 1, M_FAC*FRAMES, M_FAC*FRAMES); + octave_save_complex(fout, "pilot_lut_c", (COMP*)fdmdv->pilot_lut, 1, NPILOT_LUT, NPILOT_LUT); + octave_save_complex(fout, "pilot_baseband1_log_c", pilot_baseband1_log, 1, NPILOTBASEBAND*FRAMES, NPILOTBASEBAND*FRAMES); + octave_save_complex(fout, "pilot_baseband2_log_c", pilot_baseband2_log, 1, NPILOTBASEBAND*FRAMES, NPILOTBASEBAND*FRAMES); + octave_save_float(fout, "pilot_coeff_c", pilot_coeff, 1, NPILOTCOEFF, NPILOTCOEFF); + octave_save_complex(fout, "pilot_lpf1_log_c", pilot_lpf1_log, 1, NPILOTLPF*FRAMES, NPILOTLPF*FRAMES); + octave_save_complex(fout, "pilot_lpf2_log_c", pilot_lpf2_log, 1, NPILOTLPF*FRAMES, NPILOTLPF*FRAMES); + octave_save_complex(fout, "S1_log_c", S1_log, 1, MPILOTFFT*FRAMES, MPILOTFFT*FRAMES); + octave_save_complex(fout, "S2_log_c", S2_log, 1, MPILOTFFT*FRAMES, MPILOTFFT*FRAMES); + octave_save_float(fout, "foff_log_c", foff_log, 1, FRAMES, FRAMES); + octave_save_float(fout, "foff_coarse_log_c", foff_coarse_log, 1, FRAMES, FRAMES); + octave_save_complex(fout, "rx_fdm_filter_log_c", (COMP*)rx_fdm_filter_log, 1, rx_fdm_filter_log_index, rx_fdm_filter_log_index); + octave_save_complex(fout, "rx_filt_log_c", (COMP*)rx_filt_log, (FDMDV_NC+1), rx_filt_log_col_index, (P+1)*FRAMES); + octave_save_float(fout, "env_log_c", env_log, 1, NT*P*FRAMES, NT*P*FRAMES); + octave_save_float(fout, "rx_timing_log_c", rx_timing_log, 1, FRAMES, FRAMES); + octave_save_complex(fout, "rx_symbols_log_c", (COMP*)rx_symbols_log, (FDMDV_NC+1), FRAMES, FRAMES); + octave_save_complex(fout, "phase_difference_log_c", (COMP*)phase_difference_log, (FDMDV_NC+1), FRAMES, FRAMES); + octave_save_float(fout, "sig_est_log_c", (float*)sig_est_log, (FDMDV_NC+1), FRAMES, FRAMES); + octave_save_float(fout, "noise_est_log_c", (float*)noise_est_log, (FDMDV_NC+1), FRAMES, FRAMES); + octave_save_int(fout, "rx_bits_log_c", rx_bits_log, 1, FDMDV_BITS_PER_FRAME*FRAMES); + octave_save_float(fout, "foff_fine_log_c", foff_fine_log, 1, FRAMES, FRAMES); + octave_save_int(fout, "sync_bit_log_c", sync_bit_log, 1, FRAMES); + octave_save_int(fout, "sync_log_c", sync_log, 1, FRAMES); + octave_save_int(fout, "nin_log_c", nin_log, 1, FRAMES); + fclose(fout); + + fdmdv_destroy(fdmdv); + + return 0; +} + diff --git a/unittest/tfifo.c b/unittest/tfifo.c new file mode 100644 index 0000000..0987db6 --- /dev/null +++ b/unittest/tfifo.c @@ -0,0 +1,106 @@ +/* + tfifo.c + David Rowe + Nov 19 2012 + + Tests FIFOs, in particular thread safety. +*/ + +#include <assert.h> +#include <stdio.h> +#include <pthread.h> +#include "codec2_fifo.h" + +#define FIFO_SZ 1024 +#define WRITE_SZ 10 +#define READ_SZ 8 +#define N_MAX 100 +#define LOOPS 1000000 + +int run_thread = 1; +struct FIFO *f; + +void writer(void); +void *writer_thread(void *data); +pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + +#define USE_THREADS +//#define USE_MUTEX + +int main() { + pthread_t awriter_thread; + int i,j; + short read_buf[READ_SZ]; + int n_out = 0; + int success; + + f = codec2_fifo_create(FIFO_SZ); + #ifdef USE_THREADS + pthread_create(&awriter_thread, NULL, writer_thread, NULL); + #endif + + for(i=0; i<LOOPS; ) { + #ifndef USE_THREADS + writer(); + #endif + + #ifdef USE_MUTEX + pthread_mutex_lock(&mutex); + #endif + success = (codec2_fifo_read(f, read_buf, READ_SZ) == 0); + #ifdef USE_MUTEX + pthread_mutex_unlock(&mutex); + #endif + + if (success) { + for(j=0; j<READ_SZ; j++) { + if (read_buf[j] != n_out) { + printf("error: %d %d\n", read_buf[j], n_out); + return(1); + } + n_out++; + if (n_out == N_MAX) + n_out = 0; + } + i++; + } + + } + + #ifdef USE_THREADS + run_thread = 0; + pthread_join(awriter_thread,NULL); + #endif + + printf("%d loops tested OK\n", LOOPS); + return 0; +} + +int n_in = 0; + +void writer(void) { + short write_buf[WRITE_SZ]; + int i; + + if ((FIFO_SZ - codec2_fifo_used(f)) > WRITE_SZ) { + for(i=0; i<WRITE_SZ; i++) { + write_buf[i] = n_in++; + if (n_in == N_MAX) + n_in = 0; + } + #ifdef USE_MUTEX + pthread_mutex_lock(&mutex); + #endif + codec2_fifo_write(f, write_buf, WRITE_SZ); + pthread_mutex_unlock(&mutex); + } +} + +void *writer_thread(void *data) { + + while(run_thread) { + writer(); + } + + return NULL; +} diff --git a/unittest/tfmfsk.c b/unittest/tfmfsk.c new file mode 100644 index 0000000..c1b46d5 --- /dev/null +++ b/unittest/tfmfsk.c @@ -0,0 +1,198 @@ +/*---------------------------------------------------------------------------*\ + + FILE........: tfmfsk.c + AUTHOR......: Brady O'Brien + DATE CREATED: 8 February 2016 + + C test driver for fmfsk_mod and fmfsk_demod in fmfsk.c. Reads a file with input + bits/rf and spits out modulated/demoduladed samples and a dump of internal + state. To run unit test, see octave/tfmfsk.m + +\*---------------------------------------------------------------------------*/ + +/* + Copyright (C) 2016 David Rowe + + All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 2.1, as + published by the Free Software Foundation. This program is + distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, see <http://www.gnu.org/licenses/>. +*/ + + +//#define MODEMPROBE_ENABLE + +#include "modem_probe.h" +#include <stdio.h> + +/* Note: This is a dirty hack to force fsk.c to compile with modem probing enabled */ +#include "fmfsk.c" + +#define ST_BITS 10000 +#define ST_FS 48000 +#define ST_RS 2400 +#define ST_EBNO 8 + +#define TEST_SELF_FULL 1 /* No-arg self test */ +#define TEST_MOD 2 /* Test modulator using in and out file */ +#define TEST_DEMOD 3 /* Test demodulator using in and out file */ + + +int main(int argc,char *argv[]){ + struct FMFSK *fmfsk; + int Fs,Rs; + FILE *fin,*fout; + + uint8_t *bitbuf = NULL; + float *modbuf = NULL; + uint8_t *bitbufp; + float *modbufp; + + size_t bitbufsize = 0; + size_t modbufsize = 0; + + int test_type; + + int i; + + fin = NULL; + fout = NULL; + + /* Set up full self-test */ + if(argc == 1){ + test_type = TEST_SELF_FULL; + modem_probe_init("fmfsk","fmfsk_tfmfsk_log.txt"); + Fs = ST_FS; + Rs = ST_RS; + } else if (argc<7){ + /* Not running any test */ + printf("Usage: %s [(M|D) SampleRate BitRate InputFile OutputFile OctaveLogFile]\n",argv[0]); + exit(1); + } else { + /* Running stim-drivin test */ + /* Mod test */ + if(strcmp(argv[1],"M")==0 || strcmp(argv[1],"m")==0) { + test_type = TEST_MOD; + /* Demod test */ + } else if(strcmp(argv[1],"D")==0 || strcmp(argv[1],"d")==0) { + test_type = TEST_DEMOD; + } else { + printf("Must specify mod or demod test with M or D\n"); + exit(1); + } + /* Extract parameters */ + Fs = atoi(argv[2]); + Rs = atoi(argv[3]); + + /* Open files */ + fin = fopen(argv[4],"r"); + fout = fopen(argv[5],"w"); + + if(fin == NULL || fout == NULL){ + printf("Couldn't open test vector files\n"); + exit(1); + } + /* Init modem probing */ + modem_probe_init("fmfsk",argv[6]); + + } + + srand(1); + + /* set up FSK */ + fmfsk = fmfsk_create(Fs,Rs); + /* Modulate! */ + if(test_type == TEST_MOD || test_type == TEST_SELF_FULL){ + /* Generate random bits for self test */ + if(test_type == TEST_SELF_FULL){ + bitbufsize = ST_BITS; + bitbuf = (uint8_t*) malloc(sizeof(uint8_t)*ST_BITS); + for(i=0; i<ST_BITS; i++){ + /* Generate a randomish bit */ + bitbuf[i] = (uint8_t)(rand()&0x01); + } + } else { /* Load bits from a file */ + /* Figure out how many bits are in the input file */ + fseek(fin, 0L, SEEK_END); + bitbufsize = ftell(fin); + fseek(fin, 0L, SEEK_SET); + bitbuf = malloc(sizeof(uint8_t)*bitbufsize); + i = 0; + /* Read in some bits */ + bitbufp = bitbuf; + while( fread(bitbufp,sizeof(uint8_t),fmfsk->nbit,fin) == fmfsk->nbit){ + i++; + bitbufp+=fmfsk->nbit; + /* Make sure we don't break the buffer */ + if(i*fmfsk->nbit > bitbufsize){ + bitbuf = realloc(bitbuf,sizeof(uint8_t)*(bitbufsize+fmfsk->nbit)); + bitbufsize += fmfsk->nbit; + } + } + } + /* Allocate modulation buffer */ + modbuf = (float*)malloc(sizeof(float)*(bitbufsize/fmfsk->nbit)*fmfsk->N*4); + modbufsize = (bitbufsize/fmfsk->nbit)*fmfsk->N; + /* Do the modulation */ + modbufp = modbuf; + bitbufp = bitbuf; + while( bitbufp < bitbuf+bitbufsize){ + fmfsk_mod(fmfsk, modbufp, bitbufp); + modbufp += fmfsk->N; + bitbufp += fmfsk->nbit; + } + /* For a mod-only test, write out the result */ + if(test_type == TEST_MOD){ + fwrite(modbuf,sizeof(float),modbufsize,fout); + free(modbuf); + } + /* Free bit buffer */ + free(bitbuf); + } + + /* Add channel imp here */ + + + /* Now test the demod */ + if(test_type == TEST_DEMOD || test_type == TEST_SELF_FULL){ + free(modbuf); + modbuf = malloc(sizeof(float)*(fmfsk->N+fmfsk->Ts*2)); + bitbuf = malloc(sizeof(uint8_t)*fmfsk->nbit); + /* Demod-only test */ + if(test_type == TEST_DEMOD){ + + //fprintf(stderr,"%d\n",(fmfsk->N+fmfsk->Ts*2)); + while( fread(modbuf,sizeof(float),fmfsk_nin(fmfsk),fin) == fmfsk_nin(fmfsk) ){ + fmfsk_demod(fmfsk,bitbuf,modbuf); + fwrite(bitbuf,sizeof(uint8_t),fmfsk->nbit,fout); + } + } + /* Demod after channel imp. and mod */ + else{ + bitbufp = bitbuf; + modbufp = modbuf; + while( modbufp < modbuf + modbufsize){ + fmfsk_demod(fmfsk,bitbuf,modbuf); + modbufp += fmfsk_nin(fmfsk); + } + } + free(bitbuf); + } + + modem_probe_close(); + if(test_type == TEST_DEMOD || test_type == TEST_MOD){ + fclose(fin); + fclose(fout); + } + fmfsk_destroy(fmfsk); + exit(0); +} + diff --git a/unittest/tfreedv_2400A_rawdata.c b/unittest/tfreedv_2400A_rawdata.c new file mode 100644 index 0000000..ed65501 --- /dev/null +++ b/unittest/tfreedv_2400A_rawdata.c @@ -0,0 +1,114 @@ +/*---------------------------------------------------------------------------*\ + + FILE........: tfreedv_2400A_rawdata.c + AUTHOR......: Jeroen Vreeken + DATE CREATED: 24 May 2020 + + FreeDV 2400A rawdata test. + +\*---------------------------------------------------------------------------*/ + +/* + Copyright (C) 2020 Jeroen Vreeken <[email protected]> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, see <http://www.gnu.org/licenses/>. +*/ + +#include <stdio.h> +#include "freedv_api.h" +#include "assert.h" + +int main(int argc, char **argv) +{ + struct freedv *f; + int i; + + printf("freedv_api tests for mode 2400A\n"); + + printf("freedv_open(FREEDV_MODE_2400A) "); + f = freedv_open(FREEDV_MODE_2400A); + assert(f != NULL); + printf("Passed\n"); + + printf("freedv_get_mode() "); + int mode = freedv_get_mode(f); + assert(mode == FREEDV_MODE_2400A); + printf("Passed\n"); + + printf("freedv_get_n_max_modem_samples() "); + int max_samples = freedv_get_n_max_modem_samples(f); + assert(max_samples == 2040); + printf("%d Passed\n", max_samples); + + printf("freedv_get_n_nom_modem_samples() "); + int nom_samples = freedv_get_n_nom_modem_samples(f); + assert(nom_samples == 2000); + printf("%d Passed\n", nom_samples); + + printf("freedv_get_n_speech_samples() "); + int speech_samples = freedv_get_n_speech_samples(f); + assert(speech_samples == 320); + printf("%d Passed\n", speech_samples); + + printf("freedv_get_n_bits_per_codec_frame() "); + int codec_bits = freedv_get_bits_per_codec_frame(f); + assert(codec_bits == 52); + printf("%d Passed\n", codec_bits); + + printf("freedv_get_n_bits_per_modem_frame() "); + int frame_bits = freedv_get_bits_per_modem_frame(f); + assert(frame_bits == 52); + printf("%d Passed\n", frame_bits); + + printf("freedv_rawdatatx()/freedv_rawdatarx() "); + int frames = 0; + int fails = 0; + { + short mod[nom_samples * 10]; + /* Note: A codec frame is only 6.5 bytes! + so the seventh byte will be half empty! + */ + unsigned char payload[7] = { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x70 }; + for (i = 0; i < 10; i ++) { + freedv_rawdatatx(f, mod + i * nom_samples, payload); + } + int nin = 0; + for (i = 0; i < nom_samples * 9; i += nin) { + nin = freedv_nin(f); + unsigned char payload_rx[7] = {0}; + int r = freedv_rawdatarx(f, payload_rx, mod + i); + if (r) { + int b; + for (b = 0; b < 7; b++) { + if (payload[b] != payload_rx[b]) { + printf("Received codec bits 0x%02x do not match expected 0x%02x\n", payload_rx[b], payload[b]); + fails++; + } + } + frames++; + } + } + } + if (!frames) { + printf("Did not decode any frames successfully\n"); + goto fail; + } + + printf("Tests passed\n"); + return 0; +fail: + printf("Test failed\n"); + return 1; +} diff --git a/unittest/tfreedv_2400B_rawdata.c b/unittest/tfreedv_2400B_rawdata.c new file mode 100644 index 0000000..888ba83 --- /dev/null +++ b/unittest/tfreedv_2400B_rawdata.c @@ -0,0 +1,114 @@ +/*---------------------------------------------------------------------------*\ + + FILE........: tfreedv_2400B_rawdata.c + AUTHOR......: Jeroen Vreeken + DATE CREATED: 24 May 2020 + + FreeDV 2400B rawdata test. + +\*---------------------------------------------------------------------------*/ + +/* + Copyright (C) 2020 Jeroen Vreeken <[email protected]> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, see <http://www.gnu.org/licenses/>. +*/ + +#include <stdio.h> +#include "freedv_api.h" +#include "assert.h" + +int main(int argc, char **argv) +{ + struct freedv *f; + int i; + + printf("freedv_api tests for mode 2400B\n"); + + printf("freedv_open(FREEDV_MODE_2400B) "); + f = freedv_open(FREEDV_MODE_2400B); + assert(f != NULL); + printf("Passed\n"); + + printf("freedv_get_mode() "); + int mode = freedv_get_mode(f); + assert(mode == FREEDV_MODE_2400B); + printf("Passed\n"); + + printf("freedv_get_n_max_modem_samples() "); + int max_samples = freedv_get_n_max_modem_samples(f); + assert(max_samples == 1930); + printf("%d Passed\n", max_samples); + + printf("freedv_get_n_nom_modem_samples() "); + int nom_samples = freedv_get_n_nom_modem_samples(f); + assert(nom_samples == 1920); + printf("%d Passed\n", nom_samples); + + printf("freedv_get_n_speech_samples() "); + int speech_samples = freedv_get_n_speech_samples(f); + assert(speech_samples == 320); + printf("%d Passed\n", speech_samples); + + printf("freedv_get_n_bits_per_codec_frame() "); + int codec_bits = freedv_get_bits_per_codec_frame(f); + assert(codec_bits == 52); + printf("%d Passed\n", codec_bits); + + printf("freedv_get_n_bits_per_modem_frame() "); + int frame_bits = freedv_get_bits_per_modem_frame(f); + assert(frame_bits == 52); + printf("%d Passed\n", frame_bits); + + printf("freedv_rawdatatx()/freedv_rawdatarx() "); + int frames = 0; + int fails = 0; + { + short mod[nom_samples * 10]; + /* Note: A codec frame is only 6.5 bytes! + so the seventh byte will be half empty! + */ + unsigned char payload[7] = { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x70 }; + for (i = 0; i < 10; i ++) { + freedv_rawdatatx(f, mod + i * nom_samples, payload); + } + int nin = 0; + for (i = 0; i < nom_samples * 9; i += nin) { + nin = freedv_nin(f); + unsigned char payload_rx[7] = {0}; + int r = freedv_rawdatarx(f, payload_rx, mod + i); + if (r) { + int b; + for (b = 0; b < 7; b++) { + if (payload[b] != payload_rx[b]) { + printf("Received codec bits 0x%02x do not match expected 0x%02x\n", payload_rx[b], payload[b]); + fails++; + } + } + frames++; + } + } + } + if (!frames) { + printf("Did not decode any frames successfully\n"); + goto fail; + } + + printf("Tests passed\n"); + return 0; +fail: + printf("Test failed\n"); + return 1; +} diff --git a/unittest/tfreedv_800XA_rawdata.c b/unittest/tfreedv_800XA_rawdata.c new file mode 100644 index 0000000..2cf9bb7 --- /dev/null +++ b/unittest/tfreedv_800XA_rawdata.c @@ -0,0 +1,147 @@ +/*---------------------------------------------------------------------------*\ + + FILE........: tfreedv_800XA_rawdata.c + AUTHOR......: Jeroen Vreeken + DATE CREATED: 24 May 2020 + + FreeDV 800XA rawdata test. + +\*---------------------------------------------------------------------------*/ + +/* + Copyright (C) 2020 Jeroen Vreeken <[email protected]> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, see <http://www.gnu.org/licenses/>. +*/ + +#include <stdio.h> +#include "freedv_api.h" +#include "assert.h" + +int main(int argc, char **argv) +{ + struct freedv *f; + int i; + + printf("freedv_api tests for mode 800XA\n"); + + printf("freedv_open(FREEDV_MODE_800XA) "); + f = freedv_open(FREEDV_MODE_800XA); + assert(f != NULL); + printf("Passed\n"); + + printf("freedv_get_mode() "); + int mode = freedv_get_mode(f); + assert(mode == FREEDV_MODE_800XA); + printf("Passed\n"); + + printf("freedv_get_n_max_modem_samples() "); + int max_samples = freedv_get_n_max_modem_samples(f); + assert(max_samples == 660); + printf("%d Passed\n", max_samples); + + printf("freedv_get_n_nom_modem_samples() "); + int nom_samples = freedv_get_n_nom_modem_samples(f); + assert(nom_samples == 640); + printf("%d Passed\n", nom_samples); + + printf("freedv_get_n_speech_samples() "); + int speech_samples = freedv_get_n_speech_samples(f); + assert(speech_samples == 640); + printf("%d Passed\n", speech_samples); + + printf("freedv_get_n_bits_per_codec_frame() "); + int codec_bits = freedv_get_bits_per_codec_frame(f); + assert(codec_bits == 28); + printf("%d Passed\n", codec_bits); + + printf("freedv_get_n_bits_per_modem_frame() "); + int frame_bits = freedv_get_bits_per_modem_frame(f); + assert(frame_bits == 56); + printf("%d Passed\n", frame_bits); + + /* Note: A codec frame is only 3.5 bytes! + so the fourth and eight bytes will be half empty! + */ + unsigned char payload[8] = { 0x12, 0x34, 0x56, 0x70, 0x89, 0xab, 0xcd, 0xe0 }; + unsigned char payload_tx[7] = { 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde }; + + printf("freedv_codec_frames_from_rawdata() "); + unsigned char codec_frames[8] = { 0 }; + freedv_codec_frames_from_rawdata(f, codec_frames, payload_tx); + int fails = 0; + for (i = 0; i < 8; i++) { + if (codec_frames[i] != payload[i]) { + printf("byte %d: 0x%02x does not match expected 0x%02x\n", i, codec_frames[i], payload[i]); + fails++; + } + } + if (fails) + goto fail; + printf("Passed\n"); + + printf("freedv_rawdata_from_codec_frames() "); + unsigned char rawdata[7] = { 0 }; + freedv_rawdata_from_codec_frames(f, rawdata, payload); + fails = 0; + for (i = 0; i < 7; i++) { + if (rawdata[i] != payload_tx[i]) { + printf("byte %d: 0x%02x does not match expected 0x%02x\n", i, rawdata[i], payload_tx[i]); + fails++; + } + } + if (fails) + goto fail; + printf("Passed\n"); + + printf("freedv_rawdatatx()/freedv_rawdatarx() "); + int frames = 0; + fails = 0; + { + short mod[nom_samples * 10]; + for (i = 0; i < 10; i ++) { + freedv_rawdatatx(f, mod + i * nom_samples, payload_tx); + } + int nin = 0; + for (i = 0; i < nom_samples * 9; i += nin) { + nin = freedv_nin(f); + unsigned char payload_rx[8] = {0}; + int r = freedv_rawdatarx(f, payload_rx, mod + i); + if (r == 7) { + int b; + for (b = 0; b < 7; b++) { + if (payload_tx[b] != payload_rx[b]) { + printf("Received codec bits 0x%02x do not match expected 0x%02x\n", payload_rx[b], payload_tx[b]); + fails++; + } + } + frames++; + } + } + } + if (!frames) { + printf("Did not decode any frames successfully\n"); + goto fail; + } + if (fails) + goto fail; + printf("Passed\n"); + + printf("Tests passed\n"); + return 0; +fail: + printf("Test failed\n"); + return 1; +} diff --git a/unittest/tfreedv_data_channel.c b/unittest/tfreedv_data_channel.c new file mode 100644 index 0000000..ef713eb --- /dev/null +++ b/unittest/tfreedv_data_channel.c @@ -0,0 +1,315 @@ +/*---------------------------------------------------------------------------*\ + + FILE........: tfreedv_data_channel + AUTHOR......: Jeroen Vreeken + DATE CREATED: May 3 2016 + + Tests for the data channel code. + Data channel frame behaviour is tested with test vectors. + +\*---------------------------------------------------------------------------*/ + +/* + Copyright (C) 2016 Jeroen Vreeken + + All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 2.1, as + published by the Free Software Foundation. This program is + distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, see <http://www.gnu.org/licenses/>. +*/ + +#include "freedv_data_channel.h" + +#include <stdio.h> +#include <string.h> + +unsigned char test_header[] = { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66 }; +unsigned char bcast_header[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + + +struct testvec { + char *testname; + + unsigned char *data; + size_t data_size; + + size_t frame_size; + + unsigned char *frame_data; + size_t frame_data_size; + + unsigned char *flags; +} testvec[] = { + { + "Regular packet, does not match header and no broadcast", + (unsigned char[]){ + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + 0x11, 0x12 + }, + 0x12, + 8, + (unsigned char[]){ + 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x01, 0x02, + 0x03, 0x04, 0x05, 0x06, 0x0d, 0x0e, 0x0f, 0x10, + 0x11, 0x12, 0x47, 0x6e + }, + 0x14, + (unsigned char[]){ 0x00, 0x00, 0x04 }, + }, + { + "Header", + NULL, + 0, + 8, + (unsigned char[]){ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x5a, 0x60 }, + 0x08, + (unsigned char[]){ 0x08 }, + }, + { + "Broadcast packet", + (unsigned char[]){ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x11, 0x22, + 0x33, 0x44, 0x55, 0x66, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + 0x11 + }, + 0x19, + 8, + (unsigned char[]){ + 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + 0x11, 0x3c, 0xbe + }, + 0x0f, + (unsigned char[]){ 0xc0, 0x07 }, + }, + { + "Broadcast packet, header does not match", + (unsigned char[]){ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xaa, 0x22, + 0xbb, 0xcc, 0xdd, 0xee, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + 0x11 + }, + 0x19, + 8, + (unsigned char[]){ + 0xaa, 0x22, + 0xbb, 0xcc, 0xdd, 0xee, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + 0x11, 0x1a, 0x68 + }, + 0x15, + (unsigned char[]){ 0x40, 0x00, 0x05 }, + }, + { + "Header 6 bytes", + NULL, + 0, + 6, + (unsigned char[]){ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66 }, + 0x06, + (unsigned char[]){ 0x2f }, + }, + { + "Broadcast packet (6 byte frames)", + (unsigned char[]){ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x11, 0x22, + 0x33, 0x44, 0x55, 0x66, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + 0x11 + }, + 0x19, + 6, + (unsigned char[]){ + 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + 0x11, 0x3c, 0xbe + }, + 0x0f, + (unsigned char[]){ 0xc0, 0x00, 0x03 }, + }, + { + "Broadcast packet, header does not match (6 byte frames)", + (unsigned char[]){ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xaa, 0x22, + 0xbb, 0xcc, 0xdd, 0xee, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + 0x11 + }, + 0x19, + 6, + (unsigned char[]){ + 0xaa, 0x22, + 0xbb, 0xcc, 0xdd, 0xee, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + 0x11, 0x1a, 0x68 + }, + 0x15, + (unsigned char[]){ 0x40, 0x00, 0x00, 0x03 }, + }, +}; + + +static int ret = 0; +static int vector = 0; +static size_t frame_data_pos = 0; +static int rx_done = 0; + +void *tx_cb_arg = (void*)0xaa55; +void *rx_cb_arg = (void*)0xbb44; + +void tfreedv_data_callback_tx(void *arg, unsigned char *packet, size_t *size) +{ + if (tx_cb_arg != arg) { + ret++; + printf("FAIL: %s called with wrong argument value\n", __func__); + } + printf("--------------------------------------\n"); + printf("TX callback called for test %zd bytes data for test %d:\n'%s'\n", + testvec[vector].data_size, vector, + testvec[vector].testname); + + memcpy(packet, testvec[vector].data, testvec[vector].data_size); + *size = testvec[vector].data_size; + + return; +} + +void tfreedv_data_callback_rx(void *arg, unsigned char *packet, size_t size) +{ + if (rx_cb_arg != arg) { + ret++; + printf("FAIL: %s called with wrong argument value\n", __func__); + } + printf("RX callback called with %zd bytes\n", size); + + if (testvec[vector].data_size) { + size_t data_size = testvec[vector].data_size; + if (data_size != size) { + printf("FAIL: Received size does not match test vector: %zd != %zd\n", + size, data_size); + ret++; + } else { + size_t i; + for (i = 0; i < data_size; i++) { + if (packet[i] != testvec[vector].data[i]) { + printf("FAIL: byte %zd does not match 0x%02x != 0x%02x\n", + i, packet[i], testvec[vector].data[i]); + ret++; + } + } + } + } else { + if (size != 12) { + printf("FAIL: Received header is not 12 bytes: %zd\n", size); + ret++; + } else { + if (memcmp(packet, bcast_header, 6)) { + printf("FAIL: Header is not a broadcast!\n"); + ret++; + } + if (memcmp(packet+6, test_header, 6)) { + printf("FAIL: Header does not match!\n"); + ret++; + } + } + } + + rx_done = 1; +} + +int main(int argc, char **argv) +{ + struct freedv_data_channel *fdc; + + fdc = freedv_data_channel_create(); + + freedv_data_set_header(fdc, test_header); + freedv_data_set_cb_tx(fdc, tfreedv_data_callback_tx, tx_cb_arg); + freedv_data_set_cb_rx(fdc, tfreedv_data_callback_rx, rx_cb_arg); + + while (vector < sizeof(testvec)/sizeof(struct testvec)) { + size_t frame_size = testvec[vector].frame_size; + unsigned char frame[frame_size]; + int from, bcast, crc, end; + int i; + size_t check_size; + unsigned char flags; + int nr_frames; + + freedv_data_channel_tx_frame(fdc, frame, frame_size, &from, &bcast, &crc, &end); + + check_size = frame_size; + if (frame_data_pos + check_size > testvec[vector].frame_data_size) + check_size = testvec[vector].frame_data_size - frame_data_pos; + + flags = from * 0x80 + bcast * 0x40 + crc * 0x20 + end; + printf("0x%02x:", flags); + for (i = 0; i < check_size; i++) { + if (frame[i] != testvec[vector].frame_data[frame_data_pos + i]) { + printf(" [0x%02x!=0x%02x]", + frame[i], testvec[vector].frame_data[frame_data_pos + i]); + ret++; + } else { + printf(" 0x%02x", frame[i]); + } + } + printf("\n"); + + if (flags != testvec[vector].flags[frame_data_pos / frame_size]) { + printf("FAIL: Flags byte does not match 0x%02x != 0x%02x\n", + flags, testvec[vector].flags[frame_data_pos / frame_size]); + ret++; + } + + freedv_data_channel_rx_frame(fdc, frame, frame_size, from, bcast, crc, end); + + frame_data_pos += frame_size; + + nr_frames = freedv_data_get_n_tx_frames(fdc, frame_size); + + if (frame_data_pos >= testvec[vector].frame_data_size) { + if (nr_frames) { + printf("FAIL: nr_frames is not zero: %d\n", nr_frames); + ret++; + } + vector++; + frame_data_pos = 0; + if (!rx_done) { + printf("FAIL: RX callback not executed\n"); + ret++; + } + rx_done = 0; + } else { + int vec_frames = (testvec[vector].frame_data_size - frame_data_pos); + vec_frames /= frame_size; + vec_frames++; + if (nr_frames != vec_frames) { + printf("FAIL: nr_frames != vec_frames: %d != %d\n", nr_frames, vec_frames); + ret++; + } + } + } + + freedv_data_channel_destroy(fdc); + + printf("--------------------------------------\n"); + printf("tfreedv_data_channel test result: "); + if (ret) { + printf("Failed %d\n", ret); + } else { + printf("Passed\n"); + } + + return ret; +} diff --git a/unittest/tfsk.c b/unittest/tfsk.c new file mode 100644 index 0000000..35aa970 --- /dev/null +++ b/unittest/tfsk.c @@ -0,0 +1,232 @@ +/*---------------------------------------------------------------------------*\ + + FILE........: tfsk.c + AUTHOR......: Brady O'Brien + DATE CREATED: 20 January 2016 + + C test driver for fsk_mod and fsk_demod in fsk.c. Reads a file with input + bits/rf and spits out modulated/demoduladed samples and a dump of internal + state. To run unit test, see octave/tfsk.m + +\*---------------------------------------------------------------------------*/ + +/* + Copyright (C) 2016 David Rowe + + All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 2.1, as + published by the Free Software Foundation. This program is + distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, see <http://www.gnu.org/licenses/>. +*/ + + +//#define MODEMPROBE_ENABLE + +#include "modem_probe.h" +#include <stdio.h> + +/* Note: This is a dirty hack to force fsk.c to compile with modem probing enabled */ +#include "fsk.c" + +#define ST_BITS 10000 +#define ST_FS 8000 +#define ST_RS 100 +#define ST_F1 1200 +#define ST_Fs 400 +#define ST_EBNO 8 +#define ST_M 2 + +#define TEST_SELF_FULL 1 /* No-arg self test */ +#define TEST_MOD 2 /* Test modulator using in and out file */ +#define TEST_MOD_H 3 /* Test modulator using in and out file */ +#define TEST_DEMOD 4 /* Test demodulator using in and out file */ +#define TEST_DEMOD_H 5 /* Test demodulator using in and out file */ + + +int main(int argc,char *argv[]){ + struct FSK *fsk; + int Fs,Rs,f1,fs,M, lock_nin; + FILE *fin,*fout; + + uint8_t *bitbuf = NULL; + float *modbuf = NULL; + uint8_t *bitbufp; + float *modbufp; + + size_t bitbufsize = 0; + size_t modbufsize = 0; + + int test_type; + + int i; + + fin = NULL; + fout = NULL; + + /* Set up full self-test */ + if(argc == 1){ + test_type = TEST_SELF_FULL; + modem_probe_init("fsk2","fsk2_tfsk_log.txt"); + Fs = ST_FS; + Rs = ST_RS; + f1 = ST_F1; + fs = ST_Fs; + M = ST_M; + lock_nin = 0; + } else if (argc<10){ + /* Not running any test */ + printf("Usage: %s [(M|D|DX) Mode TXFreq1 TXFreqSpace SampleRate SymbolRate lock_nin InputFile OutputFile OctaveLogFile]\n",argv[0]); + exit(1); + } else { + /* Running stim-drivin test */ + /* Mod test */ + if(strcmp(argv[1],"MX")==0){ + test_type = TEST_MOD_H; + } else if(strcmp(argv[1],"M")==0 || strcmp(argv[1],"m")==0) { + test_type = TEST_MOD; + /* Demod test */ + } else if(strcmp(argv[1],"DX")==0) { + test_type = TEST_DEMOD_H; + } else if(strcmp(argv[1],"D")==0 || strcmp(argv[1],"d")==0) { + test_type = TEST_DEMOD; + } else { + printf("Must specify mod or demod test with M or D\n"); + exit(1); + } + /* Extract parameters */ + M = atoi(argv[2]); + f1 = atoi(argv[3]); + fs = atoi(argv[4]); + Fs = atoi(argv[5]); + Rs = atoi(argv[6]); + lock_nin = atoi(argv[7]); + + /* Open files */ + fin = fopen(argv[8],"r"); + fout = fopen(argv[9],"w"); + + if(fin == NULL || fout == NULL){ + printf("Couldn't open test vector files\n"); + exit(1); + } + /* Init modem probing */ + modem_probe_init("fsk",argv[10]); + + } + + srand(1); + /* set up FSK */ + if(test_type == TEST_DEMOD_H || test_type == TEST_MOD_H){ + fsk = fsk_create_hbr(Fs,Rs,M,10,FSK_DEFAULT_NSYM,f1,fs); + if(test_type == TEST_DEMOD_H) + test_type = TEST_DEMOD; + else + test_type = TEST_MOD; + }else{ + fsk = fsk_create(Fs,Rs,M,f1,fs); + } + fsk_set_freq_est_limits(fsk, 300, 2800); + fsk->lock_nin = lock_nin; + + /* Modulate! */ + if(test_type == TEST_MOD || test_type == TEST_SELF_FULL){ + /* Generate random bits for self test */ + if(test_type == TEST_SELF_FULL){ + bitbufsize = ST_BITS; + bitbuf = (uint8_t*) malloc(sizeof(uint8_t)*ST_BITS); + for(i=0; i<ST_BITS; i++){ + /* Generate a randomish bit */ + bitbuf[i] = (uint8_t)(rand()&0x01); + } + } else { /* Load bits from a file */ + /* Figure out how many bits are in the input file */ + fseek(fin, 0L, SEEK_END); + bitbufsize = ftell(fin); + fseek(fin, 0L, SEEK_SET); + bitbuf = malloc(sizeof(uint8_t)*bitbufsize); + i = 0; + /* Read in some bits */ + bitbufp = bitbuf; + while( fread(bitbufp,sizeof(uint8_t),fsk->Nbits,fin) == fsk->Nbits){ + i++; + bitbufp+=fsk->Nbits; + /* Make sure we don't break the buffer */ + if(i*fsk->Nbits > bitbufsize){ + bitbuf = realloc(bitbuf,sizeof(uint8_t)*(bitbufsize+fsk->Nbits)); + bitbufsize += fsk->Nbits; + } + } + } + /* Allocate modulation buffer */ + modbuf = (float*)malloc(sizeof(float)*(bitbufsize/fsk->Nbits)*fsk->N*4); + modbufsize = (bitbufsize/fsk->Nbits)*fsk->N; + /* Do the modulation */ + modbufp = modbuf; + bitbufp = bitbuf; + while( bitbufp < bitbuf+bitbufsize){ + fsk_mod(fsk, modbufp, bitbufp, fsk->Nbits); + modbufp += fsk->N; + bitbufp += fsk->Nbits; + } + /* For a mod-only test, write out the result */ + if(test_type == TEST_MOD){ + fwrite(modbuf,sizeof(float),modbufsize,fout); + free(modbuf); + } + /* Free bit buffer */ + free(bitbuf); + } + + /* Now test the demod */ + if(test_type == TEST_DEMOD || test_type == TEST_SELF_FULL){ + free(modbuf); + modbuf = malloc(sizeof(float)*(fsk->N+fsk->Ts*2)); + bitbuf = malloc(sizeof(uint8_t)*fsk->Nbits); + /* Demod-only test */ + if(test_type == TEST_DEMOD){ + while( fread(modbuf,sizeof(float),fsk_nin(fsk),fin) == fsk_nin(fsk) ){ + int n = fsk_nin(fsk); + COMP modbuf_comp[n]; + for(i=0; i<n; i++) { + modbuf_comp[i].real = modbuf[i]; + modbuf_comp[i].imag = 0.0; + } + fsk_demod(fsk,bitbuf,modbuf_comp); + fwrite(bitbuf,sizeof(uint8_t),fsk->Nbits,fout); + } + } + /* Demod after channel imp. and mod */ + else{ + bitbufp = bitbuf; + modbufp = modbuf; + while( modbufp < modbuf + modbufsize){ + int n = fsk_nin(fsk); + COMP modbuf_comp[n]; + for(i=0; i<n; i++) { + modbuf_comp[i].real = modbuf[i]; + modbuf_comp[i].imag = 0.0; + } + fsk_demod(fsk,bitbuf,modbuf_comp); + modbufp += fsk_nin(fsk); + } + } + free(bitbuf); + } + + modem_probe_close(); + if(test_type == TEST_DEMOD || test_type == TEST_MOD){ + fclose(fin); + fclose(fout); + } + fsk_destroy(fsk); + exit(0); +} + diff --git a/unittest/tfsk_llr.c b/unittest/tfsk_llr.c new file mode 100644 index 0000000..c32c592 --- /dev/null +++ b/unittest/tfsk_llr.c @@ -0,0 +1,66 @@ +/*---------------------------------------------------------------------------*\ + + FILE........: tfsk_llr.c + AUTHOR......: David Rowe + DATE CREATED: July 2020 + + Simple test program for 4FSK LLR routines. + +\*---------------------------------------------------------------------------*/ + +#include <stdio.h> +#include <math.h> +#include "mpdecode_core.h" + +#define M 4 +#define BPS 2 +#define NSYM 5 +#define V_EST 2 +#define SNR_EST 10 + +/* Generated test vectors with: + + octave:100> init_cml('~/cml/'); + octave:101> rx_filt=[1 0 0 0; 0 1 0 0; 0 0 1 0; 0 0 0 1] + octave:102> symL = DemodFSK(rx_filt,10,1); -Somap(symL) +*/ + +/* one col per symbol: + 0 1 2 3 4 */ + float rx_filt[] = { + 1.0, 0.0, 0.0, 0.0, 1.0, /* filter 0 */ + 0.0, 1.0, 0.0, 0.0, 0.0, /* filter 1 */ + 0.0, 0.0, 1.0, 0.0, 0.0, /* filter 2 */ + 0.0, 0.0, 0.0, 1.0, 0.0 /* filter 3 */ +}; + +float llrs_target[] = { + 7.3252, 7.3252, /* bit 0, 1 */ + 7.3252, -7.3252, /* 2, 3, ... */ + -7.3252, 7.3252, + -7.3252, -7.3252, + 7.3252, 7.3252 +}; + +int main(void) { + float llrs[BPS*NSYM] = {0}; + + fsk_rx_filt_to_llrs(llrs, rx_filt, V_EST, SNR_EST, M, NSYM); + + float error = 0.0; + for(int i=0; i<NSYM*BPS; i++) { + fprintf(stderr,"% f\n",llrs[i]); + error += pow(llrs[i]-llrs_target[i],2.0); + } + + if (error < 1E-3) { + fprintf(stderr, "PASS\n"); + return 0; + } + else { + fprintf(stderr, "FAIL\n"); + return 1; + } +} + + diff --git a/unittest/thash.c b/unittest/thash.c new file mode 100644 index 0000000..6610e8f --- /dev/null +++ b/unittest/thash.c @@ -0,0 +1,19 @@ +/*---------------------------------------------------------------------------*\ + + FILE........: thash.c + AUTHOR......: David Rowe + DATE CREATED: July 2020 + + Simple test program for freeDV API get hash function + +\*---------------------------------------------------------------------------*/ + +#include <stdio.h> +#include "freedv_api.h" + +int main(void) { + printf("%s\n", freedv_get_hash()); + return 0; +} + + diff --git a/unittest/tnc1_high_snr.sh b/unittest/tnc1_high_snr.sh new file mode 100755 index 0000000..e9a028a --- /dev/null +++ b/unittest/tnc1_high_snr.sh @@ -0,0 +1,103 @@ +#!/usr/bin/env bash +# tnc1_high_snr.sh +# +# HF TNC use case test 1 +# + Send unidirectional frames at high SNR over an alsa loopback virtual sound card +# + Using the sound card can take some time, so implemented as a service to run automatically in background + +NAME=tnc1 +CODEC2_PATH=${HOME}/codec2 +PIDFILE_TX=/tmp/${NAME}_tx.pid +PIDFILE_RX=/tmp/${NAME}_rx.pid +LOGFILE=${NAME}.log +PATH=${CODEC2_PATH}/build_linux/src:${PATH} +DELAY="${DELAY:-500}" +MAX_RUN_TIME=3600 +# we use single frame bursts, so BURSTS==FRAME +BURSTS=$2 + +function run_tx { + bursts=$1 + delay=$2 + freedv_data_raw_tx DATAC0 /dev/zero - --testframes ${bursts} --bursts ${bursts} --delay ${delay} | aplay --device="plughw:CARD=CHAT2,DEV=1" -f S16_LE +} + +function start_rx_background { + arecord --device="plughw:CARD=CHAT2,DEV=0" -f S16_LE -d $MAX_RUN_TIME | freedv_data_raw_rx DATAC0 - /dev/null --framesperburst 1 --vv --testframes & + echo $!>${PIDFILE_RX} +} + +function stop_service { + echo "service stopping - bye!" 1>&2 + if [ -e ${PIDFILE_RX} ]; then + pid_rx=$(cat ${PIDFILE_RX}) + rm ${PIDFILE_RX} + kill ${pid_rx} + fi + + if [ -e ${PIDFILE_TX} ]; then + pid_tx=$(cat ${PIDFILE_TX}) + rm ${PIDFILE_TX} + kill ${pid_tx} + fi +} + +function check_running { + if [ -e ${PIDFILE_TX} ]; then + echo "Tx already running... pid: ${PIDFILE_TX}" + exit 1 + fi + if [ -e ${PIDFILE_RX} ]; then + echo "Rx already running... pid: ${PIDFILE_RX}" + exit 1 + fi +} + +function check_alsa_loopback { + lsmod | grep snd_aloop >> /dev/null + if [ $? -eq 1 ]; then + echo "ALSA loopback device not present. Please install with:" + echo + echo " sudo modprobe snd-aloop index=1,2 enable=1,1 pcm_substreams=1,1 id=CHAT1,CHAT2" + exit 1 + fi +} + +case "$1" in + start) + check_running + check_alsa_loopback + ( start_rx_background && sleep 1 && run_tx ${BURSTS} ${DELAY} && stop_service ) 2>${LOGFILE} & + echo $!>${PIDFILE_TX} + echo "Results in ${LOGFILE}" + ;; + start_verbose) + set -x + check_running + check_alsa_loopback + # Show all outputs and log output to stderr rather than logfile + verbose=1 + start_rx_background && sleep 1 && run_tx ${BURSTS} ${DELAY} && stop_service + ;; + stop) + stop_service + ;; + restart) + $0 stop + $0 start + ;; + status) + if [ -e ${PIDFILE_TX} ]; then + echo ${NAME} is running, pid=`cat ${PIDFILE_TX}` + else + echo ${NAME} is NOT running + exit 1 + fi + ;; + *) + echo "Usage: $0 {start|start_verbose|stop|restart|status} NumFrames" + echo " $0 start_verbose 1 - 1 frame packet verbose, logs to stderr" + echo " $0 start 5 - 5 frames, run as service in background, logs sent to ${LOGFILE}" +esac + +exit 0 diff --git a/unittest/tnc4_high_snr_ping.sh b/unittest/tnc4_high_snr_ping.sh new file mode 100755 index 0000000..d9bee07 --- /dev/null +++ b/unittest/tnc4_high_snr_ping.sh @@ -0,0 +1,114 @@ +#!/usr/bin/env bash +# tnc4_high_snr_ping.sh +# +# HF TNC use case test 4 +# + Terminal 1 sends frames to Terminal 2 +# + Terminal 2 receives frames and re-transmits them back to Terminal 1 +# + Terminal 1 and 2 count number of frames received (see logfiles) +# + The modem samples are sent over virtual sound cards, this runs in real time, which +# can be slow for tests involving many packets. Therefore this test is implemented as a +# service script. + +NAME=tnc4 +CODEC2_PATH=${HOME}/codec2 +PIDFILE_TX1=/tmp/${NAME}_tx1.pid +PIDFILE_RX1=/tmp/${NAME}_rx1.pid +PIDFILE_RX2=/tmp/${NAME}_rx2.pid +LOGFILE1=${NAME}_1.log +LOGFILE2=${NAME}_2.log +PATH=${CODEC2_PATH}/build_linux/src:${PATH} +DELAY="${DELAY:-500}" +MAX_RUN_TIME=3600 +# in this version we use single frame bursts, so BURSTS==FRAMES +BURSTS=$2 +MODE=DATAC0 + +function tx1 { + freedv_data_raw_tx ${MODE} /dev/zero - --testframes ${BURSTS} --bursts ${BURSTS} --delay ${DELAY} | aplay --device="plughw:CARD=CHAT2,DEV=1" -f S16_LE +} + +function rx2_background { + # re-transmit any frames we receive + ( arecord --device="plughw:CARD=CHAT2,DEV=0" -f S16_LE -d $MAX_RUN_TIME | \ + freedv_data_raw_rx ${MODE} - - --framesperburst 1 --vv --testframes | \ + freedv_data_raw_tx ${MODE} - - --delay ${DELAY} | \ + aplay --device="plughw:CARD=CHAT1,DEV=1" -f S16_LE ) 2>${LOGFILE2} & + # killing arecord kills the entire pipeline + echo $(pidof arecord)>${PIDFILE_RX2} +} + +function rx1_background { + arecord --device="plughw:CARD=CHAT1,DEV=0" -f S16_LE -d $MAX_RUN_TIME | freedv_data_raw_rx ${MODE} - /dev/null --framesperburst 1 --vv --testframes & + echo $!>${PIDFILE_RX1} +} + +function stop_process { + if [ -e ${1} ]; then + pid=$(cat ${1}) + rm ${1} + kill ${pid} + fi +} + +function stop_service { + echo "service stopping - bye!" 1>&2 + stop_process ${PIDFILE_RX1} + stop_process ${PIDFILE_RX2} + stop_process ${PIDFILE_TX1} +} + +function check_running { + if [ -e ${PIDFILE_TX1} ]; then + echo "Tx already running... pid: ${PIDFILE_TX1}" + exit 1 + fi + if [ -e ${PIDFILE_RX1} ]; then + echo "Rx1 already running... pid: ${PIDFILE_RX1}" + exit 1 + fi + if [ -e ${PIDFILE_RX2} ]; then + echo "Rx2 already running... pid: ${PIDFILE_RX2}" + exit 1 + fi +} + +function check_alsa_loopback { + lsmod | grep snd_aloop >> /dev/null + if [ $? -eq 1 ]; then + echo "ALSA loopback device not present. Please install with:" + echo + echo " sudo modprobe snd-aloop index=1,2 enable=1,1 pcm_substreams=1,1 id=CHAT1,CHAT2" + exit 1 + fi +} + +case "$1" in + start) + check_running + check_alsa_loopback + rx2_background + ( rx1_background && sleep 1 && tx1 && sleep 5 && stop_service ) 2>${LOGFILE1} & + echo $!>${PIDFILE_TX1} + echo "Results for terminal 1 in ${LOGFILE1} and terminal 2 in ${LOGFILE2}" + ;; + stop) + stop_service + ;; + restart) + $0 stop + $0 start + ;; + status) + if [ -e ${PIDFILE_TX1} ]; then + echo ${NAME} is running, pid=`cat ${PIDFILE_TX1}` + else + echo ${NAME} is NOT running + exit 1 + fi + ;; + *) + echo "Usage: $0 {start|start_verbose|stop|restart|status} NumFrames" + echo " $0 start 5 - test ping over 5 frames; logs sent to ${LOGFILE1} and ${LOGFILE2}" +esac + +exit 0 diff --git a/unittest/tnewamp1.c b/unittest/tnewamp1.c new file mode 100644 index 0000000..f9b13d2 --- /dev/null +++ b/unittest/tnewamp1.c @@ -0,0 +1,301 @@ +/*---------------------------------------------------------------------------*\ + + FILE........: tnewamp1.c + AUTHOR......: David Rowe + DATE CREATED: Jan 2017 + + Tests for the C version of the newamp1 amplitude modelling used for + 700c. This program outputs a file of Octave vectors that are loaded + and automatically tested against the Octave version of the modem by + the Octave script tnewamp1.m + +\*---------------------------------------------------------------------------*/ + +/* + Copyright (C) 2017 David Rowe + + All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 2.1, as + published by the Free Software Foundation. This program is + distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, see <http://www.gnu.org/licenses/>. +*/ + +#include "defines.h" +#include "codec2_fft.h" +#include "sine.h" +#include "nlp.h" +#include "dump.h" +#include "octave.h" +#include "newamp1.h" +#include "quantise.h" + +#define FRAMES 300 + +int main(int argc, char *argv[]) { + int Fs = 8000; + C2CONST c2const = c2const_create(Fs, N_S); + int n_samp = c2const.n_samp; + int m_pitch = c2const.m_pitch; + short buf[n_samp]; /* input/output buffer */ + float Sn[m_pitch]; /* float input speech samples */ + COMP Sw[FFT_ENC]; /* DFT of Sn[] */ + codec2_fft_cfg fft_fwd_cfg; /* fwd FFT states */ + float w[m_pitch]; /* time domain hamming window */ + float W[FFT_ENC]; /* DFT of w[] */ + MODEL model; + void *nlp_states; + codec2_fft_cfg phase_fft_fwd_cfg, phase_fft_inv_cfg; + float pitch, prev_f0; + int i,m,f,k; + + if (argc != 2) { + printf("usage: ./tnewamp1 RawFile\n"); + exit(1); + } + nlp_states = nlp_create(&c2const); + prev_f0 = 1.0/P_MAX_S; + fft_fwd_cfg = codec2_fft_alloc(FFT_ENC, 0, NULL, NULL); + make_analysis_window(&c2const,fft_fwd_cfg, w, W); + + phase_fft_fwd_cfg = codec2_fft_alloc(NEWAMP1_PHASE_NFFT, 0, NULL, NULL); + phase_fft_inv_cfg = codec2_fft_alloc(NEWAMP1_PHASE_NFFT, 1, NULL, NULL); + + for(i=0; i<m_pitch; i++) { + Sn[i] = 1.0; + } + + int K = 20; + float rate_K_sample_freqs_kHz[K]; + float model_octave[FRAMES][MAX_AMP+2]; // model params in matrix format, useful for C <-> Octave + float rate_K_surface[FRAMES][K]; // rate K vecs for each frame, form a surface that makes pretty graphs + float rate_K_surface_no_mean[FRAMES][K]; // mean removed surface + float rate_K_surface_no_mean_[FRAMES][K]; // quantised mean removed surface + float mean[FRAMES]; + float mean_[FRAMES]; + float rate_K_surface_[FRAMES][K]; // quantised rate K vecs for each frame + float interpolated_surface_[FRAMES][K]; // dec/interpolated surface + //int voicing[FRAMES]; + int voicing_[FRAMES]; + float model_octave_[FRAMES][MAX_AMP+2]; + COMP H[FRAMES][MAX_AMP]; + int indexes[FRAMES][NEWAMP1_N_INDEXES]; + float se = 0.0; + float eq[K]; + + for(k=0; k<K; k++) + eq[k] = 0.0; + + for(f=0; f<FRAMES; f++) { + for(m=0; m<MAX_AMP+2; m++) { + model_octave[f][m] = 0.0; + model_octave_[f][m] = 0.0; + } + for(m=0; m<MAX_AMP; m++) { + H[f][m].real = 0.0; + H[f][m].imag = 0.0; + } + for(k=0; k<K; k++) + interpolated_surface_[f][k] = 0.0; + voicing_[f] = 0; + } + + mel_sample_freqs_kHz(rate_K_sample_freqs_kHz, K, ftomel(200.0), ftomel(3700.0)); + + //for(int k=0; k<K; k++) + // printf("k: %d sf: %f\n", k, rate_K_sample_freqs_kHz[k]); + + FILE *fin = fopen(argv[1], "rb"); + if (fin == NULL) { + fprintf(stderr, "Problem opening hts1.raw\n"); + exit(1); + } + + int M = 4; + + for(f=0; f<FRAMES; f++) { + assert(fread(buf,sizeof(short),n_samp,fin) == n_samp); + + /* shift buffer of input samples, and insert new samples */ + + for(i=0; i<m_pitch-n_samp; i++) { + Sn[i] = Sn[i+n_samp]; + } + for(i=0; i<n_samp; i++) { + Sn[i+m_pitch-n_samp] = buf[i]; + } + + /* Estimate Sinusoidal Model Parameters ----------------------*/ + + nlp(nlp_states, Sn, n_samp, &pitch, Sw, W, &prev_f0); + model.Wo = TWO_PI/pitch; + + dft_speech(&c2const, fft_fwd_cfg, Sw, Sn, w); + two_stage_pitch_refinement(&c2const, &model, Sw); + estimate_amplitudes(&model, Sw, W, 1); + est_voicing_mbe(&c2const, &model, Sw, W); + //voicing[f] = model.voiced; + + /* newamp1 processing ----------------------------------------*/ + + newamp1_model_to_indexes(&c2const, + &indexes[f][0], + &model, + &rate_K_surface[f][0], + rate_K_sample_freqs_kHz, + K, + &mean[f], + &rate_K_surface_no_mean[f][0], + &rate_K_surface_no_mean_[f][0], + &se, + eq, 0); + + newamp1_indexes_to_rate_K_vec(&rate_K_surface_[f][0], + &rate_K_surface_no_mean_[f][0], + rate_K_sample_freqs_kHz, + K, + &mean_[f], + &indexes[f][0], NULL, 1); + + #ifdef VERBOSE + fprintf(stderr,"f: %d Wo: %4.3f L: %d v: %d\n", f, model.Wo, model.L, model.voiced); + if ((f % M) == 0) { + for(i=0; i<5; i++) { + fprintf(stderr," %5.3f", rate_K_surface_[f][i]); + } + fprintf(stderr,"\n"); + fprintf(stderr," %d %d %d %d\n", indexes[f][0], indexes[f][1], indexes[f][2], indexes[f][3]); + } + #endif + /* log vectors */ + + model_octave[f][0] = model.Wo; + model_octave[f][1] = model.L; + for(m=1; m<=model.L; m++) { + model_octave[f][m+1] = model.A[m]; + } + } + + /* Decoder */ + + MODEL model__[M]; + float prev_rate_K_vec_[K]; + COMP HH[M][MAX_AMP+1]; + float Wo_left; + int voicing_left; + + /* initial conditions */ + + for(k=0; k<K; k++) + prev_rate_K_vec_[k] = 0.0; + voicing_left = 0; + Wo_left = 2.0*M_PI/100.0; + + /* decoder runs on every M-th frame, 25Hz frame rate, offset at + start is to minimise processing delay (thanks Jeroen!) */ + + fprintf(stderr,"\n"); + for(f=M-1; f<FRAMES; f+=M) { + + float a_interpolated_surface_[M][K]; + newamp1_indexes_to_model(&c2const, + model__, + (COMP*)HH, + (float*)a_interpolated_surface_, + prev_rate_K_vec_, + &Wo_left, + &voicing_left, + rate_K_sample_freqs_kHz, + K, + phase_fft_fwd_cfg, + phase_fft_inv_cfg, + &indexes[f][0], + NULL, 1); + + #ifdef VERBOSE + fprintf(stderr,"f: %d\n", f); + fprintf(stderr," %d %d %d %d\n", indexes[f][0], indexes[f][1], indexes[f][2], indexes[f][3]); + for(i=0; i<M; i++) { + fprintf(stderr," Wo: %4.3f L: %d v: %d\n", model__[i].Wo, model__[i].L, model__[i].voiced); + } + fprintf(stderr," rate_K_vec: "); + for(i=0; i<5; i++) { + fprintf(stderr,"%5.3f ", prev_rate_K_vec_[i]); + } + fprintf(stderr,"\n"); + fprintf(stderr," Am H:\n"); + + for(m=0; m<M; m++) { + fprintf(stderr," "); + for(i=1; i<=5; i++) { + fprintf(stderr,"%5.1f (%5.3f %5.3f) ", model__[m].A[i], HH[m][i].real, HH[m][i].imag); + } + fprintf(stderr,"\n"); + } + + fprintf(stderr,"\n\n"); + #endif + + //if (f == 7) + // exit(0); + + /* with f == 0, we don't store output, but memories are updated, helps to match + what happens in Codec 2 mode */ + + if (f >= M) { + for(i=0; i<M; i++) { + for(k=0; k<K; k++) { + interpolated_surface_[f-M+i][k] = a_interpolated_surface_[i][k]; + } + } + + /* store test vectors */ + + for(i=f-M, m=0; i<f; i++,m++) { + model_octave_[i][0] = model__[m].Wo; + model_octave_[i][1] = model__[m].L; + voicing_[i] = model__[m].voiced; + } + + int j; + for(i=f-M, j=0; i<f; i++,j++) { + for(m=1; m<=model__[j].L; m++) { + model_octave_[i][m+1] = model__[j].A[m]; + H[i][m-1] = HH[j][m];// aH[m]; + } + } + } + } + + fclose(fin); + + /* save vectors in Octave format */ + + FILE *fout = fopen("tnewamp1_out.txt","wt"); + assert(fout != NULL); + fprintf(fout, "# Created by tnewamp1.c\n"); + octave_save_float(fout, "rate_K_surface_c", (float*)rate_K_surface, FRAMES, K, K); + octave_save_float(fout, "mean_c", (float*)mean, 1, FRAMES, 1); + octave_save_float(fout, "eq_c", eq, 1, K, K); + octave_save_float(fout, "rate_K_surface_no_mean_c", (float*)rate_K_surface_no_mean, FRAMES, K, K); + octave_save_float(fout, "rate_K_surface_no_mean__c", (float*)rate_K_surface_no_mean_, FRAMES, K, K); + octave_save_float(fout, "mean__c", (float*)mean_, FRAMES, 1, 1); + octave_save_float(fout, "rate_K_surface__c", (float*)rate_K_surface_, FRAMES, K, K); + octave_save_float(fout, "interpolated_surface__c", (float*)interpolated_surface_, FRAMES, K, K); + octave_save_float(fout, "model_c", (float*)model_octave, FRAMES, MAX_AMP+2, MAX_AMP+2); + octave_save_float(fout, "model__c", (float*)model_octave_, FRAMES, MAX_AMP+2, MAX_AMP+2); + octave_save_int(fout, "voicing__c", (int*)voicing_, 1, FRAMES); + octave_save_complex(fout, "H_c", (COMP*)H, FRAMES, MAX_AMP, MAX_AMP); + fclose(fout); + + printf("Done! Now run\n octave:1> tnewamp1(\"../path/to/build_linux/src/hts1a\", \"../path/to/build_linux/unittest\")\n"); + return 0; +} + diff --git a/unittest/tnlp.c b/unittest/tnlp.c new file mode 100644 index 0000000..82f0a96 --- /dev/null +++ b/unittest/tnlp.c @@ -0,0 +1,164 @@ +/*---------------------------------------------------------------------------*\ + + FILE........: tnlp.c + AUTHOR......: David Rowe + DATE CREATED: 23/3/93 + + Test program for non linear pitch estimation functions. + +\*---------------------------------------------------------------------------*/ + +/* + Copyright (C) 2009 David Rowe + + All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 2.1, as + published by the Free Software Foundation. This program is + distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, see <http://www.gnu.org/licenses/>. +*/ + + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <math.h> + +#include "defines.h" +#include "dump.h" +#include "sine.h" +#include "nlp.h" +#include "kiss_fft.h" + +int frames; + +/*---------------------------------------------------------------------------*\ + + switch_present() + + Searches the command line arguments for a "switch". If the switch is + found, returns the command line argument where it ws found, else returns + NULL. + +\*---------------------------------------------------------------------------*/ + +int switch_present(sw,argc,argv) + char sw[]; /* switch in string form */ + int argc; /* number of command line arguments */ + char *argv[]; /* array of command line arguments in string form */ +{ + int i; /* loop variable */ + + for(i=1; i<argc; i++) + if (!strcmp(sw,argv[i])) + return(i); + + return 0; +} + +/*---------------------------------------------------------------------------*\ + + MAIN + +\*---------------------------------------------------------------------------*/ + +int main(int argc, char *argv[]) +{ + if (argc < 3) { + printf("\nusage: tnlp InputRawSpeechFile Outputf0PitchTextFile " + "[--dump DumpFile] [--Fs SampleRateHz]\n"); + exit(1); + } + + int Fs = 8000; + if (switch_present("--Fs",argc,argv)) { + Fs = atoi(argv[argc+1]); + } + + C2CONST c2const = c2const_create(Fs, N_S); + int n = c2const.n_samp; + int m = c2const.m_pitch; + FILE *fin,*fout; + short buf[n]; + float Sn[m]; /* float input speech samples */ + kiss_fft_cfg fft_fwd_cfg; + COMP Sw[FFT_ENC]; /* DFT of Sn[] */ + float w[m]; /* time domain hamming window */ + COMP W[FFT_ENC]; /* DFT of w[] */ + float pitch_samples; + int i; + float f0, prev_f0; + void *nlp_states; + #ifdef DUMP + int dump; + #endif + + /* Input file */ + + if ((fin = fopen(argv[1],"rb")) == NULL) { + printf("Error opening input speech file: %s\n",argv[1]); + exit(1); + } + + /* Output file */ + + if ((fout = fopen(argv[2],"wt")) == NULL) { + printf("Error opening output text file: %s\n",argv[2]); + exit(1); + } + + #ifdef DUMP + dump = switch_present("--dump",argc,argv); + if (dump) + dump_on(argv[dump+1]); + #else + /// TODO + /// #warning "Compile with -DDUMP if you expect to dump anything." + #endif + + for(i=0; i<m; i++) { + Sn[i] = 0.0; + } + + nlp_states = nlp_create(&c2const); + fft_fwd_cfg = kiss_fft_alloc(FFT_ENC, 0, NULL, NULL); + make_analysis_window(&c2const, fft_fwd_cfg, w, W); + + frames = 0; + prev_f0 = 1/P_MAX_S; + while(fread(buf, sizeof(short), n, fin)) { + /* Update input speech buffers */ + + for(i=0; i<m-n; i++) + Sn[i] = Sn[i+n]; + for(i=0; i<n; i++) + Sn[i+m-n] = buf[i]; + dft_speech(&c2const, fft_fwd_cfg, Sw, Sn, w); + #ifdef DUMP + dump_Sn(m, Sn); dump_Sw(Sw); + #endif + + f0 = nlp(nlp_states, Sn, n, &pitch_samples, Sw, W, &prev_f0); + + fprintf(stderr,"%d %f %f\n", frames++, f0, pitch_samples); + fprintf(fout,"%f %f\n", f0, pitch_samples); + } + + fclose(fin); + fclose(fout); + #ifdef DUMP + if (dump) dump_off(); + #endif + nlp_destroy(nlp_states); + + return 0; +} + + diff --git a/unittest/tofdm.c b/unittest/tofdm.c new file mode 100644 index 0000000..332e79e --- /dev/null +++ b/unittest/tofdm.c @@ -0,0 +1,583 @@ +/*---------------------------------------------------------------------------*\ + + FILE........: tofdm.c + AUTHORS.....: David Rowe & Steve Sampson + DATE CREATED: June 2017 + + Tests for the C version of the OFDM modem. This program outputs a + file of Octave vectors that are loaded and automatically tested + against the Octave version of the modem by the Octave script tofdm.m + +\*---------------------------------------------------------------------------*/ + +/* + Copyright (C) 2017 David Rowe + + All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 2, as + published by the Free Software Foundation. This program is + distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, see <http://www.gnu.org/licenses/>. +*/ + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <math.h> +#include <complex.h> +#include <getopt.h> + +#include "ofdm_internal.h" +#include "codec2_ofdm.h" +#include "octave.h" +#include "test_bits_ofdm.h" +#include "comp_prim.h" +#include "mpdecode_core.h" + +#include "HRA_112_112.h" /* generated by ldpc_fsk_lib.m:ldpc_decode() */ + +#define NFRAMES 10 +#define SAMPLE_CLOCK_OFFSET_PPM 100 +#define FOFF_HZ 0.5f + +#define ASCALE (2E5f * 1.1491f / 2.0f) /* scale from shorts back to floats */ + +#define CODED_BITSPERFRAME 224 /* number of LDPC codeword bits/frame */ + +/* QPSK constellation for symbol likelihood calculations */ + +static COMP S_matrix[] = { + { 1.0f, 0.0f}, + { 0.0f, 1.0f}, + { 0.0f, -1.0f}, + {-1.0f, 0.0f} +}; + +/* constants we use a lot and don't want to have to deference all the time */ + +static float ofdm_tx_centre; /* TX Center frequency */ +static float ofdm_rx_centre; /* RX Center frequency */ +static float ofdm_fs; /* Sample rate */ +static float ofdm_ts; /* Symbol cycle time */ +static float ofdm_rs; /* Symbol rate */ +static float ofdm_tcp; /* Cyclic prefix duration */ +static float ofdm_timing_mx_thresh; /* See 700D Part 4 Acquisition blog post and ofdm_dev.m routines for how this was set */ + +static int ofdm_nc; /* NS-1 data symbols between pilots */ +static int ofdm_ns; +static int ofdm_bps; /* Bits per symbol */ +static int ofdm_m; /* duration of each symbol in samples */ +static int ofdm_ncp; /* duration of CP in samples */ + +static int ofdm_ftwindowwidth; +static int ofdm_bitsperframe; +static int ofdm_rowsperframe; +static int ofdm_samplesperframe; +static int ofdm_samplespersymbol; +static int ofdm_max_samplesperframe; +static int ofdm_nrxbuf; +static int ofdm_ntxtbits; /* reserve bits/frame for auxiliary text information */ +static int ofdm_nuwbits; /* Unique word, used for positive indication of lock */ + +/*---------------------------------------------------------------------------*\ + + FUNCTION....: fs_offset() + AUTHOR......: David Rowe + DATE CREATED: May 2015 + + Simulates small Fs offset between mod and demod. + (Note: Won't work with float, works OK with double) + +\*---------------------------------------------------------------------------*/ + +static int fs_offset(COMP out[], COMP in[], int n, float sample_rate_ppm) { + double f; + double tin = 0.0; + double step = 1.0 + sample_rate_ppm/1E6; + int t1, t2; + int tout = 0; + + while (tin < (double) (n-1)) { + t1 = (int) floor(tin); + t2 = (int) ceil(tin); + assert(t2 < n); + f = tin - (double) t1; + + out[tout].real = ((double)1.0-f)*(double)in[t1].real + f*(double)in[t2].real; + out[tout].imag = ((double)1.0-f)*(double)in[t1].imag + f*(double)in[t2].imag; + + tin += step; + tout++; + //printf("tin: %f tout: %d f: %f\n", tin, tout, f); + } + + return tout; +} + +/*---------------------------------------------------------------------------*\ + + FUNCTION....: freq_shift() + AUTHOR......: David Rowe + DATE CREATED: 26/4/2012 + + Frequency shift modem signal. The use of complex input and output allows + single sided frequency shifting (no images). + +\*---------------------------------------------------------------------------*/ + +static void freq_shift(COMP rx_fdm_fcorr[], COMP rx_fdm[], float foff, COMP *foff_phase_rect, int nin) { + float temp = (TAU * foff / ofdm_fs); + COMP foff_rect = { cosf(temp), sinf(temp) }; + int i; + + for (i = 0; i < nin; i++) { + *foff_phase_rect = cmult(*foff_phase_rect, foff_rect); + rx_fdm_fcorr[i] = cmult(rx_fdm[i], *foff_phase_rect); + } + + /* normalise digital oscillator as the magnitude can drift over time */ + + float mag = cabsolute(*foff_phase_rect); + foff_phase_rect->real /= mag; + foff_phase_rect->imag /= mag; +} + +int main(int argc, char *argv[]) +{ + int opt_Nc = 0; + int ldpc_enable = 1; + struct OFDM *ofdm; + struct OFDM_CONFIG *ofdm_config; + + static struct option long_options[] = { + {"nc", required_argument, 0, 'n'}, + {"noldpc", no_argument, 0, 'l'}, + {0, 0, 0, 0} + }; + + int opt_index = 0; char c; + + while ((c = getopt_long (argc, argv, "n:l", long_options, &opt_index)) != -1) { + switch (c) { + case 'n': + opt_Nc = atoi(optarg); + fprintf(stderr, "Nc = %d\n", opt_Nc); + break; + case 'l': + ldpc_enable = 0; + fprintf(stderr, "LDPC disabled\n"); + break; + default: + fprintf(stderr,"usage: %s [Options]:\n [-l --noldpc]\n [-n --nc NumberoFCarriers]\n", argv[0]); + exit(1); + } + } + + // init once to get a copy of default config params + + ofdm = ofdm_create(NULL); + assert(ofdm != NULL); + struct OFDM_CONFIG ofdm_config_default; + memcpy(&ofdm_config_default, ofdm_get_config_param(ofdm), sizeof(struct OFDM_CONFIG)); + ofdm_destroy(ofdm); + + // now do a little customisation on default config, and re-create modem instance + + if (opt_Nc) + ofdm_config_default.nc = opt_Nc; + //printf("ofdm_create() 2\n"); + ofdm = ofdm_create(&ofdm_config_default); + assert(ofdm != NULL); + ofdm_config = ofdm_get_config_param(ofdm); + ofdm_set_tx_bpf(ofdm, false); + + // same levels as Octave sim + ofdm->amp_scale = 1.0; + + // make local copies for convenience + ofdm_tx_centre = ofdm_config->tx_centre; + ofdm_rx_centre = ofdm_config->rx_centre; + ofdm_fs = ofdm_config->fs; + ofdm_ts = ofdm_config->ts; + ofdm_rs = ofdm_config->rs; + ofdm_tcp = ofdm_config->tcp; + ofdm_timing_mx_thresh = ofdm_config->timing_mx_thresh; + ofdm_nc = ofdm_config->nc; + ofdm_ns = ofdm_config->ns; + ofdm_bps = ofdm_config->bps; + ofdm_m = (int) (ofdm_config->fs / ofdm_config->rs); + ofdm_ncp = (int) (ofdm_config->tcp * ofdm_config->fs); + ofdm_ftwindowwidth = ofdm_config->ftwindowwidth; + ofdm_bitsperframe = ofdm_get_bits_per_frame(ofdm); + ofdm_rowsperframe = ofdm_bitsperframe / (ofdm_config->nc * ofdm_config->bps); + ofdm_samplesperframe = ofdm_get_samples_per_frame(ofdm); + ofdm_samplespersymbol = (ofdm->m + ofdm->ncp); + ofdm_max_samplesperframe = ofdm_get_max_samples_per_frame(ofdm); + ofdm_nrxbuf = ofdm->nrxbuf; + ofdm_ntxtbits = ofdm_config->txtbits; + ofdm_nuwbits = ofdm_config->nuwbits; + + int tx_bits[ofdm_samplesperframe]; + COMP tx[ofdm_samplesperframe]; /* one frame of tx samples */ + + int rx_bits[ofdm_bitsperframe]; /* one frame of rx bits */ + printf("Nc = %d ofdm_bitsperframe: %d\n", ofdm_nc, ofdm_bitsperframe); + + /* log arrays */ + + int tx_bits_log[ofdm_bitsperframe*NFRAMES]; + COMP tx_log[ofdm_samplesperframe*NFRAMES]; + COMP rx_log[ofdm_samplesperframe*NFRAMES]; + COMP rxbuf_in_log[ofdm_max_samplesperframe*NFRAMES]; + COMP rxbuf_log[ofdm_nrxbuf*NFRAMES]; + COMP rx_sym_log[(ofdm_ns + 3)*NFRAMES][ofdm_nc + 2]; + float phase_est_pilot_log[ofdm_rowsperframe*NFRAMES][ofdm_nc]; + COMP rx_np_log[ofdm_rowsperframe*ofdm_nc*NFRAMES]; + float rx_amp_log[ofdm_rowsperframe*ofdm_nc*NFRAMES]; + float foff_hz_log[NFRAMES]; + int rx_bits_log[ofdm_bitsperframe*NFRAMES]; + int timing_est_log[NFRAMES]; + int timing_valid_log[NFRAMES]; + float timing_mx_log[NFRAMES]; + float coarse_foff_est_hz_log[NFRAMES]; + int sample_point_log[NFRAMES]; + float symbol_likelihood_log[ (CODED_BITSPERFRAME/ofdm_bps) * (1<<ofdm_bps) * NFRAMES]; + float bit_likelihood_log[CODED_BITSPERFRAME * NFRAMES]; + int detected_data_log[CODED_BITSPERFRAME * NFRAMES]; + float mean_amp_log[NFRAMES]; + float snr_log[NFRAMES]; + + FILE *fout; + int f,i,j; + + /* set up LDPC code */ + + struct LDPC ldpc; + + ldpc.max_iter = HRA_112_112_MAX_ITER; + ldpc.dec_type = 0; + ldpc.q_scale_factor = 1; + ldpc.r_scale_factor = 1; + ldpc.CodeLength = HRA_112_112_CODELENGTH; + ldpc.NumberParityBits = HRA_112_112_NUMBERPARITYBITS; + ldpc.NumberRowsHcols = HRA_112_112_NUMBERROWSHCOLS; + ldpc.max_row_weight = HRA_112_112_MAX_ROW_WEIGHT; + ldpc.max_col_weight = HRA_112_112_MAX_COL_WEIGHT; + ldpc.H_rows = (uint16_t *)HRA_112_112_H_rows; + ldpc.H_cols = (uint16_t *)HRA_112_112_H_cols; + + /* Main Loop ---------------------------------------------------------------------*/ + + for(f=0; f<NFRAMES; f++) { + + /* --------------------------------------------------------*\ + Mod + \*---------------------------------------------------------*/ + + /* See CML startup code in tofdm.m */ + + for(i=0; i<ofdm_nuwbits; i++) { + tx_bits[i] = ofdm->tx_uw[i]; + } + for(i=ofdm_nuwbits; i<ofdm_nuwbits+ofdm_ntxtbits; i++) { + tx_bits[i] = 0; + } + + if (ldpc_enable) { + unsigned char ibits[HRA_112_112_NUMBERROWSHCOLS]; + unsigned char pbits[HRA_112_112_NUMBERPARITYBITS]; + + assert(HRA_112_112_NUMBERROWSHCOLS == ldpc.CodeLength/2); + for(i=0; i<ldpc.CodeLength/2; i++) { + ibits[i] = payload_data_bits[i]; + } + encode(&ldpc, ibits, pbits); + for(j=0, i=ofdm_nuwbits+ofdm_ntxtbits; j<ldpc.CodeLength/2; i++,j++) { + tx_bits[i] = ibits[j]; + } + for(j=0; j<ldpc.CodeLength/2; i++,j++) { + tx_bits[i] = pbits[j]; + } + assert(i == ofdm_bitsperframe); + } else { + int Npayloadbits = ofdm_bitsperframe - (ofdm_nuwbits + ofdm_ntxtbits); + uint16_t r[Npayloadbits]; + uint8_t payload_bits[Npayloadbits]; + + ofdm_rand(r, Npayloadbits); + for (i = 0; i < Npayloadbits; i++) + payload_bits[i] = r[i] > 16384; + uint8_t txt_bits[ofdm_ntxtbits]; + for (i = 0; i < ofdm_ntxtbits; i++) + txt_bits[i] = 0; + + uint8_t tx_bits_char[ofdm_bitsperframe]; + ofdm_assemble_qpsk_modem_packet(ofdm, tx_bits_char, payload_bits, txt_bits); + for(i=0; i<ofdm_bitsperframe; i++) + tx_bits[i] = tx_bits_char[i]; + } + + ofdm_mod(ofdm, (COMP*)tx, tx_bits); + + /* tx vector logging */ + + memcpy(&tx_bits_log[ofdm_bitsperframe*f], tx_bits, sizeof(int)*ofdm_bitsperframe); + memcpy(&tx_log[ofdm_samplesperframe*f], tx, sizeof(COMP)*ofdm_samplesperframe); + } + + /* --------------------------------------------------------*\ + Channel + \*---------------------------------------------------------*/ + + fs_offset(rx_log, tx_log, ofdm_samplesperframe*NFRAMES, SAMPLE_CLOCK_OFFSET_PPM); + + COMP foff_phase_rect = {1.0f, 0.0f}; + + freq_shift(rx_log, rx_log, FOFF_HZ, &foff_phase_rect, ofdm_samplesperframe * NFRAMES); + + /* --------------------------------------------------------*\ + Demod + \*---------------------------------------------------------*/ + + /* Init/pre-load rx with ideal timing so we can test with timing estimation disabled */ + + int Nsam = ofdm_samplesperframe*NFRAMES; + int prx = 0; + int nin = ofdm_samplesperframe + 2*ofdm_samplespersymbol; + + int lnew; + COMP rxbuf_in[ofdm_max_samplesperframe]; + +#define FRONT_LOAD +#ifdef FRONT_LOAD + for (i=0; i<nin; i++,prx++) { + ofdm->rxbuf[ofdm_nrxbuf-nin+i] = rx_log[prx].real + rx_log[prx].imag * I; + } +#endif + + int nin_tot = 0; + + /* disable estimators for initial testing */ + + ofdm_set_verbose(ofdm, false); + ofdm_set_timing_enable(ofdm, true); + ofdm_set_foff_est_enable(ofdm, true); + ofdm_set_phase_est_enable(ofdm, true); + +//#define TESTING_FILE +#ifdef TESTING_FILE + FILE *fin=fopen("~/codec2-dev/octave/ofdm_test.raw", "rb"); + assert(fin != NULL); + int Nbitsperframe = ofdm_bitsperframe; + int Nmaxsamperframe = ofdm_max_samplesperframe; + short rx_scaled[Nmaxsamperframe]; +#endif + + /* start this with something sensible otherwise LDPC decode fails in tofdm.m */ + + ofdm->mean_amp = 1.0; + + for(f=0; f<NFRAMES; f++) { + /* For initial testing, timing est is off, so nin is always + fixed. TODO: we need a constant for rxbuf_in[] size that + is the maximum possible nin */ + + nin = ofdm_get_nin(ofdm); + assert(nin <= ofdm_max_samplesperframe); + + /* Insert samples at end of buffer, set to zero if no samples + available to disable phase estimation on future pilots on + last frame of simulation. */ + + if ((Nsam-prx) < nin) { + lnew = Nsam-prx; + } else { + lnew = nin; + } + //printf("nin: %d prx: %d lnew: %d\n", nin, prx, lnew); + for(i=0; i<nin; i++) { + rxbuf_in[i].real = 0.0; + rxbuf_in[i].imag = 0.0; + } + + if (lnew) { + for(i=0; i<lnew; i++, prx++) { + rxbuf_in[i] = rx_log[prx]; + } + } + assert(prx <= ofdm_max_samplesperframe*NFRAMES); + +#ifdef TESTING_FILE + fread(rx_scaled, sizeof(short), nin, fin); + + for(i=0; i<nin; i++) { + rxbuf_in[i].real = (float)rx_scaled[i]/ASCALE; + rxbuf_in[i].imag = 0.0; + } +#endif + + /* uncoded OFDM modem ---------------------------------------*/ + + ofdm_demod(ofdm, rx_bits, rxbuf_in); + +#ifdef TESTING_FILE + int Nerrs = 0; + for(i=0; i<Nbitsperframe; i++) { + if (test_bits_ofdm[i] != rx_bits[i]) { + Nerrs++; + } + } + printf("f: %d Nerr: %d\n", f, Nerrs); +#endif + + float symbol_likelihood[ (CODED_BITSPERFRAME/ofdm_bps) * (1<<ofdm_bps) ]; + float bit_likelihood[CODED_BITSPERFRAME]; + uint8_t out_char[CODED_BITSPERFRAME]; + + if (ldpc_enable) { + /* LDPC functions --------------------------------------*/ + + float EsNo = 10; + + /* first few symbols are used for UW and txt bits, find start of (224,112) LDPC codeword */ + + assert((ofdm_nuwbits+ofdm_ntxtbits+CODED_BITSPERFRAME) == ofdm_bitsperframe); + + COMP ldpc_codeword_symbols[(CODED_BITSPERFRAME/ofdm_bps)]; + + for(i=0, j=(ofdm_nuwbits+ofdm_ntxtbits)/ofdm_bps; i<(CODED_BITSPERFRAME/ofdm_bps); i++,j++) { + ldpc_codeword_symbols[i].real = crealf(ofdm->rx_np[j]); + ldpc_codeword_symbols[i].imag = cimagf(ofdm->rx_np[j]); + } + + float *ldpc_codeword_symbol_amps = &ofdm->rx_amp[(ofdm_nuwbits+ofdm_ntxtbits)/ofdm_bps]; + + Demod2D(symbol_likelihood, ldpc_codeword_symbols, S_matrix, EsNo, ldpc_codeword_symbol_amps, ofdm->mean_amp, CODED_BITSPERFRAME/ofdm_bps); + Somap(bit_likelihood, symbol_likelihood, 1<<ofdm_bps, ofdm_bps, CODED_BITSPERFRAME/ofdm_bps); + + float llr[CODED_BITSPERFRAME]; + int parityCheckCount; + + + // fprintf(stderr, "\n"); + for(i=0; i<CODED_BITSPERFRAME; i++) { + llr[i] = -bit_likelihood[i]; + // fprintf(stderr, "%f ", llr[i]); + } + + //fprintf(stderr, "\n"); + + run_ldpc_decoder(&ldpc, out_char, llr, &parityCheckCount); + /* + fprintf(stderr, "iter: %d parityCheckCount: %d\n", iter, parityCheckCount); + for(i=0; i<CODED_BITSPERFRAME; i++) { + fprintf(stderr, "%d ", out_char[i]); + } + */ + } + + /* rx vector logging -----------------------------------*/ + + assert(nin_tot < ofdm_samplesperframe*NFRAMES); + memcpy(&rxbuf_in_log[nin_tot], rxbuf_in, sizeof(COMP)*nin); + nin_tot += nin; + + for(i=0; i<ofdm_nrxbuf; i++) { + rxbuf_log[ofdm_nrxbuf*f+i].real = crealf(ofdm->rxbuf[i]); + rxbuf_log[ofdm_nrxbuf*f+i].imag = cimagf(ofdm->rxbuf[i]); + } + + for (i = 0; i < (ofdm_ns + 3); i++) { + for (j = 0; j < (ofdm_nc + 2); j++) { + rx_sym_log[(ofdm_ns + 3)*f+i][j].real = crealf(ofdm->rx_sym[i][j]); + rx_sym_log[(ofdm_ns + 3)*f+i][j].imag = cimagf(ofdm->rx_sym[i][j]); + } + } + + /* note corrected phase (rx no phase) is one big linear array for frame */ + + for (i = 0; i < ofdm_rowsperframe*ofdm_nc; i++) { + rx_np_log[ofdm_rowsperframe*ofdm_nc*f + i].real = crealf(ofdm->rx_np[i]); + rx_np_log[ofdm_rowsperframe*ofdm_nc*f + i].imag = cimagf(ofdm->rx_np[i]); + } + + /* note phase/amp ests the same for each col, but check them all anyway */ + + for (i = 0; i < ofdm_rowsperframe; i++) { + for (j = 0; j < ofdm_nc; j++) { + phase_est_pilot_log[ofdm_rowsperframe*f+i][j] = ofdm->aphase_est_pilot_log[ofdm_nc*i+j]; + rx_amp_log[ofdm_rowsperframe*ofdm_nc*f+ofdm_nc*i+j] = ofdm->rx_amp[ofdm_nc*i+j]; + } + } + + foff_hz_log[f] = ofdm->foff_est_hz; + timing_est_log[f] = ofdm->timing_est + 1; /* offset by 1 to match Octave */ + timing_valid_log[f] = ofdm->timing_valid; + timing_mx_log[f] = ofdm->timing_mx; + coarse_foff_est_hz_log[f] = ofdm->coarse_foff_est_hz; + sample_point_log[f] = ofdm->sample_point + 1; /* offset by 1 to match Octave */ + float EsNodB = ofdm_esno_est_calc(ofdm->rx_np, ofdm_rowsperframe*ofdm_nc); + snr_log[f] = ofdm_snr_from_esno(ofdm, EsNodB); + mean_amp_log[f] = ofdm->mean_amp; + + memcpy(&rx_bits_log[ofdm_bitsperframe*f], rx_bits, sizeof(rx_bits)); + + if (ldpc_enable) { + for(i=0; i<(CODED_BITSPERFRAME/ofdm_bps) * (1<<ofdm_bps); i++) { + symbol_likelihood_log[ (CODED_BITSPERFRAME/ofdm_bps) * (1<<ofdm_bps) * f + i] = symbol_likelihood[i]; + } + for(i=0; i<CODED_BITSPERFRAME; i++) { + bit_likelihood_log[CODED_BITSPERFRAME*f + i] = bit_likelihood[i]; + detected_data_log[CODED_BITSPERFRAME*f + i] = out_char[i]; + } + } + } + + /*---------------------------------------------------------*\ + Dump logs to Octave file for evaluation + by tofdm.m Octave script + \*---------------------------------------------------------*/ + + fout = fopen("tofdm_out.txt","wt"); + assert(fout != NULL); + fprintf(fout, "# Created by tofdm.c\n"); + octave_save_complex(fout, "pilot_samples_c", (COMP*)ofdm->pilot_samples, 1, ofdm_samplespersymbol, ofdm_samplespersymbol); + octave_save_int(fout, "tx_bits_log_c", tx_bits_log, 1, ofdm_bitsperframe*NFRAMES); + octave_save_complex(fout, "tx_log_c", (COMP*)tx_log, 1, ofdm_samplesperframe*NFRAMES, ofdm_samplesperframe*NFRAMES); + octave_save_complex(fout, "rx_log_c", (COMP*)rx_log, 1, ofdm_samplesperframe*NFRAMES, ofdm_samplesperframe*NFRAMES); + octave_save_complex(fout, "rxbuf_in_log_c", (COMP*)rxbuf_in_log, 1, nin_tot, nin_tot); + octave_save_complex(fout, "rxbuf_log_c", (COMP*)rxbuf_log, 1, ofdm_nrxbuf*NFRAMES, ofdm_nrxbuf*NFRAMES); + octave_save_complex(fout, "rx_sym_log_c", (COMP*)rx_sym_log, (ofdm_ns + 3)*NFRAMES, ofdm_nc + 2, ofdm_nc + 2); + octave_save_float(fout, "phase_est_pilot_log_c", (float*)phase_est_pilot_log, ofdm_rowsperframe*NFRAMES, ofdm_nc, ofdm_nc); + octave_save_float(fout, "rx_amp_log_c", (float*)rx_amp_log, 1, ofdm_rowsperframe*ofdm_nc*NFRAMES, ofdm_rowsperframe*ofdm_nc*NFRAMES); + octave_save_float(fout, "foff_hz_log_c", foff_hz_log, NFRAMES, 1, 1); + octave_save_int(fout, "timing_est_log_c", timing_est_log, NFRAMES, 1); + octave_save_int(fout, "timing_valid_log_c", timing_valid_log, NFRAMES, 1); + octave_save_float(fout, "timing_mx_log_c", timing_mx_log, NFRAMES, 1, 1); + octave_save_float(fout, "coarse_foff_est_hz_log_c", coarse_foff_est_hz_log, NFRAMES, 1, 1); + octave_save_int(fout, "sample_point_log_c", sample_point_log, NFRAMES, 1); + octave_save_complex(fout, "rx_np_log_c", (COMP*)rx_np_log, 1, ofdm_rowsperframe*ofdm_nc*NFRAMES, ofdm_rowsperframe*ofdm_nc*NFRAMES); + octave_save_int(fout, "rx_bits_log_c", rx_bits_log, 1, ofdm_bitsperframe*NFRAMES); + octave_save_float(fout, "symbol_likelihood_log_c", symbol_likelihood_log, (CODED_BITSPERFRAME/ofdm_bps) * (1<<ofdm_bps) * NFRAMES, 1, 1); + octave_save_float(fout, "bit_likelihood_log_c", bit_likelihood_log, CODED_BITSPERFRAME * NFRAMES, 1, 1); + octave_save_int(fout, "detected_data_log_c", detected_data_log, 1, CODED_BITSPERFRAME*NFRAMES); + octave_save_float(fout, "snr_log_c", snr_log, NFRAMES, 1, 1); + octave_save_float(fout, "mean_amp_log_c", mean_amp_log, NFRAMES, 1, 1); + fclose(fout); +#ifdef TESTING_FILE + fclose(fin); +#endif + + ofdm_destroy(ofdm); + + return 0; +} diff --git a/unittest/tofdm_acq.c b/unittest/tofdm_acq.c new file mode 100644 index 0000000..ee04d5e --- /dev/null +++ b/unittest/tofdm_acq.c @@ -0,0 +1,92 @@ +/*---------------------------------------------------------------------------*\ + + FILE........: tofdm_acq.c + AUTHORS.....: David Rowe + DATE CREATED: Mar 2021 + + Tests for the acquistion (sync) parts of the C version of the OFDM modem. + This program outputs a file of Octave vectors that are loaded and + automatically tested against the Octave version of the modem by the Octave + script tofdm_acq.m + +\*---------------------------------------------------------------------------*/ + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <math.h> + +#include "ofdm_internal.h" +#include "codec2_ofdm.h" +#include "octave.h" + +#define MAX_FRAMES 500 + +int main(int argc, char *argv[]) +{ + struct OFDM *ofdm; + struct OFDM_CONFIG ofdm_config; + + ofdm_init_mode("datac0", &ofdm_config); + ofdm = ofdm_create(&ofdm_config); + ofdm->data_mode = "burst"; + ofdm->verbose = 2; + ofdm->timing_mx_thresh = 0.15; + ofdm->postambledetectoren = 1; + assert(ofdm != NULL); + + int nin = ofdm_get_nin(ofdm); + int rxbufst = ofdm->rxbufst; + + FILE *fin = fopen(argv[1],"rb"); assert(fin != NULL); + short rx_scaled[ofdm_get_max_samples_per_frame(ofdm)]; + int f = 0; + + float timing_mx_log[MAX_FRAMES]; + int ct_est_log[MAX_FRAMES]; + float foff_est_log[MAX_FRAMES]; + int timing_valid_log[MAX_FRAMES]; + int nin_log[MAX_FRAMES]; + + while (fread(rx_scaled, sizeof (short), nin, fin) == nin) { + fprintf(stderr, "%3d ", f); + ofdm_sync_search_shorts(ofdm, rx_scaled, ofdm->amp_scale / 2.0f); + + if (f < MAX_FRAMES) { + timing_mx_log[f] = ofdm->timing_mx; + ct_est_log[f] = ofdm->ct_est; + foff_est_log[f] = ofdm->foff_est_hz; + timing_valid_log[f] = ofdm->timing_valid; + nin_log[f] = ofdm->nin; + } + f++; + + // reset these to defaults, as they get modified when timing_valid asserted + ofdm->nin = nin; + ofdm->rxbufst = rxbufst; + } + fclose(fin); + + /*---------------------------------------------------------*\ + Dump logs to Octave file for evaluation + by tofdm_acq.m Octave script + \*---------------------------------------------------------*/ + + FILE *fout = fopen("tofdm_acq_out.txt","wt"); + assert(fout != NULL); + fprintf(fout, "# Created by tofdm_acq.c\n"); + octave_save_complex(fout, "tx_preamble_c", (COMP*)ofdm->tx_preamble, 1, ofdm->samplesperframe, ofdm->samplesperframe); + octave_save_complex(fout, "tx_postamble_c", (COMP*)ofdm->tx_postamble, 1, ofdm->samplesperframe, ofdm->samplesperframe); + octave_save_float(fout, "timing_mx_log_c", timing_mx_log, 1, f, f); + octave_save_float(fout, "foff_est_log_c", foff_est_log, 1, f, f); + octave_save_int(fout, "ct_est_log_c", ct_est_log, 1, f); + octave_save_int(fout, "timing_valid_log_c", timing_valid_log, 1, f); + octave_save_int(fout, "nin_log_c", nin_log, 1, f); + fclose(fout); + + ofdm_destroy(ofdm); + + return 0; +} diff --git a/unittest/tprede.c b/unittest/tprede.c new file mode 100644 index 0000000..4d3d09c --- /dev/null +++ b/unittest/tprede.c @@ -0,0 +1,53 @@ +/* + tpre_de.c + David Rowe + Sep 24 2012 + + Unit test to generate the combined impulse response of pre & de-emphasis filters. + + pl("../unittest/out48.raw",1,3000) + pl("../unittest/out8.raw",1,3000) + + Listening to it also shows up anything nasty: + + $ play -s -2 -r 48000 out48.raw + $ play -s -2 -r 8000 out8.raw + + */ + +#include <assert.h> +#include <math.h> +#include <stdlib.h> +#include <stdio.h> +#include "lpc.h" + +#define N 10 +#define F 10 + +int main() { + FILE *fprede; + float Sn[N], Sn_pre[N], Sn_de[N]; + float pre_mem = 0.0, de_mem = 0.0; + int i, f; + + fprede = fopen("prede.txt", "wt"); + assert(fprede != NULL); + + for(i=0; i<N; i++) + Sn[i] = 0.0; + + Sn[0]= 1.0; + + for(f=0; f<F; f++) { + pre_emp(Sn_pre, Sn, &pre_mem, N); + de_emp(Sn_de, Sn_pre, &de_mem, N); + for(i=0; i<N; i++) { + fprintf(fprede, "%f\n", Sn_de[i]); + } + Sn[0] = 0.0; + } + + fclose(fprede); + + return 0; +} diff --git a/unittest/tqam16.c b/unittest/tqam16.c new file mode 100644 index 0000000..21ae9d0 --- /dev/null +++ b/unittest/tqam16.c @@ -0,0 +1,37 @@ +/*---------------------------------------------------------------------------*\ + + FILE........: tqam16.c + AUTHOR......: David Rowe + DATE CREATED: August 2020 + + Simple sanity check for QAM16 symbol mapping. + +\*---------------------------------------------------------------------------*/ + +#include <stdio.h> +#include <string.h> +#include "ofdm_internal.h" + +int main(void) { + int c; + for(c=0; c<16; c++) { + int tx_bits[4], rx_bits[4]; + for(int i=0; i<4; i++) + tx_bits[i] = (c >> (3-i)) & 0x1; + complex float symbol = qam16_mod(tx_bits); + qam16_demod(symbol, rx_bits); + if (memcmp(tx_bits, rx_bits, 4)) { + fprintf(stderr, "FAIL on %d!\ntx_bits: ",c); + for(int i=0; i<4; i++) fprintf(stderr, "%d ", tx_bits[i]); + fprintf(stderr, "%f %f\nrx_bits: ", creal(symbol), cimag(symbol)); + for(int i=0; i<4; i++) fprintf(stderr, "%d ", rx_bits[i]); + fprintf(stderr, "%f %f\n", creal(symbol), cimag(symbol)); + return 1; + } + } + + fprintf(stderr, "%d tested OK...\nPASS!\n", c); + return 0; +} + + diff --git a/unittest/tquisk_filter.c b/unittest/tquisk_filter.c new file mode 100644 index 0000000..d6c7529 --- /dev/null +++ b/unittest/tquisk_filter.c @@ -0,0 +1,48 @@ +/* + tquisk_filter.c + + Unit test for complex band pass filters in src/filter.c + + cd codec2/build_linux + ./misc/mksine - 1500 2 | unittest/tquisk_filter | aplay + + By adjusting the frequency you can audibly test filter response. +*/ + +#include <assert.h> +#include <math.h> +#include <stdlib.h> +#include <stdio.h> +#include "filter.h" +#include "filter_coef.h" + +#define N 159 /* processing buffer size (odd number deliberate) */ +#define CENTRE 1500.0 +#define FS 8000.0 + +int main() { + short buf_short[N]; + complex float buf[N]; + struct quisk_cfFilter *bpf; + int i; + int n = 0; + + bpf = malloc(sizeof(struct quisk_cfFilter)); + assert(bpf != NULL); + quisk_filt_cfInit(bpf, filtP200S400, sizeof (filtP200S400) / sizeof (float)); + quisk_cfTune(bpf, CENTRE/FS); + + while(fread(buf_short, sizeof(short), N, stdin) == N) { + for(i=0; i<N; i++) + buf[i] = buf_short[i]; + quisk_ccfFilter(buf, buf, N, bpf); + /* we only output the real part in this test */ + for(i=0; i<N; i++) + buf_short[i] = creal(buf[i]); + n += fwrite(buf_short, sizeof(short), N, stdout); + } + + quisk_filt_destroy(bpf); + free(bpf); + return 0; +} diff --git a/unittest/tst_codec2_fft_init.c b/unittest/tst_codec2_fft_init.c new file mode 100644 index 0000000..71ddc38 --- /dev/null +++ b/unittest/tst_codec2_fft_init.c @@ -0,0 +1,97 @@ +/*---------------------------------------------------------------------------*\ + + FILE........: tst_codec2_fft_init.c, + AUTHOR......: David Rowe, Don Reid + DATE CREATED: 30 May 2013, Oct 2018, Feb 2018 + + Test FFT Window initialization in Codec2_create + +\*---------------------------------------------------------------------------*/ + +/* + Copyright (C) 2014 David Rowe + + All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 2.1, as + published by the Free Software Foundation. This program is + distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, see <http://www.gnu.org/licenses/>. +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <math.h> + +#include "codec2.h" +#include "codec2_internal.h" +#include "defines.h" + +#include "machdep.h" + +static const float expect_w[] = { + 0.004293, 0.004301, 0.004309, 0.004315, + 0.004320, 0.004323, 0.004326, 0.004328, + 0.004328, 0.004328, 0.004326, 0.004323, + 0.004320, 0.004315, 0.004309, 0.004301}; + + +static const float expect_W[] = { + -0.002176, 0.002195, 0.004429, -0.008645, + -0.012196, 0.065359, 0.262390, 0.495616, + 0.601647, 0.495616, 0.262390, 0.065359, + -0.012196, -0.008645, 0.004429, 0.002195}; + + +int float_cmp(float a, float b) { + if ( fabsf(a - b) < 1e-6f ) return 1; + else return 0; + } + +int main(int argc, char *argv[]) { + + struct CODEC2 *codec2; + int i, j; + + //////// + codec2 = codec2_create(CODEC2_MODE_700C); + + j = (codec2->c2const.m_pitch / 2) - 8; + for (i=0; i<16; i++) { + printf("w[%d] = %f", j+i, + (double)codec2->w[j+i]); + if (!float_cmp(codec2->w[j+i], expect_w[i])) { + printf(" Error, expected %f", (double)expect_w[i]); + } + printf("\n"); + } + + printf("\n"); + + j = (FFT_ENC / 2) - 8; + for (i=0; i<16; i++) { + printf("W[%d] = %f", j+i, + (double)codec2->W[j+i]); + if (!float_cmp(codec2->W[j+i], expect_W[i])) { + printf(" Error, expected %f", (double)expect_W[i]); + } + printf("\n"); + } + + codec2_destroy(codec2); + + printf("\nEnd of Test\n"); + fclose(stdout); + fclose(stderr); + + return(0); +} + +/* vi:set ts=4 et sts=4: */ diff --git a/unittest/tvq_mbest.c b/unittest/tvq_mbest.c new file mode 100644 index 0000000..581d2aa --- /dev/null +++ b/unittest/tvq_mbest.c @@ -0,0 +1,31 @@ +/* + tvq_mbest.c + David Rowe Dec 2019 + + Generate some test vectors to exercise misc/vq_mbest.c +*/ + +#include <assert.h> +#include <stdlib.h> +#include <stdio.h> + +void write_float_file(char fn[], float *values, int n) { + FILE *f=fopen(fn,"wb"); + assert(f != NULL); + assert(fwrite(values, sizeof(float), n, f) == n); + fclose(f); +} + +int main(void) { + /* we're only interested in searching the inner 2 values, outer elements should be + ignored */ + float target[] = {0.0,1.0,1.0,0.0}; + write_float_file("target.f32", target, 4); + float vq1[] = {1.0,0.9,0.9,1.0, /* this will be a better match on first stage */ + 2.0,0.8,0.8,2.0}; /* but after second stage should choose this */ + write_float_file("vq1.f32", vq1, 8); + float vq2[] = {10.0,0.3,0.3,10.0, + 20.0,0.2,0.2,20.0}; /* 0.8+0.2 == 1.0 so best 2nd stage entry */ + write_float_file("vq2.f32", vq2, 8); + return 0; +} |
