FreeDV demodulator: first working version

This commit is contained in:
f4exb 2019-02-26 16:46:58 +01:00
parent 8c3791d517
commit e5931ff2dc
4 changed files with 79 additions and 52 deletions

View File

@ -49,6 +49,15 @@ const QString FreeDVDemod::m_channelId = "FreeDVDemod";
FreeDVDemod::FreeDVDemod(DeviceSourceAPI *deviceAPI) : FreeDVDemod::FreeDVDemod(DeviceSourceAPI *deviceAPI) :
ChannelSinkAPI(m_channelIdURI), ChannelSinkAPI(m_channelIdURI),
m_deviceAPI(deviceAPI), m_deviceAPI(deviceAPI),
m_hiCutoff(5000),
m_lowCutoff(300),
m_volume(2),
m_spanLog2(3),
m_sum(0),
m_inputSampleRate(48000),
m_modemSampleRate(48000),
m_speechSampleRate(8000), // fixed 8 kS/s
m_inputFrequencyOffset(0),
m_audioMute(false), m_audioMute(false),
m_agc(12000, agcTarget, 1e-2), m_agc(12000, agcTarget, 1e-2),
m_agcActive(false), m_agcActive(false),
@ -60,7 +69,6 @@ FreeDVDemod::FreeDVDemod(DeviceSourceAPI *deviceAPI) :
m_audioActive(false), m_audioActive(false),
m_sampleSink(0), m_sampleSink(0),
m_audioFifo(24000), m_audioFifo(24000),
m_modemSampleRate(48000),
m_freeDV(0), m_freeDV(0),
m_nSpeechSamples(0), m_nSpeechSamples(0),
m_nMaxModemSamples(0), m_nMaxModemSamples(0),
@ -73,20 +81,13 @@ FreeDVDemod::FreeDVDemod(DeviceSourceAPI *deviceAPI) :
{ {
setObjectName(m_channelId); setObjectName(m_channelId);
m_hiCutoff = 5000;
m_lowCutoff = 300;
m_volume = 2.0;
m_spanLog2 = 3;
m_inputSampleRate = 48000;
m_inputFrequencyOffset = 0;
DSPEngine::instance()->getAudioDeviceManager()->addAudioSink(&m_audioFifo, getInputMessageQueue()); DSPEngine::instance()->getAudioDeviceManager()->addAudioSink(&m_audioFifo, getInputMessageQueue());
m_audioSampleRate = DSPEngine::instance()->getAudioDeviceManager()->getOutputSampleRate(); uint32_t audioSampleRate = DSPEngine::instance()->getAudioDeviceManager()->getOutputSampleRate();
applyAudioSampleRate(audioSampleRate);
m_audioBuffer.resize(1<<14); m_audioBuffer.resize(1<<14);
m_audioBufferFill = 0; m_audioBufferFill = 0;
m_undersampleCount = 0; m_undersampleCount = 0;
m_sum = 0;
m_magsq = 0.0f; m_magsq = 0.0f;
m_magsqSum = 0.0f; m_magsqSum = 0.0f;
@ -212,34 +213,10 @@ void FreeDVDemod::feed(const SampleVector::const_iterator& begin, const SampleVe
fftfilt::cmplx& delayedSample = m_squelchDelayLine.readBack(m_agc.getStepDownDelay()); fftfilt::cmplx& delayedSample = m_squelchDelayLine.readBack(m_agc.getStepDownDelay());
m_audioActive = delayedSample.real() != 0.0; m_audioActive = delayedSample.real() != 0.0;
m_squelchDelayLine.write(sideband[i]*agcVal); m_squelchDelayLine.write(sideband[i]*agcVal);
if (m_audioMute)
{
m_audioBuffer[m_audioBufferFill].r = 0;
m_audioBuffer[m_audioBufferFill].l = 0;
}
else
{
fftfilt::cmplx z = delayedSample * m_agc.getStepValue(); fftfilt::cmplx z = delayedSample * m_agc.getStepValue();
Real demod = (z.real() + z.imag()) * 0.7; Real demod = (z.real() + z.imag()) * 0.7;
qint16 sample = (qint16)(demod * m_volume);
m_audioBuffer[m_audioBufferFill].l = sample;
m_audioBuffer[m_audioBufferFill].r = sample;
}
++m_audioBufferFill; pushSampleToDV((qint16) demod);
if (m_audioBufferFill >= m_audioBuffer.size())
{
uint res = m_audioFifo.write((const quint8*)&m_audioBuffer[0], m_audioBufferFill);
if (res != m_audioBufferFill)
{
qDebug("FreeDVDemod::feed: %u/%u samples written", res, m_audioBufferFill);
}
m_audioBufferFill = 0;
}
} }
} }
@ -341,6 +318,50 @@ bool FreeDVDemod::handleMessage(const Message& cmd)
} }
} }
void FreeDVDemod::pushSampleToDV(int16_t sample)
{
qint16 speechSample, audioSample;
if (m_iModem == m_nin)
{
int nout = freedv_rx(m_freeDV, m_speechOut, m_modIn);
for (int i = 0; i < nout; i++)
{
speechSample = (qint16)(m_speechOut[i] * m_volume);
while (!m_audioResampler.upSample(speechSample, audioSample)) {
pushSampleToAudio(audioSample);
}
pushSampleToAudio(audioSample);
}
m_iModem = 0;
m_iSpeech = 0;
}
m_modIn[m_iModem++] = sample;
}
void FreeDVDemod::pushSampleToAudio(int16_t sample)
{
m_audioBuffer[m_audioBufferFill].l = sample;
m_audioBuffer[m_audioBufferFill].r = sample;
++m_audioBufferFill;
if (m_audioBufferFill >= m_audioBuffer.size())
{
uint res = m_audioFifo.write((const quint8*)&m_audioBuffer[0], m_audioBufferFill);
if (res != m_audioBufferFill) {
qDebug("FreeDVDemod::pushSampleToAudio: %u/%u samples written", res, m_audioBufferFill);
}
m_audioBufferFill = 0;
}
}
void FreeDVDemod::applyChannelSettings(int inputSampleRate, int inputFrequencyOffset, bool force) void FreeDVDemod::applyChannelSettings(int inputSampleRate, int inputFrequencyOffset, bool force)
{ {
qDebug() << "FreeDVDemod::applyChannelSettings:" qDebug() << "FreeDVDemod::applyChannelSettings:"
@ -372,6 +393,8 @@ void FreeDVDemod::applyAudioSampleRate(int sampleRate)
m_settingsMutex.lock(); m_settingsMutex.lock();
m_audioFifo.setSize(sampleRate); m_audioFifo.setSize(sampleRate);
m_audioResampler.setDecimation(sampleRate / m_speechSampleRate);
m_audioResampler.setAudioFilters(sampleRate, m_speechSampleRate, 250, 3300);
m_settingsMutex.unlock(); m_settingsMutex.unlock();
m_audioSampleRate = sampleRate; m_audioSampleRate = sampleRate;
@ -473,7 +496,7 @@ void FreeDVDemod::applyFreeDVMode(FreeDVDemodSettings::FreeDVMode mode)
freedv_set_ext_vco(m_freeDV, 0); freedv_set_ext_vco(m_freeDV, 0);
int nSpeechSamples = freedv_get_n_speech_samples(m_freeDV); int nSpeechSamples = freedv_get_n_speech_samples(m_freeDV);
int nNomModemSamples = freedv_get_n_max_modem_samples(m_freeDV); int nMaxModemSamples = freedv_get_n_max_modem_samples(m_freeDV);
int Fs = freedv_get_modem_sample_rate(m_freeDV); int Fs = freedv_get_modem_sample_rate(m_freeDV);
int Rs = freedv_get_modem_symbol_rate(m_freeDV); int Rs = freedv_get_modem_symbol_rate(m_freeDV);
@ -483,18 +506,18 @@ void FreeDVDemod::applyFreeDVMode(FreeDVDemodSettings::FreeDVMode mode)
delete[] m_speechOut; delete[] m_speechOut;
} }
m_speechOut = new int16_t[m_nSpeechSamples]; m_speechOut = new int16_t[nSpeechSamples];
m_nSpeechSamples = nSpeechSamples; m_nSpeechSamples = nSpeechSamples;
} }
if (nNomModemSamples != m_nMaxModemSamples) if (nMaxModemSamples != m_nMaxModemSamples)
{ {
if (m_modIn) { if (m_modIn) {
delete[] m_modIn; delete[] m_modIn;
} }
m_modIn = new int16_t[m_nMaxModemSamples]; m_modIn = new int16_t[nMaxModemSamples];
m_nMaxModemSamples = nNomModemSamples; m_nMaxModemSamples = nMaxModemSamples;
} }
m_iSpeech = 0; m_iSpeech = 0;

