Make the low cutoff frequency of the SSB filter variable. Change spectrum overlay accordingly.

This commit is contained in:
f4exb 2015-05-12 21:50:02 +02:00
parent e15f4f26f1
commit d395ba24c7
10 changed files with 253 additions and 128 deletions

View File

@ -78,7 +78,8 @@ Done since the fork
- SSB bandwidth can now be tuned in steps of 100 Hz
- NFM and SSB receiver in focus trigger the display of the central frequency line on the spectrum frequency scale thus facilitating its identification
- Added AM demod so now you can listen to air traffic!
- Added the possibility to change the brightness and/or color of the grid.
- Added the possibility to change the brightness and/or color of the grid.
- Make the low cutoff frequency of the SSB filter variable so it can be used for CW also.
=====
To Do

View File

@ -54,11 +54,13 @@ private:
struct ChannelMarkerState {
ChannelMarker* m_channelMarker;
QRectF m_glRect;
QRectF m_glRectDsb;
QRect m_rect;
ChannelMarkerState(ChannelMarker* channelMarker) :
m_channelMarker(channelMarker),
m_glRect()
m_glRect(),
m_glRectDsb()
{ }
};
QList<ChannelMarkerState*> m_channelMarkerStates;
@ -150,8 +152,6 @@ private:
void enterEvent(QEvent* event);
void leaveEvent(QEvent* event);
float getCenterFreqLineRelPos(ChannelMarker *channelMarker);
private slots:
void tick();
void channelMarkerChanged();

View File

@ -27,6 +27,9 @@ public:
void setBandwidth(int bandwidth);
int getBandwidth() const { return m_bandwidth; }
void setLowCutoff(int lowCutoff);
int getLowCutoff() const { return m_lowCutoff; }
void setSidebands(sidebands_t sidebands);
sidebands_t getSidebands() const { return m_sidebands; }
@ -46,6 +49,7 @@ protected:
QString m_title;
int m_centerFrequency;
int m_bandwidth;
int m_lowCutoff;
sidebands_t m_sidebands;
bool m_visible;
bool m_highlighted;

View File

