mirror of
				https://github.com/f4exb/sdrangel.git
				synced 2025-10-31 13:00:26 -04:00 
			
		
		
		
	Started implementation of a Channel Analyzer plugin. Works basically
This commit is contained in:
		
							parent
							
								
									cba4942971
								
							
						
					
					
						commit
						2c84b82621
					
				| @ -17,15 +17,17 @@ public: | ||||
| 	typedef std::complex<float> cmplx; | ||||
| 
 | ||||
| 	fftfilt(float f1, float f2, int len); | ||||
| 	fftfilt(float f, int len); | ||||
| 	fftfilt(float f2, int len); | ||||
| 	~fftfilt(); | ||||
| // f1 < f2 ==> bandpass
 | ||||
| // f1 > f2 ==> band reject
 | ||||
| 	void create_filter(float f1, float f2); | ||||
| 	void rtty_filter(float); | ||||
| 	void create_dsb_filter(float f2); | ||||
| 
 | ||||
| 	int noFilt(const cmplx& in, cmplx **out); | ||||
| 	int runFilt(const cmplx& in, cmplx **out); | ||||
| 	int runSSB(const cmplx& in, cmplx **out, bool usb); | ||||
| 	int runDSB(const cmplx& in, cmplx **out); | ||||
| 
 | ||||
| protected: | ||||
| 	int flen; | ||||
| @ -44,12 +46,15 @@ protected: | ||||
| 		return (i == len/2) ? 2.0 * fc:  | ||||
| 				sin(2 * M_PI * fc * (i - len/2)) / (M_PI * (i - len/2)); | ||||
| 	} | ||||
| 
 | ||||
| 	inline float _blackman(int i, int len) { | ||||
| 		return (0.42 -  | ||||
| 				 0.50 * cos(2.0 * M_PI * i / len) +  | ||||
| 				 0.08 * cos(4.0 * M_PI * i / len)); | ||||
| 	} | ||||
| 
 | ||||
| 	void init_filter(); | ||||
| 	void init_dsb_filter(); | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
|  | ||||
| @ -6,5 +6,6 @@ add_subdirectory(nfm) | ||||
| add_subdirectory(ssb) | ||||
| add_subdirectory(tcpsrc) | ||||
| add_subdirectory(wfm) | ||||
| add_subdirectory(chanalyzer) | ||||
| 
 | ||||
| #add_subdirectory(tetra) | ||||
|  | ||||
							
								
								
									
										47
									
								
								plugins/channel/chanalyzer/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								plugins/channel/chanalyzer/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,47 @@ | ||||
| project(chanalyzer) | ||||
| 
 | ||||
| set(chanalyzer_SOURCES | ||||
| 	chanalyzer.cpp | ||||
| 	chanalyzergui.cpp | ||||
| 	chanalyzerplugin.cpp | ||||
| ) | ||||
| 
 | ||||
| set(chanalyzer_HEADERS | ||||
| 	chanalyzer.h | ||||
| 	chanalyzergui.h | ||||
| 	chanalyzerplugin.h | ||||
| ) | ||||
| 
 | ||||
| set(chanalyzer_FORMS | ||||
| 	chanalyzergui.ui | ||||
| ) | ||||
| 
 | ||||
| include_directories( | ||||
| 	. | ||||
| 	${CMAKE_CURRENT_BINARY_DIR} | ||||
| 	${CMAKE_SOURCE_DIR}/include | ||||
| 	${CMAKE_SOURCE_DIR}/include-gpl | ||||
| 	${OPENGL_INCLUDE_DIR} | ||||
| ) | ||||
| 
 | ||||
| #include(${QT_USE_FILE}) | ||||
| add_definitions(${QT_DEFINITIONS}) | ||||
| add_definitions(-DQT_PLUGIN) | ||||
| add_definitions(-DQT_SHARED) | ||||
| 
 | ||||
| #qt5_wrap_cpp(chanalyzer_HEADERS_MOC ${chanalyzer_HEADERS}) | ||||
| qt5_wrap_ui(chanalyzer_FORMS_HEADERS ${chanalyzer_FORMS}) | ||||
| 
 | ||||
| add_library(chanalyzer SHARED | ||||
| 	${chanalyzer_SOURCES} | ||||
| 	${chanalyzer_HEADERS_MOC} | ||||
| 	${chanalyzer_FORMS_HEADERS} | ||||
| ) | ||||
| 
 | ||||
| target_link_libraries(chanalyzer | ||||
| 	${QT_LIBRARIES} | ||||
| 	${OPENGL_LIBRARIES} | ||||
| 	sdrbase | ||||
| ) | ||||
| 
 | ||||
| qt5_use_modules(chanalyzer Core Widgets OpenGL Multimedia) | ||||
							
								
								
									
										193
									
								
								plugins/channel/chanalyzer/chanalyzer.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										193
									
								
								plugins/channel/chanalyzer/chanalyzer.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,193 @@ | ||||
| ///////////////////////////////////////////////////////////////////////////////////
 | ||||
| // Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany //
 | ||||
| // written by Christian Daniel                                                   //
 | ||||
| // (c) 2014 Modified by John Greb
 | ||||
| //                                                                               //
 | ||||
| // 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                  //
 | ||||
| //                                                                               //
 | ||||
| // 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 <QTime> | ||||
| #include <stdio.h> | ||||
| #include "audio/audiooutput.h" | ||||
| #include "dsp/dspcommands.h" | ||||
| 
 | ||||
| #include <iostream> | ||||
| #include "chanalyzer.h" | ||||
| 
 | ||||
| MESSAGE_CLASS_DEFINITION(ChannelAnalyzer::MsgConfigureChannelAnalyzer, Message) | ||||
| 
 | ||||
| ChannelAnalyzer::ChannelAnalyzer(SampleSink* sampleSink) : | ||||
| 	m_sampleSink(sampleSink) | ||||
| { | ||||
| 	m_Bandwidth = 5000; | ||||
| 	m_LowCutoff = 300; | ||||
| 	//m_volume = 2.0;
 | ||||
| 	m_spanLog2 = 3; | ||||
| 	m_sampleRate = 96000; | ||||
| 	m_frequency = 0; | ||||
| 	m_nco.setFreq(m_frequency, m_sampleRate); | ||||
| 	m_nco_test.setFreq(m_frequency, m_sampleRate); | ||||
| 	m_interpolator.create(16, m_sampleRate, 5000); | ||||
| 	m_sampleDistanceRemain = (Real)m_sampleRate / 48000.0; | ||||
| 
 | ||||
| 	//m_audioBuffer.resize(512);
 | ||||
| 	//m_audioBufferFill = 0;
 | ||||
| 	m_undersampleCount = 0; | ||||
| 
 | ||||
| 	m_usb = true; | ||||
| 	m_ssb = true; | ||||
| 	SSBFilter = new fftfilt(m_LowCutoff / 48000.0, m_Bandwidth / 48000.0, ssbFftLen); | ||||
| 	DSBFilter = new fftfilt(m_Bandwidth / 48000.0, 2*ssbFftLen); | ||||
| 	// if (!USBFilter) segfault;
 | ||||
| } | ||||
| 
 | ||||
| ChannelAnalyzer::~ChannelAnalyzer() | ||||
| { | ||||
| 	if (SSBFilter) delete SSBFilter; | ||||
| 	if (DSBFilter) delete DSBFilter; | ||||
| } | ||||
| 
 | ||||
| void ChannelAnalyzer::configure(MessageQueue* messageQueue, | ||||
| 		Real Bandwidth, | ||||
| 		Real LowCutoff, | ||||
| 		int  spanLog2, | ||||
| 		bool ssb) | ||||
| { | ||||
| 	Message* cmd = MsgConfigureChannelAnalyzer::create(Bandwidth, LowCutoff, spanLog2, ssb); | ||||
| 	cmd->submit(messageQueue, this); | ||||
| } | ||||
| 
 | ||||