View File

@ -282,6 +282,9 @@ private:
fftfilt::cmplx m_sum; fftfilt::cmplx m_sum;
int m_undersampleCount; int m_undersampleCount;
int m_inputSampleRate; int m_inputSampleRate;
uint32_t m_modemSampleRate;
uint32_t m_speechSampleRate;
uint32_t m_audioSampleRate;
int m_inputFrequencyOffset; int m_inputFrequencyOffset;
bool m_audioMute; bool m_audioMute;
double m_magsq; double m_magsq;
@ -310,8 +313,6 @@ private:
AudioVector m_audioBuffer; AudioVector m_audioBuffer;
uint m_audioBufferFill; uint m_audioBufferFill;
AudioFifo m_audioFifo; AudioFifo m_audioFifo;
quint32 m_audioSampleRate;
quint32 m_modemSampleRate;
QNetworkAccessManager *m_networkManager; QNetworkAccessManager *m_networkManager;
QNetworkRequest m_networkRequest; QNetworkRequest m_networkRequest;
@ -329,6 +330,8 @@ private:
QMutex m_settingsMutex; QMutex m_settingsMutex;
void pushSampleToDV(int16_t sample);
void pushSampleToAudio(int16_t sample);
void applyChannelSettings(int inputSampleRate, int inputFrequencyOffset, bool force = false); void applyChannelSettings(int inputSampleRate, int inputFrequencyOffset, bool force = false);
void applySettings(const FreeDVDemodSettings& settings, bool force = false); void applySettings(const FreeDVDemodSettings& settings, bool force = false);
void applyAudioSampleRate(int sampleRate); void applyAudioSampleRate(int sampleRate);