@ -22,6 +22,8 @@
#include "audio/audiooutput.h"
#include "dsp/dspcommands.h"
#include <iostream>
MESSAGE_CLASS_DEFINITION(SSBDemod::MsgConfigureSSBDemod, Message)
SSBDemod::SSBDemod(AudioFifo* audioFifo, SampleSink* sampleSink) :
@ -29,6 +31,7 @@ SSBDemod::SSBDemod(AudioFifo* audioFifo, SampleSink* sampleSink) :
m_audioFifo(audioFifo)
{
m_Bandwidth = 5000;
m_LowCutoff = 300;
m_volume = 2.0;
m_sampleRate = 96000;
m_frequency = 0;
@ -41,7 +44,7 @@ SSBDemod::SSBDemod(AudioFifo* audioFifo, SampleSink* sampleSink) :
m_undersampleCount = 0;
m_usb = true;
SSBFilter = new fftfilt(0.01, m_Bandwidth / 48000.0, ssbFftLen);
SSBFilter = new fftfilt(m_LowCutoff / 48000.0, m_Bandwidth / 48000.0, ssbFftLen);
// if (!USBFilter) segfault;
}
@ -50,9 +53,9 @@ SSBDemod::~SSBDemod()
if (SSBFilter) delete SSBFilter;
}
void SSBDemod::configure(MessageQueue* messageQueue, Real Bandwidth, Real volume)
void SSBDemod::configure(MessageQueue* messageQueue, Real Bandwidth, Real LowCutoff, Real volume)
{
Message* cmd = MsgConfigureSSBDemod::create(Bandwidth, volume);
Message* cmd = MsgConfigureSSBDemod::create(Bandwidth, LowCutoff, volume);
cmd->submit(messageQueue, this);
}
@ -110,7 +113,7 @@ void SSBDemod::stop()
bool SSBDemod::handleMessage(Message* cmd)
{
float band;
float band, lowCutoff;
if(DSPSignalNotification::match(cmd)) {
DSPSignalNotification* signal = (DSPSignalNotification*)cmd;
@ -125,17 +128,26 @@ bool SSBDemod::handleMessage(Message* cmd)
MsgConfigureSSBDemod* cfg = (MsgConfigureSSBDemod*)cmd;
band = cfg->getBandwidth();
lowCutoff = cfg->getLoCutoff();
if (band < 0) {
band = -band;
lowCutoff = -lowCutoff;
m_usb = false;
} else
m_usb = true;
if (band < 500.0f)
band = 500.0f;
if (band < 100.0f)
{
band = 100.0f;
lowCutoff = 0;
}
m_Bandwidth = band;
m_LowCutoff = lowCutoff;
m_interpolator.create(16, m_sampleRate, band * 2.0f);
SSBFilter->create_filter(0.3f / 48.0f, m_Bandwidth / 48000.0f);
SSBFilter->create_filter(m_LowCutoff / 48000.0f, m_Bandwidth / 48000.0f);
m_volume = cfg->getVolume();
m_volume *= m_volume * 0.1;

View File

@ -35,7 +35,7 @@ public:
SSBDemod(AudioFifo* audioFifo, SampleSink* sampleSink);
~SSBDemod();
void configure(MessageQueue* messageQueue, Real Bandwidth, Real volume);
void configure(MessageQueue* messageQueue, Real Bandwidth, Real LowCutoff, Real volume);
void feed(SampleVector::const_iterator begin, SampleVector::const_iterator end, bool positiveOnly);
void start();
@ -48,20 +48,23 @@ private:
public:
Real getBandwidth() const { return m_Bandwidth; }
Real getLoCutoff() const { return m_LowCutoff; }
Real getVolume() const { return m_volume; }
static MsgConfigureSSBDemod* create(Real Bandwidth, Real volume)
static MsgConfigureSSBDemod* create(Real Bandwidth, Real LowCutoff, Real volume)
{
return new MsgConfigureSSBDemod(Bandwidth, volume);
return new MsgConfigureSSBDemod(Bandwidth, LowCutoff, volume);
}
private:
Real m_Bandwidth;
Real m_LowCutoff;
Real m_volume;
MsgConfigureSSBDemod(Real Bandwidth, Real volume) :
MsgConfigureSSBDemod(Real Bandwidth, Real LowCutoff, Real volume) :
Message(),
m_Bandwidth(Bandwidth),
m_LowCutoff(LowCutoff),
m_volume(volume)
{ }
};
@ -73,6 +76,7 @@ private:
typedef std::vector<AudioSample> AudioVector;
Real m_Bandwidth;
Real m_LowCutoff;
Real m_volume;
int m_undersampleCount;
int m_sampleRate;

View File

@ -13,6 +13,8 @@
#include "util/simpleserializer.h"
#include "gui/basicchannelsettingswidget.h"
#include <iostream>
SSBDemodGUI* SSBDemodGUI::create(PluginAPI* pluginAPI)
{
SSBDemodGUI* gui = new SSBDemodGUI(pluginAPI);
@ -42,6 +44,7 @@ QByteArray SSBDemodGUI::serialize() const
SimpleSerializer s(1);
s.writeS32(1, m_channelMarker->getCenterFrequency());
s.writeS32(2, ui->BW->value());
s.writeS32(6, ui->lowCut->value());
s.writeS32(3, ui->volume->value());
s.writeBlob(4, ui->spectrumGUI->serialize());
s.writeU32(5, m_channelMarker->getColor().rgb());
@ -63,8 +66,10 @@ bool SSBDemodGUI::deserialize(const QByteArray& data)
qint32 tmp;
d.readS32(1, &tmp, 0);
m_channelMarker->setCenterFrequency(tmp);
d.readS32(2, &tmp, 3);
d.readS32(2, &tmp, 30);
ui->BW->setValue(tmp);
d.readS32(6, &tmp, 3);
ui->lowCut->setValue(tmp);
d.readS32(3, &tmp, 20);
ui->volume->setValue(tmp);
d.readBlob(4, &bytetmp);
@ -114,11 +119,48 @@ void SSBDemodGUI::on_BW_valueChanged(int value)
QString s = QString::number(value/10.0, 'f', 1);
ui->BWText->setText(s);
m_channelMarker->setBandwidth(value * 100 * 2);
if (value < 0) {
m_channelMarker->setSidebands(ChannelMarker::lsb);
} else {
m_channelMarker->setSidebands(ChannelMarker::usb);
}
on_lowCut_valueChanged(m_channelMarker->getLowCutoff()/100);
}
int SSBDemodGUI::getEffectiveLowCutoff(int lowCutoff)
{
int ssbBW = m_channelMarker->getBandwidth() / 2;
int effectiveLowCutoff = lowCutoff;
const int guard = 100;
if (ssbBW < 0) {
if (effectiveLowCutoff < ssbBW + guard) {
effectiveLowCutoff = ssbBW + guard;
}
if (effectiveLowCutoff > 0) {
effectiveLowCutoff = 0;
}
} else {
if (effectiveLowCutoff > ssbBW - guard) {
effectiveLowCutoff = ssbBW - guard;
}
if (effectiveLowCutoff < 0) {
effectiveLowCutoff = 0;
}
}
return effectiveLowCutoff;
}
void SSBDemodGUI::on_lowCut_valueChanged(int value)
{
int lowCutoff = getEffectiveLowCutoff(value * 100);
m_channelMarker->setLowCutoff(lowCutoff);
QString s = QString::number(lowCutoff/1000.0, 'f', 1);
ui->lowCutText->setText(s);
ui->lowCut->setValue(lowCutoff/100);
applySettings();
}
@ -207,6 +249,7 @@ void SSBDemodGUI::applySettings()
m_channelMarker->getCenterFrequency());
m_ssbDemod->configure(m_threadedSampleSink->getMessageQueue(),
ui->BW->value() * 100.0,
ui->lowCut->value() * 100.0,
ui->volume->value() / 10.0 );
}

