mirror of
https://github.com/cjcliffe/CubicSDR.git
synced 2025-07-31 12:52:25 -04:00
Merge branch 'master' of https://github.com/colbyAtCRI/CubicSDR into colbyAtCRI-master
This commit is contained in:
commit
95b7a86ed9
@ -340,6 +340,7 @@ SET (cubicsdr_sources
|
|||||||
src/modules/modem/ModemAnalog.cpp
|
src/modules/modem/ModemAnalog.cpp
|
||||||
src/modules/modem/ModemDigital.cpp
|
src/modules/modem/ModemDigital.cpp
|
||||||
src/modules/modem/analog/ModemAM.cpp
|
src/modules/modem/analog/ModemAM.cpp
|
||||||
|
src/modules/modem/analog/ModemCW.cpp
|
||||||
src/modules/modem/analog/ModemDSB.cpp
|
src/modules/modem/analog/ModemDSB.cpp
|
||||||
src/modules/modem/analog/ModemFM.cpp
|
src/modules/modem/analog/ModemFM.cpp
|
||||||
src/modules/modem/analog/ModemNBFM.cpp
|
src/modules/modem/analog/ModemNBFM.cpp
|
||||||
@ -449,6 +450,7 @@ SET (cubicsdr_headers
|
|||||||
src/modules/modem/ModemAnalog.h
|
src/modules/modem/ModemAnalog.h
|
||||||
src/modules/modem/ModemDigital.h
|
src/modules/modem/ModemDigital.h
|
||||||
src/modules/modem/analog/ModemAM.h
|
src/modules/modem/analog/ModemAM.h
|
||||||
|
src/modules/modem/analog/ModemCW.h
|
||||||
src/modules/modem/analog/ModemDSB.h
|
src/modules/modem/analog/ModemDSB.h
|
||||||
src/modules/modem/analog/ModemFM.h
|
src/modules/modem/analog/ModemFM.h
|
||||||
src/modules/modem/analog/ModemNBFM.h
|
src/modules/modem/analog/ModemNBFM.h
|
||||||
|
@ -798,7 +798,7 @@ WaterfallCanvas *AppFrame::makeWaterfallCanvas(wxWindow *parent, const wxGLAttri
|
|||||||
}
|
}
|
||||||
|
|
||||||
ModeSelectorCanvas *AppFrame::makeModemSelectorPanel(wxWindow *parent, const wxGLAttributes &attribList) {
|
ModeSelectorCanvas *AppFrame::makeModemSelectorPanel(wxWindow *parent, const wxGLAttributes &attribList) {
|
||||||
vector<string> modemList = {"FM", "FMS", "NBFM", "AM", "LSB", "USB", "DSB", "I/Q" };
|
vector<string> modemList = {"FM", "FMS", "NBFM", "AM", "CW", "LSB", "USB", "DSB", "I/Q" };
|
||||||
|
|
||||||
#ifdef CUBICSDR_MODEM_EXCLUDE
|
#ifdef CUBICSDR_MODEM_EXCLUDE
|
||||||
std::string excludeListStr = "" CUBICSDR_MODEM_EXCLUDE;
|
std::string excludeListStr = "" CUBICSDR_MODEM_EXCLUDE;
|
||||||
|
@ -306,6 +306,7 @@ bool CubicSDR::OnInit() {
|
|||||||
Modem::addModemFactory(ModemNBFM::factory, "NBFM", 12500);
|
Modem::addModemFactory(ModemNBFM::factory, "NBFM", 12500);
|
||||||
Modem::addModemFactory(ModemFMStereo::factory, "FMS", 200000);
|
Modem::addModemFactory(ModemFMStereo::factory, "FMS", 200000);
|
||||||
Modem::addModemFactory(ModemAM::factory, "AM", 6000);
|
Modem::addModemFactory(ModemAM::factory, "AM", 6000);
|
||||||
|
Modem::addModemFactory(ModemCW::factory, "CW", 500);
|
||||||
Modem::addModemFactory(ModemLSB::factory, "LSB", 5400);
|
Modem::addModemFactory(ModemLSB::factory, "LSB", 5400);
|
||||||
Modem::addModemFactory(ModemUSB::factory, "USB", 5400);
|
Modem::addModemFactory(ModemUSB::factory, "USB", 5400);
|
||||||
Modem::addModemFactory(ModemDSB::factory, "DSB", 5400);
|
Modem::addModemFactory(ModemDSB::factory, "DSB", 5400);
|
||||||
|
@ -34,6 +34,7 @@
|
|||||||
#include "ModemFM.h"
|
#include "ModemFM.h"
|
||||||
#include "ModemNBFM.h"
|
#include "ModemNBFM.h"
|
||||||
#include "ModemFMStereo.h"
|
#include "ModemFMStereo.h"
|
||||||
|
#include "ModemCW.h"
|
||||||
#include "ModemAM.h"
|
#include "ModemAM.h"
|
||||||
#include "ModemUSB.h"
|
#include "ModemUSB.h"
|
||||||
#include "ModemLSB.h"
|
#include "ModemLSB.h"
|
||||||
|
221
src/modules/modem/analog/ModemCW.cpp
Normal file
221
src/modules/modem/analog/ModemCW.cpp
Normal file
@ -0,0 +1,221 @@
|
|||||||
|
// Copyright (c) Charles J. Cliffe
|
||||||
|
// SPDX-License-Identifier: GPL-2.0+
|
||||||
|
|
||||||
|
#include "ModemCW.h"
|
||||||
|
|
||||||
|
// We are given a baseband segment BW (default 500Hz) wide which we want to
|
||||||
|
// offset by mBeepFrequency (default 650Hz). This yields a spectrum.
|
||||||
|
//
|
||||||
|
// | |....|....|
|
||||||
|
// | |....|....|
|
||||||
|
// | |....|....|
|
||||||
|
// -----------|---|----|----|--
|
||||||
|
// 0 150 650 1150
|
||||||
|
//
|
||||||
|
ModemCW::ModemCW()
|
||||||
|
: ModemAnalog(),
|
||||||
|
mBeepFrequency(650.0),
|
||||||
|
mGain(15.0),
|
||||||
|
mAutoGain(true),
|
||||||
|
mLO(nullptr),
|
||||||
|
mToReal(nullptr)
|
||||||
|
{
|
||||||
|
mLO = nco_crcf_create (LIQUID_NCO);
|
||||||
|
mToReal = firhilbf_create (5,60.0f);
|
||||||
|
useSignalOutput(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
ModemCW::~ModemCW() {
|
||||||
|
if (mLO)
|
||||||
|
nco_crcf_destroy (mLO);
|
||||||
|
if (mToReal)
|
||||||
|
firhilbf_destroy (mToReal);
|
||||||
|
}
|
||||||
|
|
||||||
|
ModemArgInfoList ModemCW::getSettings()
|
||||||
|
{
|
||||||
|
ModemArgInfoList args;
|
||||||
|
|
||||||
|
ModemArgInfo offsetArg;
|
||||||
|
offsetArg.key = "offset";
|
||||||
|
offsetArg.name = "Frequency Offset";
|
||||||
|
offsetArg.value = std::to_string(mBeepFrequency);
|
||||||
|
offsetArg.units = "Hz";
|
||||||
|
offsetArg.description = "Frequency Offset / Beep frequency";
|
||||||
|
offsetArg.type = ModemArgInfo::FLOAT;
|
||||||
|
offsetArg.range = ModemRange (200.0,1000.0);
|
||||||
|
args.push_back(offsetArg);
|
||||||
|
|
||||||
|
ModemArgInfo autoGain;
|
||||||
|
autoGain.key = "auto";
|
||||||
|
autoGain.name = "Auto Gain";
|
||||||
|
autoGain.value = "on";
|
||||||
|
autoGain.type = ModemArgInfo::STRING;
|
||||||
|
std::vector<std::string> autoOpts;
|
||||||
|
autoOpts.push_back("on");
|
||||||
|
autoOpts.push_back("off");
|
||||||
|
autoGain.optionNames = autoOpts;
|
||||||
|
autoGain.options = autoOpts;
|
||||||
|
args.push_back(autoGain);
|
||||||
|
|
||||||
|
ModemArgInfo gain;
|
||||||
|
gain.key = "gain";
|
||||||
|
gain.name = "Audio Gain";
|
||||||
|
gain.value = "15";
|
||||||
|
gain.units = "dB";
|
||||||
|
gain.description = "Gain Setting";
|
||||||
|
gain.range = ModemRange(0.0,40.0);
|
||||||
|
gain.type = ModemArgInfo::FLOAT;
|
||||||
|
args.push_back(gain);
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModemCW::writeSetting(std::string setting, std::string value)
|
||||||
|
{
|
||||||
|
if (setting == "offset") {
|
||||||
|
mBeepFrequency = std::stof(value);
|
||||||
|
rebuildKit();
|
||||||
|
} else
|
||||||
|
if (setting == "auto") {
|
||||||
|
mAutoGain = (value=="on")?true:false;
|
||||||
|
} else
|
||||||
|
if (setting == "gain") {
|
||||||
|
mGain = std::stof(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ModemCW::readSetting(std::string setting)
|
||||||
|
{
|
||||||
|
if (setting == "offset") {
|
||||||
|
return std::to_string(mBeepFrequency);
|
||||||
|
} else
|
||||||
|
if (setting == "auto") {
|
||||||
|
return (mAutoGain)?"on":"off";
|
||||||
|
} else
|
||||||
|
if (setting == "gain") {
|
||||||
|
return std::to_string(mGain);
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
ModemBase *ModemCW::factory() {
|
||||||
|
return new ModemCW;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ModemCW::getName() {
|
||||||
|
return "CW";
|
||||||
|
}
|
||||||
|
|
||||||
|
int ModemCW::checkSampleRate (long long srate, int arate)
|
||||||
|
{
|
||||||
|
if (srate < MIN_BANDWIDTH)
|
||||||
|
return MIN_BANDWIDTH;
|
||||||
|
return srate;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ModemCW::getDefaultSampleRate() {
|
||||||
|
return MIN_BANDWIDTH;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The modem object is asked to make a "ModemKit" given the IQ sample rate
|
||||||
|
// and the audio sample rate. For the CW modem the IQ sample rate is small
|
||||||
|
// or narrow bandwidth. The demodulated sample rate must be fast enough to
|
||||||
|
// sample 200-1000Hz tones. If the IQ sample rate is less than 2000Hz then
|
||||||
|
// one doesn't have the bandwidth for these tones. So we need to interpolate
|
||||||
|
// the input IQ to audioOut, frequency shift, then pass the real part.
|
||||||
|
// Simple solution is just interpolate the IQ data to the audio sample rate.
|
||||||
|
ModemKit *ModemCW::buildKit (long long sampleRate, int audioSampleRate)
|
||||||
|
{
|
||||||
|
ModemKitCW *kit = new ModemKitCW();
|
||||||
|
float As = 60.0f;
|
||||||
|
double ratio = double(audioSampleRate) / double(sampleRate);
|
||||||
|
kit->sampleRate = sampleRate;
|
||||||
|
kit->audioSampleRate = audioSampleRate;
|
||||||
|
kit->audioResampleRatio = ratio;
|
||||||
|
kit->mInputResampler = msresamp_cccf_create (ratio,As);
|
||||||
|
return kit;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModemCW::disposeKit (ModemKit *kit)
|
||||||
|
{
|
||||||
|
ModemKitCW *cwkit = (ModemKitCW*) kit;
|
||||||
|
msresamp_cccf_destroy (cwkit->mInputResampler);
|
||||||
|
delete kit;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModemCW::initOutputBuffers(ModemKitAnalog *akit, ModemIQData *input)
|
||||||
|
{
|
||||||
|
bufSize = input->data.size();
|
||||||
|
|
||||||
|
if (!bufSize) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
double audio_resample_ratio = akit->audioResampleRatio;
|
||||||
|
|
||||||
|
size_t audio_out_size = (size_t)ceil((double) (bufSize) * audio_resample_ratio) + 512;
|
||||||
|
|
||||||
|
// Just make everything the audio out size
|
||||||
|
if (mInput.size() != audio_out_size) {
|
||||||
|
if (mInput.capacity() < audio_out_size) {
|
||||||
|
mInput.reserve(audio_out_size);
|
||||||
|
}
|
||||||
|
mInput.resize(audio_out_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModemCW::demodulate(ModemKit *kit, ModemIQData *input, AudioThreadInput *audioOut) {
|
||||||
|
unsigned int outSize;
|
||||||
|
float lsb;
|
||||||
|
liquid_float_complex sig;
|
||||||
|
ModemKitCW *cwkit = (ModemKitCW *)kit;
|
||||||
|
|
||||||
|
initOutputBuffers(cwkit, input);
|
||||||
|
|
||||||
|
if (!bufSize) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interpolate IQ samples to full audio band. We need to be able to
|
||||||
|
// sample at 2 times the desired beep frequency.
|
||||||
|
msresamp_cccf_execute (cwkit->mInputResampler,&input->data[0],bufSize,&mInput[0],&outSize);
|
||||||
|
|
||||||
|
// Make the shoe fit.
|
||||||
|
if (demodOutputData.size() != outSize) {
|
||||||
|
demodOutputData.resize(outSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the LO to the desired beep frequency.
|
||||||
|
nco_crcf_set_frequency(mLO,2.0*M_PI*mBeepFrequency/kit->audioSampleRate);
|
||||||
|
|
||||||
|
// Mix up from base band by beep frequency. Extract real part
|
||||||
|
for (int i = 0; i < outSize; i++) {
|
||||||
|
nco_crcf_mix_up (mLO,mInput[i],&sig);
|
||||||
|
nco_crcf_step (mLO);
|
||||||
|
firhilbf_c2r_execute (mToReal,sig,&lsb,&demodOutputData[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine gain automagically (if desired)
|
||||||
|
if (mAutoGain) {
|
||||||
|
aOutputCeilMA = aOutputCeilMA + (aOutputCeil - aOutputCeilMA) * 0.025f;
|
||||||
|
aOutputCeilMAA = aOutputCeilMAA + (aOutputCeilMA - aOutputCeilMAA) * 0.025f;
|
||||||
|
aOutputCeil = 0;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < outSize; i++) {
|
||||||
|
if (demodOutputData[i] > aOutputCeil) {
|
||||||
|
aOutputCeil = demodOutputData[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mGain = 10.0*std::log10(0.5f / aOutputCeilMAA);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply gain to demodulated output data
|
||||||
|
for (size_t i = 0; i < outSize; i++) {
|
||||||
|
demodOutputData[i] *= std::pow(10.0,mGain/10.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
audioOut->channels = 1;
|
||||||
|
audioOut->sampleRate = cwkit->audioSampleRate;
|
||||||
|
audioOut->data.assign(demodOutputData.begin(), demodOutputData.end());
|
||||||
|
}
|
53
src/modules/modem/analog/ModemCW.h
Normal file
53
src/modules/modem/analog/ModemCW.h
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
// Copyright (c) Charles J. Cliffe
|
||||||
|
// SPDX-License-Identifier: GPL-2.0+
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "Modem.h"
|
||||||
|
#include "ModemAnalog.h"
|
||||||
|
|
||||||
|
class ModemKitCW : public ModemKitAnalog
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ModemKitCW() : ModemKitAnalog() {
|
||||||
|
};
|
||||||
|
msresamp_cccf mInputResampler;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ModemCW : public ModemAnalog {
|
||||||
|
public:
|
||||||
|
ModemCW();
|
||||||
|
~ModemCW();
|
||||||
|
|
||||||
|
std::string getName();
|
||||||
|
|
||||||
|
static ModemBase *factory();
|
||||||
|
|
||||||
|
int checkSampleRate (long long srate, int arate );
|
||||||
|
|
||||||
|
ModemKit *buildKit (long long srate, int arate);
|
||||||
|
|
||||||
|
void disposeKit (ModemKit *kit);
|
||||||
|
|
||||||
|
void initOutputBuffers (ModemKitAnalog *akit, ModemIQData *input);
|
||||||
|
|
||||||
|
int getDefaultSampleRate();
|
||||||
|
|
||||||
|
void demodulate(ModemKit *kit, ModemIQData *input, AudioThreadInput *audioOut);
|
||||||
|
|
||||||
|
ModemArgInfoList getSettings();
|
||||||
|
|
||||||
|
void writeSetting(std::string setting,std::string value);
|
||||||
|
|
||||||
|
std::string readSetting(std::string setting);
|
||||||
|
|
||||||
|
// No resampling required.
|
||||||
|
std::vector<float> *getResampledOutputData () { return &demodOutputData; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool mAutoGain;
|
||||||
|
float mGain;
|
||||||
|
float mBeepFrequency;
|
||||||
|
nco_crcf mLO;
|
||||||
|
firhilbf mToReal;
|
||||||
|
std::vector<liquid_float_complex> mInput;
|
||||||
|
};
|
Loading…
x
Reference in New Issue
Block a user