1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
|
#include "Detector.h"
#include "JS8_Include/commons.h"
#include "JS8_Main/DriftingDateTime.h"
#include <QDateTime>
#include <QLoggingCategory>
#include <QMutexLocker>
#include <QtAlgorithms>
#include <algorithm>
#include <cmath>
/******************************************************************************/
// FIR Filter Coefficients
/******************************************************************************/
namespace {
// Filter coefficients for an FIR lowpass filter designed using ScopeFIR.
//
// fsample = 48000 Hz
// Ntaps = 49
// fc = 4500 Hz
// fstop = 6000 Hz
// Ripple = 1 dB
// Stop Atten = 40 dB
// fout = 12000 Hz
constexpr std::array LOWPASS{
0.000861074040f, 0.010051920210f, 0.010161983649f, 0.011363155076f,
0.008706594219f, 0.002613872664f, -0.005202883094f, -0.011720748164f,
-0.013752163325f, -0.009431602741f, 0.000539063909f, 0.012636767098f,
0.021494659597f, 0.021951235065f, 0.011564169382f, -0.007656470131f,
-0.028965787341f, -0.042637874109f, -0.039203309748f, -0.013153301537f,
0.034320769178f, 0.094717832646f, 0.154224604789f, 0.197758325022f,
0.213715139513f, 0.197758325022f, 0.154224604789f, 0.094717832646f,
0.034320769178f, -0.013153301537f, -0.039203309748f, -0.042637874109f,
-0.028965787341f, -0.007656470131f, 0.011564169382f, 0.021951235065f,
0.021494659597f, 0.012636767098f, 0.000539063909f, -0.009431602741f,
-0.013752163325f, -0.011720748164f, -0.005202883094f, 0.002613872664f,
0.008706594219f, 0.011363155076f, 0.010161983649f, 0.010051920210f,
0.000861074040f};
} // namespace
/******************************************************************************/
// Implementation
/******************************************************************************/
#include "moc_Detector.cpp"
Q_DECLARE_LOGGING_CATEGORY(detector_js8)
Detector::Detector(unsigned frameRate, unsigned periodLengthInSeconds,
QObject *parent)
: AudioDevice(parent), m_frameRate(frameRate),
m_period(periodLengthInSeconds), m_filter(LOWPASS) {
clear();
}
void Detector::setBlockSize(unsigned n) { m_samplesPerFFT = n; }
bool Detector::reset() {
clear();
// don't call base call reset because it calls seek(0) which causes
// a warning
return isOpen();
}
void Detector::clear() {
#if JS8_RING_BUFFER
resetBufferPosition();
resetBufferContent();
#else
dec_data.params.kin = 0;
m_bufferPos = 0;
#endif
// fill buffer with zeros (G4WJS commented out because it might cause
// decoder hangs) qFill (dec_data.d2, dec_data.d2 + sizeof (dec_data.d2) /
// sizeof (dec_data.d2[0]), 0);
}
void Detector::resetBufferPosition() {
QMutexLocker mutex(&m_lock);
// set index to roughly where we are in time (1ms resolution)
qint64 const now = DriftingDateTime::currentMSecsSinceEpoch();
unsigned const msInPeriod = (now % 86400000LL) % (m_period * 1000);
int const prevKin = dec_data.params.kin;
dec_data.params.kin = qMin(
(msInPeriod * m_frameRate) / 1000,
static_cast<unsigned>(sizeof(dec_data.d2) / sizeof(dec_data.d2[0])));
m_bufferPos = 0;
m_ns = secondInPeriod();
int const delta = dec_data.params.kin - prevKin;
qCDebug(detector_js8) << "advancing detector buffer from" << prevKin << "to"
<< dec_data.params.kin << "delta" << delta;
// rotate buffer moving the contents that were at prevKin to the new kin
// position
if (delta < 0) {
std::rotate(std::begin(dec_data.d2), std::begin(dec_data.d2) - delta,
std::end(dec_data.d2));
} else {
std::rotate(std::rbegin(dec_data.d2), std::rbegin(dec_data.d2) + delta,
std::rend(dec_data.d2));
}
}
void Detector::resetBufferContent() {
QMutexLocker mutex(&m_lock);
std::fill(std::begin(dec_data.d2), std::end(dec_data.d2), 0);
qCDebug(detector_js8) << "clearing detector buffer content";
}
qint64 Detector::writeData(char const *const data, qint64 const maxSize) {
QMutexLocker mutex(&m_lock);
// When ns has wrapped around to zero, restart the buffers.
int const ns = secondInPeriod();
if (ns < m_ns) {
dec_data.params.kin = 0;
m_bufferPos = 0;
}
m_ns = ns;
// No torn frames.
Q_ASSERT(!(maxSize % static_cast<qint64>(bytesPerFrame())));
// These are in terms of input frames (not down sampled).
size_t const framesAcceptable =
(sizeof(dec_data.d2) / sizeof(dec_data.d2[0]) - dec_data.params.kin) *
Filter::NDOWN;
size_t const framesAccepted =
qMin(static_cast<size_t>(maxSize / bytesPerFrame()), framesAcceptable);
if (framesAccepted < static_cast<size_t>(maxSize / bytesPerFrame())) {
qCDebug(detector_js8)
<< "dropped " << maxSize / bytesPerFrame() - framesAccepted
<< " frames of data on the floor!" << dec_data.params.kin << ns;
}
for (auto remaining = framesAccepted; remaining;) {
size_t const numFramesProcessed =
qMin(m_samplesPerFFT * Filter::NDOWN - m_bufferPos, remaining);
store(&data[(framesAccepted - remaining) * bytesPerFrame()],
numFramesProcessed, &m_buffer[m_bufferPos]);
m_bufferPos += numFramesProcessed;
if (m_bufferPos == m_samplesPerFFT * Filter::NDOWN) {
if (dec_data.params.kin >= 0 &&
dec_data.params.kin <
static_cast<int>(JS8_NTMAX * 12000 - m_samplesPerFFT)) {
for (std::size_t i = 0; i < m_samplesPerFFT; ++i) {
dec_data.d2[dec_data.params.kin++] =
m_filter.downSample(&m_buffer[i * Filter::NDOWN]);
}
}
Q_EMIT framesWritten(dec_data.params.kin);
m_bufferPos = 0;
}
remaining -= numFramesProcessed;
}
// We drop any data past the end of the buffer on the floor
// until the next period starts
return maxSize;
}
unsigned Detector::secondInPeriod() const {
// we take the time of the data as the following assuming no latency
// delivering it to us (not true but close enough for us)
qint64 now(DriftingDateTime::currentMSecsSinceEpoch());
unsigned secondInToday((now % 86400000LL) / 1000);
return secondInToday % m_period;
}
/******************************************************************************/
Q_LOGGING_CATEGORY(detector_js8, "detector.js8", QtWarningMsg)
|