Enhanced spectrum display: Histogram: define NO_AVX, wider decay range, make stroke and late holdoff adjustable. Added option to show live spectrum (had only max hold before)

This commit is contained in:
f4exb 2015-07-16 04:55:14 +02:00
parent 528b11ebd1
commit 69101c0629
8 changed files with 164 additions and 29 deletions

View File

@ -107,16 +107,22 @@ Done since the fork
- As a consequence of the above added more interesting values for the available sampling rates of the BladeRF plugin
- Variable span for the SSB demod down to 1.5 kHz
- Filter out CTCSS tones for audio and full CTCSS support in NFMDemod
- Enhancement of the NFM squelch
- Enhancement of the NFM squelch mimicking professional analog squelch circuits (based on balance between two AF filters)
- Added a channel analyzer plugin focusing on measurement (DSA/DSO functionnality). Basic functions.
- Added a scope widget in the channel analyzer plugin
- Channel analyzer bandwidth up to half the available RF (IF) bandwidth (was 48k fixed)
- Enhanced scope display and controls: scale display, better X (time) and Y scales control, grid fit to scale, effectively implementing triggers, trigger on magnitude and phase, properly handling time shift, ...
- Enhanced spectrum display: Histogram: define NO_AVX, wider decay range, make stroke and late holdoff adjustable. Added option to show live spectrum (had only max hold before).
- Enhanced channel analyzer: enhanced scope and spectrum displays as mentioned above, make the spectrum display synchronous to scope (hence triggerable a la E4406A).
=====
To Do
=====
- Enhance presets management (Edit, Move, Import/Export from/to human readable format like JSON)
- Variable scope memory depth
- Level calibration
- Enhance WFM (stereo, RDS?)
- Even more demods ...
- Triggering capability like on expensive spectrum analyzers to trap burst signals
- recording capability
- Tx channels for Rx/Tx boards like BladeRF

View File

@ -38,10 +38,13 @@ public:
void setReferenceLevel(Real referenceLevel);
void setPowerRange(Real powerRange);
void setDecay(int decay);
void setHistoLateHoldoff(int lateHoldoff);
void setHistoStroke(int stroke);
void setDisplayWaterfall(bool display);
void setSsbSpectrum(bool ssbSpectrum);
void setInvertedWaterfall(bool inv);
void setDisplayMaxHold(bool display);
void setDisplayCurrent(bool display);
void setDisplayHistogram(bool display);
void setDisplayGrid(bool display);
void setDisplayGridIntensity(int intensity);
@ -96,6 +99,8 @@ private:
std::vector<Real> m_maxHold;
bool m_displayMaxHold;
std::vector<Real> m_current;
bool m_displayCurrent;
Real m_waterfallShare;

View File

@ -45,6 +45,7 @@ private:
bool m_displayWaterfall;
bool m_invertedWaterfall;
bool m_displayMaxHold;
bool m_displayCurrent;
bool m_displayHistogram;
bool m_displayGrid;
bool m_invert;
@ -64,6 +65,7 @@ private slots:
void on_waterfall_toggled(bool checked);
void on_histogram_toggled(bool checked);
void on_maxHold_toggled(bool checked);
void on_current_toggled(bool checked);
void on_invert_toggled(bool checked);
void on_grid_toggled(bool checked);
};

View File

