1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2026-08-15 16:03:42 -04:00

Frequency tracker (1)

This commit is contained in:
f4exb
2019-05-04 01:55:58 +02:00
parent aa60776795
commit b4b157a9a4
30 changed files with 3601 additions and 22 deletions
+1
View File
@@ -10,6 +10,7 @@ add_subdirectory(demodwfm)
add_subdirectory(chanalyzer)
add_subdirectory(demodatv)
add_subdirectory(localsink)
add_subdirectory(freqtracker)
if(LIBDSDCC_FOUND AND LIBMBE_FOUND)
add_subdirectory(demoddsd)
@@ -0,0 +1,51 @@
project(freqtracker)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
set(freqtracker_SOURCES
freqtracker.cpp
freqtrackergui.cpp
freqtrackersettings.cpp
freqtrackerplugin.cpp
)
set(freqtracker_HEADERS
freqtracker.h
freqtrackergui.h
freqtrackersettings.h
freqtrackerplugin.h
)
set(freqtracker_FORMS
freqtrackergui.ui
)
include_directories(
.
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client
)
#include(${QT_USE_FILE})
add_definitions(${QT_DEFINITIONS})
add_definitions(-DQT_PLUGIN)
add_definitions(-DQT_SHARED)
#qt5_wrap_cpp(nfm_HEADERS_MOC ${nfm_HEADERS})
qt5_wrap_ui(freqtracker_FORMS_HEADERS ${freqtracker_FORMS})
add_library(freqtracker SHARED
${freqtracker_SOURCES}
${freqtracker_HEADERS_MOC}
${freqtracker_FORMS_HEADERS}
)
target_link_libraries(freqtracker
${QT_LIBRARIES}
sdrbase
sdrgui
)
target_link_libraries(freqtracker Qt5::Core Qt5::Widgets)
install(TARGETS freqtracker DESTINATION lib/plugins/channelrx)
@@ -0,0 +1,585 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 Edouard Griffiths, F4EXB. //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include "freqtracker.h"
#include <QTime>
#include <QDebug>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QBuffer>
#include <stdio.h>
#include <complex.h>
#include "SWGChannelSettings.h"
#include "SWGFreqTrackerSettings.h"
#include "SWGChannelReport.h"
#include "SWGFreqTrackerReport.h"
#include "dsp/downchannelizer.h"
#include "audio/audiooutput.h"
#include "dsp/dspengine.h"
#include "dsp/threadedbasebandsamplesink.h"
#include "dsp/dspcommands.h"
#include "dsp/fftfilt.h"
#include "device/devicesourceapi.h"
#include "util/db.h"
#include "util/stepfunctions.h"
MESSAGE_CLASS_DEFINITION(FreqTracker::MsgConfigureFreqTracker, Message)
MESSAGE_CLASS_DEFINITION(FreqTracker::MsgSampleRateNotification, Message)
MESSAGE_CLASS_DEFINITION(FreqTracker::MsgConfigureChannelizer, Message)
const QString FreqTracker::m_channelIdURI = "sdrangel.channel.freqtracker";
const QString FreqTracker::m_channelId = "FreqTracker";
const int FreqTracker::m_udpBlockSize = 512;
FreqTracker::FreqTracker(DeviceSourceAPI *deviceAPI) :
ChannelSinkAPI(m_channelIdURI),
m_deviceAPI(deviceAPI),
m_deviceSampleRate(48000),
m_inputSampleRate(48000),
m_inputFrequencyOffset(0),
m_channelSampleRate(48000),
m_running(false),
m_squelchOpen(false),
m_magsqSum(0.0f),
m_magsqPeak(0.0f),
m_magsqCount(0),
m_settingsMutex(QMutex::Recursive)
{
setObjectName(m_channelId);
m_magsq = 0.0;
m_channelizer = new DownChannelizer(this);
m_threadedChannelizer = new ThreadedBasebandSampleSink(m_channelizer, this);
m_deviceAPI->addThreadedSink(m_threadedChannelizer);
m_deviceAPI->addChannelAPI(this);
m_pll.computeCoefficients(0.05, 0.707, 1000);
m_networkManager = new QNetworkAccessManager();
connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*)));
}
FreqTracker::~FreqTracker()
{
disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*)));
delete m_networkManager;
m_deviceAPI->removeChannelAPI(this);
m_deviceAPI->removeThreadedSink(m_threadedChannelizer);
delete m_threadedChannelizer;
delete m_channelizer;
}
void FreqTracker::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool firstOfBurst)
{
(void) firstOfBurst;
Complex ci;
if (!m_running) {
return;
}
m_settingsMutex.lock();
for (SampleVector::const_iterator it = begin; it != end; ++it)
{
Complex c(it->real(), it->imag());
c *= m_nco.nextIQ();
if (m_interpolatorDistance < 1.0f) // interpolate
{
processOneSample(ci);
while (m_interpolator.interpolate(&m_interpolatorDistanceRemain, c, &ci))
{
processOneSample(ci);
}
m_interpolatorDistanceRemain += m_interpolatorDistance;
}
else // decimate
{
if (m_interpolator.decimate(&m_interpolatorDistanceRemain, c, &ci))
{
processOneSample(ci);
m_interpolatorDistanceRemain += m_interpolatorDistance;
}
}
}
m_settingsMutex.unlock();
}
void FreqTracker::processOneSample(Complex &ci)
{
Real re = ci.real() / SDR_RX_SCALEF;
Real im = ci.imag() / SDR_RX_SCALEF;
Real magsq = re*re + im*im;
m_movingAverage(magsq);
m_magsq = m_movingAverage.asDouble();
m_magsqSum += magsq;
if (magsq > m_magsqPeak)
{
m_magsqPeak = magsq;
}
m_magsqCount++;
if (m_magsq < m_squelchLevel)
{
if (m_squelchCount > 0) {
m_squelchCount--;
}
}
else
{
if (m_squelchCount < m_channelSampleRate / 10) {
m_squelchCount++;
}
}
qint16 sample;
m_squelchOpen = (m_squelchCount >= m_channelSampleRate / 20);
if (m_squelchOpen)
{
Real demod;
}
else
{
sample = 0;
}
}
void FreqTracker::start()
{
qDebug("FreqTracker::start");
m_squelchCount = 0;
m_running = true;
}
void FreqTracker::stop()
{
qDebug("FreqTracker::stop");
m_running = false;
}
bool FreqTracker::handleMessage(const Message& cmd)
{
if (DSPSignalNotification::match(cmd))
{
DSPSignalNotification& notif = (DSPSignalNotification&) cmd;
m_deviceSampleRate = notif.getSampleRate();
qDebug() << "FreqTracker::handleMessage: DSPSignalNotification:"
<< " m_deviceSampleRate: " << m_deviceSampleRate
<< " centerFrequency: " << notif.getCenterFrequency();
configureChannelizer();
}
else if (DownChannelizer::MsgChannelizerNotification::match(cmd))
{
DownChannelizer::MsgChannelizerNotification& notif = (DownChannelizer::MsgChannelizerNotification&) cmd;
m_inputSampleRate = notif.getSampleRate();
m_inputFrequencyOffset = notif.getFrequencyOffset();
qDebug() << "FreqTracker::handleMessage: MsgChannelizerNotification:"
<< " inputSampleRate: " << m_inputSampleRate
<< " inputFrequencyOffset: " << m_inputFrequencyOffset;
setInterpolator();
return true;
}
else if (MsgConfigureFreqTracker::match(cmd))
{
MsgConfigureFreqTracker& cfg = (MsgConfigureFreqTracker&) cmd;
qDebug() << "FreqTracker::handleMessage: MsgConfigureFreqTracker";
applySettings(cfg.getSettings(), cfg.getForce());
return true;
}
else
{
return false;
}
}
void FreqTracker::applySettings(const FreqTrackerSettings& settings, bool force)
{
qDebug() << "FreqTracker::applySettings:"
<< " m_inputFrequencyOffset: " << settings.m_inputFrequencyOffset
<< " m_rfBandwidth: " << settings.m_rfBandwidth
<< " m_log2Decim: " << settings.m_log2Decim
<< " m_squelch: " << settings.m_squelch
<< " m_rgbColor: " << settings.m_rgbColor
<< " m_title: " << settings.m_title
<< " m_tracking: " << settings.m_tracking
<< " m_trackerType: " << settings.m_trackerType
<< " m_pllPskOrder: " << settings.m_pllPskOrder
<< " m_rrc: " << settings.m_rrc
<< " m_rrcRolloff: " << settings.m_rrcRolloff
<< " m_useReverseAPI: " << settings.m_useReverseAPI
<< " m_reverseAPIAddress: " << settings.m_reverseAPIAddress
<< " m_reverseAPIPort: " << settings.m_reverseAPIPort
<< " m_reverseAPIDeviceIndex: " << settings.m_reverseAPIDeviceIndex
<< " m_reverseAPIChannelIndex: " << settings.m_reverseAPIChannelIndex
<< " force: " << force;
QList<QString> reverseAPIKeys;
bool updateChannelizer = false;
if ((m_settings.m_inputFrequencyOffset != settings.m_inputFrequencyOffset) || force)
{
reverseAPIKeys.append("inputFrequencyOffset");
updateChannelizer = true;
}
if ((m_settings.m_log2Decim != settings.m_log2Decim) || force)
{
reverseAPIKeys.append("log2Decim");
updateChannelizer = true;
}
if ((m_settings.m_rfBandwidth != settings.m_rfBandwidth) || force) {
reverseAPIKeys.append("rfBandwidth");
}
if ((m_settings.m_rfBandwidth != settings.m_rfBandwidth)
&& (m_settings.m_log2Decim == settings.m_log2Decim)
&& (m_settings.m_inputFrequencyOffset != settings.m_inputFrequencyOffset)
&& !force)
{
setInterpolator();
}
if ((m_settings.m_squelch != settings.m_squelch) || force)
{
m_squelchLevel = CalcDb::powerFromdB(settings.m_squelch);
reverseAPIKeys.append("squelch");
}
if ((m_settings.m_rgbColor != settings.m_rgbColor) || force) {
reverseAPIKeys.append("rgbColor");
}
if ((m_settings.m_title != settings.m_title) || force) {
reverseAPIKeys.append("title");
}
if ((m_settings.m_tracking != settings.m_tracking) || force) {
reverseAPIKeys.append("tracking");
}
if ((m_settings.m_trackerType != settings.m_trackerType) || force) {
reverseAPIKeys.append("trackerType");
}
if ((m_settings.m_pllPskOrder != settings.m_pllPskOrder) || force) {
reverseAPIKeys.append("pllPskOrder");
}
if ((m_settings.m_rrc != settings.m_rrc) || force) {
reverseAPIKeys.append("rrc");
}
if ((m_settings.m_rrcRolloff != settings.m_rrcRolloff) || force) {
reverseAPIKeys.append("rrcRolloff");
}
if (settings.m_useReverseAPI)
{
bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) ||
(m_settings.m_reverseAPIAddress != settings.m_reverseAPIAddress) ||
(m_settings.m_reverseAPIPort != settings.m_reverseAPIPort) ||
(m_settings.m_reverseAPIDeviceIndex != settings.m_reverseAPIDeviceIndex) ||
(m_settings.m_reverseAPIChannelIndex != settings.m_reverseAPIChannelIndex);
webapiReverseSendSettings(reverseAPIKeys, settings, fullUpdate || force);
}
m_settings = settings;
if (updateChannelizer)
{
configureChannelizer();
}
}
void FreqTracker::setInterpolator()
{
m_settingsMutex.lock();
m_interpolator.create(16, m_inputSampleRate, m_settings.m_rfBandwidth / 2.2f);
m_interpolatorDistanceRemain = 0;
m_interpolatorDistance = (Real) m_inputSampleRate / (Real) m_channelSampleRate;
m_bandpass.create(301, m_channelSampleRate, 300.0, m_settings.m_rfBandwidth / 2.0f);
m_settingsMutex.unlock();
}
void FreqTracker::configureChannelizer()
{
qDebug() << "FreqTracker::configureChannelizer:"
<< " sampleRate: " << m_deviceSampleRate / (1<<m_settings.m_log2Decim)
<< " inputFrequencyOffset: " << m_settings.m_inputFrequencyOffset;
m_channelizer->configure(m_channelizer->getInputMessageQueue(),
m_deviceSampleRate / (1<<m_settings.m_log2Decim),
m_settings.m_inputFrequencyOffset);
if (m_guiMessageQueue)
{
MsgSampleRateNotification *msg = MsgSampleRateNotification::create(m_deviceSampleRate / (1<<m_settings.m_log2Decim));
m_guiMessageQueue->push(msg);
}
}
QByteArray FreqTracker::serialize() const
{
return m_settings.serialize();
}
bool FreqTracker::deserialize(const QByteArray& data)
{
if (m_settings.deserialize(data))
{
MsgConfigureFreqTracker *msg = MsgConfigureFreqTracker::create(m_settings, true);
m_inputMessageQueue.push(msg);
return true;
}
else
{
m_settings.resetToDefaults();
MsgConfigureFreqTracker *msg = MsgConfigureFreqTracker::create(m_settings, true);
m_inputMessageQueue.push(msg);
return false;
}
}
int FreqTracker::webapiSettingsGet(
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage)
{
(void) errorMessage;
response.setFreqTrackerSettings(new SWGSDRangel::SWGFreqTrackerSettings());
response.getFreqTrackerSettings()->init();
webapiFormatChannelSettings(response, m_settings);
return 200;
}
int FreqTracker::webapiSettingsPutPatch(
bool force,
const QStringList& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage)
{
(void) errorMessage;
FreqTrackerSettings settings = m_settings;
bool frequencyOffsetChanged = false;
if (channelSettingsKeys.contains("inputFrequencyOffset"))
{
settings.m_inputFrequencyOffset = response.getFreqTrackerSettings()->getInputFrequencyOffset();
frequencyOffsetChanged = true;
}
if (channelSettingsKeys.contains("rfBandwidth")) {
settings.m_rfBandwidth = response.getFreqTrackerSettings()->getRfBandwidth();
}
if (channelSettingsKeys.contains("log2Decim")) {
settings.m_log2Decim = response.getFreqTrackerSettings()->getLog2Decim();
}
if (channelSettingsKeys.contains("squelch")) {
settings.m_squelch = response.getFreqTrackerSettings()->getSquelch();
}
if (channelSettingsKeys.contains("rgbColor")) {
settings.m_rgbColor = response.getFreqTrackerSettings()->getRgbColor();
}
if (channelSettingsKeys.contains("title")) {
settings.m_title = *response.getFreqTrackerSettings()->getTitle();
}
if (channelSettingsKeys.contains("tracking")) {
settings.m_tracking = response.getFreqTrackerSettings()->getTracking() ? 1 : 0;
}
if (channelSettingsKeys.contains("trackerType"))
{
int32_t trackerTypeCode = response.getFreqTrackerSettings()->getTrackerType();
settings.m_trackerType = trackerTypeCode < 0 ?
FreqTrackerSettings::TrackerFLL : trackerTypeCode > 1 ?
FreqTrackerSettings::TrackerPLL : (FreqTrackerSettings::TrackerType) trackerTypeCode;
}
if (channelSettingsKeys.contains("pllPskOrder")) {
settings.m_pllPskOrder = response.getFreqTrackerSettings()->getPllPskOrder();
}
if (channelSettingsKeys.contains("rrc")) {
settings.m_rrc = response.getFreqTrackerSettings()->getRrc() ? 1 : 0;
}
if (channelSettingsKeys.contains("rrcRolloff")) {
settings.m_rrcRolloff = response.getFreqTrackerSettings()->getRrcRolloff();
}
if (channelSettingsKeys.contains("useReverseAPI")) {
settings.m_useReverseAPI = response.getAmDemodSettings()->getUseReverseApi() != 0;
}
if (channelSettingsKeys.contains("reverseAPIAddress")) {
settings.m_reverseAPIAddress = *response.getAmDemodSettings()->getReverseApiAddress();
}
if (channelSettingsKeys.contains("reverseAPIPort")) {
settings.m_reverseAPIPort = response.getAmDemodSettings()->getReverseApiPort();
}
if (channelSettingsKeys.contains("reverseAPIDeviceIndex")) {
settings.m_reverseAPIDeviceIndex = response.getAmDemodSettings()->getReverseApiDeviceIndex();
}
if (channelSettingsKeys.contains("reverseAPIChannelIndex")) {
settings.m_reverseAPIChannelIndex = response.getAmDemodSettings()->getReverseApiChannelIndex();
}
MsgConfigureFreqTracker *msg = MsgConfigureFreqTracker::create(settings, force);
m_inputMessageQueue.push(msg);
if ((settings.m_log2Decim != m_settings.m_log2Decim) || frequencyOffsetChanged || force)
{
MsgConfigureChannelizer *msg = MsgConfigureChannelizer::create(
m_deviceSampleRate / (1<<settings.m_log2Decim), settings.m_inputFrequencyOffset);
m_inputMessageQueue.push(msg);
}
qDebug("FreqTracker::webapiSettingsPutPatch: forward to GUI: %p", m_guiMessageQueue);
if (m_guiMessageQueue) // forward to GUI if any
{
MsgConfigureFreqTracker *msgToGUI = MsgConfigureFreqTracker::create(settings, force);
m_guiMessageQueue->push(msgToGUI);
}
webapiFormatChannelSettings(response, settings);
return 200;
}
int FreqTracker::webapiReportGet(
SWGSDRangel::SWGChannelReport& response,
QString& errorMessage)
{
(void) errorMessage;
response.setFreqTrackerReport(new SWGSDRangel::SWGFreqTrackerReport());
response.getFreqTrackerReport()->init();
webapiFormatChannelReport(response);
return 200;
}
void FreqTracker::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& response, const FreqTrackerSettings& settings)
{
response.getFreqTrackerSettings()->setInputFrequencyOffset(settings.m_inputFrequencyOffset);
response.getFreqTrackerSettings()->setRfBandwidth(settings.m_rfBandwidth);
response.getFreqTrackerSettings()->setRgbColor(settings.m_rgbColor);
response.getFreqTrackerSettings()->setSquelch(settings.m_squelch);
if (response.getFreqTrackerSettings()->getTitle()) {
*response.getFreqTrackerSettings()->getTitle() = settings.m_title;
} else {
response.getFreqTrackerSettings()->setTitle(new QString(settings.m_title));
}
response.getFreqTrackerSettings()->setTrackerType((int) m_settings.m_trackerType);
response.getFreqTrackerSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0);
if (response.getFreqTrackerSettings()->getReverseApiAddress()) {
*response.getFreqTrackerSettings()->getReverseApiAddress() = settings.m_reverseAPIAddress;
} else {
response.getFreqTrackerSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress));
}
response.getFreqTrackerSettings()->setReverseApiPort(settings.m_reverseAPIPort);
response.getFreqTrackerSettings()->setReverseApiDeviceIndex(settings.m_reverseAPIDeviceIndex);
response.getFreqTrackerSettings()->setReverseApiChannelIndex(settings.m_reverseAPIChannelIndex);
}
void FreqTracker::webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response)
{
double magsqAvg, magsqPeak;
int nbMagsqSamples;
getMagSqLevels(magsqAvg, magsqPeak, nbMagsqSamples);
response.getFreqTrackerReport()->setChannelPowerDb(CalcDb::dbPower(magsqAvg));
response.getFreqTrackerReport()->setSquelch(m_squelchOpen ? 1 : 0);
response.getFreqTrackerReport()->setSampleRate(m_channelSampleRate);
response.getFreqTrackerReport()->setChannelSampleRate(m_inputSampleRate);
}
void FreqTracker::webapiReverseSendSettings(QList<QString>& channelSettingsKeys, const FreqTrackerSettings& settings, bool force)
{
SWGSDRangel::SWGChannelSettings *swgChannelSettings = new SWGSDRangel::SWGChannelSettings();
swgChannelSettings->setTx(0);
swgChannelSettings->setOriginatorChannelIndex(getIndexInDeviceSet());
swgChannelSettings->setOriginatorDeviceSetIndex(getDeviceSetIndex());
swgChannelSettings->setChannelType(new QString("FreqTracker"));
swgChannelSettings->setFreqTrackerSettings(new SWGSDRangel::SWGFreqTrackerSettings());
SWGSDRangel::SWGFreqTrackerSettings *swgFreqTrackerSettings = swgChannelSettings->getFreqTrackerSettings();
// transfer data that has been modified. When force is on transfer all data except reverse API data
if (channelSettingsKeys.contains("inputFrequencyOffset") || force) {
swgFreqTrackerSettings->setInputFrequencyOffset(settings.m_inputFrequencyOffset);
}
if (channelSettingsKeys.contains("rfBandwidth") || force) {
swgFreqTrackerSettings->setRfBandwidth(settings.m_rfBandwidth);
}
if (channelSettingsKeys.contains("rgbColor") || force) {
swgFreqTrackerSettings->setRgbColor(settings.m_rgbColor);
}
if (channelSettingsKeys.contains("squelch") || force) {
swgFreqTrackerSettings->setSquelch(settings.m_squelch);
}
if (channelSettingsKeys.contains("title") || force) {
swgFreqTrackerSettings->setTitle(new QString(settings.m_title));
}
if (channelSettingsKeys.contains("trackerType") || force) {
swgFreqTrackerSettings->setTrackerType((int) settings.m_trackerType);
}
QString channelSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/channel/%4/settings")
.arg(settings.m_reverseAPIAddress)
.arg(settings.m_reverseAPIPort)
.arg(settings.m_reverseAPIDeviceIndex)
.arg(settings.m_reverseAPIChannelIndex);
m_networkRequest.setUrl(QUrl(channelSettingsURL));
m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QBuffer *buffer=new QBuffer();
buffer->open((QBuffer::ReadWrite));
buffer->write(swgChannelSettings->asJson().toUtf8());
buffer->seek(0);
// Always use PATCH to avoid passing reverse API settings
m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer);
delete swgChannelSettings;
}
void FreqTracker::networkManagerFinished(QNetworkReply *reply)
{
QNetworkReply::NetworkError replyError = reply->error();
if (replyError)
{
qWarning() << "FreqTracker::networkManagerFinished:"
<< " error(" << (int) replyError
<< "): " << replyError
<< ": " << reply->errorString();
return;
}
QString answer = reply->readAll();
answer.chop(1); // remove last \n
qDebug("FreqTracker::networkManagerFinished: reply:\n%s", answer.toStdString().c_str());
}
+242
View File
@@ -0,0 +1,242 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 Edouard Griffiths, F4EXB. //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_FREQTRACKER_H
#define INCLUDE_FREQTRACKER_H
#include <vector>
#include <QNetworkRequest>
#include <QMutex>
#include "dsp/basebandsamplesink.h"
#include "channel/channelsinkapi.h"
#include "dsp/nco.h"
#include "dsp/interpolator.h"
#include "util/movingaverage.h"
#include "dsp/agc.h"
#include "dsp/bandpass.h"
#include "dsp/lowpass.h"
#include "dsp/phaselockcomplex.h"
#include "dsp/freqlockcomplex.h"
#include "util/message.h"
#include "util/doublebufferfifo.h"
#include "freqtrackersettings.h"
class QNetworkAccessManager;
class QNetworkReply;
class DeviceSourceAPI;
class DownChannelizer;
class ThreadedBasebandSampleSink;
class fftfilt;
class FreqTracker : public BasebandSampleSink, public ChannelSinkAPI {
Q_OBJECT
public:
class MsgConfigureFreqTracker : public Message {
MESSAGE_CLASS_DECLARATION
public:
const FreqTrackerSettings& getSettings() const { return m_settings; }
bool getForce() const { return m_force; }
static MsgConfigureFreqTracker* create(const FreqTrackerSettings& settings, bool force)
{
return new MsgConfigureFreqTracker(settings, force);
}
private:
FreqTrackerSettings m_settings;
bool m_force;
MsgConfigureFreqTracker(const FreqTrackerSettings& settings, bool force) :
Message(),
m_settings(settings),
m_force(force)
{ }
};
class MsgConfigureChannelizer : public Message {
MESSAGE_CLASS_DECLARATION
public:
int getSampleRate() const { return m_sampleRate; }
int getCenterFrequency() const { return m_centerFrequency; }
static MsgConfigureChannelizer* create(int sampleRate, int centerFrequency)
{
return new MsgConfigureChannelizer(sampleRate, centerFrequency);
}
private:
int m_sampleRate;
int m_centerFrequency;
MsgConfigureChannelizer(int sampleRate, int centerFrequency) :
Message(),
m_sampleRate(sampleRate),
m_centerFrequency(centerFrequency)
{ }
};
class MsgSampleRateNotification : public Message {
MESSAGE_CLASS_DECLARATION
public:
static MsgSampleRateNotification* create(int sampleRate) {
return new MsgSampleRateNotification(sampleRate);
}
int getSampleRate() const { return m_sampleRate; }
private:
MsgSampleRateNotification(int sampleRate) :
Message(),
m_sampleRate(sampleRate)
{ }
int m_sampleRate;
};
FreqTracker(DeviceSourceAPI *deviceAPI);
~FreqTracker();
virtual void destroy() { delete this; }
virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool po);
virtual void start();
virtual void stop();
virtual bool handleMessage(const Message& cmd);
virtual void getIdentifier(QString& id) { id = objectName(); }
virtual void getTitle(QString& title) { title = m_settings.m_title; }
virtual qint64 getCenterFrequency() const { return m_settings.m_inputFrequencyOffset; }
virtual QByteArray serialize() const;
virtual bool deserialize(const QByteArray& data);
virtual int webapiSettingsGet(
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage);
virtual int webapiSettingsPutPatch(
bool force,
const QStringList& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage);
virtual int webapiReportGet(
SWGSDRangel::SWGChannelReport& response,
QString& errorMessage);
uint32_t getSampleRate() const { return m_channelSampleRate; }
double getMagSq() const { return m_magsq; }
bool getSquelchOpen() const { return m_squelchOpen; }
bool getPllLocked() const { return (m_settings.m_trackerType == FreqTrackerSettings::TrackerPLL) && m_pll.locked(); }
Real getPllFrequency() const { return m_pll.getFreq(); }
void getMagSqLevels(double& avg, double& peak, int& nbSamples)
{
if (m_magsqCount > 0)
{
m_magsq = m_magsqSum / m_magsqCount;
m_magSqLevelStore.m_magsq = m_magsq;
m_magSqLevelStore.m_magsqPeak = m_magsqPeak;
}
avg = m_magSqLevelStore.m_magsq;
peak = m_magSqLevelStore.m_magsqPeak;
nbSamples = m_magsqCount == 0 ? 1 : m_magsqCount;
m_magsqSum = 0.0f;
m_magsqPeak = 0.0f;
m_magsqCount = 0;
}
static const QString m_channelIdURI;
static const QString m_channelId;
private:
struct MagSqLevelsStore
{
MagSqLevelsStore() :
m_magsq(1e-12),
m_magsqPeak(1e-12)
{}
double m_magsq;
double m_magsqPeak;
};
enum RateState {
RSInitialFill,
RSRunning
};
DeviceSourceAPI *m_deviceAPI;
ThreadedBasebandSampleSink* m_threadedChannelizer;
DownChannelizer* m_channelizer;
FreqTrackerSettings m_settings;
int m_deviceSampleRate;
int m_inputSampleRate;
int m_inputFrequencyOffset;
uint32_t m_channelSampleRate;
bool m_running;
NCO m_nco;
PhaseLockComplex m_pll;
FreqLockComplex m_fll;
Interpolator m_interpolator;
Real m_interpolatorDistance;
Real m_interpolatorDistanceRemain;
fftfilt* RRCFilter;
Real m_squelchLevel;
uint32_t m_squelchCount;
bool m_squelchOpen;
double m_magsq;
double m_magsqSum;
double m_magsqPeak;
int m_magsqCount;
MagSqLevelsStore m_magSqLevelStore;
MovingAverageUtil<Real, double, 16> m_movingAverage;
Bandpass<Real> m_bandpass;
static const int m_udpBlockSize;
QNetworkAccessManager *m_networkManager;
QNetworkRequest m_networkRequest;
QMutex m_settingsMutex;
void applySettings(const FreqTrackerSettings& settings, bool force = false);
void setInterpolator();
void configureChannelizer();
void webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& response, const FreqTrackerSettings& settings);
void webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response);
void webapiReverseSendSettings(QList<QString>& channelSettingsKeys, const FreqTrackerSettings& settings, bool force);
void processOneSample(Complex &ci);
private slots:
void networkManagerFinished(QNetworkReply *reply);
};
#endif // INCLUDE_FREQTRACKER_H
@@ -0,0 +1,52 @@
#--------------------------------------------------------
#
# Pro file for Android and Windows builds with Qt Creator
#
#--------------------------------------------------------
TEMPLATE = lib
CONFIG += plugin
QT += core gui widgets multimedia
TARGET = demodam
DEFINES += USE_SSE2=1
QMAKE_CXXFLAGS += -msse2
DEFINES += USE_SSE4_1=1
QMAKE_CXXFLAGS += -msse4.1
QMAKE_CXXFLAGS += -std=c++11
INCLUDEPATH += $$PWD
INCLUDEPATH += ../../../exports
INCLUDEPATH += ../../../sdrbase
INCLUDEPATH += ../../../sdrgui
INCLUDEPATH += ../../../swagger/sdrangel/code/qt5/client
CONFIG(Release):build_subdir = release
CONFIG(Debug):build_subdir = debug
SOURCES += amdemod.cpp\
amdemodgui.cpp\
amdemodplugin.cpp\
amdemodsettings.cpp\
amdemodssbdialog.cpp
HEADERS += amdemod.h\
amdemodgui.h\
amdemodplugin.h\
amdemodsettings.h\
amdemodssbdialog.h
FORMS += amdemodgui.ui\
amdemodssb.ui
LIBS += -L../../../sdrbase/$${build_subdir} -lsdrbase
LIBS += -L../../../sdrgui/$${build_subdir} -lsdrgui
LIBS += -L../../../swagger/$${build_subdir} -lswagger
macx {
QMAKE_LFLAGS_SONAME = -Wl,-install_name,@rpath/
}
RESOURCES = ../../../sdrgui/resources/res.qrc
@@ -0,0 +1,389 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 Edouard Griffiths, F4EXB //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <QDockWidget>
#include <QMainWindow>
#include <QDebug>
#include "freqtrackergui.h"
#include "device/devicesourceapi.h"
#include "device/deviceuiset.h"
#include "dsp/downchannelizer.h"
#include "dsp/dspengine.h"
#include "dsp/threadedbasebandsamplesink.h"
#include "ui_freqtrackergui.h"
#include "plugin/pluginapi.h"
#include "util/simpleserializer.h"
#include "util/db.h"
#include "gui/basicchannelsettingsdialog.h"
#include "dsp/dspengine.h"
#include "mainwindow.h"
#include "gui/crightclickenabler.h"
#include "gui/audioselectdialog.h"
#include "freqtracker.h"
FreqTrackerGUI* FreqTrackerGUI::create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel)
{
FreqTrackerGUI* gui = new FreqTrackerGUI(pluginAPI, deviceUISet, rxChannel);
return gui;
}
void FreqTrackerGUI::destroy()
{
delete this;
}
void FreqTrackerGUI::setName(const QString& name)
{
setObjectName(name);
}
QString FreqTrackerGUI::getName() const
{
return objectName();
}
qint64 FreqTrackerGUI::getCenterFrequency() const {
return m_channelMarker.getCenterFrequency();
}
void FreqTrackerGUI::setCenterFrequency(qint64 centerFrequency)
{
m_channelMarker.setCenterFrequency(centerFrequency);
applySettings();
}
void FreqTrackerGUI::resetToDefaults()
{
m_settings.resetToDefaults();
displaySettings();
applySettings(true);
}
QByteArray FreqTrackerGUI::serialize() const
{
return m_settings.serialize();
}
bool FreqTrackerGUI::deserialize(const QByteArray& data)
{
if(m_settings.deserialize(data)) {
displaySettings();
applySettings(true);
return true;
} else {
resetToDefaults();
return false;
}
}
bool FreqTrackerGUI::handleMessage(const Message& message)
{
if (FreqTracker::MsgConfigureFreqTracker::match(message))
{
qDebug("FreqTrackerGUI::handleMessage: FreqTracker::MsgConfigureFreqTracker");
const FreqTracker::MsgConfigureFreqTracker& cfg = (FreqTracker::MsgConfigureFreqTracker&) message;
m_settings = cfg.getSettings();
blockApplySettings(true);
displaySettings();
blockApplySettings(false);
return true;
}
else if (FreqTracker::MsgSampleRateNotification::match(message))
{
qDebug("FreqTrackerGUI::handleMessage: FreqTracker::MsgSampleRateNotification");
const FreqTracker::MsgSampleRateNotification& cfg = (FreqTracker::MsgSampleRateNotification&) message;
m_channelSampleRate = cfg.getSampleRate();
ui->channelSampleRateText->setText(tr("%1k").arg(QString::number(m_channelSampleRate / 1000.0f, 'g', 5)));
if (m_channelSampleRate > 1000) {
ui->rfBW->setMaximum(m_channelSampleRate/100);
}
return true;
}
return false;
}
void FreqTrackerGUI::handleInputMessages()
{
Message* message;
while ((message = getInputMessageQueue()->pop()) != 0)
{
if (handleMessage(*message))
{
delete message;
}
}
}
void FreqTrackerGUI::channelMarkerChangedByCursor()
{
ui->deltaFrequency->setValue(m_channelMarker.getCenterFrequency());
m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency();
applySettings();
}
void FreqTrackerGUI::channelMarkerHighlightedByCursor()
{
setHighlighted(m_channelMarker.getHighlighted());
}
void FreqTrackerGUI::on_deltaFrequency_changed(qint64 value)
{
m_channelMarker.setCenterFrequency(value);
m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency();
applySettings();
}
void FreqTrackerGUI::on_log2Decim_currentIndexChanged(int index)
{
m_settings.m_log2Decim = index < 0 ? 0 : index > 6 ? 6 : index;
applySettings();
}
void FreqTrackerGUI::on_rfBW_valueChanged(int value)
{
ui->rfBWText->setText(QString("%1 kHz").arg(value / 10.0, 0, 'f', 1));
m_channelMarker.setBandwidth(value * 100);
m_settings.m_rfBandwidth = value * 100;
applySettings();
}
void FreqTrackerGUI::on_tracking_toggled(bool checked)
{
if (!checked)
{
ui->tracking->setStyleSheet("QToolButton { background:rgb(79,79,79); }");
ui->tracking->setToolTip(tr("PLL for synchronous AM"));
}
m_settings.m_tracking = checked;
applySettings();
}
void FreqTrackerGUI::on_trackerType_currentIndexChanged(int index)
{
m_settings.m_trackerType = (FreqTrackerSettings::TrackerType) index;
applySettings();
}
void FreqTrackerGUI::on_pllPskOrder_currentIndexChanged(int index)
{
m_settings.m_pllPskOrder = index;
applySettings();
}
void FreqTrackerGUI::on_rrc_toggled(bool checked)
{
m_settings.m_rrc = checked;
applySettings();
}
void FreqTrackerGUI::on_rrcRolloff_valueChanged(int value)
{
m_settings.m_rrcRolloff = value < 0 ? 0 : value > 100 ? 100 : value;
QString rolloffStr = QString::number(value/100.0, 'f', 2);
ui->rrcRolloffText->setText(rolloffStr);
applySettings();
}
void FreqTrackerGUI::on_squelch_valueChanged(int value)
{
ui->squelchText->setText(QString("%1 dB").arg(value));
m_settings.m_squelch = value;
applySettings();
}
void FreqTrackerGUI::onWidgetRolled(QWidget* widget, bool rollDown)
{
(void) widget;
(void) rollDown;
}
void FreqTrackerGUI::onMenuDialogCalled(const QPoint &p)
{
BasicChannelSettingsDialog dialog(&m_channelMarker, this);
dialog.setUseReverseAPI(m_settings.m_useReverseAPI);
dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress);
dialog.setReverseAPIPort(m_settings.m_reverseAPIPort);
dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex);
dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex);
dialog.move(p);
dialog.exec();
m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency();
m_settings.m_rgbColor = m_channelMarker.getColor().rgb();
m_settings.m_title = m_channelMarker.getTitle();
m_settings.m_useReverseAPI = dialog.useReverseAPI();
m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress();
m_settings.m_reverseAPIPort = dialog.getReverseAPIPort();
m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex();
m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex();
setWindowTitle(m_settings.m_title);
setTitleColor(m_settings.m_rgbColor);
applySettings();
}
FreqTrackerGUI::FreqTrackerGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel, QWidget* parent) :
RollupWidget(parent),
ui(new Ui::FreqTrackerGUI),
m_pluginAPI(pluginAPI),
m_deviceUISet(deviceUISet),
m_channelMarker(this),
m_channelSampleRate(0),
m_doApplySettings(true),
m_squelchOpen(false),
m_tickCount(0)
{
ui->setupUi(this);
setAttribute(Qt::WA_DeleteOnClose, true);
connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool)));
connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &)));
m_freqTracker = reinterpret_cast<FreqTracker*>(rxChannel); //new FreqTracker(m_deviceUISet->m_deviceSourceAPI);
m_freqTracker->setMessageQueueToGUI(getInputMessageQueue());
connect(&MainWindow::getInstance()->getMasterTimer(), SIGNAL(timeout()), this, SLOT(tick())); // 50 ms
ui->deltaFrequencyLabel->setText(QString("%1f").arg(QChar(0x94, 0x03)));
ui->deltaFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold));
ui->deltaFrequency->setValueRange(false, 7, -9999999, 9999999);
ui->channelPowerMeter->setColorTheme(LevelMeterSignalDB::ColorGreenAndBlue);
m_channelMarker.blockSignals(true);
m_channelMarker.setColor(Qt::yellow);
m_channelMarker.setBandwidth(5000);
m_channelMarker.setCenterFrequency(0);
m_channelMarker.setTitle("AM Demodulator");
m_channelMarker.blockSignals(false);
m_channelMarker.setVisible(true); // activate signal on the last setting only
setTitleColor(m_channelMarker.getColor());
m_settings.setChannelMarker(&m_channelMarker);
m_deviceUISet->registerRxChannelInstance(FreqTracker::m_channelIdURI, this);
m_deviceUISet->addChannelMarker(&m_channelMarker);
m_deviceUISet->addRollupWidget(this);
connect(&m_channelMarker, SIGNAL(changedByCursor()), this, SLOT(channelMarkerChangedByCursor()));
connect(&m_channelMarker, SIGNAL(highlightedByCursor()), this, SLOT(channelMarkerHighlightedByCursor()));
connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
displaySettings();
applySettings(true);
}
FreqTrackerGUI::~FreqTrackerGUI()
{
m_deviceUISet->removeRxChannelInstance(this);
delete m_freqTracker; // TODO: check this: when the GUI closes it has to delete the demodulator
delete ui;
}
void FreqTrackerGUI::blockApplySettings(bool block)
{
m_doApplySettings = !block;
}
void FreqTrackerGUI::applySettings(bool force)
{
if (m_doApplySettings)
{
FreqTracker::MsgConfigureFreqTracker* message = FreqTracker::MsgConfigureFreqTracker::create( m_settings, force);
m_freqTracker->getInputMessageQueue()->push(message);
}
}
void FreqTrackerGUI::displaySettings()
{
m_channelMarker.blockSignals(true);
m_channelMarker.setCenterFrequency(m_settings.m_inputFrequencyOffset);
m_channelMarker.setBandwidth(m_settings.m_rfBandwidth);
m_channelMarker.setTitle(m_settings.m_title);
m_channelMarker.blockSignals(false);
m_channelMarker.setColor(m_settings.m_rgbColor); // activate signal on the last setting only
setTitleColor(m_settings.m_rgbColor);
setWindowTitle(m_channelMarker.getTitle());
blockApplySettings(true);
ui->deltaFrequency->setValue(m_channelMarker.getCenterFrequency());
int displayValue = m_settings.m_rfBandwidth/100.0;
ui->rfBW->setValue(displayValue);
ui->rfBWText->setText(QString("%1 kHz").arg(displayValue / 10.0, 0, 'f', 1));
ui->squelch->setValue(m_settings.m_squelch);
ui->squelchText->setText(QString("%1 dB").arg(m_settings.m_squelch));
ui->tracking->setChecked(m_settings.m_tracking);
blockApplySettings(false);
}
void FreqTrackerGUI::leaveEvent(QEvent*)
{
m_channelMarker.setHighlighted(false);
}
void FreqTrackerGUI::enterEvent(QEvent*)
{
m_channelMarker.setHighlighted(true);
}
void FreqTrackerGUI::tick()
{
double magsqAvg, magsqPeak;
int nbMagsqSamples;
m_freqTracker->getMagSqLevels(magsqAvg, magsqPeak, nbMagsqSamples);
double powDbAvg = CalcDb::dbPower(magsqAvg);
double powDbPeak = CalcDb::dbPower(magsqPeak);
ui->channelPowerMeter->levelChanged(
(100.0f + powDbAvg) / 100.0f,
(100.0f + powDbPeak) / 100.0f,
nbMagsqSamples);
if (m_tickCount % 4 == 0) {
ui->channelPower->setText(QString::number(powDbAvg, 'f', 1));
}
bool squelchOpen = m_freqTracker->getSquelchOpen();
if (m_settings.m_tracking)
{
if (m_freqTracker->getPllLocked()) {
ui->tracking->setStyleSheet("QToolButton { background-color : green; }");
} else {
ui->tracking->setStyleSheet("QToolButton { background:rgb(79,79,79); }");
}
int freq = (m_freqTracker->getPllFrequency() * m_freqTracker->getSampleRate()) / (2.0*M_PI);
ui->tracking->setToolTip(tr("PLL for synchronous AM. Freq = %1 Hz").arg(freq));
}
m_tickCount++;
}
@@ -0,0 +1,102 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 Edouard Griffiths, F4EXB. //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_FREQTRACKERGUI_H
#define INCLUDE_FREQTRACKERGUI_H
#include <QIcon>
#include "plugin/plugininstancegui.h"
#include "gui/rollupwidget.h"
#include "dsp/channelmarker.h"
#include "dsp/movingaverage.h"
#include "util/messagequeue.h"
#include "freqtrackersettings.h"
class PluginAPI;
class DeviceUISet;
class FreqTracker;
class BasebandSampleSink;
namespace Ui {
class FreqTrackerGUI;
}
class FreqTrackerGUI : public RollupWidget, public PluginInstanceGUI {
Q_OBJECT
public:
static FreqTrackerGUI* create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel);
virtual void destroy();
void setName(const QString& name);
QString getName() const;
virtual qint64 getCenterFrequency() const;
virtual void setCenterFrequency(qint64 centerFrequency);
void resetToDefaults();
QByteArray serialize() const;
bool deserialize(const QByteArray& data);
virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; }
virtual bool handleMessage(const Message& message);
public slots:
void channelMarkerChangedByCursor();
void channelMarkerHighlightedByCursor();
private:
Ui::FreqTrackerGUI* ui;
PluginAPI* m_pluginAPI;
DeviceUISet* m_deviceUISet;
ChannelMarker m_channelMarker;
FreqTrackerSettings m_settings;
int m_channelSampleRate;
bool m_doApplySettings;
FreqTracker* m_freqTracker;
bool m_squelchOpen;
uint32_t m_tickCount;
MessageQueue m_inputMessageQueue;
explicit FreqTrackerGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel, QWidget* parent = 0);
virtual ~FreqTrackerGUI();
void blockApplySettings(bool block);
void applySettings(bool force = false);
void displaySettings();
void leaveEvent(QEvent*);
void enterEvent(QEvent*);
private slots:
void on_deltaFrequency_changed(qint64 value);
void on_log2Decim_currentIndexChanged(int index);
void on_rfBW_valueChanged(int value);
void on_tracking_toggled(bool checked);
void on_trackerType_currentIndexChanged(int index);
void on_pllPskOrder_currentIndexChanged(int index);
void on_rrc_toggled(bool checked);
void on_rrcRolloff_valueChanged(int value);
void on_squelch_valueChanged(int value);
void onWidgetRolled(QWidget* widget, bool rollDown);
void onMenuDialogCalled(const QPoint& p);
void handleInputMessages();
void tick();
};
#endif // INCLUDE_FREQTRACKERGUI_H
@@ -0,0 +1,585 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>FreqTrackerGUI</class>
<widget class="RollupWidget" name="FreqTrackerGUI">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>350</width>
<height>145</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>350</width>
<height>100</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>350</width>
<height>16777215</height>
</size>
</property>
<property name="font">
<font>
<family>Liberation Sans</family>
<pointsize>9</pointsize>
</font>
</property>
<property name="windowTitle">
<string>Frequency Tracker</string>
</property>
<property name="statusTip">
<string>Frequency Tracker</string>
</property>
<widget class="QWidget" name="settingsContainer" native="true">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>348</width>
<height>140</height>
</rect>
</property>
<property name="windowTitle">
<string>Settings</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>3</number>
</property>
<property name="leftMargin">
<number>2</number>
</property>
<property name="topMargin">
<number>2</number>
</property>
<property name="rightMargin">
<number>2</number>
</property>
<property name="bottomMargin">
<number>2</number>
</property>
<item>
<layout class="QHBoxLayout" name="deltaFreqPowLayout">
<item>
<layout class="QHBoxLayout" name="deltaFrequencyLayout">
<item>
<widget class="QLabel" name="deltaFrequencyLabel">
<property name="minimumSize">
<size>
<width>16</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Df</string>
</property>
</widget>
</item>
<item>
<widget class="ValueDialZ" name="deltaFrequency" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>32</width>
<height>16</height>
</size>
</property>
<property name="font">
<font>
<family>Liberation Mono</family>
<pointsize>12</pointsize>
</font>
</property>
<property name="cursor">
<cursorShape>PointingHandCursor</cursorShape>
</property>
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
<property name="toolTip">
<string>Demod shift frequency from center in Hz</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="deltaUnits">
<property name="text">
<string>Hz </string>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="channelPowerLayout">
<item>
<widget class="QLabel" name="channelPower">
<property name="toolTip">
<string>Channel power</string>
</property>
<property name="layoutDirection">
<enum>Qt::RightToLeft</enum>
</property>
<property name="text">
<string>0.0</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="channelPowerUnits">
<property name="text">
<string> dB</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="signalLevelLayout">
<item>
<widget class="QLabel" name="channelPowerMeterUnits">
<property name="text">
<string>dB</string>
</property>
</widget>
</item>
<item>
<widget class="LevelMeterSignalDB" name="channelPowerMeter" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>24</height>
</size>
</property>
<property name="font">
<font>
<family>Liberation Mono</family>
<pointsize>8</pointsize>
</font>
</property>
<property name="toolTip">
<string>Level meter (dB) top trace: average, bottom trace: instantaneous peak, tip: peak hold</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="rfBandwidthLayout">
<item>
<widget class="QComboBox" name="log2Decim">
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>Decimation factor</string>
</property>
<item>
<property name="text">
<string>1</string>
</property>
</item>
<item>
<property name="text">
<string>2</string>
</property>
</item>
<item>
<property name="text">
<string>4</string>
</property>
</item>
<item>
<property name="text">
<string>8</string>
</property>
</item>
<item>
<property name="text">
<string>16</string>
</property>
</item>
<item>
<property name="text">
<string>32</string>
</property>
</item>
<item>
<property name="text">
<string>64</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QLabel" name="channelSampleRateText">
<property name="minimumSize">
<size>
<width>45</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Channel sample rate</string>
</property>
<property name="text">
<string>00000k</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="rfBandwidthLabel">
<property name="text">
<string>BW</string>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="rfBW">
<property name="toolTip">
<string>Channel bandwidth</string>
</property>
<property name="minimum">
<number>10</number>
</property>
<property name="maximum">
<number>400</number>
</property>
<property name="pageStep">
<number>1</number>
</property>
<property name="value">
<number>50</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="rfBWText">
<property name="minimumSize">
<size>
<width>70</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Channel bandwidth</string>
</property>
<property name="text">
<string>5.0 kHz</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="trackerLayout">
<item>
<widget class="QToolButton" name="tracking">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Tracking enable</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../../sdrgui/resources/res.qrc">
<normaloff>:/unlocked.png</normaloff>
<normalon>:/locked.png</normalon>:/unlocked.png</iconset>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="trackerType">
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>Tracking type</string>
</property>
<item>
<property name="text">
<string>FLL</string>
</property>
</item>
<item>
<property name="text">
<string>PLL</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QComboBox" name="pllPskOrder">
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>PLL PSK order (1 for CW)</string>
</property>
<item>
<property name="text">
<string>1</string>
</property>
</item>
<item>
<property name="text">
<string>2</string>
</property>
</item>
<item>
<property name="text">
<string>4</string>
</property>
</item>
<item>
<property name="text">
<string>8</string>
</property>
</item>
<item>
<property name="text">
<string>16</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="ButtonSwitch" name="rrc">
<property name="toolTip">
<string>Toggle Reverse Root Cosine filter</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../../sdrgui/resources/res.qrc">
<normaloff>:/dsb.png</normaloff>:/dsb.png</iconset>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QDial" name="rrcRolloff">
<property name="minimumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="toolTip">
<string>Reverse Root Cosine filter rolloff factor</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="rrcRolloffText">
<property name="minimumSize">
<size>
<width>32</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Reverse Root Cosine filter rolloff factor</string>
</property>
<property name="text">
<string>0.35</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="squelchLayout">
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string>Sq</string>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="squelch">
<property name="toolTip">
<string>Squelch</string>
</property>
<property name="minimum">
<number>-100</number>
</property>
<property name="maximum">
<number>0</number>
</property>
<property name="pageStep">
<number>1</number>
</property>
<property name="value">
<number>-40</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="squelchText">
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>-40dB</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</widget>
<customwidgets>
<customwidget>
<class>RollupWidget</class>
<extends>QWidget</extends>
<header>gui/rollupwidget.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>LevelMeterSignalDB</class>
<extends>QWidget</extends>
<header>gui/levelmeter.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>ButtonSwitch</class>
<extends>QToolButton</extends>
<header>gui/buttonswitch.h</header>
</customwidget>
<customwidget>
<class>ValueDialZ</class>
<extends>QWidget</extends>
<header>gui/valuedialz.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources>
<include location="../../../sdrgui/resources/res.qrc"/>
</resources>
<connections/>
</ui>
@@ -0,0 +1,78 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 Edouard Griffiths, F4EXB. //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <QtPlugin>
#include "plugin/pluginapi.h"
#ifndef SERVER_MODE
#include "freqtrackergui.h"
#endif
#include "freqtracker.h"
#include "freqtrackerplugin.h"
const PluginDescriptor FreqTrackerPlugin::m_pluginDescriptor = {
QString("Frequency Tracker"),
QString("4.7.0"),
QString("(c) Edouard Griffiths, F4EXB"),
QString("https://github.com/f4exb/sdrangel"),
true,
QString("https://github.com/f4exb/sdrangel")
};
FreqTrackerPlugin::FreqTrackerPlugin(QObject* parent) :
QObject(parent),
m_pluginAPI(0)
{
}
const PluginDescriptor& FreqTrackerPlugin::getPluginDescriptor() const
{
return m_pluginDescriptor;
}
void FreqTrackerPlugin::initPlugin(PluginAPI* pluginAPI)
{
m_pluginAPI = pluginAPI;
// register AM demodulator
m_pluginAPI->registerRxChannel(FreqTracker::m_channelIdURI, FreqTracker::m_channelId, this);
}
#ifdef SERVER_MODE
PluginInstanceGUI* FreqTrackerPlugin::createRxChannelGUI(
DeviceUISet *deviceUISet __attribute__((unused)),
BasebandSampleSink *rxChannel __attribute__((unused)))
{
return 0;
}
#else
PluginInstanceGUI* FreqTrackerPlugin::createRxChannelGUI(DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel)
{
return FreqTrackerGUI::create(m_pluginAPI, deviceUISet, rxChannel);
}
#endif
BasebandSampleSink* FreqTrackerPlugin::createRxChannelBS(DeviceSourceAPI *deviceAPI)
{
return new FreqTracker(deviceAPI);
}
ChannelSinkAPI* FreqTrackerPlugin::createRxChannelCS(DeviceSourceAPI *deviceAPI)
{
return new FreqTracker(deviceAPI);
}
@@ -0,0 +1,48 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 Edouard Griffiths, F4EXB //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_FREQTRACKERPLUGIN_H
#define INCLUDE_FREQTRACKERPLUGIN_H
#include <QObject>
#include "plugin/plugininterface.h"
class DeviceUISet;
class BasebandSampleSink;
class FreqTrackerPlugin : public QObject, PluginInterface {
Q_OBJECT
Q_INTERFACES(PluginInterface)
Q_PLUGIN_METADATA(IID "sdrangel.channel.freqtracker")
public:
explicit FreqTrackerPlugin(QObject* parent = NULL);
const PluginDescriptor& getPluginDescriptor() const;
void initPlugin(PluginAPI* pluginAPI);
virtual PluginInstanceGUI* createRxChannelGUI(DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel);
virtual BasebandSampleSink* createRxChannelBS(DeviceSourceAPI *deviceAPI);
virtual ChannelSinkAPI* createRxChannelCS(DeviceSourceAPI *deviceAPI);
private:
static const PluginDescriptor m_pluginDescriptor;
PluginAPI* m_pluginAPI;
};
#endif // INCLUDE_FREQTRACKERPLUGIN_H
@@ -0,0 +1,142 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 Edouard Griffiths, F4EXB. //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <QColor>
#include "dsp/dspengine.h"
#include "util/simpleserializer.h"
#include "settings/serializable.h"
#include "freqtrackersettings.h"
FreqTrackerSettings::FreqTrackerSettings() :
m_channelMarker(0)
{
resetToDefaults();
}
void FreqTrackerSettings::resetToDefaults()
{
m_inputFrequencyOffset = 0;
m_rfBandwidth = 6000;
m_log2Decim = 0;
m_squelch = -40.0;
m_rgbColor = QColor(200, 244, 66).rgb();
m_title = "Frequency Tracker";
m_tracking = false;
m_trackerType = TrackerFLL;
m_pllPskOrder = 1;
m_rrc = false;
m_rrcRolloff = 35;
m_useReverseAPI = false;
m_reverseAPIAddress = "127.0.0.1";
m_reverseAPIPort = 8888;
m_reverseAPIDeviceIndex = 0;
m_reverseAPIChannelIndex = 0;
}
QByteArray FreqTrackerSettings::serialize() const
{
SimpleSerializer s(1);
s.writeS32(1, m_inputFrequencyOffset);
s.writeS32(2, m_rfBandwidth/100);
s.writeU32(3, m_log2Decim);
s.writeS32(5, m_squelch);
if (m_channelMarker) {
s.writeBlob(6, m_channelMarker->serialize());
}
s.writeU32(7, m_rgbColor);
s.writeString(9, m_title);
s.writeBool(10, m_tracking);
s.writeS32(12, (int) m_trackerType);
s.writeU32(13, m_pllPskOrder);
s.writeBool(14, m_rrc);
s.writeU32(15, m_rrcRolloff);
s.writeBool(16, m_useReverseAPI);
s.writeString(17, m_reverseAPIAddress);
s.writeU32(18, m_reverseAPIPort);
s.writeU32(19, m_reverseAPIDeviceIndex);
s.writeU32(20, m_reverseAPIChannelIndex);
return s.final();
}
bool FreqTrackerSettings::deserialize(const QByteArray& data)
{
SimpleDeserializer d(data);
if(!d.isValid())
{
resetToDefaults();
return false;
}
if(d.getVersion() == 1)
{
QByteArray bytetmp;
qint32 tmp;
uint32_t utmp;
QString strtmp;
d.readS32(1, &m_inputFrequencyOffset, 0);
d.readS32(2, &tmp, 4);
m_rfBandwidth = 100 * tmp;
d.readU32(3, &utmp, 0);
m_log2Decim = utmp > 6 ? 6 : utmp;
d.readS32(4, &tmp, 20);
d.readS32(5, &tmp, -40);
m_squelch = tmp;
d.readBlob(6, &bytetmp);
if (m_channelMarker) {
m_channelMarker->deserialize(bytetmp);
}
d.readU32(7, &m_rgbColor, QColor(200, 244, 66).rgb());
d.readString(9, &m_title, "Frequency Tracker");
d.readBool(10, &m_tracking, false);
d.readS32(12, &tmp, 0);
m_trackerType = tmp < 0 ? TrackerFLL : tmp > 1 ? TrackerPLL : (TrackerType) tmp;
d.readU32(13, &utmp, 1);
m_pllPskOrder = utmp > 4 ? 4 : utmp;
d.readBool(14, &m_rrc, false);
d.readU32(15, &utmp, 35);
m_rrcRolloff = utmp > 100 ? 100 : utmp;
d.readBool(16, &m_useReverseAPI, false);
d.readString(17, &m_reverseAPIAddress, "127.0.0.1");
d.readU32(18, &utmp, 0);
if ((utmp > 1023) && (utmp < 65535)) {
m_reverseAPIPort = utmp;
} else {
m_reverseAPIPort = 8888;
}
d.readU32(19, &utmp, 0);
m_reverseAPIDeviceIndex = utmp > 99 ? 99 : utmp;
d.readU32(20, &utmp, 0);
m_reverseAPIChannelIndex = utmp > 99 ? 99 : utmp;
return true;
}
else
{
resetToDefaults();
return false;
}
}
@@ -0,0 +1,63 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 Edouard Griffiths, F4EXB. //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef PLUGINS_CHANNELRX_FREQTRACKER_FREQTRACKERSETTINGS_H_
#define PLUGINS_CHANNELRX_FREQTRACKER_FREQTRACKERSETTINGS_H_
#include <QByteArray>
#include <QString>
#include "dsp/dsptypes.h"
class Serializable;
struct FreqTrackerSettings
{
enum TrackerType
{
TrackerFLL,
TrackerPLL
};
qint32 m_inputFrequencyOffset;
Real m_rfBandwidth;
uint32_t m_log2Decim;
Real m_squelch;
quint32 m_rgbColor;
QString m_title;
Serializable *m_channelMarker;
bool m_tracking;
TrackerType m_trackerType;
uint32_t m_pllPskOrder;
bool m_rrc;
uint32_t m_rrcRolloff; //!< in 100ths
bool m_useReverseAPI;
QString m_reverseAPIAddress;
uint16_t m_reverseAPIPort;
uint16_t m_reverseAPIDeviceIndex;
uint16_t m_reverseAPIChannelIndex;
FreqTrackerSettings();
void resetToDefaults();
void setChannelMarker(Serializable *channelMarker) { m_channelMarker = channelMarker; }
QByteArray serialize() const;
bool deserialize(const QByteArray& data);
};
#endif /* PLUGINS_CHANNELRX_FREQTRACKER_FREQTRACKERSETTINGS_H_ */
+55
View File
@@ -0,0 +1,55 @@
<h1>AM demodulator plugin</h1>
<h2>Introduction</h2>
This plugin can be used to listen to a narrowband amplitude modulated signal. "Narrowband" means that the bandwidth can vary from 1 to 40 kHz.
<h2>Interface</h2>
![AM Demodulator plugin GUI](../../../doc/img/AMDemod_plugin.png)
<h3>1: Frequency shift from center frequency of reception</h3>
Use the wheels to adjust the frequency shift in Hz from the center frequency of reception. Left click on a digit sets the cursor position at this digit. Right click on a digit sets all digits on the right to zero. This effectively floors value at the digit position. Wheels are moved with the mousewheel while pointing at the wheel or by selecting the wheel with the left mouse click and using the keyboard arrows. Pressing shift simultaneously moves digit by 5 and pressing control moves it by 2.
<h3>2: PLL and synchronous AM</h2>
Use this toggle button to turn on or off the PLL locking and synchronous AM detection. When on the input signal is mixed with the NCO of the PLL that locks to the carrier of the AM transmission. Then the signal is processed as a DSB or SSB (see control 3) modulated signal. The main advantage compared to enveloppe detection is a better resilience to carrier selective fading. This does not prevents all selective fading distorsion but addresses the most annoying.
When the PLL is locked the icon lights up in green. The frequency shift from carrier appears in the tooltip. Locking indicator is pretty sharp with about +/- 100 Hz range.
<h3>3: DSB/SSB selection</h2>
Use the left mouse button to toggle DSB/SSB operation. Soemtimes one of the two sidebands is affected by interference. Selecting SSB may help by using only the sideband without interference. Right click to open a dialog to select which sideband is used (LSB or USB).
<h3>4: Channel power</h3>
Average total power in dB relative to a +/- 1.0 amplitude signal received in the pass band.
<h3>5: Audio mute and audio output select</h3>
Left click on this button to toggle audio mute for this channel. The button will light up in green if the squelch is open. This helps identifying which channels are active in a multi-channel configuration.
If you right click on it it will open a dialog to select the audio output device. See [audio management documentation](../../../sdrgui/audio.md) for details.
<h3>6: Level meter in dB</h3>
- top bar (green): average value
- bottom bar (blue green): instantaneous peak value
- tip vertical bar (bright green): peak hold value
<h3>7:Bandpass boxcar filter toggle</h3>
Use this button to enable or disable the bandpass boxcar (sharp) filter with low cutoff at 300 Hz and high cutoff at half the RF bandwidth. This may help readability of low signals on air traffic communications but degrades audio on comfortable AM broadcast transmissions.
<h3>8: RF bandwidth</h3>
This is the bandwidth in kHz of the channel signal before demodulation. It can be set continuously in 1 kHz steps from 1 to 40 kHz.
<h3>9: Volume</h3>
This is the volume of the audio signal from 0.0 (mute) to 10.0 (maximum). It can be varied continuously in 0.1 steps using the dial button.
<h3>10: Squelch threshold</h3>
This is the squelch threshold in dB. The average total power received in the signal bandwidth before demodulation is compared to this value and the squelch input is open above this value. It can be varied continuously in 0.1 dB steps from 0.0 to -100.0 dB using the dial button.
@@ -125,7 +125,6 @@ void LocalSink::stop()
bool LocalSink::handleMessage(const Message& cmd)
{
(void) cmd;
if (DownChannelizer::MsgChannelizerNotification::match(cmd))
{
DownChannelizer::MsgChannelizerNotification& notif = (DownChannelizer::MsgChannelizerNotification&) cmd;
+5 -19
View File
@@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>320</width>
<height>100</height>
<height>110</height>
</rect>
</property>
<property name="sizePolicy">
@@ -37,13 +37,16 @@
<property name="windowTitle">
<string>Local sink</string>
</property>
<property name="statusTip">
<string>Local Sink</string>
</property>
<widget class="QWidget" name="settingsContainer" native="true">
<property name="geometry">
<rect>
<x>10</x>
<y>10</y>
<width>301</width>
<height>141</height>
<height>91</height>
</rect>
</property>
<property name="windowTitle">
@@ -310,23 +313,6 @@
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
</widget>