mirror of
https://github.com/f4exb/sdrangel.git
synced 2024-12-23 18:15:45 -05:00
FreeDV demodulator: first working version
This commit is contained in:
parent
8c3791d517
commit
e5931ff2dc
@ -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);
|
||||||
|
fftfilt::cmplx z = delayedSample * m_agc.getStepValue();
|
||||||
|
Real demod = (z.real() + z.imag()) * 0.7;
|
||||||
|
|
||||||
if (m_audioMute)
|
pushSampleToDV((qint16) demod);
|
||||||
{
|
|
||||||
m_audioBuffer[m_audioBufferFill].r = 0;
|
|
||||||
m_audioBuffer[m_audioBufferFill].l = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
fftfilt::cmplx z = delayedSample * m_agc.getStepValue();
|
|
||||||
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;
|
|
||||||
|
|
||||||
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;
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
Loading…
Reference in New Issue
Block a user