aboutsummaryrefslogtreecommitdiff
path: root/unittest
diff options
context:
space:
mode:
Diffstat (limited to 'unittest')
-rw-r--r--unittest/CMakeLists.txt143
-rwxr-xr-xunittest/check_comp.sh29
-rwxr-xr-xunittest/check_peak.sh58
-rwxr-xr-xunittest/check_real_comp.sh15
-rw-r--r--unittest/compare_floats.c89
-rw-r--r--unittest/compare_ints.c159
-rwxr-xr-xunittest/fading_files.sh14
-rw-r--r--unittest/fdmdv_mem.c63
-rw-r--r--unittest/freedv_700d_comprx.c126
-rw-r--r--unittest/freedv_700d_comptx.c43
-rw-r--r--unittest/function_trace.c36
-rw-r--r--unittest/hts1a.h8002
-rw-r--r--unittest/hts1a_1300.h8002
-rwxr-xr-xunittest/ofdm_check68
-rwxr-xr-xunittest/ofdm_fade.sh12
-rwxr-xr-xunittest/ofdm_fade_dpsk.sh15
-rw-r--r--unittest/ofdm_mem.c107
-rwxr-xr-xunittest/ofdm_phase_est_bw.sh24
-rw-r--r--unittest/ofdm_stack.c229
-rwxr-xr-xunittest/ofdm_time_sync.sh30
-rwxr-xr-xunittest/ota_auto.sh15
-rwxr-xr-xunittest/ota_last.sh70
-rwxr-xr-xunittest/ota_summary.sh55
-rwxr-xr-xunittest/ota_test.sh161
-rwxr-xr-xunittest/ota_voice_auto.sh23
-rwxr-xr-xunittest/ota_voice_summary.sh80
-rwxr-xr-xunittest/ota_voice_test.sh309
-rw-r--r--unittest/raw_data_curves/Makefile132
-rwxr-xr-xunittest/raw_data_curves/snr_curves.sh184
-rwxr-xr-xunittest/reliable_text_fade.sh27
-rw-r--r--unittest/sd.c84
-rw-r--r--unittest/sd.h33
-rwxr-xr-xunittest/spectrogram.sh17
-rwxr-xr-xunittest/sum_debug_alloc79
-rw-r--r--unittest/t16_8.c105
-rw-r--r--unittest/t16_8_short.c94
-rw-r--r--unittest/t48_8.c111
-rw-r--r--unittest/t48_8_short.c84
-rw-r--r--unittest/t_helpers.c38
-rw-r--r--unittest/t_helpers.h41
-rw-r--r--unittest/tcohpsk.c286
-rw-r--r--unittest/tcontphase.c186
-rw-r--r--unittest/tdeframer.c148
-rw-r--r--unittest/tesno_est.c31
-rwxr-xr-xunittest/test_700c_eq.sh12
-rw-r--r--unittest/test_phi0.c78
-rw-r--r--unittest/tfdmdv.c286
-rw-r--r--unittest/tfifo.c106
-rw-r--r--unittest/tfmfsk.c198
-rw-r--r--unittest/tfreedv_2400A_rawdata.c114
-rw-r--r--unittest/tfreedv_2400B_rawdata.c114
-rw-r--r--unittest/tfreedv_800XA_rawdata.c147
-rw-r--r--unittest/tfreedv_data_channel.c315
-rw-r--r--unittest/tfsk.c232
-rw-r--r--unittest/tfsk_llr.c66
-rw-r--r--unittest/thash.c19
-rwxr-xr-xunittest/tnc1_high_snr.sh103
-rwxr-xr-xunittest/tnc4_high_snr_ping.sh114
-rw-r--r--unittest/tnewamp1.c301
-rw-r--r--unittest/tnlp.c164
-rw-r--r--unittest/tofdm.c583
-rw-r--r--unittest/tofdm_acq.c92
-rw-r--r--unittest/tprede.c53
-rw-r--r--unittest/tqam16.c37
-rw-r--r--unittest/tquisk_filter.c48
-rw-r--r--unittest/tst_codec2_fft_init.c97
-rw-r--r--unittest/tvq_mbest.c31
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;
+}