mirror of
				https://github.com/f4exb/sdrangel.git
				synced 2025-10-30 20:40:20 -04:00 
			
		
		
		
	Websocket spectrum: first implementation
This commit is contained in:
		
							parent
							
								
									6a6b5f8d7e
								
							
						
					
					
						commit
						ac6c3b08f2
					
				| @ -399,6 +399,8 @@ BFMDemodGUI::BFMDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban | |||||||
| 	m_spectrumVis->configure( | 	m_spectrumVis->configure( | ||||||
| 	        m_spectrumVis->getInputMessageQueue(), | 	        m_spectrumVis->getInputMessageQueue(), | ||||||
|             64, // FFT size
 |             64, // FFT size
 | ||||||
|  |             0, // Ref level (dB)
 | ||||||
|  |             100, // Power range (dB)
 | ||||||
|             10, // overlapping %
 |             10, // overlapping %
 | ||||||
|             0,  // number of averaging samples
 |             0,  // number of averaging samples
 | ||||||
|             SpectrumVis::AvgModeNone,  // no averaging
 |             SpectrumVis::AvgModeNone,  // no averaging
 | ||||||
|  | |||||||
| @ -189,6 +189,8 @@ UDPSinkGUI::UDPSinkGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandS | |||||||
| 	ui->glSpectrum->setDisplayMaxHold(true); | 	ui->glSpectrum->setDisplayMaxHold(true); | ||||||
| 	m_spectrumVis->configure(m_spectrumVis->getInputMessageQueue(), | 	m_spectrumVis->configure(m_spectrumVis->getInputMessageQueue(), | ||||||
| 	        64, // FFT size
 | 	        64, // FFT size
 | ||||||
|  |             0, // Ref level (dB)
 | ||||||
|  |             100, // Power range (dB)
 | ||||||
| 	        10, // overlapping %
 | 	        10, // overlapping %
 | ||||||
| 	        0,  // number of averaging samples
 | 	        0,  // number of averaging samples
 | ||||||
| 	        SpectrumVis::AvgModeNone,  // no averaging
 | 	        SpectrumVis::AvgModeNone,  // no averaging
 | ||||||
|  | |||||||
| @ -146,6 +146,8 @@ UDPSourceGUI::UDPSourceGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseb | |||||||
|     ui->glSpectrum->setDisplayMaxHold(true); |     ui->glSpectrum->setDisplayMaxHold(true); | ||||||
|     m_spectrumVis->configure(m_spectrumVis->getInputMessageQueue(), |     m_spectrumVis->configure(m_spectrumVis->getInputMessageQueue(), | ||||||
|             64, // FFT size
 |             64, // FFT size
 | ||||||
|  |             0, // Ref level (dB)
 | ||||||
|  |             100, // Power range (dB)
 | ||||||
|             10, // overlapping %
 |             10, // overlapping %
 | ||||||
|             0,  // number of averaging samples
 |             0,  // number of averaging samples
 | ||||||
|             SpectrumVis::AvgModeNone,  // no averaging
 |             SpectrumVis::AvgModeNone,  // no averaging
 | ||||||
|  | |||||||
| @ -163,6 +163,8 @@ set(sdrbase_SOURCES | |||||||
|     webapi/webapirequestmapper.cpp |     webapi/webapirequestmapper.cpp | ||||||
|     webapi/webapiserver.cpp |     webapi/webapiserver.cpp | ||||||
| 
 | 
 | ||||||
|  |     websockets/wsspectrum.cpp | ||||||
|  | 
 | ||||||
|     mainparser.cpp |     mainparser.cpp | ||||||
| 
 | 
 | ||||||
|     resources/webapi.qrc |     resources/webapi.qrc | ||||||
| @ -310,7 +312,9 @@ set(sdrbase_HEADERS | |||||||
|     webapi/webapiadapterbase.h |     webapi/webapiadapterbase.h | ||||||
|     webapi/webapiadapterinterface.h |     webapi/webapiadapterinterface.h | ||||||
|     webapi/webapirequestmapper.h |     webapi/webapirequestmapper.h | ||||||
|     webapi/webapiserver |     webapi/webapiserver.h | ||||||
|  | 
 | ||||||
|  |     websockets/wsspectrum.h | ||||||
| 
 | 
 | ||||||
|     mainparser.h |     mainparser.h | ||||||
| ) | ) | ||||||
| @ -343,6 +347,7 @@ target_link_libraries(sdrbase | |||||||
|     ${sdrbase_LIMERFE_LIB} |     ${sdrbase_LIMERFE_LIB} | ||||||
|     Qt5::Core |     Qt5::Core | ||||||
|     Qt5::Multimedia |     Qt5::Multimedia | ||||||
|  |     Qt5::WebSockets | ||||||
|     httpserver |     httpserver | ||||||
|     qrtplib |     qrtplib | ||||||
|     swagger |     swagger | ||||||
|  | |||||||
| @ -36,6 +36,7 @@ inline double log2f(double n) | |||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| MESSAGE_CLASS_DEFINITION(SpectrumVis::MsgConfigureSpectrumVis, Message) | MESSAGE_CLASS_DEFINITION(SpectrumVis::MsgConfigureSpectrumVis, Message) | ||||||
|  | MESSAGE_CLASS_DEFINITION(SpectrumVis::MsgConfigureDSP, Message) | ||||||
| MESSAGE_CLASS_DEFINITION(SpectrumVis::MsgConfigureScalingFactor, Message) | MESSAGE_CLASS_DEFINITION(SpectrumVis::MsgConfigureScalingFactor, Message) | ||||||
| 
 | 
 | ||||||
| const Real SpectrumVis::m_mult = (10.0f / log2f(10.0f)); | const Real SpectrumVis::m_mult = (10.0f / log2f(10.0f)); | ||||||
| @ -53,12 +54,15 @@ SpectrumVis::SpectrumVis(Real scalef, GLSpectrumInterface* glSpectrum) : | |||||||
| 	m_averageNb(0), | 	m_averageNb(0), | ||||||
| 	m_avgMode(AvgModeNone), | 	m_avgMode(AvgModeNone), | ||||||
| 	m_linear(false), | 	m_linear(false), | ||||||
|  |     m_centerFrequency(0), | ||||||
|  |     m_sampleRate(48000), | ||||||
| 	m_ofs(0), | 	m_ofs(0), | ||||||
|     m_powFFTDiv(1.0), |     m_powFFTDiv(1.0), | ||||||
| 	m_mutex(QMutex::Recursive) | 	m_mutex(QMutex::Recursive) | ||||||
| { | { | ||||||
| 	setObjectName("SpectrumVis"); | 	setObjectName("SpectrumVis"); | ||||||
| 	handleConfigure(1024, 0, 0, AvgModeNone, FFTWindow::BlackmanHarris, false); | 	handleConfigure(1024, 0, 100, 0, 0, AvgModeNone, FFTWindow::BlackmanHarris, false); | ||||||
|  |     m_wsSpectrum.openSocket(); // FIXME: conditional
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| SpectrumVis::~SpectrumVis() | SpectrumVis::~SpectrumVis() | ||||||
| @ -69,17 +73,34 @@ SpectrumVis::~SpectrumVis() | |||||||
| 
 | 
 | ||||||
| void SpectrumVis::configure(MessageQueue* msgQueue, | void SpectrumVis::configure(MessageQueue* msgQueue, | ||||||
|         int fftSize, |         int fftSize, | ||||||
|  |         float refLevel, | ||||||
|  |         float powerRange, | ||||||
|         int overlapPercent, |         int overlapPercent, | ||||||
|         unsigned int averagingNb, |         unsigned int averagingNb, | ||||||
|         AvgMode averagingMode, |         AvgMode averagingMode, | ||||||
|         FFTWindow::Function window, |         FFTWindow::Function window, | ||||||
|         bool linear) |         bool linear) | ||||||
| { | { | ||||||
| 	MsgConfigureSpectrumVis* cmd = new MsgConfigureSpectrumVis(fftSize, overlapPercent, averagingNb, averagingMode, window, linear); | 	MsgConfigureSpectrumVis* cmd = new MsgConfigureSpectrumVis( | ||||||
|  |         fftSize, | ||||||
|  |         refLevel, | ||||||
|  |         powerRange, | ||||||
|  |         overlapPercent, | ||||||
|  |         averagingNb, | ||||||
|  |         averagingMode, | ||||||
|  |         window, | ||||||
|  |         linear | ||||||
|  |     ); | ||||||
| 	msgQueue->push(cmd); | 	msgQueue->push(cmd); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void SpectrumVis::setScalef(MessageQueue* msgQueue, Real scalef) | void SpectrumVis::configureDSP(uint64_t centerFrequency, int sampleRate) | ||||||
|  | { | ||||||
|  |     MsgConfigureDSP* cmd = new MsgConfigureDSP(centerFrequency, sampleRate); | ||||||
|  |     getInputMessageQueue()->push(cmd); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void SpectrumVis::setScalef(Real scalef) | ||||||
| { | { | ||||||
|     MsgConfigureScalingFactor* cmd = new MsgConfigureScalingFactor(scalef); |     MsgConfigureScalingFactor* cmd = new MsgConfigureScalingFactor(scalef); | ||||||
|     getInputMessageQueue()->push(cmd); |     getInputMessageQueue()->push(cmd); | ||||||
| @ -106,11 +127,189 @@ void SpectrumVis::feedTriggered(const SampleVector::const_iterator& triggerPoint | |||||||
| 	}*/ | 	}*/ | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void SpectrumVis::feed(const Complex *begin, unsigned int length) | ||||||
|  | { | ||||||
|  | 	if (!m_glSpectrum && !m_wsSpectrum.socketOpened()) { | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  |     if (!m_mutex.tryLock(0)) { // prevent conflicts with configuration process
 | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     Complex c; | ||||||
|  |     Real v; | ||||||
|  | 
 | ||||||
|  |     if (m_avgMode == AvgModeNone) | ||||||
|  |     { | ||||||
|  |         for (unsigned int i = 0; i < m_fftSize; i++) | ||||||
|  |         { | ||||||
|  |             if (i < length) { | ||||||
|  |                 c = begin[i]; | ||||||
|  |             } else { | ||||||
|  |                 c = Complex{0,0}; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             v = c.real() * c.real() + c.imag() * c.imag(); | ||||||
|  |             v = m_linear ? v/m_powFFTDiv : m_mult * log2f(v) + m_ofs; | ||||||
|  |             m_powerSpectrum[i] = v; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // send new data to visualisation
 | ||||||
|  |         if (m_glSpectrum) { | ||||||
|  |             m_glSpectrum->newSpectrum(m_powerSpectrum, m_fftSize); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // web socket spectrum connections
 | ||||||
|  |         if (m_wsSpectrum.socketOpened()) | ||||||
|  |         { | ||||||
|  |             m_wsSpectrum.newSpectrum( | ||||||
|  |                 m_powerSpectrum, | ||||||
|  |                 m_fftSize, | ||||||
|  |                 m_refLevel, | ||||||
|  |                 m_powerRange, | ||||||
|  |                 m_centerFrequency, | ||||||
|  |                 m_sampleRate, | ||||||
|  |                 m_linear | ||||||
|  |             ); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     else if (m_avgMode == AvgModeMovingAvg) | ||||||
|  |     { | ||||||
|  |         for (unsigned int i = 0; i < m_fftSize; i++) | ||||||
|  |         { | ||||||
|  |             if (i < length) { | ||||||
|  |                 c = begin[i]; | ||||||
|  |             } else { | ||||||
|  |                 c = Complex{0,0}; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             v = c.real() * c.real() + c.imag() * c.imag(); | ||||||
|  |             v = m_movingAverage.storeAndGetAvg(v, i); | ||||||
|  |             v = m_linear ? v/m_powFFTDiv : m_mult * log2f(v) + m_ofs; | ||||||
|  |             m_powerSpectrum[i] = v; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // send new data to visualisation
 | ||||||
|  |         if (m_glSpectrum) { | ||||||
|  |             m_glSpectrum->newSpectrum(m_powerSpectrum, m_fftSize); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // web socket spectrum connections
 | ||||||
|  |         if (m_wsSpectrum.socketOpened()) | ||||||
|  |         { | ||||||
|  |             m_wsSpectrum.newSpectrum( | ||||||
|  |                 m_powerSpectrum, | ||||||
|  |                 m_fftSize, | ||||||
|  |                 m_refLevel, | ||||||
|  |                 m_powerRange, | ||||||
|  |                 m_centerFrequency, | ||||||
|  |                 m_sampleRate, | ||||||
|  |                 m_linear | ||||||
|  |             ); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         m_movingAverage.nextAverage(); | ||||||
|  |     } | ||||||
|  |     else if (m_avgMode == AvgModeFixedAvg) | ||||||
|  |     { | ||||||
|  |         double avg; | ||||||
|  | 
 | ||||||
|  |         for (unsigned int i = 0; i < m_fftSize; i++) | ||||||
|  |         { | ||||||
|  |             if (i < length) { | ||||||
|  |                 c = begin[i]; | ||||||
|  |             } else { | ||||||
|  |                 c = Complex{0,0}; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             v = c.real() * c.real() + c.imag() * c.imag(); | ||||||
|  | 
 | ||||||
|  |             // result available
 | ||||||
|  |             if (m_fixedAverage.storeAndGetAvg(avg, v, i)) | ||||||
|  |             { | ||||||
|  |                 avg = m_linear ? avg/m_powFFTDiv : m_mult * log2f(avg) + m_ofs; | ||||||
|  |                 m_powerSpectrum[i] = avg; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // result available
 | ||||||
|  |         if (m_fixedAverage.nextAverage()) | ||||||
|  |         { | ||||||
|  |             // send new data to visualisation
 | ||||||
|  |             if (m_glSpectrum) { | ||||||
|  |                 m_glSpectrum->newSpectrum(m_powerSpectrum, m_fftSize); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             // web socket spectrum connections
 | ||||||
|  |             if (m_wsSpectrum.socketOpened()) | ||||||
|  |             { | ||||||
|  |                 m_wsSpectrum.newSpectrum( | ||||||
|  |                     m_powerSpectrum, | ||||||
|  |                     m_fftSize, | ||||||
|  |                     m_refLevel, | ||||||
|  |                     m_powerRange, | ||||||
|  |                     m_centerFrequency, | ||||||
|  |                     m_sampleRate, | ||||||
|  |                     m_linear | ||||||
|  |                 ); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     else if (m_avgMode == AvgModeMax) | ||||||
|  |     { | ||||||
|  |         double max; | ||||||
|  | 
 | ||||||
|  |         for (unsigned int i = 0; i < m_fftSize; i++) | ||||||
|  |         { | ||||||
|  |             if (i < length) { | ||||||
|  |                 c = begin[i]; | ||||||
|  |             } else { | ||||||
|  |                 c = Complex{0,0}; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             v = c.real() * c.real() + c.imag() * c.imag(); | ||||||
|  | 
 | ||||||
|  |             // result available
 | ||||||
|  |             if (m_max.storeAndGetMax(max, v, i)) | ||||||
|  |             { | ||||||
|  |                 max = m_linear ? max/m_powFFTDiv : m_mult * log2f(max) + m_ofs; | ||||||
|  |                 m_powerSpectrum[i] = max; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // result available
 | ||||||
|  |         if (m_max.nextMax()) | ||||||
|  |         { | ||||||
|  |             // send new data to visualisation
 | ||||||
|  |             if (m_glSpectrum) { | ||||||
|  |                 m_glSpectrum->newSpectrum(m_powerSpectrum, m_fftSize); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             // web socket spectrum connections
 | ||||||
|  |             if (m_wsSpectrum.socketOpened()) | ||||||
|  |             { | ||||||
|  |                 m_wsSpectrum.newSpectrum( | ||||||
|  |                     m_powerSpectrum, | ||||||
|  |                     m_fftSize, | ||||||
|  |                     m_refLevel, | ||||||
|  |                     m_powerRange, | ||||||
|  |                     m_centerFrequency, | ||||||
|  |                     m_sampleRate, | ||||||
|  |                     m_linear | ||||||
|  |                 ); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     m_mutex.unlock(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void SpectrumVis::feed(const SampleVector::const_iterator& cbegin, const SampleVector::const_iterator& end, bool positiveOnly) | void SpectrumVis::feed(const SampleVector::const_iterator& cbegin, const SampleVector::const_iterator& end, bool positiveOnly) | ||||||
| { | { | ||||||
| 	// if no visualisation is set, send the samples to /dev/null
 | 	// if no visualisation is set, send the samples to /dev/null
 | ||||||
| 
 | 
 | ||||||
| 	if (m_glSpectrum == 0) { | 	if (!m_glSpectrum && !m_wsSpectrum.socketOpened()) { | ||||||
| 		return; | 		return; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @ -177,8 +376,24 @@ void SpectrumVis::feed(const SampleVector::const_iterator& cbegin, const SampleV | |||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 // send new data to visualisation
 |                 // send new data to visualisation
 | ||||||
|  |                 if (m_glSpectrum) { | ||||||
|                     m_glSpectrum->newSpectrum(m_powerSpectrum, m_fftSize); |                     m_glSpectrum->newSpectrum(m_powerSpectrum, m_fftSize); | ||||||
|                 } |                 } | ||||||
|  | 
 | ||||||
|  |                 // web socket spectrum connections
 | ||||||
|  |                 if (m_wsSpectrum.socketOpened()) | ||||||
|  |                 { | ||||||
|  |                     m_wsSpectrum.newSpectrum( | ||||||
|  |                         m_powerSpectrum, | ||||||
|  |                         m_fftSize, | ||||||
|  |                         m_refLevel, | ||||||
|  |                         m_powerRange, | ||||||
|  |                         m_centerFrequency, | ||||||
|  |                         m_sampleRate, | ||||||
|  |                         m_linear | ||||||
|  |                     ); | ||||||
|  |                 } | ||||||
|  | 			} | ||||||
| 			else if (m_avgMode == AvgModeMovingAvg) | 			else if (m_avgMode == AvgModeMovingAvg) | ||||||
| 			{ | 			{ | ||||||
| 	            if ( positiveOnly ) | 	            if ( positiveOnly ) | ||||||
| @ -212,7 +427,24 @@ void SpectrumVis::feed(const SampleVector::const_iterator& cbegin, const SampleV | |||||||
| 	            } | 	            } | ||||||
| 
 | 
 | ||||||
| 	            // send new data to visualisation
 | 	            // send new data to visualisation
 | ||||||
|  |                 if (m_glSpectrum) { | ||||||
|     	            m_glSpectrum->newSpectrum(m_powerSpectrum, m_fftSize); |     	            m_glSpectrum->newSpectrum(m_powerSpectrum, m_fftSize); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 // web socket spectrum connections
 | ||||||
|  |                 if (m_wsSpectrum.socketOpened()) | ||||||
|  |                 { | ||||||
|  |                     m_wsSpectrum.newSpectrum( | ||||||
|  |                         m_powerSpectrum, | ||||||
|  |                         m_fftSize, | ||||||
|  |                         m_refLevel, | ||||||
|  |                         m_powerRange, | ||||||
|  |                         m_centerFrequency, | ||||||
|  |                         m_sampleRate, | ||||||
|  |                         m_linear | ||||||
|  |                     ); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
| 	            m_movingAverage.nextAverage(); | 	            m_movingAverage.nextAverage(); | ||||||
| 			} | 			} | ||||||
| 			else if (m_avgMode == AvgModeFixedAvg) | 			else if (m_avgMode == AvgModeFixedAvg) | ||||||
| @ -241,8 +473,9 @@ void SpectrumVis::feed(const SampleVector::const_iterator& cbegin, const SampleV | |||||||
|                         c = fftOut[i + halfSize]; |                         c = fftOut[i + halfSize]; | ||||||
|                         v = c.real() * c.real() + c.imag() * c.imag(); |                         v = c.real() * c.real() + c.imag() * c.imag(); | ||||||
| 
 | 
 | ||||||
|  |                         // result available
 | ||||||
|                         if (m_fixedAverage.storeAndGetAvg(avg, v, i+halfSize)) |                         if (m_fixedAverage.storeAndGetAvg(avg, v, i+halfSize)) | ||||||
|                         { // result available
 |                         { | ||||||
|                             avg = m_linear ? avg/m_powFFTDiv : m_mult * log2f(avg) + m_ofs; |                             avg = m_linear ? avg/m_powFFTDiv : m_mult * log2f(avg) + m_ofs; | ||||||
|                             m_powerSpectrum[i] = avg; |                             m_powerSpectrum[i] = avg; | ||||||
|                         } |                         } | ||||||
| @ -250,16 +483,36 @@ void SpectrumVis::feed(const SampleVector::const_iterator& cbegin, const SampleV | |||||||
|                         c = fftOut[i]; |                         c = fftOut[i]; | ||||||
|                         v = c.real() * c.real() + c.imag() * c.imag(); |                         v = c.real() * c.real() + c.imag() * c.imag(); | ||||||
| 
 | 
 | ||||||
|  |                         // result available
 | ||||||
|                         if (m_fixedAverage.storeAndGetAvg(avg, v, i)) |                         if (m_fixedAverage.storeAndGetAvg(avg, v, i)) | ||||||
|                         { // result available
 |                         { | ||||||
|                             avg = m_linear ? avg/m_powFFTDiv : m_mult * log2f(avg) + m_ofs; |                             avg = m_linear ? avg/m_powFFTDiv : m_mult * log2f(avg) + m_ofs; | ||||||
|                             m_powerSpectrum[i + halfSize] = avg; |                             m_powerSpectrum[i + halfSize] = avg; | ||||||
|                         } |                         } | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 if (m_fixedAverage.nextAverage()) { // result available
 |                 // result available
 | ||||||
|                     m_glSpectrum->newSpectrum(m_powerSpectrum, m_fftSize); // send new data to visualisation
 |                 if (m_fixedAverage.nextAverage()) | ||||||
|  |                 { | ||||||
|  |                     // send new data to visualisation
 | ||||||
|  |                     if (m_glSpectrum) { | ||||||
|  |                         m_glSpectrum->newSpectrum(m_powerSpectrum, m_fftSize); | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     // web socket spectrum connections
 | ||||||
|  |                     if (m_wsSpectrum.socketOpened()) | ||||||
|  |                     { | ||||||
|  |                         m_wsSpectrum.newSpectrum( | ||||||
|  |                             m_powerSpectrum, | ||||||
|  |                             m_fftSize, | ||||||
|  |                             m_refLevel, | ||||||
|  |                             m_powerRange, | ||||||
|  |                             m_centerFrequency, | ||||||
|  |                             m_sampleRate, | ||||||
|  |                             m_linear | ||||||
|  |                         ); | ||||||
|  |                     } | ||||||
|                 } |                 } | ||||||
| 			} | 			} | ||||||
| 			else if (m_avgMode == AvgModeMax) | 			else if (m_avgMode == AvgModeMax) | ||||||
| @ -288,8 +541,9 @@ void SpectrumVis::feed(const SampleVector::const_iterator& cbegin, const SampleV | |||||||
|                         c = fftOut[i + halfSize]; |                         c = fftOut[i + halfSize]; | ||||||
|                         v = c.real() * c.real() + c.imag() * c.imag(); |                         v = c.real() * c.real() + c.imag() * c.imag(); | ||||||
| 
 | 
 | ||||||
|  |                         // result available
 | ||||||
|                         if (m_max.storeAndGetMax(max, v, i+halfSize)) |                         if (m_max.storeAndGetMax(max, v, i+halfSize)) | ||||||
|                         { // result available
 |                         { | ||||||
|                             max = m_linear ? max/m_powFFTDiv : m_mult * log2f(max) + m_ofs; |                             max = m_linear ? max/m_powFFTDiv : m_mult * log2f(max) + m_ofs; | ||||||
|                             m_powerSpectrum[i] = max; |                             m_powerSpectrum[i] = max; | ||||||
|                         } |                         } | ||||||
| @ -297,16 +551,36 @@ void SpectrumVis::feed(const SampleVector::const_iterator& cbegin, const SampleV | |||||||
|                         c = fftOut[i]; |                         c = fftOut[i]; | ||||||
|                         v = c.real() * c.real() + c.imag() * c.imag(); |                         v = c.real() * c.real() + c.imag() * c.imag(); | ||||||
| 
 | 
 | ||||||
|  |                         // result available
 | ||||||
|                         if (m_max.storeAndGetMax(max, v, i)) |                         if (m_max.storeAndGetMax(max, v, i)) | ||||||
|                         { // result available
 |                         { | ||||||
|                             max = m_linear ? max/m_powFFTDiv : m_mult * log2f(max) + m_ofs; |                             max = m_linear ? max/m_powFFTDiv : m_mult * log2f(max) + m_ofs; | ||||||
|                             m_powerSpectrum[i + halfSize] = max; |                             m_powerSpectrum[i + halfSize] = max; | ||||||
|                         } |                         } | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 if (m_max.nextMax()) { // result available
 |                 // result available
 | ||||||
|                     m_glSpectrum->newSpectrum(m_powerSpectrum, m_fftSize); // send new data to visualisation
 |                 if (m_max.nextMax()) | ||||||
|  |                 { | ||||||
|  |                     // send new data to visualisation
 | ||||||
|  |                     if (m_glSpectrum) { | ||||||
|  |                         m_glSpectrum->newSpectrum(m_powerSpectrum, m_fftSize); | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     // web socket spectrum connections
 | ||||||
|  |                     if (m_wsSpectrum.socketOpened()) | ||||||
|  |                     { | ||||||
|  |                         m_wsSpectrum.newSpectrum( | ||||||
|  |                             m_powerSpectrum, | ||||||
|  |                             m_fftSize, | ||||||
|  |                             m_refLevel, | ||||||
|  |                             m_powerRange, | ||||||
|  |                             m_centerFrequency, | ||||||
|  |                             m_sampleRate, | ||||||
|  |                             m_linear | ||||||
|  |                         ); | ||||||
|  |                     } | ||||||
|                 } |                 } | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| @ -347,6 +621,8 @@ bool SpectrumVis::handleMessage(const Message& message) | |||||||
| 	{ | 	{ | ||||||
| 		MsgConfigureSpectrumVis& conf = (MsgConfigureSpectrumVis&) message; | 		MsgConfigureSpectrumVis& conf = (MsgConfigureSpectrumVis&) message; | ||||||
| 		handleConfigure(conf.getFFTSize(), | 		handleConfigure(conf.getFFTSize(), | ||||||
|  |                 conf.getRefLevel(), | ||||||
|  |                 conf.getPowerRange(), | ||||||
| 		        conf.getOverlapPercent(), | 		        conf.getOverlapPercent(), | ||||||
| 		        conf.getAverageNb(), | 		        conf.getAverageNb(), | ||||||
| 		        conf.getAvgMode(), | 		        conf.getAvgMode(), | ||||||
| @ -354,6 +630,12 @@ bool SpectrumVis::handleMessage(const Message& message) | |||||||
| 		        conf.getLinear()); | 		        conf.getLinear()); | ||||||
| 		return true; | 		return true; | ||||||
| 	} | 	} | ||||||
|  |     else if (MsgConfigureDSP::match(message)) | ||||||
|  |     { | ||||||
|  |         MsgConfigureDSP& conf = (MsgConfigureDSP&) message; | ||||||
|  |         handleConfigureDSP(conf.getCenterFrequency(), conf.getSampleRate()); | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|     else if (MsgConfigureScalingFactor::match(message)) |     else if (MsgConfigureScalingFactor::match(message)) | ||||||
|     { |     { | ||||||
|         MsgConfigureScalingFactor& conf = (MsgConfigureScalingFactor&) message; |         MsgConfigureScalingFactor& conf = (MsgConfigureScalingFactor&) message; | ||||||
| @ -367,6 +649,8 @@ bool SpectrumVis::handleMessage(const Message& message) | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void SpectrumVis::handleConfigure(int fftSize, | void SpectrumVis::handleConfigure(int fftSize, | ||||||
|  |         float refLevel, | ||||||
|  |         float powerRange, | ||||||
|         int overlapPercent, |         int overlapPercent, | ||||||
|         unsigned int averageNb, |         unsigned int averageNb, | ||||||
|         AvgMode averagingMode, |         AvgMode averagingMode, | ||||||
| @ -377,25 +661,20 @@ void SpectrumVis::handleConfigure(int fftSize, | |||||||
| //            fftSize, overlapPercent, averageNb, (int) averagingMode, (int) window, linear ? "true" : "false");
 | //            fftSize, overlapPercent, averageNb, (int) averagingMode, (int) window, linear ? "true" : "false");
 | ||||||
| 	QMutexLocker mutexLocker(&m_mutex); | 	QMutexLocker mutexLocker(&m_mutex); | ||||||
| 
 | 
 | ||||||
| 	if (fftSize > MAX_FFT_SIZE) | 	if (fftSize > MAX_FFT_SIZE) { | ||||||
| 	{ |  | ||||||
| 		fftSize = MAX_FFT_SIZE; | 		fftSize = MAX_FFT_SIZE; | ||||||
| 	} | 	} else if (fftSize < 64) { | ||||||
| 	else if (fftSize < 64) |  | ||||||
| 	{ |  | ||||||
| 		fftSize = 64; | 		fftSize = 64; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (overlapPercent > 100) |     m_refLevel = refLevel; | ||||||
| 	{ |     m_powerRange = powerRange; | ||||||
|  | 
 | ||||||
|  | 	if (overlapPercent > 100) { | ||||||
| 		m_overlapPercent = 100; | 		m_overlapPercent = 100; | ||||||
| 	} | 	} else if (overlapPercent < 0) { | ||||||
| 	else if (overlapPercent < 0) |  | ||||||
| 	{ |  | ||||||
| 		m_overlapPercent = 0; | 		m_overlapPercent = 0; | ||||||
| 	} | 	} else { | ||||||
| 	else |  | ||||||
| 	{ |  | ||||||
|         m_overlapPercent = overlapPercent; |         m_overlapPercent = overlapPercent; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @ -417,6 +696,13 @@ void SpectrumVis::handleConfigure(int fftSize, | |||||||
| 	m_powFFTDiv = m_fftSize*m_fftSize; | 	m_powFFTDiv = m_fftSize*m_fftSize; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void SpectrumVis::handleConfigureDSP(uint64_t centerFrequency, int sampleRate) | ||||||
|  | { | ||||||
|  |     QMutexLocker mutexLocker(&m_mutex); | ||||||
|  |     m_centerFrequency = centerFrequency; | ||||||
|  |     m_sampleRate = sampleRate; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void SpectrumVis::handleScalef(Real scalef) | void SpectrumVis::handleScalef(Real scalef) | ||||||
| { | { | ||||||
|     QMutexLocker mutexLocker(&m_mutex); |     QMutexLocker mutexLocker(&m_mutex); | ||||||
|  | |||||||
| @ -21,8 +21,9 @@ | |||||||
| #ifndef INCLUDE_SPECTRUMVIS_H | #ifndef INCLUDE_SPECTRUMVIS_H | ||||||
| #define INCLUDE_SPECTRUMVIS_H | #define INCLUDE_SPECTRUMVIS_H | ||||||
| 
 | 
 | ||||||
| #include <dsp/basebandsamplesink.h> |  | ||||||
| #include <QMutex> | #include <QMutex> | ||||||
|  | 
 | ||||||
|  | #include "dsp/basebandsamplesink.h" | ||||||
| #include "dsp/fftengine.h" | #include "dsp/fftengine.h" | ||||||
| #include "dsp/fftwindow.h" | #include "dsp/fftwindow.h" | ||||||
| #include "export.h" | #include "export.h" | ||||||
| @ -30,6 +31,7 @@ | |||||||
| #include "util/movingaverage2d.h" | #include "util/movingaverage2d.h" | ||||||
| #include "util/fixedaverage2d.h" | #include "util/fixedaverage2d.h" | ||||||
| #include "util/max2d.h" | #include "util/max2d.h" | ||||||
|  | #include "websockets/wsspectrum.h" | ||||||
| 
 | 
 | ||||||
| class GLSpectrumInterface; | class GLSpectrumInterface; | ||||||
| class MessageQueue; | class MessageQueue; | ||||||
| @ -51,6 +53,8 @@ public: | |||||||
| 	public: | 	public: | ||||||
| 		MsgConfigureSpectrumVis( | 		MsgConfigureSpectrumVis( | ||||||
| 		        int fftSize, | 		        int fftSize, | ||||||
|  |                 float refLevel, | ||||||
|  |                 float powerRange, | ||||||
| 		        int overlapPercent, | 		        int overlapPercent, | ||||||
| 		        unsigned int averageNb, | 		        unsigned int averageNb, | ||||||
| 		        AvgMode avgMode, | 		        AvgMode avgMode, | ||||||
| @ -59,6 +63,8 @@ public: | |||||||
| 			Message(), | 			Message(), | ||||||
| 			m_fftSize(fftSize), | 			m_fftSize(fftSize), | ||||||
| 			m_overlapPercent(overlapPercent), | 			m_overlapPercent(overlapPercent), | ||||||
|  |             m_refLevel(refLevel), | ||||||
|  |             m_powerRange(powerRange), | ||||||
| 			m_averageNb(averageNb), | 			m_averageNb(averageNb), | ||||||
|             m_avgMode(avgMode), |             m_avgMode(avgMode), | ||||||
| 			m_window(window), | 			m_window(window), | ||||||
| @ -66,6 +72,8 @@ public: | |||||||
| 		{} | 		{} | ||||||
| 
 | 
 | ||||||
| 		int getFFTSize() const { return m_fftSize; } | 		int getFFTSize() const { return m_fftSize; } | ||||||
|  |         float getRefLevel() const { return m_refLevel; } | ||||||
|  |         float getPowerRange() const { return m_powerRange; } | ||||||
| 		int getOverlapPercent() const { return m_overlapPercent; } | 		int getOverlapPercent() const { return m_overlapPercent; } | ||||||
| 		unsigned int getAverageNb() const { return m_averageNb; } | 		unsigned int getAverageNb() const { return m_averageNb; } | ||||||
| 		SpectrumVis::AvgMode getAvgMode() const { return m_avgMode; } | 		SpectrumVis::AvgMode getAvgMode() const { return m_avgMode; } | ||||||
| @ -75,12 +83,33 @@ public: | |||||||
| 	private: | 	private: | ||||||
| 		int m_fftSize; | 		int m_fftSize; | ||||||
| 		int m_overlapPercent; | 		int m_overlapPercent; | ||||||
|  |         float m_refLevel; | ||||||
|  |         float m_powerRange; | ||||||
| 		unsigned int m_averageNb; | 		unsigned int m_averageNb; | ||||||
| 		SpectrumVis::AvgMode m_avgMode; | 		SpectrumVis::AvgMode m_avgMode; | ||||||
| 		FFTWindow::Function m_window; | 		FFTWindow::Function m_window; | ||||||
| 		bool m_linear; | 		bool m_linear; | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
|  |     class MsgConfigureDSP : public Message | ||||||
|  |     { | ||||||
|  |         MESSAGE_CLASS_DECLARATION | ||||||
|  | 
 | ||||||
|  |     public: | ||||||
|  |         MsgConfigureDSP(uint64_t centerFrequency, int sampleRate) : | ||||||
|  |             Message(), | ||||||
|  |             m_centerFrequency(centerFrequency), | ||||||
|  |             m_sampleRate(sampleRate) | ||||||
|  |         {} | ||||||
|  | 
 | ||||||
|  |         uint64_t getCenterFrequency() const { return m_centerFrequency; } | ||||||
|  |         int getSampleRate() const { return m_sampleRate; } | ||||||
|  | 
 | ||||||
|  |     private: | ||||||
|  |         uint64_t m_centerFrequency; | ||||||
|  |         int m_sampleRate; | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|     class MsgConfigureScalingFactor : public Message |     class MsgConfigureScalingFactor : public Message | ||||||
|     { |     { | ||||||
| 		MESSAGE_CLASS_DECLARATION | 		MESSAGE_CLASS_DECLARATION | ||||||
| @ -102,12 +131,15 @@ public: | |||||||
| 
 | 
 | ||||||
| 	void configure(MessageQueue* msgQueue, | 	void configure(MessageQueue* msgQueue, | ||||||
| 	        int fftSize, | 	        int fftSize, | ||||||
|  |             float refLevel, | ||||||
|  |             float powerRange, | ||||||
| 	        int overlapPercent, | 	        int overlapPercent, | ||||||
| 	        unsigned int averagingNb, | 	        unsigned int averagingNb, | ||||||
| 	        AvgMode averagingMode, | 	        AvgMode averagingMode, | ||||||
| 	        FFTWindow::Function window, | 	        FFTWindow::Function window, | ||||||
| 	        bool m_linear); | 	        bool m_linear); | ||||||
|     void setScalef(MessageQueue* msgQueue, Real scalef); |     void configureDSP(uint64_t centerFrequency, int sampleRate); | ||||||
|  |     void setScalef(Real scalef); | ||||||
| 
 | 
 | ||||||
| 	virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool positiveOnly); | 	virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool positiveOnly); | ||||||
| 	void feedTriggered(const SampleVector::const_iterator& triggerPoint, const SampleVector::const_iterator& end, bool positiveOnly); | 	void feedTriggered(const SampleVector::const_iterator& triggerPoint, const SampleVector::const_iterator& end, bool positiveOnly); | ||||||
| @ -124,6 +156,8 @@ private: | |||||||
| 	std::vector<Real> m_powerSpectrum; | 	std::vector<Real> m_powerSpectrum; | ||||||
| 
 | 
 | ||||||
| 	std::size_t m_fftSize; | 	std::size_t m_fftSize; | ||||||
|  |     float m_refLevel; | ||||||
|  |     float m_powerRange; | ||||||
| 	std::size_t m_overlapPercent; | 	std::size_t m_overlapPercent; | ||||||
| 	std::size_t m_overlapSize; | 	std::size_t m_overlapSize; | ||||||
| 	std::size_t m_refillSize; | 	std::size_t m_refillSize; | ||||||
| @ -132,6 +166,7 @@ private: | |||||||
| 
 | 
 | ||||||
| 	Real m_scalef; | 	Real m_scalef; | ||||||
| 	GLSpectrumInterface* m_glSpectrum; | 	GLSpectrumInterface* m_glSpectrum; | ||||||
|  |     WSSpectrum m_wsSpectrum; | ||||||
| 	MovingAverage2D<double> m_movingAverage; | 	MovingAverage2D<double> m_movingAverage; | ||||||
| 	FixedAverage2D<double> m_fixedAverage; | 	FixedAverage2D<double> m_fixedAverage; | ||||||
| 	Max2D<double> m_max; | 	Max2D<double> m_max; | ||||||
| @ -139,6 +174,9 @@ private: | |||||||
| 	AvgMode m_avgMode; | 	AvgMode m_avgMode; | ||||||
| 	bool m_linear; | 	bool m_linear; | ||||||
| 
 | 
 | ||||||
|  |     uint64_t m_centerFrequency; | ||||||
|  |     int m_sampleRate; | ||||||
|  | 
 | ||||||
| 	Real m_ofs; | 	Real m_ofs; | ||||||
| 	Real m_powFFTDiv; | 	Real m_powFFTDiv; | ||||||
| 	static const Real m_mult; | 	static const Real m_mult; | ||||||
| @ -146,11 +184,15 @@ private: | |||||||
| 	QMutex m_mutex; | 	QMutex m_mutex; | ||||||
| 
 | 
 | ||||||
| 	void handleConfigure(int fftSize, | 	void handleConfigure(int fftSize, | ||||||
|  |             float refLevel, | ||||||
|  |             float powerRange, | ||||||
| 	        int overlapPercent, | 	        int overlapPercent, | ||||||
| 	        unsigned int averageNb, | 	        unsigned int averageNb, | ||||||
| 	        AvgMode averagingMode, | 	        AvgMode averagingMode, | ||||||
| 	        FFTWindow::Function window, | 	        FFTWindow::Function window, | ||||||
| 	        bool linear); | 	        bool linear); | ||||||
|  |     void handleConfigureDSP(uint64_t centerFrequency, | ||||||
|  |             int sampleRate); | ||||||
|     void handleScalef(Real scalef); |     void handleScalef(Real scalef); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										164
									
								
								sdrbase/websockets/wsspectrum.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										164
									
								
								sdrbase/websockets/wsspectrum.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,164 @@ | |||||||
|  | ///////////////////////////////////////////////////////////////////////////////////
 | ||||||
|  | // Copyright (C) 2020 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 <QtWebSockets> | ||||||
|  | #include <QHostAddress> | ||||||
|  | #include <QDebug> | ||||||
|  | 
 | ||||||
|  | #include "wsspectrum.h" | ||||||
|  | 
 | ||||||
|  | WSSpectrum::WSSpectrum(QObject *parent) : | ||||||
|  |     QObject(parent), | ||||||
|  |     m_listeningAddress(QHostAddress::LocalHost), | ||||||
|  |     m_port(8887), | ||||||
|  |     m_webSocketServer(nullptr) | ||||||
|  | { | ||||||
|  |     m_timer.start(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | WSSpectrum::~WSSpectrum() | ||||||
|  | { | ||||||
|  |     closeSocket(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void WSSpectrum::openSocket() | ||||||
|  | { | ||||||
|  |     m_webSocketServer = new QWebSocketServer( | ||||||
|  |         QStringLiteral("Spectrum Server"), | ||||||
|  |         QWebSocketServer::NonSecureMode, | ||||||
|  |         this); | ||||||
|  | 
 | ||||||
|  |     if (m_webSocketServer->listen(m_listeningAddress, m_port)) | ||||||
|  |     { | ||||||
|  |         qDebug() << "WSSpectrum::openSocket: spectrum server listening at " << m_listeningAddress.toString() << " on port " << m_port; | ||||||
|  |         connect(m_webSocketServer, &QWebSocketServer::newConnection, this, &WSSpectrum::onNewConnection); | ||||||
|  |     } | ||||||
|  |     else | ||||||
|  |     { | ||||||
|  |         qInfo("WSSpectrum::openSocket: cannot start spectrum server at %s on port %u", qPrintable(m_listeningAddress.toString()), m_port); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void WSSpectrum::closeSocket() | ||||||
|  | { | ||||||
|  |     if (m_webSocketServer) | ||||||
|  |     { | ||||||
|  |         delete m_webSocketServer; | ||||||
|  |         m_webSocketServer = nullptr; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool WSSpectrum::socketOpened() | ||||||
|  | { | ||||||
|  |     return m_webSocketServer && m_webSocketServer->isListening(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | QString WSSpectrum::getWebSocketIdentifier(QWebSocket *peer) | ||||||
|  | { | ||||||
|  |     return QStringLiteral("%1:%2").arg(peer->peerAddress().toString(), QString::number(peer->peerPort())); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void WSSpectrum::onNewConnection() | ||||||
|  | { | ||||||
|  |     auto pSocket = m_webSocketServer->nextPendingConnection(); | ||||||
|  |     qDebug() << " WSSpectrum::onNewConnection: " << getWebSocketIdentifier(pSocket) << " connected"; | ||||||
|  |     pSocket->setParent(this); | ||||||
|  | 
 | ||||||
|  |     connect(pSocket, &QWebSocket::textMessageReceived, this, &WSSpectrum::processClientMessage); | ||||||
|  |     connect(pSocket, &QWebSocket::disconnected, this, &WSSpectrum::socketDisconnected); | ||||||
|  | 
 | ||||||
|  |     m_clients << pSocket; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void WSSpectrum::processClientMessage(const QString &message) | ||||||
|  | { | ||||||
|  |      qDebug() << "WSSpectrum::processClientMessage: " << message; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void WSSpectrum::socketDisconnected() | ||||||
|  | { | ||||||
|  |     QWebSocket *pClient = qobject_cast<QWebSocket *>(sender()); | ||||||
|  |     qDebug() << getWebSocketIdentifier(pClient) << " disconnected"; | ||||||
|  | 
 | ||||||
|  |     if (pClient) | ||||||
|  |     { | ||||||
|  |         m_clients.removeAll(pClient); | ||||||
|  |         pClient->deleteLater(); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void WSSpectrum::newSpectrum( | ||||||
|  |     const std::vector<Real>& spectrum, | ||||||
|  |     int fftSize, | ||||||
|  |     float refLevel, | ||||||
|  |     float powerRange, | ||||||
|  |     uint64_t centerFrequency, | ||||||
|  |     int bandwidth, | ||||||
|  |     bool linear | ||||||
|  | ) | ||||||
|  | { | ||||||
|  |     if (m_timer.elapsed() < 200) { // Max 5 frames per second
 | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     qint64 elapsed = m_timer.restart(); | ||||||
|  |     QWebSocket *pSender = qobject_cast<QWebSocket *>(sender()); | ||||||
|  |     QByteArray payload; | ||||||
|  | 
 | ||||||
|  |     buildPayload( | ||||||
|  |         payload, | ||||||
|  |         spectrum, | ||||||
|  |         fftSize, | ||||||
|  |         elapsed, | ||||||
|  |         refLevel, | ||||||
|  |         powerRange, | ||||||
|  |         centerFrequency, | ||||||
|  |         bandwidth, | ||||||
|  |         linear | ||||||
|  |     ); | ||||||
|  |     //qDebug() << "WSSpectrum::newSpectrum: " << payload.size() << " bytes in " << elapsed << " ms";
 | ||||||
|  | 
 | ||||||
|  |     for (QWebSocket *pClient : qAsConst(m_clients)) { | ||||||
|  |         pClient->sendBinaryMessage(payload); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void WSSpectrum::buildPayload( | ||||||
|  |     QByteArray& bytes, | ||||||
|  |     const std::vector<Real>& spectrum, | ||||||
|  |     int fftSize, | ||||||
|  |     int64_t fftTimeMs, | ||||||
|  |     float refLevel, | ||||||
|  |     float powerRange, | ||||||
|  |     uint64_t centerFrequency, | ||||||
|  |     int bandwidth, | ||||||
|  |     bool linear | ||||||
|  | ) | ||||||
|  | { | ||||||
|  |     QBuffer buffer(&bytes); | ||||||
|  |     buffer.open(QIODevice::WriteOnly); | ||||||
|  |     buffer.write((char*) &fftSize, sizeof(int)); | ||||||
|  |     buffer.write((char*) &fftTimeMs, sizeof(int64_t)); | ||||||
|  |     buffer.write((char*) &refLevel, sizeof(float)); | ||||||
|  |     buffer.write((char*) &powerRange, sizeof(float)); | ||||||
|  |     buffer.write((char*) ¢erFrequency, sizeof(uint64_t)); | ||||||
|  |     buffer.write((char*) &bandwidth, sizeof(int)); | ||||||
|  |     int linearInt = linear ? 1 : 0; | ||||||
|  |     buffer.write((char*) &linearInt, sizeof(int)); | ||||||
|  |     buffer.write((char*) spectrum.data(), fftSize*sizeof(Real)); | ||||||
|  |     buffer.close(); | ||||||
|  | } | ||||||
							
								
								
									
										83
									
								
								sdrbase/websockets/wsspectrum.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								sdrbase/websockets/wsspectrum.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,83 @@ | |||||||
|  | ///////////////////////////////////////////////////////////////////////////////////
 | ||||||
|  | // Copyright (C) 2020 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 SDRBASE_WEBSOCKETS_WSSPECTRUM_H_ | ||||||
|  | #define SDRBASE_WEBSOCKETS_WSSPECTRUM_H_ | ||||||
|  | 
 | ||||||
|  | #include <vector> | ||||||
|  | 
 | ||||||
|  | #include <QObject> | ||||||
|  | #include <QList> | ||||||
|  | #include <QElapsedTimer> | ||||||
|  | #include <QHostAddress> | ||||||
|  | 
 | ||||||
|  | #include "dsp/dsptypes.h" | ||||||
|  | 
 | ||||||
|  | #include "export.h" | ||||||
|  | 
 | ||||||
|  | class QWebSocketServer; | ||||||
|  | class QWebSocket; | ||||||
|  | 
 | ||||||
|  | class SDRBASE_API WSSpectrum : public QObject | ||||||
|  | { | ||||||
|  |     Q_OBJECT | ||||||
|  | public: | ||||||
|  |     explicit WSSpectrum(QObject *parent = nullptr); | ||||||
|  |     ~WSSpectrum() override; | ||||||
|  | 
 | ||||||
|  |     void openSocket(); | ||||||
|  |     void closeSocket(); | ||||||
|  |     bool socketOpened(); | ||||||
|  |     void setListeningAddress(const QString& address) { m_listeningAddress.setAddress(address); } | ||||||
|  |     void setPort(quint16 port) { m_port = port; } | ||||||
|  |     void newSpectrum( | ||||||
|  |         const std::vector<Real>& spectrum, | ||||||
|  |         int fftSize, | ||||||
|  |         float refLevel, | ||||||
|  |         float powerRange, | ||||||
|  |         uint64_t centerFrequency, | ||||||
|  |         int bandwidth, | ||||||
|  |         bool linear | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  | private slots: | ||||||
|  |     void onNewConnection(); | ||||||
|  |     void processClientMessage(const QString &message); | ||||||
|  |     void socketDisconnected(); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     QHostAddress m_listeningAddress; | ||||||
|  |     quint16 m_port; | ||||||
|  |     QWebSocketServer* m_webSocketServer; | ||||||
|  |     QList<QWebSocket*> m_clients; | ||||||
|  |     QElapsedTimer m_timer; | ||||||
|  | 
 | ||||||
|  |     static QString getWebSocketIdentifier(QWebSocket *peer); | ||||||
|  |     void buildPayload( | ||||||
|  |         QByteArray& bytes, | ||||||
|  |         const std::vector<Real>& spectrum, | ||||||
|  |         int fftSize, | ||||||
|  |         int64_t fftTimeMs, | ||||||
|  |         float refLevel, | ||||||
|  |         float powerRange, | ||||||
|  |         uint64_t centerFrequency, | ||||||
|  |         int bandwidth, | ||||||
|  |         bool linear | ||||||
|  |     ); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | #endif // SDRBASE_WEBSOCKETS_WSSPECTRUM_H_
 | ||||||
| @ -74,7 +74,7 @@ DeviceUISet::~DeviceUISet() | |||||||
| 
 | 
 | ||||||
| void DeviceUISet::setSpectrumScalingFactor(float scalef) | void DeviceUISet::setSpectrumScalingFactor(float scalef) | ||||||
| { | { | ||||||
|     m_spectrumVis->setScalef(m_spectrumVis->getInputMessageQueue(), scalef); |     m_spectrumVis->setScalef(scalef); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void DeviceUISet::addChannelMarker(ChannelMarker* channelMarker) | void DeviceUISet::addChannelMarker(ChannelMarker* channelMarker) | ||||||
|  | |||||||
| @ -231,6 +231,8 @@ void GLSpectrumGUI::applySettingsVis() | |||||||
| 	{ | 	{ | ||||||
| 	    m_spectrumVis->configure(m_messageQueueToVis, | 	    m_spectrumVis->configure(m_messageQueueToVis, | ||||||
| 	            m_fftSize, | 	            m_fftSize, | ||||||
|  |                 m_refLevel, | ||||||
|  |                 m_powerRange, | ||||||
| 	            m_fftOverlap, | 	            m_fftOverlap, | ||||||
| 	            m_averagingNb, | 	            m_averagingNb, | ||||||
| 	            (SpectrumVis::AvgMode) m_averagingMode, | 	            (SpectrumVis::AvgMode) m_averagingMode, | ||||||
| @ -338,6 +340,8 @@ void GLSpectrumGUI::on_refLevel_currentIndexChanged(int index) | |||||||
| 	    m_glSpectrum->setReferenceLevel(refLevel); | 	    m_glSpectrum->setReferenceLevel(refLevel); | ||||||
|         m_glSpectrum->setPowerRange(powerRange); |         m_glSpectrum->setPowerRange(powerRange); | ||||||
| 	} | 	} | ||||||
|  | 
 | ||||||
|  |     applySettingsVis(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GLSpectrumGUI::on_levelRange_currentIndexChanged(int index) | void GLSpectrumGUI::on_levelRange_currentIndexChanged(int index) | ||||||
| @ -352,6 +356,8 @@ void GLSpectrumGUI::on_levelRange_currentIndexChanged(int index) | |||||||
|         m_glSpectrum->setReferenceLevel(refLevel); |         m_glSpectrum->setReferenceLevel(refLevel); | ||||||
| 	    m_glSpectrum->setPowerRange(powerRange); | 	    m_glSpectrum->setPowerRange(powerRange); | ||||||
| 	} | 	} | ||||||
|  | 
 | ||||||
|  |     applySettingsVis(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GLSpectrumGUI::on_decay_valueChanged(int index) | void GLSpectrumGUI::on_decay_valueChanged(int index) | ||||||
|  | |||||||
| @ -37,8 +37,6 @@ class DSPEngine; | |||||||
| class DSPDeviceSourceEngine; | class DSPDeviceSourceEngine; | ||||||
| class DSPDeviceSinkEngine; | class DSPDeviceSinkEngine; | ||||||
| class Indicator; | class Indicator; | ||||||
| class SpectrumVis; |  | ||||||
| class GLSpectrum; |  | ||||||
| class GLSpectrumGUI; | class GLSpectrumGUI; | ||||||
| class ChannelWindow; | class ChannelWindow; | ||||||
| class PluginAPI; | class PluginAPI; | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user