mirror of
https://github.com/f4exb/sdrangel.git
synced 2025-08-22 15:32:25 -04:00
FreeDV modulator: basic 2400A from file modulation
This commit is contained in:
parent
f481a28a6c
commit
e34faee3ce
@ -24,6 +24,7 @@ include_directories(
|
|||||||
.
|
.
|
||||||
${CMAKE_CURRENT_BINARY_DIR}
|
${CMAKE_CURRENT_BINARY_DIR}
|
||||||
${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client
|
${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client
|
||||||
|
${CODEC2_INCLUDE_DIR}
|
||||||
)
|
)
|
||||||
|
|
||||||
add_definitions(${QT_DEFINITIONS})
|
add_definitions(${QT_DEFINITIONS})
|
||||||
@ -43,6 +44,7 @@ target_link_libraries(modfreedv
|
|||||||
sdrbase
|
sdrbase
|
||||||
sdrgui
|
sdrgui
|
||||||
swagger
|
swagger
|
||||||
|
${CODEC2_LIBRARIES}
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(modfreedv Qt5::Core Qt5::Widgets)
|
target_link_libraries(modfreedv Qt5::Core Qt5::Widgets)
|
||||||
|
@ -27,6 +27,8 @@
|
|||||||
#include <complex.h>
|
#include <complex.h>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include "codec2/freedv_api.h"
|
||||||
|
|
||||||
#include "SWGChannelSettings.h"
|
#include "SWGChannelSettings.h"
|
||||||
#include "SWGChannelReport.h"
|
#include "SWGChannelReport.h"
|
||||||
#include "SWGFreeDVModReport.h"
|
#include "SWGFreeDVModReport.h"
|
||||||
@ -70,7 +72,14 @@ FreeDVMod::FreeDVMod(DeviceSinkAPI *deviceAPI) :
|
|||||||
m_sampleRate(48000),
|
m_sampleRate(48000),
|
||||||
m_levelCalcCount(0),
|
m_levelCalcCount(0),
|
||||||
m_peakLevel(0.0f),
|
m_peakLevel(0.0f),
|
||||||
m_levelSum(0.0f)
|
m_levelSum(0.0f),
|
||||||
|
m_freeDV(0),
|
||||||
|
m_nSpeechSamples(0),
|
||||||
|
m_nNomModemSamples(0),
|
||||||
|
m_iSpeech(0),
|
||||||
|
m_iModem(0),
|
||||||
|
m_speechIn(0),
|
||||||
|
m_modOut(0)
|
||||||
{
|
{
|
||||||
setObjectName(m_channelId);
|
setObjectName(m_channelId);
|
||||||
|
|
||||||
@ -124,6 +133,10 @@ FreeDVMod::~FreeDVMod()
|
|||||||
|
|
||||||
delete m_SSBFilter;
|
delete m_SSBFilter;
|
||||||
delete[] m_SSBFilterBuffer;
|
delete[] m_SSBFilterBuffer;
|
||||||
|
|
||||||
|
if (m_freeDV) {
|
||||||
|
freedv_close(m_freeDV);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FreeDVMod::pull(Sample& sample)
|
void FreeDVMod::pull(Sample& sample)
|
||||||
@ -207,8 +220,12 @@ void FreeDVMod::pullAF(Complex& sample)
|
|||||||
sample = m_toneNco.nextIQ();
|
sample = m_toneNco.nextIQ();
|
||||||
break;
|
break;
|
||||||
case FreeDVModSettings::FreeDVModInputFile:
|
case FreeDVModSettings::FreeDVModInputFile:
|
||||||
|
if (m_iModem >= m_nNomModemSamples)
|
||||||
|
{
|
||||||
if (m_ifstream.is_open())
|
if (m_ifstream.is_open())
|
||||||
{
|
{
|
||||||
|
std::fill(m_speechIn, m_speechIn + m_nSpeechSamples, 0);
|
||||||
|
|
||||||
if (m_ifstream.eof())
|
if (m_ifstream.eof())
|
||||||
{
|
{
|
||||||
if (m_settings.m_playLoop)
|
if (m_settings.m_playLoop)
|
||||||
@ -220,25 +237,36 @@ void FreeDVMod::pullAF(Complex& sample)
|
|||||||
|
|
||||||
if (m_ifstream.eof())
|
if (m_ifstream.eof())
|
||||||
{
|
{
|
||||||
ci.real(0.0f);
|
std::fill(m_modOut, m_modOut + m_nNomModemSamples, 0);
|
||||||
ci.imag(0.0f);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Real real;
|
|
||||||
m_ifstream.read(reinterpret_cast<char*>(&real), sizeof(Real));
|
m_ifstream.read(reinterpret_cast<char*>(m_speechIn), sizeof(int16_t) * m_nSpeechSamples);
|
||||||
ci.real(real * m_settings.m_volumeFactor);
|
|
||||||
ci.imag(0.0f);
|
if (m_settings.m_volumeFactor != 1.0)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < m_nSpeechSamples; i++) {
|
||||||
|
m_speechIn[i] *= m_settings.m_volumeFactor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
freedv_tx(m_freeDV, m_modOut, m_speechIn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ci.real(0.0f);
|
std::fill(m_modOut, m_modOut + m_nNomModemSamples, 0);
|
||||||
ci.imag(0.0f);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_iModem = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ci.real(m_modOut[m_iModem++] / (SDR_TX_SCALEF/8.0f));
|
||||||
|
ci.imag(0.0f);
|
||||||
break;
|
break;
|
||||||
case FreeDVModSettings::FreeDVModInputAudio:
|
case FreeDVModSettings::FreeDVModInputAudio:
|
||||||
ci.real(((m_audioBuffer[m_audioBufferFill].l + m_audioBuffer[m_audioBufferFill].r) / 65536.0f) * m_settings.m_volumeFactor);
|
ci.real(((m_audioBuffer[m_audioBufferFill].l + m_audioBuffer[m_audioBufferFill].r) / (2.0 * SDR_TX_SCALEF)) * m_settings.m_volumeFactor);
|
||||||
ci.imag(0.0f);
|
ci.imag(0.0f);
|
||||||
break;
|
break;
|
||||||
case FreeDVModSettings::FreeDVModInputCWTone:
|
case FreeDVModSettings::FreeDVModInputCWTone:
|
||||||
@ -582,11 +610,87 @@ void FreeDVMod::applyFreeDVMode(FreeDVModSettings::FreeDVMode mode)
|
|||||||
m_lowCutoff = FreeDVModSettings::getLowCutoff(mode);
|
m_lowCutoff = FreeDVModSettings::getLowCutoff(mode);
|
||||||
|
|
||||||
m_settingsMutex.lock();
|
m_settingsMutex.lock();
|
||||||
|
|
||||||
|
// baseband interpolator and filter
|
||||||
|
|
||||||
m_interpolatorDistanceRemain = 0;
|
m_interpolatorDistanceRemain = 0;
|
||||||
m_interpolatorConsumed = false;
|
m_interpolatorConsumed = false;
|
||||||
m_interpolatorDistance = (Real) m_audioSampleRate / (Real) m_outputSampleRate;
|
m_interpolatorDistance = (Real) m_audioSampleRate / (Real) m_outputSampleRate;
|
||||||
m_interpolator.create(48, m_audioSampleRate, m_hiCutoff, 3.0);
|
m_interpolator.create(48, m_audioSampleRate, m_hiCutoff, 3.0);
|
||||||
m_SSBFilter->create_filter(m_lowCutoff / m_audioSampleRate, m_hiCutoff / m_audioSampleRate);
|
m_SSBFilter->create_filter(m_lowCutoff / m_audioSampleRate, m_hiCutoff / m_audioSampleRate);
|
||||||
|
|
||||||
|
// FreeDV object
|
||||||
|
|
||||||
|
if (m_freeDV) {
|
||||||
|
freedv_close(m_freeDV);
|
||||||
|
}
|
||||||
|
|
||||||
|
int fdv_mode = -1;
|
||||||
|
|
||||||
|
switch(mode)
|
||||||
|
{
|
||||||
|
case FreeDVModSettings::FreeDVMode700D:
|
||||||
|
fdv_mode = FREEDV_MODE_700D;
|
||||||
|
break;
|
||||||
|
case FreeDVModSettings::FreeDVMode800XA:
|
||||||
|
fdv_mode = FREEDV_MODE_800XA;
|
||||||
|
break;
|
||||||
|
case FreeDVModSettings::FreeDVMode1600:
|
||||||
|
fdv_mode = FREEDV_MODE_1600;
|
||||||
|
break;
|
||||||
|
case FreeDVModSettings::FreeDVMode2400A:
|
||||||
|
default:
|
||||||
|
fdv_mode = FREEDV_MODE_2400A;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fdv_mode == FREEDV_MODE_700D)
|
||||||
|
{
|
||||||
|
struct freedv_advanced adv;
|
||||||
|
adv.interleave_frames = 1;
|
||||||
|
m_freeDV = freedv_open_advanced(fdv_mode, &adv);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_freeDV = freedv_open(fdv_mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_freeDV)
|
||||||
|
{
|
||||||
|
freedv_set_test_frames(m_freeDV, 0);
|
||||||
|
freedv_set_snr_squelch_thresh(m_freeDV, -100.0);
|
||||||
|
freedv_set_squelch_en(m_freeDV, 1);
|
||||||
|
freedv_set_clip(m_freeDV, 0);
|
||||||
|
freedv_set_tx_bpf(m_freeDV, 1);
|
||||||
|
freedv_set_ext_vco(m_freeDV, 0);
|
||||||
|
|
||||||
|
int nSpeechSamples = freedv_get_n_speech_samples(m_freeDV);
|
||||||
|
int nNomModemSamples = freedv_get_n_nom_modem_samples(m_freeDV);
|
||||||
|
int Fs = freedv_get_modem_sample_rate(m_freeDV);
|
||||||
|
int Rs = freedv_get_modem_symbol_rate(m_freeDV);
|
||||||
|
if ((m_speechIn) && (nSpeechSamples != m_nSpeechSamples)) {
|
||||||
|
delete[] m_speechIn;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((m_modOut) && (nNomModemSamples != m_nNomModemSamples)) {
|
||||||
|
delete[] m_modOut;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_nSpeechSamples = nSpeechSamples;
|
||||||
|
m_nNomModemSamples = nNomModemSamples;
|
||||||
|
m_speechIn = new int16_t[m_nSpeechSamples];
|
||||||
|
m_modOut = new int16_t[m_nNomModemSamples];
|
||||||
|
m_iSpeech = 0;
|
||||||
|
m_iModem = 0;
|
||||||
|
|
||||||
|
qDebug() << "FreeDVMod::applyFreeDVMode:"
|
||||||
|
<< " fdv_mode: " << fdv_mode
|
||||||
|
<< " Fs: " << Fs
|
||||||
|
<< " Rs: " << Rs
|
||||||
|
<< " m_nSpeechSamples: " << m_nSpeechSamples
|
||||||
|
<< " m_nNomModemSamples: " << m_nNomModemSamples;
|
||||||
|
}
|
||||||
|
|
||||||
m_settingsMutex.unlock();
|
m_settingsMutex.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,6 +43,7 @@ class QNetworkReply;
|
|||||||
class DeviceSinkAPI;
|
class DeviceSinkAPI;
|
||||||
class ThreadedBasebandSampleSource;
|
class ThreadedBasebandSampleSource;
|
||||||
class UpChannelizer;
|
class UpChannelizer;
|
||||||
|
struct freedv;
|
||||||
|
|
||||||
class FreeDVMod : public BasebandSampleSource, public ChannelSourceAPI {
|
class FreeDVMod : public BasebandSampleSource, public ChannelSourceAPI {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@ -314,6 +315,14 @@ private:
|
|||||||
QNetworkAccessManager *m_networkManager;
|
QNetworkAccessManager *m_networkManager;
|
||||||
QNetworkRequest m_networkRequest;
|
QNetworkRequest m_networkRequest;
|
||||||
|
|
||||||
|
struct freedv *m_freeDV;
|
||||||
|
int m_nSpeechSamples;
|
||||||
|
int m_nNomModemSamples;
|
||||||
|
int m_iSpeech;
|
||||||
|
int m_iModem;
|
||||||
|
int16_t *m_speechIn;
|
||||||
|
int16_t *m_modOut;
|
||||||
|
|
||||||
static const int m_levelNbSamples;
|
static const int m_levelNbSamples;
|
||||||
|
|
||||||
void applyAudioSampleRate(int sampleRate);
|
void applyAudioSampleRate(int sampleRate);
|
||||||
|
@ -173,7 +173,7 @@ int FreeDVModSettings::getHiCutoff(FreeDVMode freeDVMode)
|
|||||||
switch(freeDVMode)
|
switch(freeDVMode)
|
||||||
{
|
{
|
||||||
case FreeDVModSettings::FreeDVMode800XA: // C4FM NB
|
case FreeDVModSettings::FreeDVMode800XA: // C4FM NB
|
||||||
return 2400;
|
return 2400.0;
|
||||||
break;
|
break;
|
||||||
case FreeDVModSettings::FreeDVMode700D: // OFDM
|
case FreeDVModSettings::FreeDVMode700D: // OFDM
|
||||||
case FreeDVModSettings::FreeDVMode1600: // OFDM
|
case FreeDVModSettings::FreeDVMode1600: // OFDM
|
||||||
@ -199,7 +199,7 @@ int FreeDVModSettings::getLowCutoff(FreeDVMode freeDVMode)
|
|||||||
break;
|
break;
|
||||||
case FreeDVModSettings::FreeDVMode2400A: // C4FM WB
|
case FreeDVModSettings::FreeDVMode2400A: // C4FM WB
|
||||||
default:
|
default:
|
||||||
return 0.0;
|
return 50.0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user