View File

@ -65,30 +65,30 @@ bool AudioResampler::downSample(qint16 sampleIn, qint16& sampleOut)
} }
} }
qint16 AudioResampler::upSample(qint16 sampleIn, bool& consumed) bool AudioResampler::upSample(qint16 sampleIn, qint16& sampleOut)
{ {
float lpSample; float lpSample;
if (m_decimation == 1) if (m_decimation == 1)
{ {
consumed = true; sampleOut = sampleIn;
return sampleIn; return true;
} }
if (m_decimationCount >= m_decimation - 1) if (m_decimationCount >= m_decimation - 1)
{ {
consumed = true;
m_decimationCount = 0; m_decimationCount = 0;
lpSample = m_audioFilter.run(sampleIn / 32768.0f); lpSample = m_audioFilter.run(sampleIn / 32768.0f);
sampleOut = lpSample * 32768.0f;
return true;
} }
else else
{ {
consumed = false;
m_decimationCount++; m_decimationCount++;
lpSample = m_audioFilter.run(0.0f); lpSample = m_audioFilter.run(0.0f);
sampleOut = lpSample * 32768.0f;
return false;
} }
return lpSample * 32768.0f;
} }

View File

@ -28,9 +28,10 @@ public:
~AudioResampler(); ~AudioResampler();
void setDecimation(uint32_t decimation); void setDecimation(uint32_t decimation);
uint32_t getDecimation() const { return m_decimation; }
void setAudioFilters(int srHigh, int srLow, int fcLow, int fcHigh); void setAudioFilters(int srHigh, int srLow, int fcLow, int fcHigh);
bool downSample(qint16 sampleIn, qint16& sampleOut); bool downSample(qint16 sampleIn, qint16& sampleOut);
qint16 upSample(qint16 sampleIn, bool& consumed); bool upSample(qint16 sampleIn, qint16& sampleOut);
private: private:
AudioFilter m_audioFilter; AudioFilter m_audioFilter;