Tweaks to new audio path code:

1. Move lambda usage for RX to member functions (adds readability and possibly fixes #250).
2. Use sync as well as SNR/squelch setting to determine whether to let audio through (reduces extra noise in speakers).
pull/246/head
Mooneer Salem 2022-07-01 16:21:21 -07:00
parent 5c05513e0f
commit d803330458
2 changed files with 140 additions and 99 deletions

View File

@ -26,6 +26,33 @@
#include "pipeline/FreeDVTransmitStep.h"
#include "pipeline/FreeDVReceiveStep.h"
using namespace std::placeholders;
static const char* GetCurrentModeStrImpl_(int mode)
{
switch(mode)
{
case FREEDV_MODE_700C:
return "700C";
case FREEDV_MODE_700D:
return "700D";
case FREEDV_MODE_700E:
return "700E";
case FREEDV_MODE_1600:
return "1600";
case FREEDV_MODE_2020:
return "2020";
case FREEDV_MODE_2020B:
return "2020B";
case FREEDV_MODE_800XA:
return "800XA";
case FREEDV_MODE_2400B:
return "2400B";
default:
return "unk";
}
}
FreeDVInterface::FreeDVInterface() :
textRxFunc_(nullptr),
singleRxThread_(false),
@ -588,113 +615,121 @@ IPipelineStep* FreeDVInterface::createReceivePipeline(
parallelSteps.push_back(recvStep);
}
auto preProcessFn = [&](ParallelStep* stepObj) {
int rxIndex = 0;
std::shared_ptr<ReceivePipelineState> state = std::static_pointer_cast<ReceivePipelineState>(stepObj->getState());
// Set initial state for each step prior to execution.
for (auto& step : stepObj->getParallelSteps())
{
assert(step != nullptr);
FreeDVReceiveStep* castedStep = (FreeDVReceiveStep*)step.get();
castedStep->setSigPwrAvg(*state->getSigPwrAvgFn());
castedStep->setChannelNoiseEnable(state->getChannelNoiseFn(), state->getChannelNoiseSnrFn());
castedStep->setFreqOffset(state->getFreqOffsetFn());
}
state->preProcessFn = std::bind(&FreeDVInterface::preProcessRxFn_, this, _1);
state->postProcessFn = std::bind(&FreeDVInterface::postProcessRxFn_, this, _1);
// If the current RX mode is still sync'd, only process through that one.
for (auto& dv : dvObjects_)
{
if (dv == currentRxMode_ && freedv_get_sync(currentRxMode_))
{
return rxIndex;
}
rxIndex++;
}
return -1;
};
auto postProcessFn = [&](ParallelStep* stepObj) {
std::shared_ptr<ReceivePipelineState> state = std::static_pointer_cast<ReceivePipelineState>(stepObj->getState());
// If the current RX mode is still sync'd, only let that one out.
int rxIndex = 0;
int indexWithSync = 0;
int maxSyncFound = -25;
struct freedv* dvWithSync = nullptr;
for (auto& dv : dvObjects_)
{
if (dv == currentRxMode_ && freedv_get_sync(currentRxMode_))
{
dvWithSync = dv;
indexWithSync = rxIndex;
goto skipSyncCheck;
}
rxIndex++;
}
// Otherwise, find the mode that's sync'd and has the highest SNR.
rxIndex = 0;
for (auto& dv : dvObjects_)
{
struct MODEM_STATS *tmpStats = &modemStatsList_[rxIndex];
freedv_get_modem_extended_stats(dv, tmpStats);
if (!(isnan(tmpStats->snr_est) || isinf(tmpStats->snr_est))) {
snrVals_[rxIndex] = 0.95*snrVals_[rxIndex] + (1.0 - 0.95)*tmpStats->snr_est;
}
int snr = (int)(snrVals_[rxIndex]+0.5);
if (snr > maxSyncFound && tmpStats->sync != 0)
{
maxSyncFound = snr;
indexWithSync = rxIndex;
dvWithSync = dv;
}
rxIndex++;
}
if (dvWithSync == nullptr)
{
// Default to the first DV object if there's no sync.
indexWithSync = 0;
dvWithSync = dvObjects_[0];
}
skipSyncCheck:
struct MODEM_STATS* stats = getCurrentRxModemStats();
// grab extended stats so we can plot spectrum, scatter diagram etc
freedv_get_modem_extended_stats(dvWithSync, stats);
// Update sync as it may have gone stale during decode
*state->getRxStateFn() = stats->sync != 0;
if (*state->getRxStateFn())
{
rxMode_ = enabledModes_[indexWithSync];
currentRxMode_ = dvWithSync;
lastSyncRxMode_ = currentRxMode_;
}
*state->getSigPwrAvgFn() = ((FreeDVReceiveStep*)stepObj->getParallelSteps()[indexWithSync].get())->getSigPwrAvg();
return indexWithSync;
};
auto parallelStep = new ParallelStep(
inputSampleRate,
outputSampleRate,
!singleRxThread_,
preProcessFn,
postProcessFn,
state->preProcessFn,
state->postProcessFn,
parallelSteps,
state
);
return parallelStep;
}
int FreeDVInterface::preProcessRxFn_(ParallelStep* stepObj)
{
int rxIndex = 0;
std::shared_ptr<ReceivePipelineState> state = std::static_pointer_cast<ReceivePipelineState>(stepObj->getState());
// Set initial state for each step prior to execution.
for (auto& step : stepObj->getParallelSteps())
{
assert(step != nullptr);
FreeDVReceiveStep* castedStep = (FreeDVReceiveStep*)step.get();
castedStep->setSigPwrAvg(*state->getSigPwrAvgFn());
castedStep->setChannelNoiseEnable(state->getChannelNoiseFn(), state->getChannelNoiseSnrFn());
castedStep->setFreqOffset(state->getFreqOffsetFn());
}
// If the current RX mode is still sync'd, only process through that one.
for (auto& dv : dvObjects_)
{
if (dv == currentRxMode_ && freedv_get_sync(currentRxMode_))
{
return rxIndex;
}
rxIndex++;
}
return -1;
};
int FreeDVInterface::postProcessRxFn_(ParallelStep* stepObj)
{
std::shared_ptr<ReceivePipelineState> state = std::static_pointer_cast<ReceivePipelineState>(stepObj->getState());
// If the current RX mode is still sync'd, only let that one out.
int rxIndex = 0;
int indexWithSync = 0;
int maxSyncFound = -25;
struct freedv* dvWithSync = nullptr;
for (auto& dv : dvObjects_)
{
if (dv == currentRxMode_ && freedv_get_sync(currentRxMode_))
{
dvWithSync = dv;
indexWithSync = rxIndex;
goto skipSyncCheck;
}
rxIndex++;
}
// Otherwise, find the mode that's sync'd and has the highest SNR.
rxIndex = 0;
for (auto& dv : dvObjects_)
{
struct MODEM_STATS *tmpStats = &modemStatsList_[rxIndex];
freedv_get_modem_extended_stats(dv, tmpStats);
if (!(isnan(tmpStats->snr_est) || isinf(tmpStats->snr_est))) {
snrVals_[rxIndex] = 0.95*snrVals_[rxIndex] + (1.0 - 0.95)*tmpStats->snr_est;
}
int snr = (int)(snrVals_[rxIndex]+0.5);
bool canUnsquelch = !squelchEnabled_ ||
(squelchEnabled_ && snr >= squelchVals_[rxIndex]);
if (snr > maxSyncFound && tmpStats->sync != 0 && canUnsquelch)
{
maxSyncFound = snr;
indexWithSync = rxIndex;
dvWithSync = dv;
}
rxIndex++;
}
if (dvWithSync == nullptr)
{
// Default to the first DV object if there's no sync.
indexWithSync = 0;
dvWithSync = dvObjects_[0];
}
skipSyncCheck:
struct MODEM_STATS* stats = getCurrentRxModemStats();
// grab extended stats so we can plot spectrum, scatter diagram etc
freedv_get_modem_extended_stats(dvWithSync, stats);
// Update sync as it may have gone stale during decode
*state->getRxStateFn() = stats->sync != 0;
if (*state->getRxStateFn())
{
rxMode_ = enabledModes_[indexWithSync];
currentRxMode_ = dvWithSync;
lastSyncRxMode_ = currentRxMode_;
}
*state->getSigPwrAvgFn() = ((FreeDVReceiveStep*)stepObj->getParallelSteps()[indexWithSync].get())->getSigPwrAvg();
return indexWithSync;
};

View File

@ -40,6 +40,7 @@
#include <samplerate.h>
class IPipelineStep;
class ParallelStep;
class FreeDVInterface
{
@ -120,6 +121,8 @@ private:
std::function<int()> getChannelNoiseSnrFn;
std::function<float()> getFreqOffsetFn;
std::function<float*()> getSigPwrAvgFn;
std::function<int(ParallelStep*)> preProcessFn;
std::function<int(ParallelStep*)> postProcessFn;
};
struct FreeDVTextFnState
@ -161,6 +164,9 @@ private:
std::deque<reliable_text_t> reliableText_;
std::string receivedReliableText_;
int preProcessRxFn_(ParallelStep* ps);
int postProcessRxFn_(ParallelStep* ps);
};
#endif // CODEC2_INTERFACE_H