View File

@ -37,6 +37,7 @@ private slots:
void on_deltaFrequency_changed(quint64 value);
void on_deltaMinus_clicked(bool minus);
void on_BW_valueChanged(int value);
void on_lowCut_valueChanged(int value);
void on_volume_valueChanged(int value);
void onWidgetRolled(QWidget* widget, bool rollDown);
void onMenuDoubleClicked();
@ -56,6 +57,7 @@ private:
explicit SSBDemodGUI(PluginAPI* pluginAPI, QWidget* parent = NULL);
~SSBDemodGUI();
int getEffectiveLowCutoff(int lowCutoff);
void applySettings();
void leaveEvent(QEvent*);

View File

@ -41,6 +41,41 @@
<property name="spacing">
<number>3</number>
</property>
<item row="1" column="1">
<widget class="QSlider" name="BW">
<property name="minimum">
<number>-60</number>
</property>
<property name="maximum">
<number>60</number>
</property>
<property name="pageStep">
<number>1</number>
</property>
<property name="value">
<number>30</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QLabel" name="volumeText">
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>2.0</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLabel" name="BWText">
<property name="minimumSize">
@ -57,7 +92,7 @@
</property>
</widget>
</item>
<item row="2" column="1">
<item row="3" column="1">
<widget class="QSlider" name="volume">
<property name="maximum">
<number>100</number>
@ -70,65 +105,6 @@
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSlider" name="BW">
<property name="minimum">
<number>-60</number>
</property>
<property name="maximum">
<number>60</number>
</property>
<property name="pageStep">
<number>10</number>
</property>
<property name="value">
<number>30</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Volume</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QLabel" name="volumeText">
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>2.0</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Bandwidth</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QRadioButton" name="deltaMinus">
<property name="layoutDirection">
<enum>Qt::RightToLeft</enum>
</property>
<property name="text">
<string>Minus</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="ValueDial" name="deltaFrequency" native="true">
<property name="sizePolicy">
@ -160,6 +136,20 @@
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="BWLabel">
<property name="text">
<string>Bandwidth</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="volumeLabel">
<property name="text">
<string>Volume</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="deltaUnits">
<property name="text">
@ -167,6 +157,58 @@
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QRadioButton" name="deltaMinus">
<property name="layoutDirection">
<enum>Qt::RightToLeft</enum>
</property>
<property name="text">
<string>Minus</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="lowCutLabel">
<property name="text">
<string>Low cutoff</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QSlider" name="lowCut">
<property name="minimum">
<number>-60</number>
</property>
<property name="maximum">
<number>60</number>
</property>
<property name="pageStep">
<number>1</number>
</property>
<property name="value">
<number>3</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QLabel" name="lowCutText">
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>0.3 kHz</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="spectrumContainer" native="true">
@ -214,18 +256,6 @@
</widget>
</widget>
<customwidgets>
<customwidget>
<class>GLSpectrum</class>
<extends>QWidget</extends>
<header>gui/glspectrum.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>GLSpectrumGUI</class>
<extends>QWidget</extends>
<header>gui/glspectrumgui.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>RollupWidget</class>
<extends>QWidget</extends>
@ -238,6 +268,18 @@
<header>gui/valuedial.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>GLSpectrum</class>
<extends>QWidget</extends>
<header>gui/glspectrum.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>GLSpectrumGUI</class>
<extends>QWidget</extends>
<header>gui/glspectrumgui.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>

