kopia lustrzana https://github.com/f4exb/sdrangel
NFM demod: new discriminator and optional FM deviatoin based squelch
rodzic
b805cc89c9
commit
2318419716
|
@ -1,3 +1,9 @@
|
|||
sdrangel (3.3.0-1) unstable; urgency=medium
|
||||
|
||||
* NFM demod: new discriminator and optional FM deviatoin based squelch
|
||||
|
||||
-- Edouard Griffiths, F4EXB <f4exb06@gmail.com> Thu, 02 Mar 2017 23:14:18 +0100
|
||||
|
||||
sdrangel (3.2.0-1) unstable; urgency=medium
|
||||
|
||||
* ATV demodulator for amateur Analog TV
|
||||
|
|
|
@ -55,6 +55,7 @@ NFMDemod::NFMDemod() :
|
|||
m_config.m_afBandwidth = 3000;
|
||||
m_config.m_fmDeviation = 2000;
|
||||
m_config.m_squelchGate = 5; // 10s of ms at 48000 Hz sample rate. Corresponds to 2400 for AGC attack
|
||||
m_config.m_deltaSquelch = false;
|
||||
m_config.m_squelch = -30.0;
|
||||
m_config.m_volume = 1.0;
|
||||
m_config.m_ctcssOn = false;
|
||||
|
@ -68,7 +69,7 @@ NFMDemod::NFMDemod() :
|
|||
|
||||
m_agcLevel = 1.0;
|
||||
m_AGC.resize(m_squelchGate, m_agcLevel);
|
||||
m_movingAverage.resize(16, 0);
|
||||
m_movingAverage.resize(32, 0);
|
||||
|
||||
m_ctcssDetector.setCoefficients(3000, 6000.0); // 0.5s / 2 Hz resolution
|
||||
m_afSquelch.setCoefficients(24, 600, 48000.0, 200, 0); // 4000 Hz span, 250us, 100ms attack
|
||||
|
@ -87,6 +88,7 @@ void NFMDemod::configure(MessageQueue* messageQueue,
|
|||
int fmDeviation,
|
||||
Real volume,
|
||||
int squelchGate,
|
||||
bool deltaSquelch,
|
||||
Real squelch,
|
||||
bool ctcssOn,
|
||||
bool audioMute)
|
||||
|
@ -96,6 +98,7 @@ void NFMDemod::configure(MessageQueue* messageQueue,
|
|||
fmDeviation,
|
||||
volume,
|
||||
squelchGate,
|
||||
deltaSquelch,
|
||||
squelch,
|
||||
ctcssOn,
|
||||
audioMute);
|
||||
|
@ -154,8 +157,9 @@ void NFMDemod::feed(const SampleVector::const_iterator& begin, const SampleVecto
|
|||
|
||||
//double magsqRaw = m_AGC.getMagSq();
|
||||
long double magsqRaw; // = ci.real()*ci.real() + c.imag()*c.imag();
|
||||
Real deviation;
|
||||
|
||||
Real demod = m_phaseDiscri.phaseDiscriminator3(ci, magsqRaw);
|
||||
Real demod = m_phaseDiscri.phaseDiscriminatorDelta(ci, magsqRaw, deviation);
|
||||
|
||||
Real magsq = magsqRaw / (1<<30);
|
||||
m_movingAverage.feed(magsq);
|
||||
|
@ -174,20 +178,28 @@ void NFMDemod::feed(const SampleVector::const_iterator& begin, const SampleVecto
|
|||
|
||||
// AF processing
|
||||
|
||||
if (m_movingAverage.average() > m_squelchLevel)
|
||||
if ( (m_running.m_deltaSquelch && ((deviation > m_squelchLevel) || (deviation < -m_squelchLevel))) ||
|
||||
(!m_running.m_deltaSquelch && (m_movingAverage.average() < m_squelchLevel)) )
|
||||
{
|
||||
if (m_squelchCount < m_squelchGate)
|
||||
{
|
||||
m_squelchCount++;
|
||||
m_squelchCount = 0; // return to 0
|
||||
}
|
||||
else
|
||||
{
|
||||
m_squelchCount--; // grace period
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_squelchCount = 0;
|
||||
if (m_squelchCount < m_squelchGate + 2)
|
||||
{
|
||||
m_squelchCount++;
|
||||
}
|
||||
}
|
||||
|
||||
//squelchOpen = (getMag() > m_squelchLevel);
|
||||
m_squelchOpen = m_squelchCount == m_squelchGate; // wait for AGC to stabilize
|
||||
m_squelchOpen = m_squelchCount >= m_squelchGate; // wait for AGC to stabilize
|
||||
|
||||
/*
|
||||
if (m_afSquelch.analyze(demod))
|
||||
|
@ -322,6 +334,7 @@ bool NFMDemod::handleMessage(const Message& cmd)
|
|||
m_config.m_fmDeviation = cfg.getFMDeviation();
|
||||
m_config.m_volume = cfg.getVolume();
|
||||
m_config.m_squelchGate = cfg.getSquelchGate();
|
||||
m_config.m_deltaSquelch = cfg.getDeltaSquelch();
|
||||
m_config.m_squelch = cfg.getSquelch();
|
||||
m_config.m_ctcssOn = cfg.getCtcssOn();
|
||||
m_config.m_audioMute = cfg.getAudioMute();
|
||||
|
@ -332,8 +345,9 @@ bool NFMDemod::handleMessage(const Message& cmd)
|
|||
<< " m_afBandwidth: " << m_config.m_afBandwidth
|
||||
<< " m_fmDeviation: " << m_config.m_fmDeviation
|
||||
<< " m_volume: " << m_config.m_volume
|
||||
<< " m_squelchGate" << m_config.m_squelchGate
|
||||
<< " m_squelch: " << m_config.m_squelch
|
||||
<< " m_squelchGate: " << m_config.m_squelchGate
|
||||
<< " m_deltaSquelch: " << m_config.m_deltaSquelch
|
||||
<< " m_squelch: " << m_squelchLevel
|
||||
<< " m_ctcssOn: " << m_config.m_ctcssOn
|
||||
<< " m_audioMute: " << m_config.m_audioMute;
|
||||
|
||||
|
@ -360,13 +374,13 @@ void NFMDemod::apply()
|
|||
m_interpolator.create(16, m_config.m_inputSampleRate, m_config.m_rfBandwidth / 2.2);
|
||||
m_interpolatorDistanceRemain = 0;
|
||||
m_interpolatorDistance = (Real) m_config.m_inputSampleRate / (Real) m_config.m_audioSampleRate;
|
||||
m_phaseDiscri.setFMScaling(m_config.m_rfBandwidth / (float) m_config.m_fmDeviation);
|
||||
m_phaseDiscri.setFMScaling((2.0f*m_config.m_rfBandwidth) / (float) m_config.m_fmDeviation);
|
||||
m_settingsMutex.unlock();
|
||||
}
|
||||
|
||||
if (m_config.m_fmDeviation != m_running.m_fmDeviation)
|
||||
{
|
||||
m_phaseDiscri.setFMScaling(m_config.m_rfBandwidth / (float) m_config.m_fmDeviation);
|
||||
m_phaseDiscri.setFMScaling((2.0f*m_config.m_rfBandwidth) / (float) m_config.m_fmDeviation);
|
||||
}
|
||||
|
||||
if ((m_config.m_afBandwidth != m_running.m_afBandwidth) ||
|
||||
|
@ -384,12 +398,16 @@ void NFMDemod::apply()
|
|||
m_squelchCount = 0; // reset squelch open counter
|
||||
}
|
||||
|
||||
if (m_config.m_squelch != m_running.m_squelch)
|
||||
if ((m_config.m_squelch != m_running.m_squelch) ||
|
||||
(m_config.m_deltaSquelch != m_running.m_deltaSquelch))
|
||||
{
|
||||
// input is a value in tenths of dB
|
||||
m_squelchLevel = std::pow(10.0, m_config.m_squelch / 10.0);
|
||||
if (m_config.m_deltaSquelch) { // input is a value in negative millis
|
||||
m_squelchLevel = - m_config.m_squelch / 1000.0;
|
||||
} else { // input is a value in centi-Bels
|
||||
m_squelchLevel = std::pow(10.0, m_config.m_squelch / 100.0);
|
||||
}
|
||||
//m_squelchLevel *= m_squelchLevel;
|
||||
m_afSquelch.setThreshold(m_squelchLevel);
|
||||
//m_afSquelch.setThreshold(m_squelchLevel);
|
||||
}
|
||||
|
||||
m_running.m_inputSampleRate = m_config.m_inputSampleRate;
|
||||
|
@ -398,6 +416,7 @@ void NFMDemod::apply()
|
|||
m_running.m_afBandwidth = m_config.m_afBandwidth;
|
||||
m_running.m_fmDeviation = m_config.m_fmDeviation;
|
||||
m_running.m_squelchGate = m_config.m_squelchGate;
|
||||
m_running.m_deltaSquelch = m_config.m_deltaSquelch;
|
||||
m_running.m_squelch = m_config.m_squelch;
|
||||
m_running.m_volume = m_config.m_volume;
|
||||
m_running.m_audioSampleRate = m_config.m_audioSampleRate;
|
||||
|
|
|
@ -46,6 +46,7 @@ public:
|
|||
int fmDeviation,
|
||||
Real volume,
|
||||
int squelchGate,
|
||||
bool deltaSquelch,
|
||||
Real squelch,
|
||||
bool ctcssOn,
|
||||
bool audioMute);
|
||||
|
@ -92,6 +93,7 @@ private:
|
|||
int getFMDeviation() const { return m_fmDeviation; }
|
||||
Real getVolume() const { return m_volume; }\
|
||||
int getSquelchGate() const { return m_squelchGate; }
|
||||
bool getDeltaSquelch() const { return m_deltaSquelch; }
|
||||
Real getSquelch() const { return m_squelch; }
|
||||
bool getCtcssOn() const { return m_ctcssOn; }
|
||||
bool getAudioMute() const { return m_audioMute; }
|
||||
|
@ -101,11 +103,21 @@ private:
|
|||
int fmDeviation,
|
||||
Real volume,
|
||||
int squelchGate,
|
||||
bool deltaSquelch,
|
||||
Real squelch,
|
||||
bool ctcssOn,
|
||||
bool audioMute)
|
||||
{
|
||||
return new MsgConfigureNFMDemod(rfBandwidth, afBandwidth, fmDeviation, volume, squelchGate, squelch, ctcssOn, audioMute);
|
||||
return new MsgConfigureNFMDemod(
|
||||
rfBandwidth,
|
||||
afBandwidth,
|
||||
fmDeviation,
|
||||
volume,
|
||||
squelchGate,
|
||||
deltaSquelch,
|
||||
squelch,
|
||||
ctcssOn,
|
||||
audioMute);
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -114,6 +126,7 @@ private:
|
|||
int m_fmDeviation;
|
||||
Real m_volume;
|
||||
int m_squelchGate;
|
||||
bool m_deltaSquelch;
|
||||
Real m_squelch;
|
||||
bool m_ctcssOn;
|
||||
bool m_audioMute;
|
||||
|
@ -123,6 +136,7 @@ private:
|
|||
int fmDeviation,
|
||||
Real volume,
|
||||
int squelchGate,
|
||||
bool deltaSquelch,
|
||||
Real squelch,
|
||||
bool ctcssOn,
|
||||
bool audioMute) :
|
||||
|
@ -132,6 +146,7 @@ private:
|
|||
m_fmDeviation(fmDeviation),
|
||||
m_volume(volume),
|
||||
m_squelchGate(squelchGate),
|
||||
m_deltaSquelch(deltaSquelch),
|
||||
m_squelch(squelch),
|
||||
m_ctcssOn(ctcssOn),
|
||||
m_audioMute(audioMute)
|
||||
|
@ -156,6 +171,7 @@ private:
|
|||
Real m_afBandwidth;
|
||||
int m_fmDeviation;
|
||||
int m_squelchGate;
|
||||
bool m_deltaSquelch;
|
||||
Real m_squelch;
|
||||
Real m_volume;
|
||||
bool m_ctcssOn;
|
||||
|
@ -170,6 +186,7 @@ private:
|
|||
m_afBandwidth(-1),
|
||||
m_fmDeviation(1),
|
||||
m_squelchGate(1),
|
||||
m_deltaSquelch(false),
|
||||
m_squelch(0),
|
||||
m_volume(0),
|
||||
m_ctcssOn(false),
|
||||
|
|
|
@ -89,6 +89,7 @@ QByteArray NFMDemodGUI::serialize() const
|
|||
s.writeBool(9, ui->ctcssOn->isChecked());
|
||||
s.writeBool(10, ui->audioMute->isChecked());
|
||||
s.writeS32(11, ui->squelchGate->value());
|
||||
s.writeBool(12, ui->deltaSquelch->isChecked());
|
||||
return s.final();
|
||||
}
|
||||
|
||||
|
@ -136,6 +137,8 @@ bool NFMDemodGUI::deserialize(const QByteArray& data)
|
|||
ui->audioMute->setChecked(boolTmp);
|
||||
d.readS32(11, &tmp, 5);
|
||||
ui->squelchGate->setValue(tmp);
|
||||
d.readBool(12, &boolTmp, false);
|
||||
ui->deltaSquelch->setChecked(boolTmp);
|
||||
|
||||
blockApplySettings(false);
|
||||
m_channelMarker.blockSignals(false);
|
||||
|
@ -208,9 +211,33 @@ void NFMDemodGUI::on_squelchGate_valueChanged(int value)
|
|||
applySettings();
|
||||
}
|
||||
|
||||
void NFMDemodGUI::on_deltaSquelch_toggled(bool checked)
|
||||
{
|
||||
if (ui->deltaSquelch->isChecked())
|
||||
{
|
||||
ui->squelchText->setText(QString("%1").arg((-ui->squelch->value()) / 10.0, 0, 'f', 1));
|
||||
ui->squelchText->setToolTip(tr("Squelch deviation threshold (%)"));
|
||||
}
|
||||
else
|
||||
{
|
||||
ui->squelchText->setText(QString("%1").arg(ui->squelch->value() / 10.0, 0, 'f', 1));
|
||||
ui->squelchText->setToolTip(tr("Squelch power threshold (dB)"));
|
||||
}
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void NFMDemodGUI::on_squelch_valueChanged(int value)
|
||||
{
|
||||
if (ui->deltaSquelch->isChecked())
|
||||
{
|
||||
ui->squelchText->setText(QString("%1").arg(-value / 10.0, 0, 'f', 1));
|
||||
ui->squelchText->setToolTip(tr("Squelch deviation threshold (%)"));
|
||||
}
|
||||
else
|
||||
{
|
||||
ui->squelchText->setText(QString("%1").arg(value / 10.0, 0, 'f', 1));
|
||||
ui->squelchText->setToolTip(tr("Squelch power threshold (dB)"));
|
||||
}
|
||||
applySettings();
|
||||
}
|
||||
|
||||
|
@ -311,6 +338,9 @@ NFMDemodGUI::NFMDemodGUI(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI, QWidg
|
|||
m_deviceAPI->addChannelMarker(&m_channelMarker);
|
||||
m_deviceAPI->addRollupWidget(this);
|
||||
|
||||
QChar delta = QChar(0x94, 0x03);
|
||||
ui->deltaSquelch->setText(delta);
|
||||
|
||||
applySettings();
|
||||
}
|
||||
|
||||
|
@ -347,7 +377,8 @@ void NFMDemodGUI::applySettings()
|
|||
m_fmDev[ui->rfBW->currentIndex()],
|
||||
ui->volume->value() / 10.0f,
|
||||
ui->squelchGate->value(), // in 10ths of ms
|
||||
ui->squelch->value() / 10.0f,
|
||||
ui->deltaSquelch->isChecked(),
|
||||
ui->squelch->value(), // -1000 -> 0
|
||||
ui->ctcssOn->isChecked(),
|
||||
ui->audioMute->isChecked());
|
||||
}
|
||||
|
|
|
@ -47,6 +47,7 @@ private slots:
|
|||
void on_afBW_valueChanged(int value);
|
||||
void on_volume_valueChanged(int value);
|
||||
void on_squelchGate_valueChanged(int value);
|
||||
void on_deltaSquelch_toggled(bool checked);
|
||||
void on_squelch_valueChanged(int value);
|
||||
void on_ctcss_currentIndexChanged(int index);
|
||||
void on_ctcssOn_toggled(bool checked);
|
||||
|
|
|
@ -53,7 +53,16 @@
|
|||
<property name="spacing">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<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>
|
||||
|
@ -390,6 +399,25 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="ButtonSwitch" name="deltaSquelch">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>24</width>
|
||||
<height>24</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Toggle frequency deviation (on) or channel power (off) based squelch</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>D</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDial" name="squelch">
|
||||
<property name="maximumSize">
|
||||
|
@ -422,13 +450,13 @@
|
|||
<widget class="QLabel" name="squelchText">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<width>34</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<width>16777215</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
|
@ -596,6 +624,11 @@
|
|||
<header>gui/levelmeter.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>ButtonSwitch</class>
|
||||
<extends>QToolButton</extends>
|
||||
<header>gui/buttonswitch.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources>
|
||||
<include location="../../../sdrbase/resources/res.qrc"/>
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
const PluginDescriptor NFMPlugin::m_pluginDescriptor = {
|
||||
QString("NFM Demodulator"),
|
||||
QString("3.2.0"),
|
||||
QString("3.3.0"),
|
||||
QString("(c) Edouard Griffiths, F4EXB"),
|
||||
QString("https://github.com/f4exb/sdrangel"),
|
||||
true,
|
||||
|
|
|
@ -51,6 +51,29 @@ public:
|
|||
return (std::atan2(d.imag(), d.real()) / M_PI) * m_fmScaling;
|
||||
}
|
||||
|
||||
/**
|
||||
* Discriminator with phase detection using atan2 and frequency by derivation.
|
||||
* This yields a precise deviation to sample rate ratio: Sample rate => +/-1.0
|
||||
*/
|
||||
Real phaseDiscriminatorDelta(const Complex& sample, long double& magsq, Real& fmDev)
|
||||
{
|
||||
Real fltI = sample.real();
|
||||
Real fltQ = sample.imag();
|
||||
magsq = fltI*fltI + fltQ*fltQ;
|
||||
|
||||
Real curArg = atan2_approximation2((float) fltQ, (float) fltI);
|
||||
fmDev = (curArg - m_prevArg) / M_PI;
|
||||
m_prevArg = curArg;
|
||||
|
||||
if (fmDev < -1.0f) {
|
||||
fmDev += 2.0f;
|
||||
} else if (fmDev > 1.0f) {
|
||||
fmDev -= 2.0f;
|
||||
}
|
||||
|
||||
return fmDev * m_fmScaling;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alternative without atan at the expense of a slight distorsion on very wideband signals
|
||||
* http://www.embedded.com/design/configurable-systems/4212086/DSP-Tricks--Frequency-demodulation-algorithms-
|
||||
|
@ -73,14 +96,14 @@ public:
|
|||
/**
|
||||
* Second alternative
|
||||
*/
|
||||
Real phaseDiscriminator3(const Complex& sample, long double& magsq)
|
||||
Real phaseDiscriminator3(const Complex& sample, long double& magsq, Real& fltVal)
|
||||
{
|
||||
Real fltI = sample.real();
|
||||
Real fltQ = sample.imag();
|
||||
double fltNorm;
|
||||
Real fltNormI;
|
||||
Real fltNormQ;
|
||||
Real fltVal;
|
||||
//Real fltVal;
|
||||
|
||||
magsq = fltI*fltI + fltQ*fltQ;
|
||||
fltNorm = std::sqrt(magsq);
|
||||
|
@ -91,7 +114,7 @@ public:
|
|||
fltVal = m_fltPreviousI*(fltNormQ - m_fltPreviousQ2);
|
||||
fltVal -= m_fltPreviousQ*(fltNormI - m_fltPreviousI2);
|
||||
fltVal += 2.0f;
|
||||
fltVal /= 2.0f; // normally it is /4
|
||||
fltVal /= 4.0f; // normally it is /4
|
||||
|
||||
m_fltPreviousQ2 = m_fltPreviousQ;
|
||||
m_fltPreviousI2 = m_fltPreviousI;
|
||||
|
@ -110,7 +133,65 @@ private:
|
|||
Real m_fltPreviousQ;
|
||||
Real m_fltPreviousI2;
|
||||
Real m_fltPreviousQ2;
|
||||
Real m_prevArg;
|
||||
|
||||
float atan2_approximation1(float y, float x)
|
||||
{
|
||||
//http://pubs.opengroup.org/onlinepubs/009695399/functions/atan2.html
|
||||
//Volkan SALMA
|
||||
|
||||
const float ONEQTR_PI = M_PI / 4.0;
|
||||
const float THRQTR_PI = 3.0 * M_PI / 4.0;
|
||||
float r, angle;
|
||||
float abs_y = std::fabs(y) + 1e-10f; // kludge to prevent 0/0 condition
|
||||
if ( x < 0.0f )
|
||||
{
|
||||
r = (x + abs_y) / (abs_y - x);
|
||||
angle = THRQTR_PI;
|
||||
}
|
||||
else
|
||||
{
|
||||
r = (x - abs_y) / (x + abs_y);
|
||||
angle = ONEQTR_PI;
|
||||
}
|
||||
angle += (0.1963f * r * r - 0.9817f) * r;
|
||||
if ( y < 0.0f )
|
||||
return( -angle ); // negate if in quad III or IV
|
||||
else
|
||||
return( angle );
|
||||
|
||||
|
||||
}
|
||||
|
||||
#define PI_FLOAT 3.14159265f
|
||||
#define PIBY2_FLOAT 1.5707963f
|
||||
// |error| < 0.005
|
||||
float atan2_approximation2( float y, float x )
|
||||
{
|
||||
if ( x == 0.0f )
|
||||
{
|
||||
if ( y > 0.0f ) return PIBY2_FLOAT;
|
||||
if ( y == 0.0f ) return 0.0f;
|
||||
return -PIBY2_FLOAT;
|
||||
}
|
||||
float atan;
|
||||
float z = y/x;
|
||||
if ( std::fabs( z ) < 1.0f )
|
||||
{
|
||||
atan = z/(1.0f + 0.28f*z*z);
|
||||
if ( x < 0.0f )
|
||||
{
|
||||
if ( y < 0.0f ) return atan - PI_FLOAT;
|
||||
return atan + PI_FLOAT;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
atan = PIBY2_FLOAT - z/(z*z + 0.28f);
|
||||
if ( y < 0.0f ) return atan - PI_FLOAT;
|
||||
}
|
||||
return atan;
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* INCLUDE_DSP_PHASEDISCRI_H_ */
|
||||
|
|
|
@ -84,7 +84,7 @@
|
|||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string><html><head/><body><p>Version 3.2.0 - Copyright (C) 2015-2017 Edouard Griffiths, F4EXB. </p><p>Code at <a href="https://github.com/f4exb/sdrangel"><span style=" text-decoration: underline; color:#0000ff;">https://github.com/f4exb/sdrangel</span></a></p><p>Many thanks to the original developers:</p><p>The osmocom developer team - especially horizon, Hoernchen &amp; tnt.</p><p>Christian Daniel from maintech GmbH.</p><p>John Greb (hexameron) for the contributions in <a href="https://github.com/hexameron/rtl-sdrangelove"><span style=" text-decoration: underline; color:#0000ff;">RTL-SDRangelove</span></a></p><p>The following rules apply to the SDRangel main application and libsdrbase:<br/>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; either version 2 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. You should have received a copy of the GNU General Public License along with this program. If not, see <a href="http://www.gnu.org/licenses/"><span style=" text-decoration: underline; color:#0000ff;">http://www.gnu.org/licenses/</span></a>.</p><p>For the license of installed plugins, look into the plugin list.</p></body></html></string>
|
||||
<string><html><head/><body><p>Version 3.3.0 - Copyright (C) 2015-2017 Edouard Griffiths, F4EXB. </p><p>Code at <a href="https://github.com/f4exb/sdrangel"><span style=" text-decoration: underline; color:#0000ff;">https://github.com/f4exb/sdrangel</span></a></p><p>Many thanks to the original developers:</p><p>The osmocom developer team - especially horizon, Hoernchen &amp; tnt.</p><p>Christian Daniel from maintech GmbH.</p><p>John Greb (hexameron) for the contributions in <a href="https://github.com/hexameron/rtl-sdrangelove"><span style=" text-decoration: underline; color:#0000ff;">RTL-SDRangelove</span></a></p><p>The following rules apply to the SDRangel main application and libsdrbase:<br/>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; either version 2 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. You should have received a copy of the GNU General Public License along with this program. If not, see <a href="http://www.gnu.org/licenses/"><span style=" text-decoration: underline; color:#0000ff;">http://www.gnu.org/licenses/</span></a>.</p><p>For the license of installed plugins, look into the plugin list.</p></body></html></string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
|
|
|
@ -453,9 +453,9 @@ void MainWindow::createStatusBar()
|
|||
{
|
||||
QString qtVersionStr = QString("Qt %1 ").arg(QT_VERSION_STR);
|
||||
#if QT_VERSION >= 0x050400
|
||||
m_showSystemWidget = new QLabel("SDRangel v3.2.0 " + qtVersionStr + QSysInfo::prettyProductName(), this);
|
||||
m_showSystemWidget = new QLabel("SDRangel v3.3.0 " + qtVersionStr + QSysInfo::prettyProductName(), this);
|
||||
#else
|
||||
m_showSystemWidget = new QLabel("SDRangel v3.2.0 " + qtVersionStr, this);
|
||||
m_showSystemWidget = new QLabel("SDRangel v3.3.0 " + qtVersionStr, this);
|
||||
#endif
|
||||
statusBar()->addPermanentWidget(m_showSystemWidget);
|
||||
|
||||
|
|
Ładowanie…
Reference in New Issue