diff --git a/configure.ac b/configure.ac index c6730cd6..9118a475 100644 --- a/configure.ac +++ b/configure.ac @@ -126,6 +126,12 @@ AC_FLDIGI_OPT # Substitute RDYNAMIC in Makefile AC_FLDIGI_DEBUG +### benchmark mode +# Set ac_cv_benchmark to yes/no +# Define BENCHMARK_MODE in config.h +# Set ENABLE_BENCHMARK Makefile conditional +AC_FLDIGI_BENCHMARK + ### TLS flag # Set ac_cv_tls to yes/no # Define USE_TLS in config.h diff --git a/m4/benchmark.m4 b/m4/benchmark.m4 new file mode 100644 index 00000000..c8cbda97 --- /dev/null +++ b/m4/benchmark.m4 @@ -0,0 +1,17 @@ +AC_DEFUN([AC_FLDIGI_BENCHMARK], [ + AC_ARG_ENABLE([benchmark], + AC_HELP_STRING([--enable-benchmark], [build for benchmark-only operation]), + [case "${enableval}" in + yes|no) ac_cv_benchmark="${enableval}" ;; + *) AC_MSG_ERROR([bad value ${enableval} for --enable-benchmark]) ;; + esac], + [ac_cv_benchmark=no]) + + if test "x$ac_cv_benchmark" = "xyes"; then + AC_DEFINE(BENCHMARK_MODE, 1, [Defined if we are building for benchmarking]) + else + AC_DEFINE(BENCHMARK_MODE, 0, [Defined if we are building for benchmarking]) + fi + + AM_CONDITIONAL([ENABLE_BENCHMARK], [test "x$ac_cv_benchmark" = "xyes"]) +]) diff --git a/src/Makefile.am b/src/Makefile.am index 9145a915..d3edf2ff 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -24,9 +24,10 @@ HAMLIB_SRC = include/hamlib.h rigcontrol/hamlib.cxx include/rigclass.h rigcontro XMLRPC_SRC = include/xmlrpc.h misc/xmlrpc.cxx WIN32_RES_SRC = fldigirc.rc LOCATOR_SRC = misc/locator.c +BENCHMARK_SRC = include/benchmark.h misc/benchmark.cxx # We distribute these but do not always compile them -EXTRA_fldigi_SOURCES = $(HAMLIB_SRC) $(XMLRPC_SRC) $(WIN32_RES_SRC) $(LOCATOR_SRC) +EXTRA_fldigi_SOURCES = $(HAMLIB_SRC) $(XMLRPC_SRC) $(WIN32_RES_SRC) $(LOCATOR_SRC) $(BENCHMARK_SRC) fldigi_SOURCES = @@ -48,6 +49,10 @@ if ENABLE_XMLRPC fldigi_SOURCES += $(XMLRPC_SRC) endif +if ENABLE_BENCHMARK + fldigi_SOURCES += $(BENCHMARK_SRC) +endif + FLDIGI_VERSION_MAJOR = @FLDIGI_VERSION_MAJOR@ FLDIGI_VERSION_MINOR = @FLDIGI_VERSION_MINOR@ FLDIGI_VERSION_PATCH = @FLDIGI_VERSION_PATCH@ diff --git a/src/dialogs/fl_digi.cxx b/src/dialogs/fl_digi.cxx index e5915d9c..87096c3d 100644 --- a/src/dialogs/fl_digi.cxx +++ b/src/dialogs/fl_digi.cxx @@ -112,6 +112,9 @@ #if USE_XMLRPC # include "xmlrpc.h" #endif +#if BENCHMARK_MODE +# include "benchmark.h" +#endif #include "debug.h" #include "re.h" #include "network.h" @@ -486,6 +489,9 @@ static void default_cursor(void*) void startup_modem(modem *m) { trx_start_modem(m); +#if BENCHMARK_MODE + return; +#endif restoreFocus(); @@ -574,9 +580,6 @@ void init_modem(trx_mode mode) { ENSURE_THREAD(FLMAIN_TID); - quick_change = 0; - modem_config_tab = tabsModems->child(0); - switch (mode) { case MODE_NEXT: if ((mode = active_modem->get_mode() + 1) == NUM_MODES) @@ -691,6 +694,13 @@ void init_modem(trx_mode mode) return init_modem(MODE_BPSK31); } +#if BENCHMARK_MODE + return; +#endif + + quick_change = 0; + modem_config_tab = tabsModems->child(0); + clear_StatusMessages(); progStatus.lastmode = mode; @@ -2907,6 +2917,14 @@ void set_zdata(complex *zarray, int len) void put_rx_char(unsigned int data) { +#if BENCHMARK_MODE + if (!benchmark.output.empty()) { + if (unlikely(benchmark.buffer.length() + 16 > benchmark.buffer.capacity())) + benchmark.buffer.reserve(benchmark.buffer.capacity() + BUFSIZ); + benchmark.buffer += (char)data; + } + return; +#endif static unsigned int last = 0; const char **asc = ascii; trx_mode mode = active_modem->get_mode(); diff --git a/src/include/benchmark.h b/src/include/benchmark.h new file mode 100644 index 00000000..331a92d6 --- /dev/null +++ b/src/include/benchmark.h @@ -0,0 +1,22 @@ +#ifndef BENCHMARK_H_ +#define BENCHMARK_H_ + +#include +#include + +struct benchmark_params { + trx_mode modem; + int freq; + bool afc, sql; + double sqlevel; + double src_ratio; + int src_type; + std::string input, output, buffer; + size_t samples; +}; +extern struct benchmark_params benchmark; + +int setup_benchmark(void); +void do_benchmark(void); + +#endif diff --git a/src/include/main.h b/src/include/main.h index b1649458..4696b4f1 100644 --- a/src/include/main.h +++ b/src/include/main.h @@ -1,6 +1,7 @@ #ifndef _MAIN_H #define _MAIN_H +#include #include #include #include diff --git a/src/include/qrunner.h b/src/include/qrunner.h index e3f7b302..e4186280 100644 --- a/src/include/qrunner.h +++ b/src/include/qrunner.h @@ -156,6 +156,13 @@ public: extern qrunner *cbq[NUM_QRUNNER_THREADS]; +#if BENCHMARK_MODE +#define REQ(...) ((void)0) +#define REQ_DROP(...) ((void)0) +#define REQ_SYNC(...) ((void)0) +#define REQ_FLUSH(...) ((void)0) +#define QRUNNER_DROP(...) ((void)0) +#else #define REQ REQ_ASYNC #define REQ_DROP REQ_ASYNC_DROP @@ -212,6 +219,7 @@ extern qrunner *cbq[NUM_QRUNNER_THREADS]; if ((GET_THREAD_ID() != FLMAIN_TID)) \ cbq[GET_THREAD_ID()]->drop_flag = v_; \ } while (0) +#endif // BENCHMARK_MODE #endif // QRUNNER_H_ diff --git a/src/include/timeops.h b/src/include/timeops.h index 1d1119d3..635a192c 100644 --- a/src/include/timeops.h +++ b/src/include/timeops.h @@ -3,6 +3,7 @@ #include #include +#include #if !HAVE_CLOCK_GETTIME enum clockid_t { CLOCK_REALTIME, CLOCK_MONOTONIC }; @@ -13,7 +14,14 @@ int clock_gettime(clockid_t clock_id, struct timespec* tp); struct timespec operator+(const struct timespec &t0, const double &t); struct timespec operator-(const struct timespec &t0, const struct timespec &t1); +struct timespec& operator-=(struct timespec &t0, const struct timespec &t1); bool operator>(const struct timespec &t0, const struct timespec &t1); bool operator==(const struct timespec &t0, const struct timespec &t1); +struct timeval operator+(const struct timeval &t0, const double &t); +struct timeval operator-(const struct timeval &t0, const struct timeval &t1); +struct timeval& operator-=(struct timeval &t0, const struct timeval &t1); +bool operator>(const struct timeval &t0, const struct timeval &t1); +bool operator==(const struct timeval &t0, const struct timeval &t1); + #endif // TIMEOPS_H_ diff --git a/src/main.cxx b/src/main.cxx index 401cf026..88a8ee86 100644 --- a/src/main.cxx +++ b/src/main.cxx @@ -85,6 +85,10 @@ #include "xmlrpc.h" #endif +#if BENCHMARK_MODE + #include "benchmark.h" +#endif + using namespace std; string appname; @@ -253,6 +257,11 @@ int main(int argc, char ** argv) Fl::scheme(progdefaults.ui_scheme.c_str()); create_fl_digi_main(); + +#if BENCHMARK_MODE + return setup_benchmark(); +#endif + FSEL::create(); createConfig(); @@ -372,6 +381,45 @@ void generate_option_help(void) { << " List all available methods\n\n" #endif +#if BENCHMARK_MODE + << " --benchmark-modem ID\n" + << " Specify the modem\n" + << " Default: " << mode_info[benchmark.modem].sname << "\n\n" + << " --benchmark-frequency FREQ\n" + << " Specify the modem frequency\n" + << " Default: " << benchmark.freq << "\n\n" + << " --benchmark-afc BOOLEAN\n" + << " Set modem AFC\n" + << " Default: " << benchmark.afc + << " (" << boolalpha << benchmark.afc << noboolalpha << ")\n\n" + << " --benchmark-squelch BOOLEAN\n" + << " Set modem squelch\n" + << " Default: " << benchmark.sql + << " (" << boolalpha << benchmark.sql << noboolalpha << ")\n\n" + << " --benchmark-squelch-level LEVEL\n" + << " Set modem squelch level\n" + << " Default: " << benchmark.sqlevel << " (%)\n\n" + << " --benchmark-input INPUT\n" + << " Specify the input\n" + << " Must be a positive integer indicating the number of samples\n" + " of silence to generate as the input" +# if USE_SNDFILE + ", or a filename containing\n" + " non-digit characters" +#endif + "\n\n" + + << " --benchmark-output FILE\n" + << " Specify the output data file\n" + << " Default: decoder output is discarded\n\n" + << " --benchmark-src-ratio RATIO\n" + << " Specify the sample rate conversion ratio\n" + << " Default: 1.0 (input is not resampled)\n\n" + << " --benchmark-src-type TYPE\n" + << " Specify the sample rate conversion type\n" + << " Default: " << benchmark.src_type << " (" << src_get_name(benchmark.src_type) << ")\n\n" +#endif + << " --debug-level LEVEL\n" << " Set the event log verbosity\n\n" @@ -448,6 +496,13 @@ int parse_args(int argc, char **argv, int& idx) OPT_CONFIG_XMLRPC_ADDRESS, OPT_CONFIG_XMLRPC_PORT, OPT_CONFIG_XMLRPC_ALLOW, OPT_CONFIG_XMLRPC_DENY, OPT_CONFIG_XMLRPC_LIST, #endif + +#if BENCHMARK_MODE + OPT_BENCHMARK_MODEM, OPT_BENCHMARK_AFC, OPT_BENCHMARK_SQL, OPT_BENCHMARK_SQLEVEL, + OPT_BENCHMARK_FREQ, OPT_BENCHMARK_INPUT, OPT_BENCHMARK_OUTPUT, + OPT_BENCHMARK_SRC_RATIO, OPT_BENCHMARK_SRC_TYPE, +#endif + OPT_FONT, OPT_WFALL_HEIGHT, OPT_WFALL_WIDTH, OPT_WINDOW_WIDTH, OPT_WINDOW_HEIGHT, // OPT_TOGGLE_CHECK, @@ -478,6 +533,19 @@ int parse_args(int argc, char **argv, int& idx) { "xmlrpc-deny", 1, 0, OPT_CONFIG_XMLRPC_DENY }, { "xmlrpc-list", 0, 0, OPT_CONFIG_XMLRPC_LIST }, #endif + +#if BENCHMARK_MODE + { "benchmark-modem", 1, 0, OPT_BENCHMARK_MODEM }, + { "benchmark-frequency", 1, 0, OPT_BENCHMARK_FREQ }, + { "benchmark-afc", 1, 0, OPT_BENCHMARK_AFC }, + { "benchmark-squelch", 1, 0, OPT_BENCHMARK_SQL }, + { "benchmark-squelch-level", 1, 0, OPT_BENCHMARK_SQLEVEL }, + { "benchmark-input", 1, 0, OPT_BENCHMARK_INPUT }, + { "benchmark-output", 1, 0, OPT_BENCHMARK_OUTPUT }, + { "benchmark-src-ratio", 1, 0, OPT_BENCHMARK_SRC_RATIO }, + { "benchmark-src-type", 1, 0, OPT_BENCHMARK_SRC_TYPE }, +#endif + { "font", 1, 0, OPT_FONT }, { "wfall-width", 1, 0, OPT_WFALL_WIDTH }, @@ -564,6 +632,52 @@ int parse_args(int argc, char **argv, int& idx) exit(EXIT_SUCCESS); #endif +#if BENCHMARK_MODE + case OPT_BENCHMARK_MODEM: + benchmark.modem = strtol(optarg, NULL, 10); + if (!(benchmark.modem >= 0 && benchmark.modem < NUM_MODES)) { + cerr << "Bad modem id\n"; + exit(EXIT_FAILURE); + } + break; + + case OPT_BENCHMARK_FREQ: + benchmark.freq = strtol(optarg, NULL, 10); + if (benchmark.freq < 0) { + cerr << "Bad frequency\n"; + exit(EXIT_FAILURE); + } + break; + + case OPT_BENCHMARK_AFC: + benchmark.afc = strtol(optarg, NULL, 10); + break; + + case OPT_BENCHMARK_SQL: + benchmark.sql = strtol(optarg, NULL, 10); + break; + + case OPT_BENCHMARK_SQLEVEL: + benchmark.sqlevel = strtod(optarg, NULL); + break; + + case OPT_BENCHMARK_INPUT: + benchmark.input = optarg; + break; + + case OPT_BENCHMARK_OUTPUT: + benchmark.output = optarg; + break; + + case OPT_BENCHMARK_SRC_RATIO: + benchmark.src_ratio = strtod(optarg, NULL); + break; + + case OPT_BENCHMARK_SRC_TYPE: + benchmark.src_type = strtol(optarg, NULL, 10); + break; +#endif + case OPT_FONT: { char *p; diff --git a/src/misc/benchmark.cxx b/src/misc/benchmark.cxx new file mode 100644 index 00000000..fa0776ba --- /dev/null +++ b/src/misc/benchmark.cxx @@ -0,0 +1,281 @@ +// ---------------------------------------------------------------------------- +// benchmark.cxx +// +// Copyright (C) 2009 +// Stelios Bounanos, M0GLD +// +// This file is part of fldigi. +// +// fldigi is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 3 of the License, or +// (at your option) any later version. +// +// fldigi 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 General Public License +// along with this program. If not, see . +// ---------------------------------------------------------------------------- + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#if USE_SNDFILE +# include +#endif + +#include "modem.h" +#include "trx.h" +#include "timeops.h" +#include "configuration.h" +#include "status.h" +#include "debug.h" + +#include "benchmark.h" + +struct benchmark_params benchmark = { MODE_BPSK31, 1000, false, false, 0.0, 1.0, SRC_SINC_FASTEST }; + + +int setup_benchmark(void) +{ + ENSURE_THREAD(FLMAIN_TID); + + if (benchmark.input.empty()) { + LOG_ERROR("Missing input"); + return 1; + } + else { + char* p; + benchmark.samples = (size_t)strtol(benchmark.input.c_str(), &p, 10); + if (*p != '\0') { // invalid char in input string +#if USE_SNDFILE + // treat as filename + benchmark.samples = 0; +#else + LOG_ERROR("Bad input string, \"%s\"", benchmark.input.c_str()); + return 1; +#endif + } + } + if (!benchmark.output.empty()) + benchmark.buffer.reserve(BUFSIZ); + + progdefaults.rsid = false; + progdefaults.StartAtSweetSpot = false; + + if (benchmark.modem != NUM_MODES) + progStatus.lastmode = benchmark.modem; + if (benchmark.freq) + progStatus.carrier = benchmark.freq; + progStatus.afconoff = benchmark.afc; + progStatus.sqlonoff = benchmark.sql; + progStatus.sldrSquelchValue = benchmark.sqlevel; + + debug::level = debug::INFO_LEVEL; + TRX_WAIT(STATE_ENDED, trx_start(); init_modem(progStatus.lastmode)); + if (!benchmark.output.empty()) { + ofstream out(benchmark.output.c_str()); + if (out) + out << benchmark.buffer; + } + + return 0; +} + +#if USE_SNDFILE +SNDFILE* infile = 0; +#endif + +static size_t do_rx(struct rusage ru[2], struct timespec wall_time[2]); +static size_t do_rx_src(struct rusage ru[2], struct timespec wall_time[2]); + +void do_benchmark(void) +{ + ENSURE_THREAD(TRX_TID); + + if (benchmark.src_ratio != 1.0) + LOG_INFO("modem=%" PRIdPTR " (%s) rate=%d ratio=%f converter=%d (\"%s\")", + active_modem->get_mode(), mode_info[active_modem->get_mode()].sname, + active_modem->get_samplerate(), + benchmark.src_ratio, benchmark.src_type, src_get_name(benchmark.src_type)); + else + LOG_INFO("modem=%" PRIdPTR " (%s) rate=%d", active_modem->get_mode(), + mode_info[active_modem->get_mode()].sname, active_modem->get_samplerate()); + +#if USE_SNDFILE + if (!benchmark.samples) { + SF_INFO info = { 0, 0, 0, 0, 0, 0 }; + if ((infile = sf_open(benchmark.input.c_str(), SFM_READ, &info)) == NULL) { + LOG_ERROR("Could not open input file \"%s\"", benchmark.input.c_str()); + return; + } + } +#endif + + struct rusage ru[2]; + struct timespec wall_time[2]; + size_t nproc, nrx; + if (benchmark.src_ratio == 1.0) + nrx = nproc = do_rx(ru, wall_time); + else { + nproc = do_rx_src(ru, wall_time); + nrx = (size_t)(nproc * benchmark.src_ratio); + } + ru[1].ru_utime -= ru[0].ru_utime; + wall_time[1] -= wall_time[0]; + +#if USE_SNDFILE + if (infile) { + sf_close(infile); + infile = 0; + } +#endif + + LOG_INFO("processed: %zu samples (decoded %zu) in %.3f seconds", nproc, nrx, + wall_time[1].tv_sec + wall_time[1].tv_nsec / 1e9); + double speed = nproc / (ru[1].ru_utime.tv_sec + ru[1].ru_utime.tv_usec / 1e6); + LOG_INFO("cpu time : %jd.%03jd; speed=%.3f samples/s; factor=%.3f", + (intmax_t)ru[1].ru_utime.tv_sec, (intmax_t)ru[1].ru_utime.tv_usec / 1000, + speed, speed / active_modem->get_samplerate()); +} + +// ----------------------------------------------------------------------------- + +static size_t do_rx(struct rusage ru[2], struct timespec wall_time[2]) +{ + size_t nread; + size_t inlen = 1 << 19; + double* inbuf = new double[inlen]; + +#if USE_SNDFILE + if (infile) { + nread = 0; + clock_gettime(CLOCK_MONOTONIC, &wall_time[0]); + getrusage(RUSAGE_SELF, &ru[0]); + + for (size_t n; (n = sf_readf_double(infile, inbuf, inlen)); nread += n) + active_modem->rx_process(inbuf, n); + } + else +#endif + { + memset(inbuf, 0, sizeof(double) * inlen); + clock_gettime(CLOCK_MONOTONIC, &wall_time[0]); + getrusage(RUSAGE_SELF, &ru[0]); + + for (nread = benchmark.samples; nread > inlen; nread -= inlen) + active_modem->rx_process(inbuf, inlen); + if (nread) + active_modem->rx_process(inbuf, nread); + nread = benchmark.samples; + } + + getrusage(RUSAGE_SELF, &ru[1]); + clock_gettime(CLOCK_MONOTONIC, &wall_time[1]); + + delete [] inbuf; + return nread; +} + + +size_t inlen = 1 << 19; +static float* inbuf = 0; +static long src_read(void* arg, float** data) +{ + *data = inbuf; + return inlen; +} +#if USE_SNDFILE +static long src_readf(void* arg, float** data) +{ + long n = (long)sf_readf_float(infile, inbuf, inlen); + *data = n ? inbuf : 0; + return n; +} +#endif + +static size_t do_rx_src(struct rusage ru[2], struct timespec wall_time[2]) +{ + int err; + SRC_STATE* src_state; + +#if USE_SNDFILE + if (infile) + src_state = src_callback_new(src_readf, benchmark.src_type, 1, &err, NULL); + else +#endif + src_state = src_callback_new(src_read, benchmark.src_type, 1, &err, NULL); + + if (!src_state) { + LOG_ERROR("src_callback_new error %d: %s", err, src_strerror(err)); + return 0; + } + + inbuf = new float[inlen]; + size_t outlen = (size_t)floor(inlen * benchmark.src_ratio); + float* outbuf = new float[outlen]; + double* rxbuf = new double[outlen]; + + long n; + size_t nread; +#if USE_SNDFILE + if (infile) { // read until src returns 0 + nread = 0; + clock_gettime(CLOCK_MONOTONIC, &wall_time[0]); + getrusage(RUSAGE_SELF, &ru[0]); + + while ((n = src_callback_read(src_state, benchmark.src_ratio, outlen, outbuf))) { + for (long i = 0; i < n; i++) + rxbuf[i] = outbuf[i]; + active_modem->rx_process(rxbuf, n); + nread += n; + } + + nread = (size_t)round(nread * benchmark.src_ratio); + } + else +#endif + { // read benchmark.samples * benchmark.src_ratio + nread = (size_t)round(benchmark.samples * benchmark.src_ratio); + clock_gettime(CLOCK_MONOTONIC, &wall_time[0]); + getrusage(RUSAGE_SELF, &ru[0]); + + while (nread > outlen) { + if ((n = src_callback_read(src_state, benchmark.src_ratio, outlen, outbuf)) == 0) + break; + for (long i = 0; i < n; i++) + rxbuf[i] = outbuf[i]; + active_modem->rx_process(rxbuf, n); + nread -= (size_t)n; + } + if (nread) { + if ((n = src_callback_read(src_state, benchmark.src_ratio, nread, outbuf))) { + for (long i = 0; i < n; i++) + rxbuf[i] = outbuf[i]; + active_modem->rx_process(rxbuf, n); + } + } + nread = benchmark.samples; + } + + getrusage(RUSAGE_SELF, &ru[1]); + clock_gettime(CLOCK_MONOTONIC, &wall_time[1]); + + delete [] inbuf; + delete [] outbuf; + delete [] rxbuf; + + return nread; +} diff --git a/src/misc/status.cxx b/src/misc/status.cxx index 3eec4a0a..97a1b950 100644 --- a/src/misc/status.cxx +++ b/src/misc/status.cxx @@ -305,9 +305,7 @@ void status::initLastState() if (!bLastStateRead) loadLastState(); - init_modem(lastmode); - - while (!active_modem) MilliSleep(100); + init_modem_sync(lastmode); wf->opmode(); wf->Mag(mag); diff --git a/src/misc/timeops.cxx b/src/misc/timeops.cxx index 7bce450e..b670f161 100644 --- a/src/misc/timeops.cxx +++ b/src/misc/timeops.cxx @@ -68,6 +68,18 @@ struct timespec operator-(const struct timespec &t0, const struct timespec &t1) return r; } +struct timespec& operator-=(struct timespec &t0, const struct timespec &t1) +{ + if (t0.tv_nsec < t1.tv_nsec) { + --t0.tv_sec; + t0.tv_nsec += 1000000000L; + } + t0.tv_sec -= t1.tv_sec; + t0.tv_nsec -= t1.tv_nsec; + + return t0; +} + bool operator>(const struct timespec &t0, const struct timespec &t1) { if (t0.tv_sec == t1.tv_sec) @@ -82,3 +94,57 @@ bool operator==(const struct timespec &t0, const struct timespec &t1) { return t0.tv_sec == t1.tv_sec && t0.tv_nsec == t1.tv_nsec; } + + +struct timeval operator+(const struct timeval &t0, const double &t) +{ + struct timeval r; + r.tv_sec = t0.tv_sec + static_cast(t); + r.tv_usec = t0.tv_usec + static_cast((t - static_cast(t)) * 1e9); + if (r.tv_usec > 1000000) { + r.tv_usec -= 1000000; + r.tv_sec++; + } + return r; +} + +struct timeval operator-(const struct timeval &t0, const struct timeval &t1) +{ + struct timeval r = t0; + + if (r.tv_usec < t1.tv_usec) { + --r.tv_sec; + r.tv_usec += 1000000; + } + r.tv_sec -= t1.tv_sec; + r.tv_usec -= t1.tv_usec; + + return r; +} + +struct timeval& operator-=(struct timeval &t0, const struct timeval &t1) +{ + if (t0.tv_usec < t1.tv_usec) { + --t0.tv_sec; + t0.tv_usec += 1000000L; + } + t0.tv_sec -= t1.tv_sec; + t0.tv_usec -= t1.tv_usec; + + return t0; +} + +bool operator>(const struct timeval &t0, const struct timeval &t1) +{ + if (t0.tv_sec == t1.tv_sec) + return t0.tv_usec > t1.tv_usec; + else if (t0.tv_sec > t1.tv_sec) + return true; + else + return false; +} + +bool operator==(const struct timeval &t0, const struct timeval &t1) +{ + return t0.tv_sec == t1.tv_sec && t0.tv_usec == t1.tv_usec; +} diff --git a/src/soundcard/sound.cxx b/src/soundcard/sound.cxx index e35501dc..650d8d4d 100644 --- a/src/soundcard/sound.cxx +++ b/src/soundcard/sound.cxx @@ -1829,17 +1829,19 @@ size_t SoundNull::Write_stereo(double* bufleft, double* bufright, size_t count) size_t SoundNull::Read(double *buf, size_t count) { - memset(buf, 0, count * sizeof(*buf)); - #if USE_SNDFILE - if (capture) - write_file(ofCapture, buf, count); if (playback) { read_file(ifPlayback, buf, count); if (progdefaults.EnableMixer) for (size_t i = 0; i < count; i++) buf[i] *= progStatus.RcvMixer; } + else +#endif + memset(buf, 0, count * sizeof(*buf)); +#if USE_SNDFILE + if (capture) + write_file(ofCapture, buf, count); #endif usleep((useconds_t)ceil((1e6 * count) / sample_frequency)); diff --git a/src/trx/modem.cxx b/src/trx/modem.cxx index 9df0ed6c..576f8813 100644 --- a/src/trx/modem.cxx +++ b/src/trx/modem.cxx @@ -111,8 +111,10 @@ void modem::init() else set_freq(progdefaults.PSKsweetspot); } else if (progStatus.carrier != 0) { - set_freq(progStatus.carrier); - progStatus.carrier = 0; + set_freq(progStatus.carrier); +#if !BENCHMARK_MODE + progStatus.carrier = 0; +#endif } else set_freq(wf->Carrier()); } diff --git a/src/trx/trx.cxx b/src/trx/trx.cxx index 550206e4..6b918a9e 100644 --- a/src/trx/trx.cxx +++ b/src/trx/trx.cxx @@ -47,6 +47,10 @@ #include "qrunner.h" #include "debug.h" +#if BENCHMARK_MODE +# include "benchmark.h" +#endif + LOG_SET_SOURCE(debug::LOG_MODEM); using namespace std; @@ -96,118 +100,127 @@ void trx_trx_receive_loop() int current_samplerate; assert(powerof2(SCBLOCKSIZE)); - if (!scard) { + if (unlikely(!active_modem)) { MilliSleep(10); return; } - if (active_modem) { - try { - current_samplerate = active_modem->get_samplerate(); - if (scard->Open(O_RDONLY, current_samplerate)) - REQ(sound_update, progdefaults.btnAudioIOis); - } - catch (const SndException& e) { - LOG_ERROR("%s", e.what()); - put_status(e.what(), 5); - scard->Close(); - if (e.error() == EBUSY && progdefaults.btnAudioIOis == SND_IDX_PORT) { - sound_close(); - sound_init(); - } - MilliSleep(1000); - return; - } - active_modem->rx_init(); - while (1) { - if (progdefaults.rsid == true && rsid_detecting == false) { - rsid_detecting = true; - try { - current_samplerate = ReedSolomon->samplerate(); - scard->Open(O_RDONLY, current_samplerate); - } - catch (const SndException& e) { - LOG_ERROR("%s", e.what()); - put_status(e.what(), 5); - scard->Close(); - if (e.error() == EBUSY && progdefaults.btnAudioIOis == SND_IDX_PORT) { - sound_close(); - sound_init(); - } - MilliSleep(1000); - return; - } - } - if (progdefaults.rsid == false && rsid_detecting == true) { - rsid_detecting = false; - active_modem->rx_init(); - try { - current_samplerate = active_modem->get_samplerate(); - scard->Open(O_RDONLY, current_samplerate); - } - catch (const SndException& e) { - LOG_ERROR("%s", e.what()); - put_status(e.what(), 5); - scard->Close(); - if (e.error() == EBUSY && progdefaults.btnAudioIOis == SND_IDX_PORT) { - sound_close(); - sound_init(); - } - MilliSleep(1000); - return; - } - } - // If we change to an 8000Hz modem while RSID is on we'll never detect anything. - // Toggle rsid_detecting so that the audio device is reopened with the ReedSolomon - // samplerate in the next loop iteration. - if (progdefaults.rsid && rsid_detecting && current_samplerate != ReedSolomon->samplerate()) - rsid_detecting = false; +#if BENCHMARK_MODE + do_benchmark(); + trx_state = STATE_ENDED; + return; +#endif + if (unlikely(!scard)) { + MilliSleep(10); + return; + } + + try { + current_samplerate = active_modem->get_samplerate(); + if (scard->Open(O_RDONLY, current_samplerate)) + REQ(sound_update, progdefaults.btnAudioIOis); + } + catch (const SndException& e) { + LOG_ERROR("%s", e.what()); + put_status(e.what(), 5); + scard->Close(); + if (e.error() == EBUSY && progdefaults.btnAudioIOis == SND_IDX_PORT) { + sound_close(); + sound_init(); + } + MilliSleep(1000); + return; + } + active_modem->rx_init(); + + while (1) { + if (progdefaults.rsid == true && rsid_detecting == false) { + rsid_detecting = true; try { - if (trxrb.write_space() == 0) // discard some old data - trxrb.read_advance(SCBLOCKSIZE); - trxrb.get_wv(rbvec); - numread = 0; - while (numread < SCBLOCKSIZE && trx_state == STATE_RX) - numread += scard->Read(rbvec[0].buf + numread, SCBLOCKSIZE - numread); + current_samplerate = ReedSolomon->samplerate(); + scard->Open(O_RDONLY, current_samplerate); } catch (const SndException& e) { - scard->Close(); LOG_ERROR("%s", e.what()); put_status(e.what(), 5); - MilliSleep(10); + scard->Close(); + if (e.error() == EBUSY && progdefaults.btnAudioIOis == SND_IDX_PORT) { + sound_close(); + sound_init(); + } + MilliSleep(1000); return; } - if (trx_state != STATE_RX) - break; - - trxrb.write_advance(numread); - REQ(&waterfall::sig_data, wf, rbvec[0].buf, numread, current_samplerate); - - if (!bHistory) { - if (rsid_detecting == true) - ReedSolomon->search(rbvec[0].buf, numread); - else - active_modem->rx_process(rbvec[0].buf, numread); + } + if (progdefaults.rsid == false && rsid_detecting == true) { + rsid_detecting = false; + active_modem->rx_init(); + try { + current_samplerate = active_modem->get_samplerate(); + scard->Open(O_RDONLY, current_samplerate); } - else { - bool afc = progStatus.afconoff; - progStatus.afconoff = false; - QRUNNER_DROP(true); - active_modem->HistoryON(true); - trxrb.get_rv(rbvec); - if (rbvec[0].len) - active_modem->rx_process(rbvec[0].buf, rbvec[0].len); - if (rbvec[1].len) - active_modem->rx_process(rbvec[1].buf, rbvec[1].len); - QRUNNER_DROP(false); - progStatus.afconoff = afc; - bHistory = false; - active_modem->HistoryON(false); + catch (const SndException& e) { + LOG_ERROR("%s", e.what()); + put_status(e.what(), 5); + scard->Close(); + if (e.error() == EBUSY && progdefaults.btnAudioIOis == SND_IDX_PORT) { + sound_close(); + sound_init(); + } + MilliSleep(1000); + return; } } - } else - MilliSleep(10); + // If we change to an 8000Hz modem while RSID is on we'll never detect anything. + // Toggle rsid_detecting so that the audio device is reopened with the ReedSolomon + // samplerate in the next loop iteration. + if (progdefaults.rsid && rsid_detecting && current_samplerate != ReedSolomon->samplerate()) + rsid_detecting = false; + + try { + if (trxrb.write_space() == 0) // discard some old data + trxrb.read_advance(SCBLOCKSIZE); + trxrb.get_wv(rbvec); + numread = 0; + while (numread < SCBLOCKSIZE && trx_state == STATE_RX) + numread += scard->Read(rbvec[0].buf + numread, SCBLOCKSIZE - numread); + } + catch (const SndException& e) { + scard->Close(); + LOG_ERROR("%s", e.what()); + put_status(e.what(), 5); + MilliSleep(10); + return; + } + if (trx_state != STATE_RX) + break; + + trxrb.write_advance(numread); + REQ(&waterfall::sig_data, wf, rbvec[0].buf, numread, current_samplerate); + + if (!bHistory) { + if (rsid_detecting == true) + ReedSolomon->search(rbvec[0].buf, numread); + else + active_modem->rx_process(rbvec[0].buf, numread); + } + else { + bool afc = progStatus.afconoff; + progStatus.afconoff = false; + QRUNNER_DROP(true); + active_modem->HistoryON(true); + trxrb.get_rv(rbvec); + if (rbvec[0].len) + active_modem->rx_process(rbvec[0].buf, rbvec[0].len); + if (rbvec[1].len) + active_modem->rx_process(rbvec[1].buf, rbvec[1].len); + QRUNNER_DROP(false); + progStatus.afconoff = afc; + bHistory = false; + active_modem->HistoryON(false); + } + } } @@ -338,6 +351,8 @@ void *trx_loop(void *args) delete scard; scard = 0; trx_state = STATE_ENDED; + // fall through + case STATE_ENDED: return 0; case STATE_RESTART: trx_reset_loop(); @@ -474,6 +489,7 @@ void trx_start_macro_timer() //============================================================================= void trx_start(void) { +#if !BENCHMARK_MODE if (trxrunning) { LOG(debug::ERROR_LEVEL, debug::LOG_MODEM, "trx already running!"); return; @@ -507,7 +523,8 @@ void trx_start(void) } ReedSolomon = new cRsId; - +#endif // !BENCHMARK_MODE + trx_state = STATE_RX; _trx_tune = 0; active_modem = 0;