View File

@ -1,5 +1,7 @@
#include "dsp/channelmarker.h"
#include <iostream>
QRgb ChannelMarker::m_colorTable[] = {
qRgb(0xc0, 0x00, 0x00),
qRgb(0x00, 0xc0, 0x00),
@ -29,6 +31,7 @@ ChannelMarker::ChannelMarker(QObject* parent) :
QObject(parent),
m_centerFrequency(0),
m_bandwidth(0),
m_lowCutoff(0),
m_sidebands(dsb),
m_visible(false),
m_highlighted(false),
@ -57,6 +60,12 @@ void ChannelMarker::setBandwidth(int bandwidth)
emit changed();
}
void ChannelMarker::setLowCutoff(int lowCutoff)
{
m_lowCutoff = lowCutoff;
emit changed();
}
void ChannelMarker::setSidebands(sidebands_t sidebands)
{
m_sidebands = sidebands;

View File

@ -551,20 +551,10 @@ void GLSpectrum::paintGL()
// paint channels
if(m_mouseInside) {
// Effective BW overlays
for(int i = 0; i < m_channelMarkerStates.size(); ++i) {
ChannelMarkerState* dv = m_channelMarkerStates[i];
if(dv->m_channelMarker->getVisible()) {
/*
ChannelMarker::sidebands_t sidebands = dv->m_channelMarker->getSidebands();
float fcLineRelativePos;
if (sidebands == ChannelMarker::usb) {
fcLineRelativePos = 0.0;
} else if (sidebands == ChannelMarker::lsb) {
fcLineRelativePos = 1.0;
} else {
fcLineRelativePos = 0.5;
}
*/
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glColor4f(dv->m_channelMarker->getColor().redF(), dv->m_channelMarker->getColor().greenF(), dv->m_channelMarker->getColor().blueF(), 0.3f);
@ -577,11 +567,22 @@ void GLSpectrum::paintGL()
glVertex2f(1, 1);
glVertex2f(0, 1);
glEnd();
glPopMatrix();
}
}
// Center line overlays based on DSB enveloppe
for(int i = 0; i < m_channelMarkerStates.size(); ++i) {
ChannelMarkerState* dv = m_channelMarkerStates[i];
if(dv->m_channelMarker->getVisible()) {
glDisable(GL_BLEND);
glColor3f(0.8f, 0.8f, 0.6f);
glPushMatrix();
glTranslatef(dv->m_glRectDsb.x(), dv->m_glRectDsb.y(), 0);
glScalef(dv->m_glRectDsb.width(), dv->m_glRectDsb.height(), 1);
glBegin(GL_LINE_LOOP);
glVertex2f(getCenterFreqLineRelPos(dv->m_channelMarker), 0);
glVertex2f(getCenterFreqLineRelPos(dv->m_channelMarker), 1);
glVertex2f(0.5, 0);
glVertex2f(0.5, 1);
glEnd();
glPopMatrix();
}
@ -662,6 +663,8 @@ void GLSpectrum::paintGL()
glPushMatrix();
glTranslatef(m_glWaterfallRect.x(), m_glFrequencyScaleRect.y(), 0);
glScalef(m_glWaterfallRect.width(), m_glFrequencyScaleRect.height(), 1);
// Effective bandwidth overlays
for(int i = 0; i < m_channelMarkerStates.size(); ++i) {
ChannelMarkerState* dv = m_channelMarkerStates[i];
if(dv->m_channelMarker->getVisible()) {
@ -677,19 +680,29 @@ void GLSpectrum::paintGL()
glVertex2f(1, 0.5);
glVertex2f(0, 0.5);
glEnd();
if (dv->m_channelMarker->getHighlighted()) {
glColor3f(0.8f, 0.8f, 0.6f);
glBegin(GL_LINE_LOOP);
glVertex2f(getCenterFreqLineRelPos(dv->m_channelMarker), 0);
glVertex2f(getCenterFreqLineRelPos(dv->m_channelMarker), 1);
glEnd();
}
glDisable(GL_BLEND);
glPopMatrix();
}
}
// Center frequency mark on highlighted channels based on DSB enveloppe
for(int i = 0; i < m_channelMarkerStates.size(); ++i) {
ChannelMarkerState* dv = m_channelMarkerStates[i];
if(dv->m_channelMarker->getVisible()) {
if (dv->m_channelMarker->getHighlighted()) {
glColor3f(0.8f, 0.8f, 0.6f);
glPushMatrix();
glTranslatef(dv->m_glRectDsb.x(), dv->m_glRectDsb.y(), 0);
glScalef(dv->m_glRectDsb.width(), dv->m_glRectDsb.height(), 1);
glBegin(GL_LINE_LOOP);
glVertex2f(0.5, 0);
glVertex2f(0.5, 1);
glEnd();
glPopMatrix();
}
}
}
glPopMatrix();
}
@ -1026,23 +1039,31 @@ void GLSpectrum::applyChanges()
for(int i = 0; i < m_channelMarkerStates.size(); ++i) {
ChannelMarkerState* dv = m_channelMarkerStates[i];
qreal xc, pw, nw;
qreal xc, pw, nw, dsbw;
ChannelMarker::sidebands_t sidebands = dv->m_channelMarker->getSidebands();
xc = m_centerFrequency + dv->m_channelMarker->getCenterFrequency(); // marker center frequency
dsbw = dv->m_channelMarker->getBandwidth();
if (sidebands == ChannelMarker::usb) {
nw = 0; // negative bandwidth
nw = dv->m_channelMarker->getLowCutoff(); // negative bandwidth
pw = dv->m_channelMarker->getBandwidth() / 2; // positive bandwidth
} else if (sidebands == ChannelMarker::lsb) {
pw = 0;
pw = dv->m_channelMarker->getLowCutoff();
nw = dv->m_channelMarker->getBandwidth() / 2;
} else {
pw = dv->m_channelMarker->getBandwidth() / 2;
pw = dsbw / 2;
nw = -pw;
}
//std::cerr << xc << "; " << nw << "; " << pw << std::endl;
// draw the DSB rectangle
dv->m_glRectDsb.setRect(
m_frequencyScale.getPosFromValue(xc - (dsbw/2)) / (float)(width() - leftMargin - rightMargin),
0,
dsbw / (float)m_sampleRate,
1);
// draw the effective BW rectangle
dv->m_glRect.setRect(
m_frequencyScale.getPosFromValue(xc + nw) / (float)(width() - leftMargin - rightMargin),
0,
@ -1366,16 +1387,3 @@ void GLSpectrum::channelMarkerDestroyed(QObject* object)
{
removeChannelMarker((ChannelMarker*)object);
}
float GLSpectrum::getCenterFreqLineRelPos(ChannelMarker *channelMarker)
{
ChannelMarker::sidebands_t sidebands = channelMarker->getSidebands();
if (sidebands == ChannelMarker::usb) {
return 0.0;
} else if (sidebands == ChannelMarker::lsb) {
return 1.0;
} else {
return 0.5;
}
}