| 
									
										
										
										
											2014-05-18 16:52:39 +01:00
										 |  |  | #include "dsp/spectrumvis.h"
 | 
					
						
							|  |  |  | #include "gui/glspectrum.h"
 | 
					
						
							|  |  |  | #include "dsp/dspcommands.h"
 | 
					
						
							|  |  |  | #include "util/messagequeue.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define MAX_FFT_SIZE 4096
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-06 15:10:22 +01:00
										 |  |  | #ifndef LINUX
 | 
					
						
							|  |  |  | inline double log2f(double n) | 
					
						
							| 
									
										
										
										
											2014-05-18 16:52:39 +01:00
										 |  |  | { | 
					
						
							|  |  |  | 	return log(n) / log(2.0); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-18 02:47:14 +02:00
										 |  |  | MESSAGE_CLASS_DEFINITION(SpectrumVis::MsgConfigureSpectrumVis, Message) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-18 16:52:39 +01:00
										 |  |  | SpectrumVis::SpectrumVis(GLSpectrum* glSpectrum) : | 
					
						
							| 
									
										
										
										
											2016-10-02 22:29:04 +02:00
										 |  |  | 	BasebandSampleSink(), | 
					
						
							| 
									
										
										
										
											2014-05-18 16:52:39 +01:00
										 |  |  | 	m_fft(FFTEngine::create()), | 
					
						
							|  |  |  | 	m_fftBuffer(MAX_FFT_SIZE), | 
					
						
							|  |  |  | 	m_logPowerSpectrum(MAX_FFT_SIZE), | 
					
						
							|  |  |  | 	m_fftBufferFill(0), | 
					
						
							| 
									
										
										
										
											2015-07-15 01:19:39 +02:00
										 |  |  | 	m_needMoreSamples(false), | 
					
						
							| 
									
										
										
										
											2015-10-22 02:27:56 +02:00
										 |  |  | 	m_glSpectrum(glSpectrum), | 
					
						
							|  |  |  | 	m_mutex(QMutex::Recursive) | 
					
						
							| 
									
										
										
										
											2014-05-18 16:52:39 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2015-08-12 09:03:02 +02:00
										 |  |  | 	setObjectName("SpectrumVis"); | 
					
						
							| 
									
										
										
										
											2014-06-10 12:24:52 +01:00
										 |  |  | 	handleConfigure(1024, 0, FFTWindow::BlackmanHarris); | 
					
						
							| 
									
										
										
										
											2014-05-18 16:52:39 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | SpectrumVis::~SpectrumVis() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	delete m_fft; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void SpectrumVis::configure(MessageQueue* msgQueue, int fftSize, int overlapPercent, FFTWindow::Function window) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2015-08-18 02:47:14 +02:00
										 |  |  | 	MsgConfigureSpectrumVis* cmd = new MsgConfigureSpectrumVis(fftSize, overlapPercent, window); | 
					
						
							| 
									
										
										
										
											2015-08-14 05:00:28 +02:00
										 |  |  | 	msgQueue->push(cmd); | 
					
						
							| 
									
										
										
										
											2014-05-18 16:52:39 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-25 08:24:23 +02:00
										 |  |  | void SpectrumVis::feedTriggered(const SampleVector::const_iterator& triggerPoint, const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool positiveOnly) | 
					
						
							| 
									
										
										
										
											2015-07-15 01:19:39 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2015-07-21 22:52:52 +02:00
										 |  |  | 	feed(triggerPoint, end, positiveOnly); // normal feed from trigger point
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							| 
									
										
										
										
											2015-07-15 01:19:39 +02:00
										 |  |  | 	if (triggerPoint == end) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		// the following piece of code allows to terminate the FFT that ends past the end of scope captured data
 | 
					
						
							|  |  |  | 		// that is the spectrum will include the captured data
 | 
					
						
							|  |  |  | 		// just do nothing if you want the spectrum to be included inside the scope captured data
 | 
					
						
							|  |  |  | 		// that is to drop the FFT that dangles past the end of captured data
 | 
					
						
							|  |  |  | 		if (m_needMoreSamples) { | 
					
						
							|  |  |  | 			feed(begin, end, positiveOnly); | 
					
						
							|  |  |  | 			m_needMoreSamples = false;      // force finish
 | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		feed(triggerPoint, end, positiveOnly); // normal feed from trigger point
 | 
					
						
							| 
									
										
										
										
											2015-07-21 22:52:52 +02:00
										 |  |  | 	}*/ | 
					
						
							| 
									
										
										
										
											2015-07-15 01:19:39 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-25 08:24:23 +02:00
										 |  |  | void SpectrumVis::feed(const SampleVector::const_iterator& cbegin, const SampleVector::const_iterator& end, bool positiveOnly) | 
					
						
							| 
									
										
										
										
											2014-05-18 16:52:39 +01:00
										 |  |  | { | 
					
						
							|  |  |  | 	// if no visualisation is set, send the samples to /dev/null
 | 
					
						
							| 
									
										
										
										
											2015-08-25 08:24:23 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if(m_glSpectrum == 0) | 
					
						
							|  |  |  | 	{ | 
					
						
							| 
									
										
										
										
											2014-05-18 16:52:39 +01:00
										 |  |  | 		return; | 
					
						
							| 
									
										
										
										
											2015-08-25 08:24:23 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2014-05-18 16:52:39 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-25 08:24:23 +02:00
										 |  |  | 	SampleVector::const_iterator begin(cbegin); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	while (begin < end) | 
					
						
							|  |  |  | 	{ | 
					
						
							| 
									
										
										
										
											2015-08-17 08:29:34 +02:00
										 |  |  | 		std::size_t todo = end - begin; | 
					
						
							|  |  |  | 		std::size_t samplesNeeded = m_refillSize - m_fftBufferFill; | 
					
						
							| 
									
										
										
										
											2014-05-18 16:52:39 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-25 08:24:23 +02:00
										 |  |  | 		if (todo >= samplesNeeded) | 
					
						
							|  |  |  | 		{ | 
					
						
							| 
									
										
										
										
											2015-10-22 02:27:56 +02:00
										 |  |  | 			QMutexLocker mutexLocker(&m_mutex); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-18 16:52:39 +01:00
										 |  |  | 			// fill up the buffer
 | 
					
						
							|  |  |  | 			std::vector<Complex>::iterator it = m_fftBuffer.begin() + m_fftBufferFill; | 
					
						
							| 
									
										
										
										
											2015-08-25 08:24:23 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			for (std::size_t i = 0; i < samplesNeeded; ++i, ++begin) | 
					
						
							|  |  |  | 			{ | 
					
						
							| 
									
										
										
										
											2015-10-09 03:59:28 +02:00
										 |  |  | 				*it++ = Complex(begin->real() / 32768.0f, begin->imag() / 32768.0f); | 
					
						
							| 
									
										
										
										
											2015-08-25 08:24:23 +02:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2014-05-18 16:52:39 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			// apply fft window (and copy from m_fftBuffer to m_fftIn)
 | 
					
						
							|  |  |  | 			m_window.apply(&m_fftBuffer[0], m_fft->in()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// calculate FFT
 | 
					
						
							|  |  |  | 			m_fft->transform(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// extract power spectrum and reorder buckets
 | 
					
						
							|  |  |  | 			Real ofs = 20.0f * log10f(1.0f / m_fftSize); | 
					
						
							|  |  |  | 			Real mult = (10.0f / log2f(10.0f)); | 
					
						
							|  |  |  | 			const Complex* fftOut = m_fft->out(); | 
					
						
							| 
									
										
										
										
											2014-06-17 20:13:49 +01:00
										 |  |  | 			Complex c; | 
					
						
							|  |  |  | 			Real v; | 
					
						
							| 
									
										
										
										
											2015-08-17 08:29:34 +02:00
										 |  |  | 			std::size_t halfSize = m_fftSize / 2; | 
					
						
							| 
									
										
										
										
											2014-06-15 09:32:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-25 08:24:23 +02:00
										 |  |  | 			if ( positiveOnly ) | 
					
						
							|  |  |  | 			{ | 
					
						
							|  |  |  | 				for (std::size_t i = 0; i < halfSize; i++) | 
					
						
							|  |  |  | 				{ | 
					
						
							| 
									
										
										
										
											2014-06-17 20:13:49 +01:00
										 |  |  | 					c = fftOut[i]; | 
					
						
							|  |  |  | 					v = c.real() * c.real() + c.imag() * c.imag(); | 
					
						
							|  |  |  | 					v = mult * log2f(v) + ofs; | 
					
						
							|  |  |  | 					m_logPowerSpectrum[i * 2] = v; | 
					
						
							|  |  |  | 					m_logPowerSpectrum[i * 2 + 1] = v; | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2015-08-25 08:24:23 +02:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			else | 
					
						
							|  |  |  | 			{ | 
					
						
							|  |  |  | 				for (std::size_t i = 0; i < halfSize; i++) | 
					
						
							|  |  |  | 				{ | 
					
						
							| 
									
										
										
										
											2014-06-17 20:13:49 +01:00
										 |  |  | 					c = fftOut[i + halfSize]; | 
					
						
							|  |  |  | 					v = c.real() * c.real() + c.imag() * c.imag(); | 
					
						
							|  |  |  | 					v = mult * log2f(v) + ofs; | 
					
						
							|  |  |  | 					m_logPowerSpectrum[i] = v; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					c = fftOut[i]; | 
					
						
							|  |  |  | 					v = c.real() * c.real() + c.imag() * c.imag(); | 
					
						
							|  |  |  | 					v = mult * log2f(v) + ofs; | 
					
						
							|  |  |  | 					m_logPowerSpectrum[i + halfSize] = v; | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2014-05-18 16:52:39 +01:00
										 |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// send new data to visualisation
 | 
					
						
							|  |  |  | 			m_glSpectrum->newSpectrum(m_logPowerSpectrum, m_fftSize); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// advance buffer respecting the fft overlap factor
 | 
					
						
							|  |  |  | 			std::copy(m_fftBuffer.begin() + m_refillSize, m_fftBuffer.end(), m_fftBuffer.begin()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// start over
 | 
					
						
							|  |  |  | 			m_fftBufferFill = m_overlapSize; | 
					
						
							| 
									
										
										
										
											2015-07-15 01:19:39 +02:00
										 |  |  | 			m_needMoreSamples = false; | 
					
						
							| 
									
										
										
										
											2015-08-25 08:24:23 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 		{ | 
					
						
							| 
									
										
										
										
											2014-05-18 16:52:39 +01:00
										 |  |  | 			// not enough samples for FFT - just fill in new data and return
 | 
					
						
							|  |  |  | 			for(std::vector<Complex>::iterator it = m_fftBuffer.begin() + m_fftBufferFill; begin < end; ++begin) | 
					
						
							| 
									
										
										
										
											2015-08-25 08:24:23 +02:00
										 |  |  | 			{ | 
					
						
							| 
									
										
										
										
											2015-10-09 03:59:28 +02:00
										 |  |  | 				*it++ = Complex(begin->real() / 32768.0f, begin->imag() / 32768.0f); | 
					
						
							| 
									
										
										
										
											2015-08-25 08:24:23 +02:00
										 |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-18 16:52:39 +01:00
										 |  |  | 			m_fftBufferFill += todo; | 
					
						
							| 
									
										
										
										
											2015-07-15 01:19:39 +02:00
										 |  |  | 			m_needMoreSamples = true; | 
					
						
							| 
									
										
										
										
											2014-05-18 16:52:39 +01:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void SpectrumVis::start() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void SpectrumVis::stop() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-14 05:00:28 +02:00
										 |  |  | bool SpectrumVis::handleMessage(const Message& message) | 
					
						
							| 
									
										
										
										
											2014-05-18 16:52:39 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2015-08-18 02:47:14 +02:00
										 |  |  | 	if (MsgConfigureSpectrumVis::match(message)) | 
					
						
							| 
									
										
										
										
											2015-08-14 05:00:28 +02:00
										 |  |  | 	{ | 
					
						
							| 
									
										
										
										
											2015-08-18 02:47:14 +02:00
										 |  |  | 		MsgConfigureSpectrumVis& conf = (MsgConfigureSpectrumVis&) message; | 
					
						
							| 
									
										
										
										
											2015-08-17 08:29:34 +02:00
										 |  |  | 		handleConfigure(conf.getFFTSize(), conf.getOverlapPercent(), conf.getWindow()); | 
					
						
							| 
									
										
										
										
											2014-05-18 16:52:39 +01:00
										 |  |  | 		return true; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-08-14 05:00:28 +02:00
										 |  |  | 	else | 
					
						
							| 
									
										
										
										
											2015-06-23 20:05:28 +02:00
										 |  |  | 	{ | 
					
						
							| 
									
										
										
										
											2015-08-14 05:00:28 +02:00
										 |  |  | 		return false; | 
					
						
							| 
									
										
										
										
											2015-06-23 20:05:28 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-18 16:52:39 +01:00
										 |  |  | void SpectrumVis::handleConfigure(int fftSize, int overlapPercent, FFTWindow::Function window) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2015-10-22 02:27:56 +02:00
										 |  |  | 	QMutexLocker mutexLocker(&m_mutex); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-14 05:00:28 +02:00
										 |  |  | 	if (fftSize > MAX_FFT_SIZE) | 
					
						
							|  |  |  | 	{ | 
					
						
							| 
									
										
										
										
											2014-05-18 16:52:39 +01:00
										 |  |  | 		fftSize = MAX_FFT_SIZE; | 
					
						
							| 
									
										
										
										
											2015-08-14 05:00:28 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	else if (fftSize < 64) | 
					
						
							|  |  |  | 	{ | 
					
						
							| 
									
										
										
										
											2014-05-18 16:52:39 +01:00
										 |  |  | 		fftSize = 64; | 
					
						
							| 
									
										
										
										
											2015-08-14 05:00:28 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (overlapPercent > 100) | 
					
						
							|  |  |  | 	{ | 
					
						
							| 
									
										
										
										
											2014-05-18 16:52:39 +01:00
										 |  |  | 		m_overlapPercent = 100; | 
					
						
							| 
									
										
										
										
											2015-08-14 05:00:28 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	else if (overlapPercent < 0) | 
					
						
							|  |  |  | 	{ | 
					
						
							| 
									
										
										
										
											2014-05-18 16:52:39 +01:00
										 |  |  | 		m_overlapPercent = 0; | 
					
						
							| 
									
										
										
										
											2015-08-14 05:00:28 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2014-05-18 16:52:39 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	m_fftSize = fftSize; | 
					
						
							|  |  |  | 	m_overlapPercent = overlapPercent; | 
					
						
							|  |  |  | 	m_fft->configure(m_fftSize, false); | 
					
						
							|  |  |  | 	m_window.create(window, m_fftSize); | 
					
						
							|  |  |  | 	m_overlapSize = (m_fftSize * m_overlapPercent) / 100; | 
					
						
							|  |  |  | 	m_refillSize = m_fftSize - m_overlapSize; | 
					
						
							|  |  |  | 	m_fftBufferFill = m_overlapSize; | 
					
						
							|  |  |  | } |