kopia lustrzana https://github.com/jamescoxon/dl-fldigi
Add benchmark and batch decoding switches
This patch adds a new configure switch (--enable-benchmark) which builds a binary suitable for measuring the modems' decoding speed. The new --benchmark-* switches can also be used to batch-decode audio files if sndfile support is enabled.pull/2/head
rodzic
3ab8df2b6c
commit
f134124ddd
|
@ -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
|
||||
|
|
|
@ -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"])
|
||||
])
|
|
@ -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@
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
#ifndef BENCHMARK_H_
|
||||
#define BENCHMARK_H_
|
||||
|
||||
#include <string>
|
||||
#include <sys/types.h>
|
||||
|
||||
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
|
|
@ -1,6 +1,7 @@
|
|||
#ifndef _MAIN_H
|
||||
#define _MAIN_H
|
||||
|
||||
#include <config.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.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_
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include <config.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#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_
|
||||
|
|
114
src/main.cxx
114
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;
|
||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/resource.h>
|
||||
#include <time.h>
|
||||
|
||||
#if USE_SNDFILE
|
||||
# include <sndfile.h>
|
||||
#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;
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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<time_t>(t);
|
||||
r.tv_usec = t0.tv_usec + static_cast<suseconds_t>((t - static_cast<time_t>(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;
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
213
src/trx/trx.cxx
213
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;
|
||||
|
|
Ładowanie…
Reference in New Issue