@ -36,6 +36,7 @@ GLSpectrum::GLSpectrum(QWidget* parent) :
m_displayGridIntensity(5),
m_invertedWaterfall(false),
m_displayMaxHold(false),
m_displayCurrent(false),
m_leftMarginTextureAllocated(false),
m_frequencyTextureAllocated(false),
m_waterfallBuffer(NULL),
@ -86,6 +87,9 @@ GLSpectrum::GLSpectrum(QWidget* parent) :
((quint8*)&m_histogramPalette[i])[2] = c.blue();
((quint8*)&m_histogramPalette[i])[3] = c.alpha();
}
m_current.resize(m_fftSize);
m_histogramHoldoffBase = 1; // was 4
m_histogramHoldoffCount = m_histogramHoldoffBase;
m_histogramLateHoldoff = 1; // was 20
@ -169,12 +173,30 @@ void GLSpectrum::setPowerRange(Real powerRange)
void GLSpectrum::setDecay(int decay)
{
m_decay = decay;
if(m_decay < -2)
m_decay = -2;
if(m_decay < 0)
m_decay = 0;
else if(m_decay > 10)
m_decay = 10;
}
void GLSpectrum::setHistoLateHoldoff(int lateHoldoff)
{
m_histogramLateHoldoff = lateHoldoff;
if(m_histogramLateHoldoff < 0)
m_histogramLateHoldoff = 0;
else if(m_histogramLateHoldoff > 20)
m_histogramLateHoldoff = 20;
}
void GLSpectrum::setHistoStroke(int stroke)
{
m_histogramStroke = stroke;
if(m_histogramStroke < 4)
m_histogramStroke = 4;
else if(m_histogramStroke > 240)
m_histogramStroke = 240;
}
void GLSpectrum::setSampleRate(qint32 sampleRate)
{
m_sampleRate = sampleRate;
@ -212,6 +234,18 @@ void GLSpectrum::setDisplayMaxHold(bool display)
update();
}
void GLSpectrum::setDisplayCurrent(bool display)
{
if(display && (m_current.size() < (uint)m_fftSize)) {
m_current.resize(m_fftSize);
}
m_displayCurrent = display;
m_changesPending = true;
stopDrag();
update();
}
void GLSpectrum::setDisplayHistogram(bool display)
{
m_displayHistogram = display;
@ -333,12 +367,23 @@ void GLSpectrum::updateHistogram(const std::vector<Real>& spectrum)
m_histogramHoldoffCount = m_histogramHoldoffBase;
}
if(m_current.size() < (uint)m_fftSize)
m_current.resize(m_fftSize);
#define NO_AVX
#ifdef NO_AVX
for(int i = 0; i < m_fftSize; i++) {
int v = (int)((spectrum[i] - m_referenceLevel) * 100.0 / m_powerRange + 100.0);
if((v >= 0) && (v <= 99)) {
if (v < 0) {
m_current[i] = m_referenceLevel - m_powerRange;
}
else if (v > 99) {
m_current[i] = m_referenceLevel;
}
else {
//m_current[i] = m_referenceLevel - m_powerRange + (v * m_powerRange) / 99.0;
m_current[i] = spectrum[i];
b = m_histogram + i * 100 + v;
if(*b < 220)
*b += m_histogramStroke; // was 4
@ -517,7 +562,7 @@ void GLSpectrum::paintGL()
}
// paint histogram
if(m_displayHistogram || m_displayMaxHold) {
if(m_displayHistogram || m_displayMaxHold || m_displayCurrent) {
glPushMatrix();
glTranslatef(m_glHistogramRect.x(), m_glHistogramRect.y(), 0);
glScalef(m_glHistogramRect.width(), m_glHistogramRect.height(), 1);
@ -613,7 +658,7 @@ void GLSpectrum::paintGL()
}
// paint left scales (time and power)
if(m_displayWaterfall || m_displayMaxHold || m_displayHistogram ) {
if(m_displayWaterfall || m_displayMaxHold || m_displayCurrent || m_displayHistogram ) {
glBindTexture(GL_TEXTURE_2D, m_leftMarginTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
@ -640,7 +685,7 @@ void GLSpectrum::paintGL()
}
// paint frequency scale
if(m_displayWaterfall || m_displayMaxHold || m_displayHistogram ) {
if(m_displayWaterfall || m_displayMaxHold || m_displayCurrent || m_displayHistogram ) {
glBindTexture(GL_TEXTURE_2D, m_frequencyTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
@ -753,6 +798,31 @@ void GLSpectrum::paintGL()
glPopMatrix();
}
// paint current spectrum line on top of histogram
if(m_displayCurrent) {
glPushMatrix();
glTranslatef(m_glHistogramRect.x(), m_glHistogramRect.y(), 0);
glScalef(m_glHistogramRect.width() / (float)(m_fftSize - 1), -m_glHistogramRect.height() / m_powerRange, 1);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_LINE_SMOOTH);
glLineWidth(1.0f);
glColor3f(1.0f, 1.0f, 0.25f); // intense yellow
Real bottom = -m_powerRange;
glBegin(GL_LINE_STRIP);
for(int i = 0; i < m_fftSize; i++) {
Real v = m_current[i] - m_referenceLevel;
if(v > 0)
v = 0;
else if(v < bottom)
v = bottom;
glVertex2f(i, v);
}
glEnd();
glDisable(GL_LINE_SMOOTH);
glPopMatrix();
}
// paint waterfall grid
if(m_displayWaterfall && m_displayGrid) {
glEnable(GL_BLEND);
@ -800,7 +870,7 @@ void GLSpectrum::paintGL()
}
// paint histogram grid
if((m_displayHistogram || m_displayMaxHold) && (m_displayGrid)) {
if((m_displayHistogram || m_displayMaxHold || m_displayCurrent) && (m_displayGrid)) {
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glLineWidth(1.0f);
@ -881,7 +951,7 @@ void GLSpectrum::applyChanges()
int leftMargin;
int rightMargin = fm.width("000");
if(m_displayWaterfall && (m_displayHistogram | m_displayMaxHold))
if(m_displayWaterfall && (m_displayHistogram | m_displayMaxHold | m_displayCurrent))
{
waterfallHeight = height() * m_waterfallShare - 1;
@ -1041,7 +1111,7 @@ void GLSpectrum::applyChanges()
(float)1
);
}
else if(m_displayHistogram || m_displayMaxHold)
else if(m_displayHistogram || m_displayMaxHold || m_displayCurrent)
{
bottomMargin = frequencyScaleHeight;
frequencyScaleTop = height() - bottomMargin;
@ -1133,7 +1203,7 @@ void GLSpectrum::applyChanges()
1);
*/
if(m_displayHistogram || m_displayMaxHold || m_displayWaterfall) {
if(m_displayHistogram || m_displayMaxHold || m_displayCurrent || m_displayWaterfall) {
dv->m_rect.setRect(m_frequencyScale.getPosFromValue(xc) + leftMargin - 1,
topMargin,
5,
@ -1170,7 +1240,7 @@ void GLSpectrum::applyChanges()
}
}
}
if(m_displayHistogram || m_displayMaxHold) {
if(m_displayHistogram || m_displayMaxHold || m_displayCurrent) {
tickList = &m_powerScale.getTickList();
for(int i = 0; i < tickList->count(); i++) {
tick = &(*tickList)[i];
@ -1191,7 +1261,7 @@ void GLSpectrum::applyChanges()
m_leftMarginTextureAllocated = true;
}
// prepare frequency scale
if(m_displayWaterfall || m_displayHistogram || m_displayMaxHold){
if(m_displayWaterfall || m_displayHistogram || m_displayMaxHold || m_displayCurrent){
m_frequencyPixmap = QPixmap(width(), frequencyScaleHeight);
m_frequencyPixmap.fill(Qt::transparent);
{
@ -1320,7 +1390,7 @@ void GLSpectrum::applyChanges()
void GLSpectrum::mouseMoveEvent(QMouseEvent* event)
{
if(m_displayWaterfall && (m_displayWaterfall || m_displayHistogram || m_displayMaxHold)) {
if(m_displayWaterfall && (m_displayWaterfall || m_displayHistogram || m_displayMaxHold || m_displayCurrent)) {
if(m_frequencyScaleRect.contains(event->pos())) {
if(m_cursorState == CSNormal) {
setCursor(Qt::SizeVerCursor);
@ -1355,7 +1425,7 @@ void GLSpectrum::mouseMoveEvent(QMouseEvent* event)
m_channelMarkerStates[m_cursorChannel]->m_channelMarker->setCenterFrequency(freq);
}
if(m_displayWaterfall || m_displayHistogram || m_displayMaxHold) {
if(m_displayWaterfall || m_displayHistogram || m_displayMaxHold || m_displayCurrent) {
for(int i = 0; i < m_channelMarkerStates.size(); ++i) {
if(m_channelMarkerStates[i]->m_rect.contains(event->pos())) {
if(m_cursorState == CSNormal) {

View File

@ -23,6 +23,7 @@ GLSpectrumGUI::GLSpectrumGUI(QWidget* parent) :
m_displayWaterfall(true),
m_invertedWaterfall(false),
m_displayMaxHold(false),
m_displayCurrent(false),
m_displayHistogram(false),
m_displayGrid(false),
m_invert(true)
@ -85,6 +86,7 @@ QByteArray GLSpectrumGUI::serialize() const
s.writeS32(13, m_displayGridIntensity);
s.writeS32(14, m_histogramLateHoldoff);
s.writeS32(15, m_histogramStroke);
s.writeBool(16, m_displayCurrent);
return s.final();
}
@ -113,6 +115,7 @@ bool GLSpectrumGUI::deserialize(const QByteArray& data)
d.readS32(13, &m_displayGridIntensity, 0);
d.readS32(14, &m_histogramLateHoldoff, 1);
d.readS32(15, &m_histogramStroke, 40);
d.readBool(16, &m_displayCurrent, false);
applySettings();
return true;
} else {
@ -137,6 +140,7 @@ void GLSpectrumGUI::applySettings()
ui->stroke->setSliderPosition(m_histogramStroke);
ui->waterfall->setChecked(m_displayWaterfall);
ui->maxHold->setChecked(m_displayMaxHold);
ui->current->setChecked(m_displayCurrent);
ui->histogram->setChecked(m_displayHistogram);
ui->invert->setChecked(m_invert);
ui->grid->setChecked(m_displayGrid);
@ -150,8 +154,11 @@ void GLSpectrumGUI::applySettings()
m_glSpectrum->setDisplayWaterfall(m_displayWaterfall);
m_glSpectrum->setInvertedWaterfall(m_invertedWaterfall);
m_glSpectrum->setDisplayMaxHold(m_displayMaxHold);
m_glSpectrum->setDisplayCurrent(m_displayCurrent);
m_glSpectrum->setDisplayHistogram(m_displayHistogram);
m_glSpectrum->setDecay(m_decay);
m_glSpectrum->setHistoLateHoldoff(m_histogramLateHoldoff);
m_glSpectrum->setHistoStroke(m_histogramStroke);
m_glSpectrum->setInvertedWaterfall(m_invert);
m_glSpectrum->setDisplayGrid(m_displayGrid);
m_glSpectrum->setDisplayGridIntensity(m_displayGridIntensity);
@ -198,13 +205,14 @@ void GLSpectrumGUI::on_decay_valueChanged(int index)
void GLSpectrumGUI::on_holdoff_valueChanged(int index)
{
if (index < 1) {
if (index < 0) {
return;
}
m_histogramLateHoldoff = index;
ui->holdoff->setToolTip(QString("Holdoff: %1").arg(m_histogramLateHoldoff));
if(m_glSpectrum != NULL)
m_glSpectrum->setDecay(m_decay);
//ui->holdoff->setToolTip(QString("Holdoff: %1").arg(m_histogramLateHoldoff));
if(m_glSpectrum != NULL) {
applySettings();
}
}
void GLSpectrumGUI::on_stroke_valueChanged(int index)
@ -213,9 +221,10 @@ void GLSpectrumGUI::on_stroke_valueChanged(int index)
return;
}
m_histogramStroke = index;
ui->stroke->setToolTip(QString("Stroke: %1").arg(m_histogramStroke));
if(m_glSpectrum != NULL)
m_glSpectrum->setDecay(m_decay);
//ui->stroke->setToolTip(QString("Stroke: %1").arg(m_histogramStroke));
if(m_glSpectrum != NULL) {
applySettings();
}
}
void GLSpectrumGUI::on_waterfall_toggled(bool checked)
@ -239,6 +248,13 @@ void GLSpectrumGUI::on_maxHold_toggled(bool checked)
m_glSpectrum->setDisplayMaxHold(m_displayMaxHold);
}
void GLSpectrumGUI::on_current_toggled(bool checked)
{
m_displayCurrent = checked;
if(m_glSpectrum != NULL)
m_glSpectrum->setDisplayCurrent(m_displayCurrent);
}
void GLSpectrumGUI::on_invert_toggled(bool checked)
{
m_invert = checked;

View File

@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>234</width>
<width>273</width>
<height>94</height>
</rect>
</property>
@ -234,7 +234,7 @@
<string>Decay:</string>
</property>
<property name="maximum">
<number>10</number>
<number>240</number>
</property>
<property name="pageStep">
<number>1</number>
@ -253,10 +253,10 @@
<string>Holdoff:</string>
</property>
<property name="minimum">
<number>1</number>
<number>0</number>
</property>
<property name="maximum">
<number>100</number>
<number>20</number>
</property>
<property name="pageStep">
<number>1</number>
@ -278,7 +278,7 @@
<number>4</number>
</property>
<property name="maximum">
<number>100</number>
<number>240</number>
</property>
<property name="pageStep">
<number>1</number>
@ -370,7 +370,7 @@
</size>
</property>
<property name="toolTip">
<string>Display live spectrum</string>
<string>Display max hold</string>
</property>
<property name="text">
<string>Max Hold</string>
@ -390,6 +390,41 @@
</property>
</widget>
</item>
<item>
<widget class="ButtonSwitch" name="current">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="toolTip">
<string>Display live spectrum</string>
</property>
<property name="text">
<string>Max Hold</string>
</property>
<property name="icon">
<iconset resource="../resources/res.qrc">
<normaloff>:/current.png</normaloff>:/current.png</iconset>
</property>
<property name="iconSize">
<size>
<width>16</width>
<height>16</height>
</size>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="ButtonSwitch" name="invert">
<property name="sizePolicy">

Binary file not shown.

After

Width:  |  Height:  |  Size: 742 B

View File

@ -22,5 +22,6 @@
<file>display2_w.png</file>
<file>horizontal_w.png</file>
<file>vertical_w.png</file>
<file>current.png</file>
</qresource>
</RCC>