| void ChannelAnalyzer::feed(SampleVector::const_iterator begin, SampleVector::const_iterator end, bool positiveOnly) | ||||
| { | ||||
| 	Complex ci; | ||||
| 	fftfilt::cmplx *sideband, sum; | ||||
| 	int n_out; | ||||
| 	int decim = 1<<(m_spanLog2 - 1); | ||||
| 	unsigned char decim_mask = decim - 1; // counter LSB bit mask for decimation by 2^(m_scaleLog2 - 1)
 | ||||
| 
 | ||||
| 	for(SampleVector::const_iterator it = begin; it < end; ++it) { | ||||
| 		Complex c(it->real() / 32768.0, it->imag() / 32768.0); | ||||
| 		c *= m_nco.nextIQ(); | ||||
| 
 | ||||
| 		/*
 | ||||
| 		ci = c; | ||||
| 		if (m_ssb) { | ||||
| 			n_out = SSBFilter->runSSB(ci, &sideband, m_usb); | ||||
| 		} else { | ||||
| 			n_out = DSBFilter->noFilt(ci, &sideband); | ||||
| 		} | ||||
| 		*/ | ||||
| 
 | ||||
| 		if(m_interpolator.interpolate(&m_sampleDistanceRemain, c, &ci)) | ||||
| 		{ | ||||
| 			if (m_ssb) | ||||
| 			{ | ||||
| 				n_out = SSBFilter->runSSB(ci, &sideband, m_usb); | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				n_out = DSBFilter->runDSB(ci, &sideband); | ||||
| 			} | ||||
| 			m_sampleDistanceRemain += (Real)m_sampleRate / 48000.0; | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			n_out = 0; | ||||
| 		} | ||||
| 
 | ||||
| 		for (int i = 0; i < n_out; i++) | ||||
| 		{ | ||||
| 			// Downsample by 2^(m_scaleLog2 - 1) for SSB band spectrum display
 | ||||
| 			// smart decimation with bit gain using float arithmetic (23 bits significand)
 | ||||
| 
 | ||||
| 			sum += sideband[i]; | ||||
| 
 | ||||
| 			if (!(m_undersampleCount++ & decim_mask)) | ||||
| 			{ | ||||
| 				sum /= decim; | ||||
| 				m_sampleBuffer.push_back(Sample(sum.real() * 32768.0, sum.imag() * 32768.0)); | ||||
| 				sum = 0; | ||||
| 			} | ||||
| 
 | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if(m_sampleSink != NULL) | ||||
| 	{ | ||||
| 		m_sampleSink->feed(m_sampleBuffer.begin(), m_sampleBuffer.end(), m_ssb); // m_ssb = positive only
 | ||||
| 	} | ||||
| 
 | ||||
| 	m_sampleBuffer.clear(); | ||||
| } | ||||
| 
 | ||||
| void ChannelAnalyzer::start() | ||||
| { | ||||
| } | ||||
| 
 | ||||
| void ChannelAnalyzer::stop() | ||||
| { | ||||
| } | ||||
| 
 | ||||
| bool ChannelAnalyzer::handleMessage(Message* cmd) | ||||
| { | ||||
| 	float band, lowCutoff; | ||||
| 
 | ||||
| 	if(DSPSignalNotification::match(cmd)) { | ||||
| 		DSPSignalNotification* signal = (DSPSignalNotification*)cmd; | ||||
| 		//fprintf(stderr, "%d samples/sec, %lld Hz offset", signal->getSampleRate(), signal->getFrequencyOffset());
 | ||||
| 		m_sampleRate = signal->getSampleRate(); | ||||
| 		m_nco.setFreq(-signal->getFrequencyOffset(), m_sampleRate); | ||||
| 		m_interpolator.create(16, m_sampleRate, m_Bandwidth); | ||||
| 		m_sampleDistanceRemain = m_sampleRate / 48000.0; | ||||
| 		cmd->completed(); | ||||
| 		return true; | ||||
| 	} else if(MsgConfigureChannelAnalyzer::match(cmd)) { | ||||
| 		MsgConfigureChannelAnalyzer* cfg = (MsgConfigureChannelAnalyzer*)cmd; | ||||
| 
 | ||||
| 		band = cfg->getBandwidth(); | ||||
| 		lowCutoff = cfg->getLoCutoff(); | ||||
| 
 | ||||
| 		if (band < 0) { | ||||
| 			band = -band; | ||||
| 			lowCutoff = -lowCutoff; | ||||
| 			m_usb = false; | ||||
| 		} else | ||||
| 			m_usb = true; | ||||
| 
 | ||||
| 		if (band < 100.0f) | ||||
| 		{ | ||||
| 			band = 100.0f; | ||||
| 			lowCutoff = 0; | ||||
| 		} | ||||
| 
 | ||||
| 		m_Bandwidth = band; | ||||
| 		m_LowCutoff = lowCutoff; | ||||
| 
 | ||||
| 		m_interpolator.create(16, m_sampleRate, band * 2.0f); | ||||
| 		SSBFilter->create_filter(m_LowCutoff / 48000.0f, m_Bandwidth / 48000.0f); | ||||
| 		DSBFilter->create_dsb_filter(m_Bandwidth / 48000.0f); | ||||
| 
 | ||||
| 		//m_volume = cfg->getVolume();
 | ||||
| 		//m_volume *= m_volume * 0.1;
 | ||||
| 
 | ||||
| 		m_spanLog2 = cfg->getSpanLog2(); | ||||
| 		m_ssb = cfg->getSSB(); | ||||
| 
 | ||||
| 		cmd->completed(); | ||||
| 		return true; | ||||
| 	} else { | ||||
| 		if(m_sampleSink != NULL) | ||||
| 		   return m_sampleSink->handleMessage(cmd); | ||||
| 		else return false; | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										115
									
								
								plugins/channel/chanalyzer/chanalyzer.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								plugins/channel/chanalyzer/chanalyzer.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,115 @@ | ||||
| ///////////////////////////////////////////////////////////////////////////////////
 | ||||
| // Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany //
 | ||||
| // written by Christian Daniel                                                   //
 | ||||
| //                                                                               //
 | ||||
| // 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                  //
 | ||||
| //                                                                               //
 | ||||
| // 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 INCLUDE_CHANALYZER_H | ||||
| #define INCLUDE_CHANALYZER_H | ||||
| 
 | ||||
| #include <vector> | ||||
| #include "dsp/samplesink.h" | ||||
| #include "dsp/nco.h" | ||||
| #include "dsp/interpolator.h" | ||||
| #include "dsp/fftfilt.h" | ||||
| #include "audio/audiofifo.h" | ||||
| #include "util/message.h" | ||||
| 
 | ||||
| #define ssbFftLen 1024 | ||||
| 
 | ||||
| //class AudioFifo;
 | ||||
| 
 | ||||
| class ChannelAnalyzer : public SampleSink { | ||||
| public: | ||||
| 	ChannelAnalyzer(SampleSink* sampleSink); | ||||
| 	~ChannelAnalyzer(); | ||||
| 
 | ||||
| 	void configure(MessageQueue* messageQueue, | ||||
| 			Real Bandwidth, | ||||
| 			Real LowCutoff, | ||||
| 			int spanLog2, | ||||
| 			bool ssb); | ||||
| 
 | ||||
| 	void feed(SampleVector::const_iterator begin, SampleVector::const_iterator end, bool positiveOnly); | ||||
| 	void start(); | ||||
| 	void stop(); | ||||
| 	bool handleMessage(Message* cmd); | ||||
| 
 | ||||
| private: | ||||
| 	class MsgConfigureChannelAnalyzer : public Message { | ||||
| 		MESSAGE_CLASS_DECLARATION | ||||
| 
 | ||||
| 	public: | ||||
| 		Real getBandwidth() const { return m_Bandwidth; } | ||||
| 		Real getLoCutoff() const { return m_LowCutoff; } | ||||
| 		int  getSpanLog2() const { return m_spanLog2; } | ||||
| 		bool getSSB() const { return m_ssb; } | ||||
| 
 | ||||
| 		static MsgConfigureChannelAnalyzer* create(Real Bandwidth, | ||||
| 				Real LowCutoff, | ||||
| 				int spanLog2, | ||||
| 				bool ssb) | ||||
| 		{ | ||||
| 			return new MsgConfigureChannelAnalyzer(Bandwidth, LowCutoff, spanLog2, ssb); | ||||
| 		} | ||||
| 
 | ||||
| 	private: | ||||
| 		Real m_Bandwidth; | ||||
| 		Real m_LowCutoff; | ||||
| 		int  m_spanLog2; | ||||
| 		bool m_ssb; | ||||
| 
 | ||||
| 		MsgConfigureChannelAnalyzer(Real Bandwidth, | ||||
| 				Real LowCutoff, | ||||
| 				int spanLog2, | ||||
| 				bool ssb) : | ||||
| 			Message(), | ||||
| 			m_Bandwidth(Bandwidth), | ||||
| 			m_LowCutoff(LowCutoff), | ||||
| 			m_spanLog2(spanLog2), | ||||
| 			m_ssb(ssb) | ||||
| 		{ } | ||||
| 	}; | ||||
| 
 | ||||
| 	//struct AudioSample {
 | ||||
| 	//	qint16 l;
 | ||||
| 	//	qint16 r;
 | ||||
| 	//};
 | ||||
| 	//typedef std::vector<AudioSample> AudioVector;
 | ||||
| 
 | ||||
| 	Real m_Bandwidth; | ||||
| 	Real m_LowCutoff; | ||||
| 	int m_spanLog2; | ||||
| 	int m_undersampleCount; | ||||
| 	int m_sampleRate; | ||||
| 	int m_frequency; | ||||
| 	bool m_usb; | ||||
| 	bool m_ssb; | ||||
| 
 | ||||
| 	NCO m_nco; | ||||
| 	NCO m_nco_test; | ||||
| 	Interpolator m_interpolator; | ||||
| 	Real m_sampleDistanceRemain; | ||||
| 	fftfilt* SSBFilter; | ||||
| 	fftfilt* DSBFilter; | ||||
| 
 | ||||
| 	SampleSink* m_sampleSink; | ||||
| 	SampleVector m_sampleBuffer; | ||||
| 
 | ||||
| 	//AudioVector m_audioBuffer;
 | ||||
| 	//uint m_audioBufferFill;
 | ||||
| 	//AudioFifo* m_audioFifo;
 | ||||
| }; | ||||
| 
 | ||||
| #endif // INCLUDE_CHANALYZER_H
 | ||||
							
								
								
									
										358
									
								
								plugins/channel/chanalyzer/chanalyzergui.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										358
									
								
								plugins/channel/chanalyzer/chanalyzergui.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,358 @@ | ||||
| #include <QDockWidget> | ||||
| #include <QMainWindow> | ||||
| #include "ui_chanalyzergui.h" | ||||
| #include "dsp/threadedsamplesink.h" | ||||
| #include "dsp/channelizer.h" | ||||
| #include "dsp/spectrumvis.h" | ||||
| #include "gui/glspectrum.h" | ||||
| #include "plugin/pluginapi.h" | ||||
| #include "util/simpleserializer.h" | ||||
| #include "gui/basicchannelsettingswidget.h" | ||||
| 
 | ||||
| #include <iostream> | ||||
| #include "chanalyzer.h" | ||||
| #include "chanalyzergui.h" | ||||
| 
 | ||||
| ChannelAnalyzerGUI* ChannelAnalyzerGUI::create(PluginAPI* pluginAPI) | ||||
| { | ||||
| 	ChannelAnalyzerGUI* gui = new ChannelAnalyzerGUI(pluginAPI); | ||||
| 	return gui; | ||||
| } | ||||
| 
 | ||||
| void ChannelAnalyzerGUI::destroy() | ||||
| { | ||||
| 	delete this; | ||||
| } | ||||
| 
 | ||||
| void ChannelAnalyzerGUI::setName(const QString& name) | ||||
| { | ||||
| 	setObjectName(name); | ||||
| } | ||||
| 
 | ||||
| QString ChannelAnalyzerGUI::getName() const | ||||
| { | ||||
| 	return objectName(); | ||||
| } | ||||
| 
 | ||||
| void ChannelAnalyzerGUI::resetToDefaults() | ||||
| { | ||||
| 	ui->BW->setValue(30); | ||||
| 	ui->deltaFrequency->setValue(0); | ||||
| 	ui->spanLog2->setValue(3); | ||||
| 	applySettings(); | ||||
| } | ||||
| 
 | ||||
| QByteArray ChannelAnalyzerGUI::serialize() const | ||||
| { | ||||
| 	SimpleSerializer s(1); | ||||
| 	s.writeS32(1, m_channelMarker->getCenterFrequency()); | ||||
| 	s.writeS32(2, ui->BW->value()); | ||||
| 	s.writeBlob(3, ui->spectrumGUI->serialize()); | ||||
| 	s.writeU32(4, m_channelMarker->getColor().rgb()); | ||||
| 	s.writeS32(5, ui->lowCut->value()); | ||||
| 	s.writeS32(6, ui->spanLog2->value()); | ||||
| 	return s.final(); | ||||
| } | ||||
| 
 | ||||
| bool ChannelAnalyzerGUI::deserialize(const QByteArray& data) | ||||
| { | ||||
| 	SimpleDeserializer d(data); | ||||
| 
 | ||||
| 	if(!d.isValid()) { | ||||
| 		resetToDefaults(); | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| 	if(d.getVersion() == 1) { | ||||
| 		QByteArray bytetmp; | ||||
| 		quint32 u32tmp; | ||||
| 		qint32 tmp; | ||||
| 		d.readS32(1, &tmp, 0); | ||||
| 		m_channelMarker->setCenterFrequency(tmp); | ||||
| 		d.readS32(2, &tmp, 30); | ||||
| 		ui->BW->setValue(tmp); | ||||
| 		d.readBlob(3, &bytetmp); | ||||
| 		ui->spectrumGUI->deserialize(bytetmp); | ||||
| 		if(d.readU32(4, &u32tmp)) | ||||
| 			m_channelMarker->setColor(u32tmp); | ||||
| 		d.readS32(5, &tmp, 3); | ||||
| 		ui->lowCut->setValue(tmp); | ||||
| 		d.readS32(6, &tmp, 20); | ||||
| 		ui->spanLog2->setValue(tmp); | ||||
| 		setNewRate(tmp); | ||||
| 		applySettings(); | ||||
| 		return true; | ||||
| 	} else { | ||||
| 		resetToDefaults(); | ||||
| 		return false; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| bool ChannelAnalyzerGUI::handleMessage(Message* message) | ||||
| { | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| void ChannelAnalyzerGUI::viewChanged() | ||||
| { | ||||
| 	applySettings(); | ||||
| } | ||||
| 
 | ||||
| void ChannelAnalyzerGUI::on_deltaMinus_clicked(bool minus) | ||||
| { | ||||
| 	int deltaFrequency = m_channelMarker->getCenterFrequency(); | ||||
| 	bool minusDelta = (deltaFrequency < 0); | ||||
| 
 | ||||
| 	if (minus ^ minusDelta) // sign change
 | ||||
| 	{ | ||||
| 		m_channelMarker->setCenterFrequency(-deltaFrequency); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void ChannelAnalyzerGUI::on_deltaFrequency_changed(quint64 value) | ||||
| { | ||||
| 	if (ui->deltaMinus->isChecked()) { | ||||
| 		m_channelMarker->setCenterFrequency(-value); | ||||
| 	} else { | ||||
| 		m_channelMarker->setCenterFrequency(value); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void ChannelAnalyzerGUI::on_BW_valueChanged(int value) | ||||
| { | ||||
| 	QString s = QString::number(value/10.0, 'f', 1); | ||||
| 	ui->BWText->setText(tr("%1k").arg(s)); | ||||
| 	m_channelMarker->setBandwidth(value * 100 * 2); | ||||
| 
 | ||||
| 	if (value < 0) { | ||||
| 		m_channelMarker->setSidebands(ChannelMarker::lsb); | ||||
| 	} else { | ||||
| 		m_channelMarker->setSidebands(ChannelMarker::usb); | ||||
| 	} | ||||
| 
 | ||||
| 	on_lowCut_valueChanged(m_channelMarker->getLowCutoff()/100); | ||||
| } | ||||
| 
 | ||||
| int ChannelAnalyzerGUI::getEffectiveLowCutoff(int lowCutoff) | ||||
| { | ||||
| 	int ssbBW = m_channelMarker->getBandwidth() / 2; | ||||
| 	int effectiveLowCutoff = lowCutoff; | ||||
| 	const int guard = 100; | ||||
| 
 | ||||
| 	if (ssbBW < 0) { | ||||
| 		if (effectiveLowCutoff < ssbBW + guard) { | ||||
| 			effectiveLowCutoff = ssbBW + guard; | ||||
| 		} | ||||
| 		if (effectiveLowCutoff > 0)	{ | ||||
| 			effectiveLowCutoff = 0; | ||||
| 		} | ||||
| 	} else { | ||||
| 		if (effectiveLowCutoff > ssbBW - guard)	{ | ||||
| 			effectiveLowCutoff = ssbBW - guard; | ||||
| 		} | ||||
| 		if (effectiveLowCutoff < 0)	{ | ||||
| 			effectiveLowCutoff = 0; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return effectiveLowCutoff; | ||||
| } | ||||
| 
 | ||||
| void ChannelAnalyzerGUI::on_lowCut_valueChanged(int value) | ||||
| { | ||||
| 	int lowCutoff = getEffectiveLowCutoff(value * 100); | ||||
| 	m_channelMarker->setLowCutoff(lowCutoff); | ||||
| 	QString s = QString::number(lowCutoff/1000.0, 'f', 1); | ||||
| 	ui->lowCutText->setText(tr("%1k").arg(s)); | ||||
| 	ui->lowCut->setValue(lowCutoff/100); | ||||
| 	applySettings(); | ||||
| } | ||||
| 
 | ||||
| void ChannelAnalyzerGUI::on_spanLog2_valueChanged(int value) | ||||
| { | ||||
| 	if (setNewRate(value)) { | ||||
| 		applySettings(); | ||||
| 	} | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| void ChannelAnalyzerGUI::on_ssb_toggled(bool checked) | ||||
| { | ||||
| 	if (checked) | ||||
| 	{ | ||||
| 		if (ui->BW->value() < 0) { | ||||
| 			m_channelMarker->setSidebands(ChannelMarker::lsb); | ||||
| 		} else { | ||||
| 			m_channelMarker->setSidebands(ChannelMarker::usb); | ||||
| 		} | ||||
| 
 | ||||
| 		ui->glSpectrum->setCenterFrequency(m_rate/2); | ||||
| 		ui->glSpectrum->setSampleRate(m_rate); | ||||
| 		ui->glSpectrum->setSsbSpectrum(true); | ||||
| 
 | ||||
| 		on_lowCut_valueChanged(m_channelMarker->getLowCutoff()/100); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		m_channelMarker->setSidebands(ChannelMarker::dsb); | ||||
| 
 | ||||
| 		ui->glSpectrum->setCenterFrequency(0); | ||||
| 		ui->glSpectrum->setSampleRate(2*m_rate); | ||||
| 		ui->glSpectrum->setSsbSpectrum(false); | ||||
| 
 | ||||
| 		applySettings(); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void ChannelAnalyzerGUI::onWidgetRolled(QWidget* widget, bool rollDown) | ||||
| { | ||||
| 	/*
 | ||||
| 	if((widget == ui->spectrumContainer) && (m_ssbDemod != NULL)) | ||||
| 		m_ssbDemod->setSpectrum(m_threadedSampleSink->getMessageQueue(), rollDown); | ||||
| 	*/ | ||||
| } | ||||
| 
 | ||||
| void ChannelAnalyzerGUI::onMenuDoubleClicked() | ||||
| { | ||||
| 	if(!m_basicSettingsShown) { | ||||
| 		m_basicSettingsShown = true; | ||||
| 		BasicChannelSettingsWidget* bcsw = new BasicChannelSettingsWidget(m_channelMarker, this); | ||||
| 		bcsw->show(); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| ChannelAnalyzerGUI::ChannelAnalyzerGUI(PluginAPI* pluginAPI, QWidget* parent) : | ||||
| 	RollupWidget(parent), | ||||
| 	ui(new Ui::ChannelAnalyzerGUI), | ||||
| 	m_pluginAPI(pluginAPI), | ||||
| 	m_basicSettingsShown(false), | ||||
| 	m_rate(6000), | ||||
| 	m_spanLog2(3) | ||||
| { | ||||
| 	ui->setupUi(this); | ||||
| 	setAttribute(Qt::WA_DeleteOnClose, true); | ||||
| 	connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); | ||||
| 	connect(this, SIGNAL(menuDoubleClickEvent()), this, SLOT(onMenuDoubleClicked())); | ||||
| 
 | ||||
| 	//m_audioFifo = new AudioFifo(4, 48000 / 4);
 | ||||
| 	m_spectrumVis = new SpectrumVis(ui->glSpectrum); | ||||
| 	m_channelAnalyzer = new ChannelAnalyzer(m_spectrumVis); | ||||
| 	m_channelizer = new Channelizer(m_channelAnalyzer); | ||||
| 	m_threadedSampleSink = new ThreadedSampleSink(m_channelizer); | ||||
| 	//m_pluginAPI->addAudioSource(m_audioFifo);
 | ||||
| 	m_pluginAPI->addSampleSink(m_threadedSampleSink); | ||||
| 
 | ||||
| 	ui->glSpectrum->setCenterFrequency(m_rate/2); | ||||
| 	ui->glSpectrum->setSampleRate(m_rate); | ||||
| 	ui->glSpectrum->setDisplayWaterfall(true); | ||||
| 	ui->glSpectrum->setDisplayMaxHold(true); | ||||
| 	ui->glSpectrum->setSsbSpectrum(true); | ||||
| 
 | ||||
| 	m_channelMarker = new ChannelMarker(this); | ||||
| 	m_channelMarker->setColor(Qt::gray); | ||||
| 	m_channelMarker->setBandwidth(m_rate); | ||||
| 	m_channelMarker->setSidebands(ChannelMarker::usb); | ||||
| 	m_channelMarker->setCenterFrequency(0); | ||||
| 	m_channelMarker->setVisible(true); | ||||
| 	connect(m_channelMarker, SIGNAL(changed()), this, SLOT(viewChanged())); | ||||
| 	m_pluginAPI->addChannelMarker(m_channelMarker); | ||||
| 
 | ||||
| 	ui->spectrumGUI->setBuddies(m_threadedSampleSink->getMessageQueue(), m_spectrumVis, ui->glSpectrum); | ||||
| 
 | ||||
| 	applySettings(); | ||||
| } | ||||
| 
 | ||||
| ChannelAnalyzerGUI::~ChannelAnalyzerGUI() | ||||
| { | ||||
| 	m_pluginAPI->removeChannelInstance(this); | ||||
| 	//m_pluginAPI->removeAudioSource(m_audioFifo);
 | ||||
| 	m_pluginAPI->removeSampleSink(m_threadedSampleSink); | ||||
| 	delete m_threadedSampleSink; | ||||
| 	delete m_channelizer; | ||||
| 	delete m_channelAnalyzer; | ||||
| 	delete m_spectrumVis; | ||||
| 	//delete m_audioFifo;
 | ||||
| 	delete m_channelMarker; | ||||
| 	delete ui; | ||||
| } | ||||
| 
 | ||||
| bool ChannelAnalyzerGUI::setNewRate(int spanLog2) | ||||
| { | ||||
| 	if ((spanLog2 < 1) || (spanLog2 > 5)) { | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| 	m_spanLog2 = spanLog2; | ||||
| 	m_rate = 48000 / (1<<spanLog2); | ||||
| 
 | ||||
| 	if (ui->BW->value() < -m_rate/100) { | ||||
| 		ui->BW->setValue(-m_rate/100); | ||||
| 		m_channelMarker->setBandwidth(-m_rate*2); | ||||
| 	} else if (ui->BW->value() > m_rate/100) { | ||||
| 		ui->BW->setValue(m_rate/100); | ||||
| 		m_channelMarker->setBandwidth(m_rate*2); | ||||
| 	} | ||||
| 
 | ||||
| 	if (ui->lowCut->value() < -m_rate/100) { | ||||
| 		ui->lowCut->setValue(-m_rate/100); | ||||
| 		m_channelMarker->setLowCutoff(-m_rate); | ||||
| 	} else if (ui->lowCut->value() > m_rate/100) { | ||||
| 		ui->lowCut->setValue(m_rate/100); | ||||
| 		m_channelMarker->setLowCutoff(m_rate); | ||||
| 	} | ||||
| 
 | ||||
| 	ui->BW->setMinimum(-m_rate/100); | ||||
| 	ui->lowCut->setMinimum(-m_rate/100); | ||||
| 	ui->BW->setMaximum(m_rate/100); | ||||
| 	ui->lowCut->setMaximum(m_rate/100); | ||||
| 
 | ||||
| 	QString s = QString::number(m_rate/1000.0, 'f', 1); | ||||
| 	ui->spanText->setText(tr("%1k").arg(s)); | ||||
| 
 | ||||
| 	if (ui->ssb->isChecked()) | ||||
| 	{ | ||||
| 		if (ui->BW->value() < 0) { | ||||
| 			m_channelMarker->setSidebands(ChannelMarker::lsb); | ||||
| 		} else { | ||||
| 			m_channelMarker->setSidebands(ChannelMarker::usb); | ||||
| 		} | ||||
| 
 | ||||
| 		ui->glSpectrum->setCenterFrequency(m_rate/2); | ||||
| 		ui->glSpectrum->setSampleRate(m_rate); | ||||
| 		ui->glSpectrum->setSsbSpectrum(true); | ||||
| 	} else { | ||||
| 		m_channelMarker->setSidebands(ChannelMarker::dsb); | ||||
| 
 | ||||
| 		ui->glSpectrum->setCenterFrequency(0); | ||||
| 		ui->glSpectrum->setSampleRate(2*m_rate); | ||||
| 		ui->glSpectrum->setSsbSpectrum(false); | ||||
| 	} | ||||
| 
 | ||||
| 	return true; | ||||
| } | ||||
| 
 | ||||
| void ChannelAnalyzerGUI::applySettings() | ||||
| { | ||||
| 	setTitleColor(m_channelMarker->getColor()); | ||||
| 	ui->deltaFrequency->setValue(abs(m_channelMarker->getCenterFrequency())); | ||||
| 	ui->deltaMinus->setChecked(m_channelMarker->getCenterFrequency() < 0); | ||||
| 	m_channelizer->configure(m_threadedSampleSink->getMessageQueue(), | ||||
| 		48000, | ||||
| 		m_channelMarker->getCenterFrequency()); | ||||
| 	m_channelAnalyzer->configure(m_threadedSampleSink->getMessageQueue(), | ||||
| 		ui->BW->value() * 100.0, | ||||
| 		ui->lowCut->value() * 100.0, | ||||
| 		m_spanLog2, | ||||
| 		ui->ssb->isChecked()); | ||||
| } | ||||
| 
 | ||||
| void ChannelAnalyzerGUI::leaveEvent(QEvent*) | ||||
| { | ||||
| 	m_channelMarker->setHighlighted(false); | ||||
| } | ||||
| 
 | ||||
| void ChannelAnalyzerGUI::enterEvent(QEvent*) | ||||
| { | ||||
| 	m_channelMarker->setHighlighted(true); | ||||
| } | ||||
| 
 | ||||
							
								
								
									
										72
									
								
								plugins/channel/chanalyzer/chanalyzergui.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								plugins/channel/chanalyzer/chanalyzergui.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,72 @@ | ||||
| #ifndef INCLUDE_CHANNELANALYZERGUI_H | ||||
| #define INCLUDE_CHANNELANALYZERGUI_H | ||||
| 
 | ||||
| #include "gui/rollupwidget.h" | ||||
| #include "plugin/plugingui.h" | ||||
| 
 | ||||
| class PluginAPI; | ||||
| class ChannelMarker; | ||||
| 
 | ||||
| //class AudioFifo;
 | ||||
| class ThreadedSampleSink; | ||||
| class Channelizer; | ||||
| class ChannelAnalyzer; | ||||
| class SpectrumVis; | ||||
| 
 | ||||
| namespace Ui { | ||||
| 	class ChannelAnalyzerGUI; | ||||
| } | ||||
| 
 | ||||
| class ChannelAnalyzerGUI : public RollupWidget, public PluginGUI { | ||||
| 	Q_OBJECT | ||||
| 
 | ||||
| public: | ||||
| 	static ChannelAnalyzerGUI* create(PluginAPI* pluginAPI); | ||||
| 	void destroy(); | ||||
| 
 | ||||
| 	void setName(const QString& name); | ||||
| 	QString getName() const; | ||||
| 
 | ||||
| 	void resetToDefaults(); | ||||
| 	QByteArray serialize() const; | ||||
| 	bool deserialize(const QByteArray& data); | ||||
| 
 | ||||
| 	bool handleMessage(Message* message); | ||||
| 
 | ||||
| private slots: | ||||
| 	void viewChanged(); | ||||
| 	void on_deltaFrequency_changed(quint64 value); | ||||
| 	void on_deltaMinus_clicked(bool minus); | ||||
| 	void on_BW_valueChanged(int value); | ||||
| 	void on_lowCut_valueChanged(int value); | ||||
| 	void on_spanLog2_valueChanged(int value); | ||||
| 	void on_ssb_toggled(bool checked); | ||||
| 	void onWidgetRolled(QWidget* widget, bool rollDown); | ||||
| 	void onMenuDoubleClicked(); | ||||
| 
 | ||||
| private: | ||||
| 	Ui::ChannelAnalyzerGUI* ui; | ||||
| 	PluginAPI* m_pluginAPI; | ||||
| 	ChannelMarker* m_channelMarker; | ||||
| 	bool m_basicSettingsShown; | ||||
| 	int m_rate; | ||||
| 	int m_spanLog2; | ||||
| 
 | ||||
| 	//AudioFifo* m_audioFifo;
 | ||||
| 	ThreadedSampleSink* m_threadedSampleSink; | ||||
| 	Channelizer* m_channelizer; | ||||
| 	ChannelAnalyzer* m_channelAnalyzer; | ||||
| 	SpectrumVis* m_spectrumVis; | ||||
| 
 | ||||
| 	explicit ChannelAnalyzerGUI(PluginAPI* pluginAPI, QWidget* parent = NULL); | ||||
| 	~ChannelAnalyzerGUI(); | ||||
| 
 | ||||
| 	int  getEffectiveLowCutoff(int lowCutoff); | ||||
| 	bool setNewRate(int spanLog2); | ||||
| 	void applySettings(); | ||||
| 
 | ||||
| 	void leaveEvent(QEvent*); | ||||
| 	void enterEvent(QEvent*); | ||||
| }; | ||||
| 
 | ||||
| #endif // INCLUDE_CHANNELANALYZERGUI_H
 | ||||
							
								
								
									
										416
									
								
								plugins/channel/chanalyzer/chanalyzergui.ui
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										416
									
								
								plugins/channel/chanalyzer/chanalyzergui.ui
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,416 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <ui version="4.0"> | ||||
|  <class>ChannelAnalyzerGUI</class> | ||||
|  <widget class="RollupWidget" name="ChannelAnalyzerGUI"> | ||||
|   <property name="geometry"> | ||||
|    <rect> | ||||
|     <x>0</x> | ||||
|     <y>0</y> | ||||
|     <width>302</width> | ||||
|     <height>439</height> | ||||
|    </rect> | ||||
|   </property> | ||||
|   <property name="minimumSize"> | ||||
|    <size> | ||||
|     <width>302</width> | ||||
|     <height>0</height> | ||||
|    </size> | ||||
|   </property> | ||||
|   <property name="windowTitle"> | ||||
|    <string>Channel Analyzer</string> | ||||
|   </property> | ||||
|   <widget class="QWidget" name="settingsContainer" native="true"> | ||||
|    <property name="geometry"> | ||||
|     <rect> | ||||
|      <x>35</x> | ||||
|      <y>35</y> | ||||
|      <width>242</width> | ||||
|      <height>96</height> | ||||
|     </rect> | ||||
|    </property> | ||||
|    <property name="font"> | ||||
|     <font> | ||||
|      <pointsize>8</pointsize> | ||||
|     </font> | ||||
|    </property> | ||||
|    <property name="windowTitle"> | ||||
|     <string>Settings</string> | ||||
|    </property> | ||||
|    <layout class="QGridLayout" name="gridLayout"> | ||||
|     <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> | ||||
|     <property name="horizontalSpacing"> | ||||
|      <number>4</number> | ||||
|     </property> | ||||
|     <property name="verticalSpacing"> | ||||
|      <number>3</number> | ||||
|     </property> | ||||
|     <item row="2" column="1"> | ||||
|      <widget class="QSlider" name="BW"> | ||||
|       <property name="font"> | ||||
|        <font> | ||||
|         <pointsize>8</pointsize> | ||||
|        </font> | ||||
|       </property> | ||||
|       <property name="minimum"> | ||||
|        <number>-60</number> | ||||
|       </property> | ||||
|       <property name="maximum"> | ||||
|        <number>60</number> | ||||
|       </property> | ||||
|       <property name="pageStep"> | ||||
|        <number>1</number> | ||||
|       </property> | ||||
|       <property name="value"> | ||||
|        <number>30</number> | ||||
|       </property> | ||||
|       <property name="orientation"> | ||||
|        <enum>Qt::Horizontal</enum> | ||||
|       </property> | ||||
|      </widget> | ||||
|     </item> | ||||
|     <item row="2" column="2"> | ||||
|      <widget class="QLabel" name="BWText"> | ||||
|       <property name="minimumSize"> | ||||
|        <size> | ||||
|         <width>50</width> | ||||
|         <height>0</height> | ||||
|        </size> | ||||
|       </property> | ||||
|       <property name="font"> | ||||
|        <font> | ||||
|         <pointsize>8</pointsize> | ||||
|        </font> | ||||
|       </property> | ||||
|       <property name="text"> | ||||
|        <string>3.0k</string> | ||||
|       </property> | ||||
|       <property name="alignment"> | ||||
|        <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> | ||||
|       </property> | ||||
|      </widget> | ||||
|     </item> | ||||
|     <item row="0" column="1"> | ||||
|      <widget class="ValueDial" name="deltaFrequency" native="true"> | ||||
|       <property name="sizePolicy"> | ||||
|        <sizepolicy hsizetype="Maximum" vsizetype="Maximum"> | ||||
|         <horstretch>0</horstretch> | ||||
|         <verstretch>0</verstretch> | ||||
|        </sizepolicy> | ||||
|       </property> | ||||
|       <property name="minimumSize"> | ||||
|        <size> | ||||
|         <width>32</width> | ||||
|         <height>16</height> | ||||
|        </size> | ||||
|       </property> | ||||
|       <property name="font"> | ||||
|        <font> | ||||
|         <family>Monospace</family> | ||||
|         <pointsize>10</pointsize> | ||||
|        </font> | ||||
|       </property> | ||||
|       <property name="cursor"> | ||||
|        <cursorShape>SizeVerCursor</cursorShape> | ||||
|       </property> | ||||
|       <property name="focusPolicy"> | ||||
|        <enum>Qt::StrongFocus</enum> | ||||
|       </property> | ||||
|       <property name="toolTip"> | ||||
|        <string>Demod shift frequency from center in Hz</string> | ||||
|       </property> | ||||
|      </widget> | ||||
|     </item> | ||||
|     <item row="2" column="0"> | ||||
|      <widget class="QLabel" name="BWLabel"> | ||||
|       <property name="font"> | ||||
|        <font> | ||||
|         <pointsize>8</pointsize> | ||||
|        </font> | ||||
|       </property> | ||||
|       <property name="text"> | ||||
|        <string>BW</string> | ||||
|       </property> | ||||
|      </widget> | ||||
|     </item> | ||||
|     <item row="0" column="2"> | ||||
|      <widget class="QLabel" name="deltaUnits"> | ||||
|       <property name="font"> | ||||
|        <font> | ||||
|         <pointsize>8</pointsize> | ||||
|        </font> | ||||
|       </property> | ||||
|       <property name="text"> | ||||
|        <string>Hz</string> | ||||
|       </property> | ||||
|      </widget> | ||||
|     </item> | ||||
|     <item row="1" column="0"> | ||||
|      <widget class="QLabel" name="spanLabel"> | ||||
|       <property name="font"> | ||||
|        <font> | ||||
|         <pointsize>8</pointsize> | ||||
|        </font> | ||||
|       </property> | ||||
|       <property name="text"> | ||||
|        <string>Span</string> | ||||
|       </property> | ||||
|      </widget> | ||||
|     </item> | ||||
|     <item row="3" column="0"> | ||||
|      <widget class="QLabel" name="lowCutLabel"> | ||||
|       <property name="font"> | ||||
|        <font> | ||||
|         <pointsize>8</pointsize> | ||||
|        </font> | ||||
|       </property> | ||||
|       <property name="text"> | ||||
|        <string>Low cut.</string> | ||||
|       </property> | ||||
|      </widget> | ||||
|     </item> | ||||
|     <item row="0" column="0"> | ||||
|      <widget class="QRadioButton" name="deltaMinus"> | ||||
|       <property name="font"> | ||||
|        <font> | ||||
|         <pointsize>8</pointsize> | ||||
|        </font> | ||||
|       </property> | ||||
|       <property name="layoutDirection"> | ||||
|        <enum>Qt::RightToLeft</enum> | ||||
|       </property> | ||||
|       <property name="text"> | ||||
|        <string>Minus</string> | ||||
|       </property> | ||||
|      </widget> | ||||
|     </item> | ||||
|     <item row="3" column="1"> | ||||
|      <widget class="QSlider" name="lowCut"> | ||||
|       <property name="font"> | ||||
|        <font> | ||||
|         <pointsize>8</pointsize> | ||||
|        </font> | ||||
|       </property> | ||||
|       <property name="minimum"> | ||||
|        <number>-60</number> | ||||
|       </property> | ||||
|       <property name="maximum"> | ||||
|        <number>60</number> | ||||
|       </property> | ||||
|       <property name="pageStep"> | ||||
|        <number>1</number> | ||||
|       </property> | ||||
|       <property name="value"> | ||||
|        <number>3</number> | ||||
|       </property> | ||||
|       <property name="orientation"> | ||||
|        <enum>Qt::Horizontal</enum> | ||||
|       </property> | ||||
|      </widget> | ||||
|     </item> | ||||
|     <item row="3" column="2"> | ||||
|      <widget class="QLabel" name="lowCutText"> | ||||
|       <property name="minimumSize"> | ||||
|        <size> | ||||
|         <width>50</width> | ||||
|         <height>0</height> | ||||
|        </size> | ||||
|       </property> | ||||
|       <property name="font"> | ||||
|        <font> | ||||
|         <pointsize>8</pointsize> | ||||
|        </font> | ||||
|       </property> | ||||
|       <property name="text"> | ||||
|        <string>0.3k</string> | ||||
|       </property> | ||||
|       <property name="alignment"> | ||||
|        <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> | ||||
|       </property> | ||||
|      </widget> | ||||
|     </item> | ||||
|     <item row="1" column="2"> | ||||
|      <widget class="QLabel" name="spanText"> | ||||
|       <property name="font"> | ||||
|        <font> | ||||
|         <pointsize>8</pointsize> | ||||
|        </font> | ||||
|       </property> | ||||
|       <property name="text"> | ||||
|        <string>6.0k</string> | ||||
|       </property> | ||||
|       <property name="alignment"> | ||||
|        <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> | ||||
|       </property> | ||||
|      </widget> | ||||
|     </item> | ||||
|     <item row="1" column="1"> | ||||
|      <widget class="QSlider" name="spanLog2"> | ||||
|       <property name="font"> | ||||
|        <font> | ||||
|         <pointsize>11</pointsize> | ||||
|        </font> | ||||
|       </property> | ||||
|       <property name="minimum"> | ||||
|        <number>1</number> | ||||
|       </property> | ||||
|       <property name="maximum"> | ||||
|        <number>5</number> | ||||
|       </property> | ||||
|       <property name="pageStep"> | ||||
|        <number>1</number> | ||||
|       </property> | ||||
|       <property name="value"> | ||||
|        <number>3</number> | ||||
|       </property> | ||||
|       <property name="sliderPosition"> | ||||
|        <number>3</number> | ||||
|       </property> | ||||
|       <property name="orientation"> | ||||
|        <enum>Qt::Horizontal</enum> | ||||
|       </property> | ||||
|       <property name="invertedAppearance"> | ||||
|        <bool>true</bool> | ||||
|       </property> | ||||
|       <property name="invertedControls"> | ||||
|        <bool>true</bool> | ||||
|       </property> | ||||
|      </widget> | ||||
|     </item> | ||||
|     <item row="0" column="3"> | ||||
|      <spacer name="hSpacer1"> | ||||
|       <property name="orientation"> | ||||
|        <enum>Qt::Horizontal</enum> | ||||
|       </property> | ||||
|       <property name="sizeHint" stdset="0"> | ||||
|        <size> | ||||
|         <width>40</width> | ||||
|         <height>20</height> | ||||
|        </size> | ||||
|       </property> | ||||
|      </spacer> | ||||
|     </item> | ||||
|     <item row="2" column="3"> | ||||
|      <spacer name="hSpacer3"> | ||||
|       <property name="orientation"> | ||||
|        <enum>Qt::Horizontal</enum> | ||||
|       </property> | ||||
|       <property name="sizeHint" stdset="0"> | ||||
|        <size> | ||||
|         <width>40</width> | ||||
|         <height>20</height> | ||||
|        </size> | ||||
|       </property> | ||||
|      </spacer> | ||||
|     </item> | ||||
|     <item row="3" column="3"> | ||||
|      <spacer name="hSpacer4"> | ||||
|       <property name="orientation"> | ||||
|        <enum>Qt::Horizontal</enum> | ||||
|       </property> | ||||
|       <property name="sizeHint" stdset="0"> | ||||
|        <size> | ||||
|         <width>40</width> | ||||
|         <height>20</height> | ||||
|        </size> | ||||
|       </property> | ||||
|      </spacer> | ||||
|     </item> | ||||
|     <item row="1" column="3"> | ||||
|      <widget class="QCheckBox" name="ssb"> | ||||
|       <property name="text"> | ||||
|        <string>SSB</string> | ||||
|       </property> | ||||
|      </widget> | ||||
|     </item> | ||||
|    </layout> | ||||
|   </widget> | ||||
|   <widget class="QWidget" name="spectrumContainer" native="true"> | ||||
|    <property name="geometry"> | ||||
|     <rect> | ||||
|      <x>40</x> | ||||
|      <y>140</y> | ||||
|      <width>241</width> | ||||
|      <height>284</height> | ||||
|     </rect> | ||||
|    </property> | ||||
|    <property name="windowTitle"> | ||||
|     <string>Channel Spectrum</string> | ||||
|    </property> | ||||
|    <layout class="QVBoxLayout" name="verticalLayout"> | ||||
|     <property name="spacing"> | ||||
|      <number>2</number> | ||||
|     </property> | ||||
|     <property name="leftMargin"> | ||||
|      <number>3</number> | ||||
|     </property> | ||||
|     <property name="topMargin"> | ||||
|      <number>3</number> | ||||
|     </property> | ||||
|     <property name="rightMargin"> | ||||
|      <number>3</number> | ||||
|     </property> | ||||
|     <property name="bottomMargin"> | ||||
|      <number>3</number> | ||||
|     </property> | ||||
|     <item> | ||||
|      <widget class="GLSpectrum" name="glSpectrum" native="true"> | ||||
|       <property name="minimumSize"> | ||||
|        <size> | ||||
|         <width>200</width> | ||||
|         <height>250</height> | ||||
|        </size> | ||||
|       </property> | ||||
|       <property name="font"> | ||||
|        <font> | ||||
|         <family>Courier</family> | ||||
|         <pointsize>8</pointsize> | ||||
|        </font> | ||||
|       </property> | ||||
|      </widget> | ||||
|     </item> | ||||
|     <item> | ||||
|      <widget class="GLSpectrumGUI" name="spectrumGUI" native="true"/> | ||||
|     </item> | ||||
|    </layout> | ||||
|   </widget> | ||||
|  </widget> | ||||
|  <customwidgets> | ||||
|   <customwidget> | ||||
|    <class>GLSpectrum</class> | ||||
|    <extends>QWidget</extends> | ||||
|    <header>gui/glspectrum.h</header> | ||||
|    <container>1</container> | ||||
|   </customwidget> | ||||
|   <customwidget> | ||||
|    <class>GLSpectrumGUI</class> | ||||
|    <extends>QWidget</extends> | ||||
|    <header>gui/glspectrumgui.h</header> | ||||
|    <container>1</container> | ||||
|   </customwidget> | ||||
|   <customwidget> | ||||
|    <class>RollupWidget</class> | ||||
|    <extends>QWidget</extends> | ||||
|    <header>gui/rollupwidget.h</header> | ||||
|    <container>1</container> | ||||
|   </customwidget> | ||||
|   <customwidget> | ||||
|    <class>ValueDial</class> | ||||
|    <extends>QWidget</extends> | ||||
|    <header>gui/valuedial.h</header> | ||||
|    <container>1</container> | ||||
|   </customwidget> | ||||
|  </customwidgets> | ||||
|  <resources/> | ||||
|  <connections/> | ||||
| </ui> | ||||
							
								
								
									
										53
									
								
								plugins/channel/chanalyzer/chanalyzerplugin.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								plugins/channel/chanalyzer/chanalyzerplugin.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,53 @@ | ||||
| #include <QtPlugin> | ||||
| #include <QAction> | ||||
| #include "plugin/pluginapi.h" | ||||
| #include "chanalyzergui.h" | ||||
| #include "chanalyzerplugin.h" | ||||
| 
 | ||||
| const PluginDescriptor ChannelAnalyzerPlugin::m_pluginDescriptor = { | ||||
| 	QString("Channel Analyzer"), | ||||
| 	QString("1.0"), | ||||
| 	QString("(c) 2015 Edouard Griffiths, F4EXB"), | ||||
| 	QString("https://github.com/f4exb/rtl-sdrangelove/tree/f4exb"), | ||||
| 	true, | ||||
| 	QString("https://github.com/f4exb/rtl-sdrangelove/tree/f4exb") | ||||
| }; | ||||
| 
 | ||||
| ChannelAnalyzerPlugin::ChannelAnalyzerPlugin(QObject* parent) : | ||||
| 	QObject(parent) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| const PluginDescriptor& ChannelAnalyzerPlugin::getPluginDescriptor() const | ||||
| { | ||||
| 	return m_pluginDescriptor; | ||||
| } | ||||
| 
 | ||||
| void ChannelAnalyzerPlugin::initPlugin(PluginAPI* pluginAPI) | ||||
| { | ||||
| 	m_pluginAPI = pluginAPI; | ||||
| 
 | ||||
| 	// register demodulator
 | ||||
| 	QAction* action = new QAction(tr("&ChannelAnalyzer"), this); | ||||
| 	connect(action, SIGNAL(triggered()), this, SLOT(createInstanceChannelAnalyzer())); | ||||
| 	m_pluginAPI->registerChannel("org.f4exb.sdrangelove.channel.chanalyzer", this, action); | ||||
| } | ||||
| 
 | ||||
| PluginGUI* ChannelAnalyzerPlugin::createChannel(const QString& channelName) | ||||
| { | ||||
| 	if(channelName == "org.f4exb.sdrangelove.channel.chanalyzer") { | ||||
| 		ChannelAnalyzerGUI* gui = ChannelAnalyzerGUI::create(m_pluginAPI); | ||||
| 		m_pluginAPI->registerChannelInstance("org.f4exb.sdrangelove.channel.chanalyzer", gui); | ||||
| 		m_pluginAPI->addChannelRollup(gui); | ||||
| 		return gui; | ||||
| 	} else { | ||||
| 		return NULL; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void ChannelAnalyzerPlugin::createInstanceChannelAnalyzer() | ||||
| { | ||||
| 	ChannelAnalyzerGUI* gui = ChannelAnalyzerGUI::create(m_pluginAPI); | ||||
| 	m_pluginAPI->registerChannelInstance("org.f4exb.sdrangelove.channel.chanalyzer", gui); | ||||
| 	m_pluginAPI->addChannelRollup(gui); | ||||
| } | ||||
							
								
								
									
										29
									
								
								plugins/channel/chanalyzer/chanalyzerplugin.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								plugins/channel/chanalyzer/chanalyzerplugin.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,29 @@ | ||||
| #ifndef INCLUDE_CHANALYZERPLUGIN_H | ||||
| #define INCLUDE_CHANALYZERPLUGIN_H | ||||
| 
 | ||||
| #include <QObject> | ||||
| #include "plugin/plugininterface.h" | ||||
| 
 | ||||
| class ChannelAnalyzerPlugin : public QObject, PluginInterface { | ||||
| 	Q_OBJECT | ||||
| 	Q_INTERFACES(PluginInterface) | ||||
| 	Q_PLUGIN_METADATA(IID "org.f4exb.sdrangelove.channel.chanalyzer") | ||||
| 
 | ||||
| public: | ||||
| 	explicit ChannelAnalyzerPlugin(QObject* parent = NULL); | ||||
| 
 | ||||
| 	const PluginDescriptor& getPluginDescriptor() const; | ||||
| 	void initPlugin(PluginAPI* pluginAPI); | ||||
| 
 | ||||
| 	PluginGUI* createChannel(const QString& channelName); | ||||
| 
 | ||||
| private: | ||||
| 	static const PluginDescriptor m_pluginDescriptor; | ||||
| 
 | ||||
| 	PluginAPI* m_pluginAPI; | ||||
| 
 | ||||
| private slots: | ||||
| 	void createInstanceChannelAnalyzer(); | ||||
| }; | ||||
| 
 | ||||
| #endif // INCLUDE_CHANALYZERPLUGIN_H
 | ||||
| @ -80,6 +80,13 @@ fftfilt::fftfilt(float f1, float f2, int len) | ||||
| 	create_filter(f1, f2); | ||||
| } | ||||
| 
 | ||||
| fftfilt::fftfilt(float f2, int len) | ||||
| { | ||||
| 	flen	= len; | ||||
| 	init_filter(); | ||||
| 	create_dsb_filter(f2); | ||||
| } | ||||
| 
 | ||||
| fftfilt::~fftfilt() | ||||
| { | ||||
| 	if (fft) delete fft; | ||||
| @ -130,6 +137,43 @@ void fftfilt::create_filter(float f1, float f2) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Double the size of FFT used for equivalent SSB filter or assume FFT is half the size of the one used for SSB
 | ||||
| void fftfilt::create_dsb_filter(float f2) | ||||
| { | ||||
| 	// initialize the filter to zero
 | ||||
| 	memset(filter, 0, flen * sizeof(cmplx)); | ||||
| 
 | ||||
| 	for (int i = 0; i < flen2; i++) { | ||||
| 		filter[i] = fsinc(f2, i, flen2); | ||||
| 		filter[i] *= _blackman(i, flen2); | ||||
| 	} | ||||
| 
 | ||||
| 	fft->ComplexFFT(filter); | ||||
| 
 | ||||
| 	// normalize the output filter for unity gain
 | ||||
| 	float scale = 0, mag; | ||||
| 	for (int i = 0; i < flen2; i++) { | ||||
| 		mag = abs(filter[i]); | ||||
| 		if (mag > scale) scale = mag; | ||||
| 	} | ||||
| 	if (scale != 0) { | ||||
| 		for (int i = 0; i < flen; i++) | ||||
| 			filter[i] /= scale; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // test bypass
 | ||||
| int fftfilt::noFilt(const cmplx & in, cmplx **out) | ||||
| { | ||||
| 	data[inptr++] = in; | ||||
| 	if (inptr < flen2) | ||||
| 		return 0; | ||||
| 	inptr = 0; | ||||
| 
 | ||||
| 	*out = data; | ||||
| 	return flen2; | ||||
| } | ||||
| 
 | ||||
| // Filter with fast convolution (overlap-add algorithm).
 | ||||
| int fftfilt::runFilt(const cmplx & in, cmplx **out) | ||||
| { | ||||
| @ -190,6 +234,36 @@ int fftfilt::runSSB(const cmplx & in, cmplx **out, bool usb) | ||||
| 	return flen2; | ||||
| } | ||||
| 
 | ||||
| // Version for double sideband. You have to double the FFT size used for SSB.
 | ||||
| int fftfilt::runDSB(const cmplx & in, cmplx **out) | ||||
| { | ||||
| 	data[inptr++] = in; | ||||
| 	if (inptr < flen2) | ||||
| 		return 0; | ||||
| 	inptr = 0; | ||||
| 
 | ||||
| 	fft->ComplexFFT(data); | ||||
| 
 | ||||
| 	for (int i = 0; i < flen2; i++) { | ||||
| 		data[i] *= filter[i]; | ||||
| 		data[flen2 + i] *= filter[flen2 + i]; | ||||
| 	} | ||||
| 
 | ||||
| 	// in-place FFT: freqdata overwritten with filtered timedata
 | ||||
| 	fft->InverseComplexFFT(data); | ||||
| 
 | ||||
| 	// overlap and add
 | ||||
| 	for (int i = 0; i < flen2; i++) { | ||||
| 		output[i] = ovlbuf[i] + data[i]; | ||||
| 		ovlbuf[i] = data[i+flen2]; | ||||
| 	} | ||||
| 
 | ||||
| 	memset (data, 0, flen * sizeof(cmplx)); | ||||
| 
 | ||||
| 	*out = output; | ||||
| 	return flen2; | ||||
| } | ||||
| 
 | ||||
| /* Sliding FFT from Fldigi */ | ||||
| 
 | ||||
| struct sfft::vrot_bins_pair { | ||||
|  | ||||
| @ -89,9 +89,11 @@ void ScaleEngine::calcCharSize() | ||||
| 
 | ||||
| void ScaleEngine::calcScaleFactor() | ||||
| { | ||||
| 	double median; | ||||
| 	double median, range, freqBase; | ||||
| 
 | ||||
| 	median = ((m_rangeMax - m_rangeMin) / 2.0) + m_rangeMin; | ||||
| 	range = (m_rangeMax - m_rangeMin); | ||||
| 	freqBase = (median == 0 ? range : median); | ||||
| 	m_scale = 1.0; | ||||
| 
 | ||||
| 	switch(m_physicalUnit) { | ||||
| @ -100,15 +102,15 @@ void ScaleEngine::calcScaleFactor() | ||||
| 			break; | ||||
| 
 | ||||
| 		case Unit::Frequency: | ||||
| 			if(median < 1000.0) { | ||||
| 			if(freqBase < 1000.0) { | ||||
| 				m_unitStr = QObject::tr("Hz"); | ||||
| 			} else if(median < 1000000.0) { | ||||
| 			} else if(freqBase < 1000000.0) { | ||||
| 				m_unitStr = QObject::tr("kHz"); | ||||
| 				m_scale = 1000.0; | ||||
| 			} else if(median < 1000000000.0) { | ||||
| 			} else if(freqBase < 1000000000.0) { | ||||
| 				m_unitStr = QObject::tr("MHz"); | ||||
| 				m_scale = 1000000.0; | ||||
| 			} else if(median < 1000000000000.0){ | ||||
| 			} else if(freqBase < 1000000000000.0){ | ||||
| 				m_unitStr = QObject::tr("GHz"); | ||||
| 				m_scale = 1000000000.0; | ||||
| 			} else { | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user