kopia lustrzana https://github.com/f4exb/sdrangel
				
				
				
			Added AM demodulator plugin
							rodzic
							
								
									c59e15ea3f
								
							
						
					
					
						commit
						8d15cabe70
					
				|  | @ -77,14 +77,12 @@ Done since the fork | ||||||
|   - Make the sidebands appear correctly on SSB channel overlay. Limit to +/- 6 kHz to fit channel spectrum analyzer window |   - Make the sidebands appear correctly on SSB channel overlay. Limit to +/- 6 kHz to fit channel spectrum analyzer window | ||||||
|   - SSB bandwidth can now be tuned in steps of 100 Hz |   - SSB bandwidth can now be tuned in steps of 100 Hz | ||||||
|   - NFM and SSB receiver in focus trigger the display of the central frequency line on the spectrum frequency scale thus facilitating its identification |   - NFM and SSB receiver in focus trigger the display of the central frequency line on the spectrum frequency scale thus facilitating its identification | ||||||
|    |   - Added AM demod so now you can listen to air traffic! | ||||||
|    |    | ||||||
| ===== | ===== | ||||||
| To Do | To Do | ||||||
| ===== | ===== | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|   - AM demod. What about the air band?! |  | ||||||
|   - Add the possibility to change the brightness and/or color of the grid. Sometimes it is barely visible yet useful |   - Add the possibility to change the brightness and/or color of the grid. Sometimes it is barely visible yet useful | ||||||
|   - Possibility to completely undock the receiver in a separate window. Useful when there are many receivers |   - Possibility to completely undock the receiver in a separate window. Useful when there are many receivers | ||||||
|   - Larger decimation capability for narrowband and very narrowband work (32, 64, ...) |   - Larger decimation capability for narrowband and very narrowband work (32, 64, ...) | ||||||
|  |  | ||||||
|  | @ -1,6 +1,7 @@ | ||||||
| project(demod) | project(demod) | ||||||
| 
 | 
 | ||||||
| add_subdirectory(lora) | add_subdirectory(lora) | ||||||
|  | add_subdirectory(am) | ||||||
| add_subdirectory(nfm) | add_subdirectory(nfm) | ||||||
| add_subdirectory(ssb) | add_subdirectory(ssb) | ||||||
| add_subdirectory(tcpsrc) | add_subdirectory(tcpsrc) | ||||||
|  |  | ||||||
|  | @ -0,0 +1,47 @@ | ||||||
|  | project(am) | ||||||
|  | 
 | ||||||
|  | set(am_SOURCES | ||||||
|  | 	amdemod.cpp | ||||||
|  | 	amdemodgui.cpp | ||||||
|  | 	amplugin.cpp | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | set(am_HEADERS | ||||||
|  | 	amdemod.h | ||||||
|  | 	amdemodgui.h | ||||||
|  | 	amplugin.h | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | set(am_FORMS | ||||||
|  | 	amdemodgui.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(nfm_HEADERS_MOC ${nfm_HEADERS}) | ||||||
|  | qt5_wrap_ui(am_FORMS_HEADERS ${am_FORMS}) | ||||||
|  | 
 | ||||||
|  | add_library(demodam SHARED | ||||||
|  | 	${am_SOURCES} | ||||||
|  | 	${am_HEADERS_MOC} | ||||||
|  | 	${am_FORMS_HEADERS} | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | target_link_libraries(demodam | ||||||
|  | 	${QT_LIBRARIES} | ||||||
|  | 	${OPENGL_LIBRARIES} | ||||||
|  | 	sdrbase | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | qt5_use_modules(demodam Core Widgets OpenGL Multimedia) | ||||||
|  | @ -0,0 +1,244 @@ | ||||||
|  | ///////////////////////////////////////////////////////////////////////////////////
 | ||||||
|  | // Copyright (C) 2015 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                  //
 | ||||||
|  | //                                                                               //
 | ||||||
|  | // 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 "amdemod.h" | ||||||
|  | 
 | ||||||
|  | #include <QTime> | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <complex.h> | ||||||
|  | #include "audio/audiooutput.h" | ||||||
|  | #include "dsp/dspcommands.h" | ||||||
|  | #include "dsp/pidcontroller.h" | ||||||
|  | 
 | ||||||
|  | MESSAGE_CLASS_DEFINITION(AMDemod::MsgConfigureAMDemod, Message) | ||||||
|  | 
 | ||||||
|  | AMDemod::AMDemod(AudioFifo* audioFifo, SampleSink* sampleSink) : | ||||||
|  | 	m_sampleSink(sampleSink), | ||||||
|  | 	m_audioFifo(audioFifo) | ||||||
|  | { | ||||||
|  | 	m_config.m_inputSampleRate = 96000; | ||||||
|  | 	m_config.m_inputFrequencyOffset = 0; | ||||||
|  | 	m_config.m_rfBandwidth = 12500; | ||||||
|  | 	m_config.m_afBandwidth = 3000; | ||||||
|  | 	m_config.m_squelch = -40.0; | ||||||
|  | 	m_config.m_volume = 2.0; | ||||||
|  | 	m_config.m_audioSampleRate = 48000; | ||||||
|  | 
 | ||||||
|  | 	apply(); | ||||||
|  | 
 | ||||||
|  | 	m_audioBuffer.resize(16384); | ||||||
|  | 	m_audioBufferFill = 0; | ||||||
|  | 
 | ||||||
|  | 	m_movingAverage.resize(16, 0); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | AMDemod::~AMDemod() | ||||||
|  | { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void AMDemod::configure(MessageQueue* messageQueue, Real rfBandwidth, Real afBandwidth, Real volume, Real squelch) | ||||||
|  | { | ||||||
|  | 	Message* cmd = MsgConfigureAMDemod::create(rfBandwidth, afBandwidth, volume, squelch); | ||||||
|  | 	cmd->submit(messageQueue, this); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | float arctan2(Real y, Real x) | ||||||
|  | { | ||||||
|  | 	Real coeff_1 = M_PI / 4; | ||||||
|  | 	Real coeff_2 = 3 * coeff_1; | ||||||
|  | 	Real abs_y = fabs(y) + 1e-10;      // kludge to prevent 0/0 condition
 | ||||||
|  | 	Real angle; | ||||||
|  | 	if( x>= 0) { | ||||||
|  | 		Real r = (x - abs_y) / (x + abs_y); | ||||||
|  | 		angle = coeff_1 - coeff_1 * r; | ||||||
|  | 	} else { | ||||||
|  | 		Real r = (x + abs_y) / (abs_y - x); | ||||||
|  | 		angle = coeff_2 - coeff_1 * r; | ||||||
|  | 	} | ||||||
|  | 	if(y < 0) | ||||||
|  | 		return(-angle); | ||||||
|  | 	else return(angle); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Real angleDist(Real a, Real b) | ||||||
|  | { | ||||||
|  | 	Real dist = b - a; | ||||||
|  | 
 | ||||||
|  | 	while(dist <= M_PI) | ||||||
|  | 		dist += 2 * M_PI; | ||||||
|  | 	while(dist >= M_PI) | ||||||
|  | 		dist -= 2 * M_PI; | ||||||
|  | 
 | ||||||
|  | 	return dist; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void AMDemod::feed(SampleVector::const_iterator begin, SampleVector::const_iterator end, bool firstOfBurst) | ||||||
|  | { | ||||||
|  | 	Complex ci; | ||||||
|  | 
 | ||||||
|  | 	if(m_audioFifo->size() <= 0) | ||||||
|  | 		return; | ||||||
|  | 
 | ||||||
|  | 	for(SampleVector::const_iterator it = begin; it != end; ++it) { | ||||||
|  | 		Complex c(it->real() / 32768.0, it->imag() / 32768.0); | ||||||
|  | 		c *= m_nco.nextIQ(); | ||||||
|  | 
 | ||||||
|  | 		{ | ||||||
|  | 			if(m_interpolator.interpolate(&m_interpolatorDistanceRemain, c, &ci)) { | ||||||
|  | 				m_sampleBuffer.push_back(Sample(ci.real() * 32767.0, ci.imag() * 32767.0)); | ||||||
|  | 
 | ||||||
|  | 				Real magsq = ci.real() * ci.real() + ci.imag() * ci.imag(); | ||||||
|  | 				m_movingAverage.feed(magsq); | ||||||
|  | 				if(m_movingAverage.average() >= m_squelchLevel) | ||||||
|  | 					m_squelchState = m_running.m_audioSampleRate/ 20; | ||||||
|  | 
 | ||||||
|  | 				qint16 sample; | ||||||
|  | 				if(m_squelchState > 0) { | ||||||
|  | 					m_squelchState--; | ||||||
|  | 					/*
 | ||||||
|  | 					Real argument = arg(ci); | ||||||
|  | 					Real demod = argument - m_lastArgument; | ||||||
|  | 					m_lastArgument = argument; | ||||||
|  | 					*/ | ||||||
|  | 
 | ||||||
|  | 					/* NFM demod:
 | ||||||
|  | 					Complex d = conj(m_lastSample) * ci; | ||||||
|  | 					m_lastSample = ci; | ||||||
|  | 					Real demod = atan2(d.imag(), d.real()); | ||||||
|  | 					//Real demod = arctan2(d.imag(), d.real());
 | ||||||
|  | 					*/ | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  | 					Real argument1 = arg(ci);//atan2(ci.imag(), ci.real());
 | ||||||
|  | 					Real argument2 = m_lastSample.real(); | ||||||
|  | 					Real demod = angleDist(argument2, argument1); | ||||||
|  | 					m_lastSample = Complex(argument1, 0); | ||||||
|  | */ | ||||||
|  | 					Real demod = sqrt(magsq); | ||||||
|  | 
 | ||||||
|  | 					//demod /= M_PI;
 | ||||||
|  | 
 | ||||||
|  | 					demod = m_lowpass.filter(demod); | ||||||
|  | 
 | ||||||
|  | 					if(demod < -1) | ||||||
|  | 						demod = -1; | ||||||
|  | 					else if(demod > 1) | ||||||
|  | 						demod = 1; | ||||||
|  | 
 | ||||||
|  | 					demod *= m_running.m_volume; | ||||||
|  | 					sample = demod * 32700; | ||||||
|  | 
 | ||||||
|  | 				} else { | ||||||
|  | 					sample = 0; | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				m_audioBuffer[m_audioBufferFill].l = sample; | ||||||
|  | 				m_audioBuffer[m_audioBufferFill].r = sample; | ||||||
|  | 				++m_audioBufferFill; | ||||||
|  | 				if(m_audioBufferFill >= m_audioBuffer.size()) { | ||||||
|  | 					uint res = m_audioFifo->write((const quint8*)&m_audioBuffer[0], m_audioBufferFill, 1); | ||||||
|  | 					if(res != m_audioBufferFill) | ||||||
|  | 						qDebug("lost %u audio samples", m_audioBufferFill - res); | ||||||
|  | 					m_audioBufferFill = 0; | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				m_interpolatorDistanceRemain += m_interpolatorDistance; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if(m_audioBufferFill > 0) { | ||||||
|  | 		uint res = m_audioFifo->write((const quint8*)&m_audioBuffer[0], m_audioBufferFill, 1); | ||||||
|  | 		if(res != m_audioBufferFill) | ||||||
|  | 			qDebug("lost %u samples", m_audioBufferFill - res); | ||||||
|  | 		m_audioBufferFill = 0; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if(m_sampleSink != NULL) | ||||||
|  | 		m_sampleSink->feed(m_sampleBuffer.begin(), m_sampleBuffer.end(), false); | ||||||
|  | 	m_sampleBuffer.clear(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void AMDemod::start() | ||||||
|  | { | ||||||
|  | 	m_squelchState = 0; | ||||||
|  | 	m_audioFifo->clear(); | ||||||
|  | 	m_interpolatorRegulation = 0.9999; | ||||||
|  | 	m_interpolatorDistance = 1.0; | ||||||
|  | 	m_interpolatorDistanceRemain = 0.0; | ||||||
|  | 	m_lastSample = 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void AMDemod::stop() | ||||||
|  | { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool AMDemod::handleMessage(Message* cmd) | ||||||
|  | { | ||||||
|  | 	if(DSPSignalNotification::match(cmd)) { | ||||||
|  | 		DSPSignalNotification* signal = (DSPSignalNotification*)cmd; | ||||||
|  | 
 | ||||||
|  | 		m_config.m_inputSampleRate = signal->getSampleRate(); | ||||||
|  | 		m_config.m_inputFrequencyOffset = signal->getFrequencyOffset(); | ||||||
|  | 		apply(); | ||||||
|  | 		cmd->completed(); | ||||||
|  | 		return true; | ||||||
|  | 	} else if(MsgConfigureAMDemod::match(cmd)) { | ||||||
|  | 		MsgConfigureAMDemod* cfg = (MsgConfigureAMDemod*)cmd; | ||||||
|  | 		m_config.m_rfBandwidth = cfg->getRFBandwidth(); | ||||||
|  | 		m_config.m_afBandwidth = cfg->getAFBandwidth(); | ||||||
|  | 		m_config.m_volume = cfg->getVolume(); | ||||||
|  | 		m_config.m_squelch = cfg->getSquelch(); | ||||||
|  | 		apply(); | ||||||
|  | 		return true; | ||||||
|  | 	} else { | ||||||
|  | 		if(m_sampleSink != NULL) | ||||||
|  | 		   return m_sampleSink->handleMessage(cmd); | ||||||
|  | 		else return false; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void AMDemod::apply() | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  | 	if((m_config.m_inputFrequencyOffset != m_running.m_inputFrequencyOffset) || | ||||||
|  | 		(m_config.m_inputSampleRate != m_running.m_inputSampleRate)) { | ||||||
|  | 		m_nco.setFreq(-m_config.m_inputFrequencyOffset, m_config.m_inputSampleRate); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if((m_config.m_inputSampleRate != m_running.m_inputSampleRate) || | ||||||
|  | 		(m_config.m_rfBandwidth != m_running.m_rfBandwidth)) { | ||||||
|  | 		m_interpolator.create(16, m_config.m_inputSampleRate, m_config.m_rfBandwidth / 2.2); | ||||||
|  | 		m_interpolatorDistanceRemain = 0; | ||||||
|  | 		m_interpolatorDistance =  m_config.m_inputSampleRate / m_config.m_audioSampleRate; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if((m_config.m_afBandwidth != m_running.m_afBandwidth) || | ||||||
|  | 		(m_config.m_audioSampleRate != m_running.m_audioSampleRate)) { | ||||||
|  | 		m_lowpass.create(21, m_config.m_audioSampleRate, m_config.m_afBandwidth); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if(m_config.m_squelch != m_running.m_squelch) { | ||||||
|  | 		m_squelchLevel = pow(10.0, m_config.m_squelch / 20.0); | ||||||
|  | 		m_squelchLevel *= m_squelchLevel; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	m_running.m_inputSampleRate = m_config.m_inputSampleRate; | ||||||
|  | 	m_running.m_inputFrequencyOffset = m_config.m_inputFrequencyOffset; | ||||||
|  | 	m_running.m_rfBandwidth = m_config.m_rfBandwidth; | ||||||
|  | 	m_running.m_squelch = m_config.m_squelch; | ||||||
|  | 	m_running.m_volume = m_config.m_volume; | ||||||
|  | 	m_running.m_audioSampleRate = m_config.m_audioSampleRate; | ||||||
|  | } | ||||||
|  | @ -0,0 +1,131 @@ | ||||||
|  | ///////////////////////////////////////////////////////////////////////////////////
 | ||||||
|  | // Copyright (C) 2015 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                  //
 | ||||||
|  | //                                                                               //
 | ||||||
|  | // 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_AMDEMOD_H | ||||||
|  | #define INCLUDE_AMDEMOD_H | ||||||
|  | 
 | ||||||
|  | #include <vector> | ||||||
|  | #include "dsp/samplesink.h" | ||||||
|  | #include "dsp/nco.h" | ||||||
|  | #include "dsp/interpolator.h" | ||||||
|  | #include "dsp/lowpass.h" | ||||||
|  | #include "dsp/movingaverage.h" | ||||||
|  | #include "audio/audiofifo.h" | ||||||
|  | #include "util/message.h" | ||||||
|  | 
 | ||||||
|  | class AudioFifo; | ||||||
|  | 
 | ||||||
|  | class AMDemod : public SampleSink { | ||||||
|  | public: | ||||||
|  | 	AMDemod(AudioFifo* audioFifo, SampleSink* sampleSink); | ||||||
|  | 	~AMDemod(); | ||||||
|  | 
 | ||||||
|  | 	void configure(MessageQueue* messageQueue, Real rfBandwidth, Real afBandwidth, Real volume, Real squelch); | ||||||
|  | 
 | ||||||
|  | 	void feed(SampleVector::const_iterator begin, SampleVector::const_iterator end, bool po); | ||||||
|  | 	void start(); | ||||||
|  | 	void stop(); | ||||||
|  | 	bool handleMessage(Message* cmd); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  | 	class MsgConfigureAMDemod : public Message { | ||||||
|  | 		MESSAGE_CLASS_DECLARATION | ||||||
|  | 
 | ||||||
|  | 	public: | ||||||
|  | 		Real getRFBandwidth() const { return m_rfBandwidth; } | ||||||
|  | 		Real getAFBandwidth() const { return m_afBandwidth; } | ||||||
|  | 		Real getVolume() const { return m_volume; } | ||||||
|  | 		Real getSquelch() const { return m_squelch; } | ||||||
|  | 
 | ||||||
|  | 		static MsgConfigureAMDemod* create(Real rfBandwidth, Real afBandwidth, Real volume, Real squelch) | ||||||
|  | 		{ | ||||||
|  | 			return new MsgConfigureAMDemod(rfBandwidth, afBandwidth, volume, squelch); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 	private: | ||||||
|  | 		Real m_rfBandwidth; | ||||||
|  | 		Real m_afBandwidth; | ||||||
|  | 		Real m_volume; | ||||||
|  | 		Real m_squelch; | ||||||
|  | 
 | ||||||
|  | 		MsgConfigureAMDemod(Real rfBandwidth, Real afBandwidth, Real volume, Real squelch) : | ||||||
|  | 			Message(), | ||||||
|  | 			m_rfBandwidth(rfBandwidth), | ||||||
|  | 			m_afBandwidth(afBandwidth), | ||||||
|  | 			m_volume(volume), | ||||||
|  | 			m_squelch(squelch) | ||||||
|  | 		{ } | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	struct AudioSample { | ||||||
|  | 		qint16 l; | ||||||
|  | 		qint16 r; | ||||||
|  | 	}; | ||||||
|  | 	typedef std::vector<AudioSample> AudioVector; | ||||||
|  | 
 | ||||||
|  | 	enum RateState { | ||||||
|  | 		RSInitialFill, | ||||||
|  | 		RSRunning | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	struct Config { | ||||||
|  | 		int m_inputSampleRate; | ||||||
|  | 		qint64 m_inputFrequencyOffset; | ||||||
|  | 		Real m_rfBandwidth; | ||||||
|  | 		Real m_afBandwidth; | ||||||
|  | 		Real m_squelch; | ||||||
|  | 		Real m_volume; | ||||||
|  | 		quint32 m_audioSampleRate; | ||||||
|  | 
 | ||||||
|  | 		Config() : | ||||||
|  | 			m_inputSampleRate(-1), | ||||||
|  | 			m_inputFrequencyOffset(0), | ||||||
|  | 			m_rfBandwidth(-1), | ||||||
|  | 			m_afBandwidth(-1), | ||||||
|  | 			m_squelch(0), | ||||||
|  | 			m_volume(0), | ||||||
|  | 			m_audioSampleRate(0) | ||||||
|  | 		{ } | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	Config m_config; | ||||||
|  | 	Config m_running; | ||||||
|  | 
 | ||||||
|  | 	NCO m_nco; | ||||||
|  | 	Real m_interpolatorRegulation; | ||||||
|  | 	Interpolator m_interpolator; | ||||||
|  | 	Real m_interpolatorDistance; | ||||||
|  | 	Real m_interpolatorDistanceRemain; | ||||||
|  | 	Lowpass<Real> m_lowpass; | ||||||
|  | 
 | ||||||
|  | 	Real m_squelchLevel; | ||||||
|  | 	int m_squelchState; | ||||||
|  | 
 | ||||||
|  | 	Real m_lastArgument; | ||||||
|  | 	Complex m_lastSample; | ||||||
|  | 	MovingAverage m_movingAverage; | ||||||
|  | 
 | ||||||
|  | 	AudioVector m_audioBuffer; | ||||||
|  | 	uint m_audioBufferFill; | ||||||
|  | 
 | ||||||
|  | 	SampleSink* m_sampleSink; | ||||||
|  | 	AudioFifo* m_audioFifo; | ||||||
|  | 	SampleVector m_sampleBuffer; | ||||||
|  | 
 | ||||||
|  | 	void apply(); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | #endif // INCLUDE_AMDEMOD_H
 | ||||||
|  | @ -0,0 +1,236 @@ | ||||||
|  | #include "amdemodgui.h" | ||||||
|  | 
 | ||||||
|  | #include <QDockWidget> | ||||||
|  | #include <QMainWindow> | ||||||
|  | #include "ui_amdemodgui.h" | ||||||
|  | #include "dsp/threadedsamplesink.h" | ||||||
|  | #include "dsp/channelizer.h" | ||||||
|  | #include "dsp/nullsink.h" | ||||||
|  | #include "gui/glspectrum.h" | ||||||
|  | #include "plugin/pluginapi.h" | ||||||
|  | #include "util/simpleserializer.h" | ||||||
|  | #include "gui/basicchannelsettingswidget.h" | ||||||
|  | 
 | ||||||
|  | #include <iostream> | ||||||
|  | #include "amdemod.h" | ||||||
|  | 
 | ||||||
|  | const int AMDemodGUI::m_rfBW[] = { | ||||||
|  | 	5000, 6250, 8330, 10000, 12500, 15000, 20000, 25000, 40000 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | AMDemodGUI* AMDemodGUI::create(PluginAPI* pluginAPI) | ||||||
|  | { | ||||||
|  | 	AMDemodGUI* gui = new AMDemodGUI(pluginAPI); | ||||||
|  | 	return gui; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void AMDemodGUI::destroy() | ||||||
|  | { | ||||||
|  | 	delete this; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void AMDemodGUI::setName(const QString& name) | ||||||
|  | { | ||||||
|  | 	setObjectName(name); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void AMDemodGUI::resetToDefaults() | ||||||
|  | { | ||||||
|  | 	ui->rfBW->setValue(4); | ||||||
|  | 	ui->afBW->setValue(3); | ||||||
|  | 	ui->volume->setValue(20); | ||||||
|  | 	ui->squelch->setValue(-40); | ||||||
|  | 	ui->deltaFrequency->setValue(0); | ||||||
|  | 	applySettings(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | QByteArray AMDemodGUI::serialize() const | ||||||
|  | { | ||||||
|  | 	SimpleSerializer s(1); | ||||||
|  | 	s.writeS32(1, m_channelMarker->getCenterFrequency()); | ||||||
|  | 	s.writeS32(2, ui->rfBW->value()); | ||||||
|  | 	s.writeS32(3, ui->afBW->value()); | ||||||
|  | 	s.writeS32(4, ui->volume->value()); | ||||||
|  | 	s.writeS32(5, ui->squelch->value()); | ||||||
|  | 	//s.writeBlob(6, ui->spectrumGUI->serialize());
 | ||||||
|  | 	s.writeU32(7, m_channelMarker->getColor().rgb()); | ||||||
|  | 	return s.final(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool AMDemodGUI::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, 4); | ||||||
|  | 		ui->rfBW->setValue(tmp); | ||||||
|  | 		d.readS32(3, &tmp, 3); | ||||||
|  | 		ui->afBW->setValue(tmp); | ||||||
|  | 		d.readS32(4, &tmp, 20); | ||||||
|  | 		ui->volume->setValue(tmp); | ||||||
|  | 		d.readS32(5, &tmp, -40); | ||||||
|  | 		ui->squelch->setValue(tmp); | ||||||
|  | 		//d.readBlob(6, &bytetmp);
 | ||||||
|  | 		//ui->spectrumGUI->deserialize(bytetmp);
 | ||||||
|  | 		if(d.readU32(7, &u32tmp)) | ||||||
|  | 			m_channelMarker->setColor(u32tmp); | ||||||
|  | 		applySettings(); | ||||||
|  | 		return true; | ||||||
|  | 	} else { | ||||||
|  | 		resetToDefaults(); | ||||||
|  | 		return false; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool AMDemodGUI::handleMessage(Message* message) | ||||||
|  | { | ||||||
|  | 	return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void AMDemodGUI::viewChanged() | ||||||
|  | { | ||||||
|  | 	applySettings(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void AMDemodGUI::on_deltaMinus_clicked(bool minus) | ||||||
|  | { | ||||||
|  | 	int deltaFrequency = m_channelMarker->getCenterFrequency(); | ||||||
|  | 	bool minusDelta = (deltaFrequency < 0); | ||||||
|  | 
 | ||||||
|  | 	if (minus ^ minusDelta) // sign change
 | ||||||
|  | 	{ | ||||||
|  | 		m_channelMarker->setCenterFrequency(-deltaFrequency); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void AMDemodGUI::on_deltaFrequency_changed(quint64 value) | ||||||
|  | { | ||||||
|  | 	if (ui->deltaMinus->isChecked()) { | ||||||
|  | 		m_channelMarker->setCenterFrequency(-value); | ||||||
|  | 	} else { | ||||||
|  | 		m_channelMarker->setCenterFrequency(value); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void AMDemodGUI::on_rfBW_valueChanged(int value) | ||||||
|  | { | ||||||
|  | 	ui->rfBWText->setText(QString("%1 kHz").arg(m_rfBW[value] / 1000.0)); | ||||||
|  | 	m_channelMarker->setBandwidth(m_rfBW[value]); | ||||||
|  | 	applySettings(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void AMDemodGUI::on_afBW_valueChanged(int value) | ||||||
|  | { | ||||||
|  | 	ui->afBWText->setText(QString("%1 kHz").arg(value)); | ||||||
|  | 	applySettings(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void AMDemodGUI::on_volume_valueChanged(int value) | ||||||
|  | { | ||||||
|  | 	ui->volumeText->setText(QString("%1").arg(value / 10.0, 0, 'f', 1)); | ||||||
|  | 	applySettings(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void AMDemodGUI::on_squelch_valueChanged(int value) | ||||||
|  | { | ||||||
|  | 	ui->squelchText->setText(QString("%1 dB").arg(value)); | ||||||
|  | 	applySettings(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | void AMDemodGUI::onWidgetRolled(QWidget* widget, bool rollDown) | ||||||
|  | { | ||||||
|  | 	/*
 | ||||||
|  | 	if((widget == ui->spectrumContainer) && (m_nfmDemod != NULL)) | ||||||
|  | 		m_nfmDemod->setSpectrum(m_threadedSampleSink->getMessageQueue(), rollDown); | ||||||
|  | 	*/ | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void AMDemodGUI::onMenuDoubleClicked() | ||||||
|  | { | ||||||
|  | 	if(!m_basicSettingsShown) { | ||||||
|  | 		m_basicSettingsShown = true; | ||||||
|  | 		BasicChannelSettingsWidget* bcsw = new BasicChannelSettingsWidget(m_channelMarker, this); | ||||||
|  | 		bcsw->show(); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | AMDemodGUI::AMDemodGUI(PluginAPI* pluginAPI, QWidget* parent) : | ||||||
|  | 	RollupWidget(parent), | ||||||
|  | 	ui(new Ui::AMDemodGUI), | ||||||
|  | 	m_pluginAPI(pluginAPI), | ||||||
|  | 	m_basicSettingsShown(false) | ||||||
|  | { | ||||||
|  | 	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); | ||||||
|  | 	m_nullSink = new NullSink(); | ||||||
|  | 	m_amDemod = new AMDemod(m_audioFifo, m_nullSink); | ||||||
|  | 	m_channelizer = new Channelizer(m_amDemod); | ||||||
|  | 	m_threadedSampleSink = new ThreadedSampleSink(m_channelizer); | ||||||
|  | 	m_pluginAPI->addAudioSource(m_audioFifo); | ||||||
|  | 	m_pluginAPI->addSampleSink(m_threadedSampleSink); | ||||||
|  | 
 | ||||||
|  | 	m_channelMarker = new ChannelMarker(this); | ||||||
|  | 	m_channelMarker->setColor(Qt::yellow); | ||||||
|  | 	m_channelMarker->setBandwidth(12500); | ||||||
|  | 	m_channelMarker->setCenterFrequency(0); | ||||||
|  | 	m_channelMarker->setVisible(true); | ||||||
|  | 	connect(m_channelMarker, SIGNAL(changed()), this, SLOT(viewChanged())); | ||||||
|  | 	m_pluginAPI->addChannelMarker(m_channelMarker); | ||||||
|  | 
 | ||||||
|  | 	applySettings(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | AMDemodGUI::~AMDemodGUI() | ||||||
|  | { | ||||||
|  | 	m_pluginAPI->removeChannelInstance(this); | ||||||
|  | 	m_pluginAPI->removeAudioSource(m_audioFifo); | ||||||
|  | 	m_pluginAPI->removeSampleSink(m_threadedSampleSink); | ||||||
|  | 	delete m_threadedSampleSink; | ||||||
|  | 	delete m_channelizer; | ||||||
|  | 	delete m_amDemod; | ||||||
|  | 	delete m_nullSink; | ||||||
|  | 	delete m_audioFifo; | ||||||
|  | 	delete m_channelMarker; | ||||||
|  | 	delete ui; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void AMDemodGUI::applySettings() | ||||||
|  | { | ||||||
|  | 	setTitleColor(m_channelMarker->getColor()); | ||||||
|  | 	m_channelizer->configure(m_threadedSampleSink->getMessageQueue(), | ||||||
|  | 		48000, | ||||||
|  | 		m_channelMarker->getCenterFrequency()); | ||||||
|  | 	ui->deltaFrequency->setValue(abs(m_channelMarker->getCenterFrequency())); | ||||||
|  | 	ui->deltaMinus->setChecked(m_channelMarker->getCenterFrequency() < 0); | ||||||
|  | 	m_amDemod->configure(m_threadedSampleSink->getMessageQueue(), | ||||||
|  | 		m_rfBW[ui->rfBW->value()], | ||||||
|  | 		ui->afBW->value() * 1000.0, | ||||||
|  | 		ui->volume->value() / 10.0, | ||||||
|  | 		ui->squelch->value()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void AMDemodGUI::leaveEvent(QEvent*) | ||||||
|  | { | ||||||
|  | 	m_channelMarker->setHighlighted(false); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void AMDemodGUI::enterEvent(QEvent*) | ||||||
|  | { | ||||||
|  | 	m_channelMarker->setHighlighted(true); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @ -0,0 +1,69 @@ | ||||||
|  | #ifndef INCLUDE_AMDEMODGUI_H | ||||||
|  | #define INCLUDE_AMDEMODGUI_H | ||||||
|  | 
 | ||||||
|  | #include "gui/rollupwidget.h" | ||||||
|  | #include "plugin/plugingui.h" | ||||||
|  | 
 | ||||||
|  | class PluginAPI; | ||||||
|  | class ChannelMarker; | ||||||
|  | 
 | ||||||
|  | class AudioFifo; | ||||||
|  | class ThreadedSampleSink; | ||||||
|  | class Channelizer; | ||||||
|  | class AMDemod; | ||||||
|  | class NullSink; | ||||||
|  | 
 | ||||||
|  | namespace Ui { | ||||||
|  | 	class AMDemodGUI; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class AMDemodGUI : public RollupWidget, public PluginGUI { | ||||||
|  | 	Q_OBJECT | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	static AMDemodGUI* create(PluginAPI* pluginAPI); | ||||||
|  | 	void destroy(); | ||||||
|  | 
 | ||||||
|  | 	void setName(const QString& name); | ||||||
|  | 
 | ||||||
|  | 	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_rfBW_valueChanged(int value); | ||||||
|  | 	void on_afBW_valueChanged(int value); | ||||||
|  | 	void on_volume_valueChanged(int value); | ||||||
|  | 	void on_squelch_valueChanged(int value); | ||||||
|  | 	void onWidgetRolled(QWidget* widget, bool rollDown); | ||||||
|  | 	void onMenuDoubleClicked(); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  | 	Ui::AMDemodGUI* ui; | ||||||
|  | 	PluginAPI* m_pluginAPI; | ||||||
|  | 	ChannelMarker* m_channelMarker; | ||||||
|  | 	bool m_basicSettingsShown; | ||||||
|  | 
 | ||||||
|  | 	AudioFifo* m_audioFifo; | ||||||
|  | 	ThreadedSampleSink* m_threadedSampleSink; | ||||||
|  | 	Channelizer* m_channelizer; | ||||||
|  | 	AMDemod* m_amDemod; | ||||||
|  | 	NullSink *m_nullSink; | ||||||
|  | 
 | ||||||
|  | 	static const int m_rfBW[]; | ||||||
|  | 
 | ||||||
|  | 	explicit AMDemodGUI(PluginAPI* pluginAPI, QWidget* parent = NULL); | ||||||
|  | 	~AMDemodGUI(); | ||||||
|  | 
 | ||||||
|  | 	void applySettings(); | ||||||
|  | 
 | ||||||
|  | 	void leaveEvent(QEvent*); | ||||||
|  | 	void enterEvent(QEvent*); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | #endif // INCLUDE_AMDEMODGUI_H
 | ||||||
|  | @ -0,0 +1,270 @@ | ||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <ui version="4.0"> | ||||||
|  |  <class>AMDemodGUI</class> | ||||||
|  |  <widget class="RollupWidget" name="AMDemodGUI"> | ||||||
|  |   <property name="geometry"> | ||||||
|  |    <rect> | ||||||
|  |     <x>0</x> | ||||||
|  |     <y>0</y> | ||||||
|  |     <width>302</width> | ||||||
|  |     <height>138</height> | ||||||
|  |    </rect> | ||||||
|  |   </property> | ||||||
|  |   <property name="focusPolicy"> | ||||||
|  |    <enum>Qt::StrongFocus</enum> | ||||||
|  |   </property> | ||||||
|  |   <property name="windowTitle"> | ||||||
|  |    <string>AM Demodulator</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="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="spacing"> | ||||||
|  |      <number>3</number> | ||||||
|  |     </property> | ||||||
|  |     <item row="3" column="1"> | ||||||
|  |      <widget class="QSlider" name="volume"> | ||||||
|  |       <property name="maximum"> | ||||||
|  |        <number>100</number> | ||||||
|  |       </property> | ||||||
|  |       <property name="value"> | ||||||
|  |        <number>20</number> | ||||||
|  |       </property> | ||||||
|  |       <property name="orientation"> | ||||||
|  |        <enum>Qt::Horizontal</enum> | ||||||
|  |       </property> | ||||||
|  |      </widget> | ||||||
|  |     </item> | ||||||
|  |     <item row="1" column="1"> | ||||||
|  |      <widget class="QSlider" name="rfBW"> | ||||||
|  |       <property name="maximum"> | ||||||
|  |        <number>8</number> | ||||||
|  |       </property> | ||||||
|  |       <property name="pageStep"> | ||||||
|  |        <number>1</number> | ||||||
|  |       </property> | ||||||
|  |       <property name="value"> | ||||||
|  |        <number>4</number> | ||||||
|  |       </property> | ||||||
|  |       <property name="orientation"> | ||||||
|  |        <enum>Qt::Horizontal</enum> | ||||||
|  |       </property> | ||||||
|  |      </widget> | ||||||
|  |     </item> | ||||||
|  |     <item row="0" column="0"> | ||||||
|  |      <widget class="QRadioButton" name="deltaMinus"> | ||||||
|  |       <property name="layoutDirection"> | ||||||
|  |        <enum>Qt::RightToLeft</enum> | ||||||
|  |       </property> | ||||||
|  |       <property name="text"> | ||||||
|  |        <string>Minus</string> | ||||||
|  |       </property> | ||||||
|  |      </widget> | ||||||
|  |     </item> | ||||||
|  |     <item row="4" column="0"> | ||||||
|  |      <widget class="QLabel" name="label_4"> | ||||||
|  |       <property name="text"> | ||||||
|  |        <string>Squelch</string> | ||||||
|  |       </property> | ||||||
|  |      </widget> | ||||||
|  |     </item> | ||||||
|  |     <item row="2" column="0"> | ||||||
|  |      <widget class="QLabel" name="label_2"> | ||||||
|  |       <property name="text"> | ||||||
|  |        <string>AF Bandwidth</string> | ||||||
|  |       </property> | ||||||
|  |      </widget> | ||||||
|  |     </item> | ||||||
|  |     <item row="4" column="1"> | ||||||
|  |      <widget class="QSlider" name="squelch"> | ||||||
|  |       <property name="minimum"> | ||||||
|  |        <number>-100</number> | ||||||
|  |       </property> | ||||||
|  |       <property name="maximum"> | ||||||
|  |        <number>0</number> | ||||||
|  |       </property> | ||||||
|  |       <property name="value"> | ||||||
|  |        <number>-40</number> | ||||||
|  |       </property> | ||||||
|  |       <property name="orientation"> | ||||||
|  |        <enum>Qt::Horizontal</enum> | ||||||
|  |       </property> | ||||||
|  |      </widget> | ||||||
|  |     </item> | ||||||
|  |     <item row="1" column="2"> | ||||||
|  |      <widget class="QLabel" name="rfBWText"> | ||||||
|  |       <property name="minimumSize"> | ||||||
|  |        <size> | ||||||
|  |         <width>50</width> | ||||||
|  |         <height>0</height> | ||||||
|  |        </size> | ||||||
|  |       </property> | ||||||
|  |       <property name="text"> | ||||||
|  |        <string>12.5kHz</string> | ||||||
|  |       </property> | ||||||
|  |       <property name="alignment"> | ||||||
|  |        <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> | ||||||
|  |       </property> | ||||||
|  |      </widget> | ||||||
|  |     </item> | ||||||
|  |     <item row="1" column="0"> | ||||||
|  |      <widget class="QLabel" name="label"> | ||||||
|  |       <property name="text"> | ||||||
|  |        <string>RF Bandwidth</string> | ||||||
|  |       </property> | ||||||
|  |      </widget> | ||||||
|  |     </item> | ||||||
|  |     <item row="2" column="2"> | ||||||
|  |      <widget class="QLabel" name="afBWText"> | ||||||
|  |       <property name="minimumSize"> | ||||||
|  |        <size> | ||||||
|  |         <width>50</width> | ||||||
|  |         <height>0</height> | ||||||
|  |        </size> | ||||||
|  |       </property> | ||||||
|  |       <property name="text"> | ||||||
|  |        <string>3 kHz</string> | ||||||
|  |       </property> | ||||||
|  |       <property name="alignment"> | ||||||
|  |        <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> | ||||||
|  |       </property> | ||||||
|  |      </widget> | ||||||
|  |     </item> | ||||||
|  |     <item row="2" column="1"> | ||||||
|  |      <widget class="QSlider" name="afBW"> | ||||||
|  |       <property name="minimum"> | ||||||
|  |        <number>1</number> | ||||||
|  |       </property> | ||||||
|  |       <property name="maximum"> | ||||||
|  |        <number>20</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="4" column="2"> | ||||||
|  |      <widget class="QLabel" name="squelchText"> | ||||||
|  |       <property name="minimumSize"> | ||||||
|  |        <size> | ||||||
|  |         <width>50</width> | ||||||
|  |         <height>0</height> | ||||||
|  |        </size> | ||||||
|  |       </property> | ||||||
|  |       <property name="text"> | ||||||
|  |        <string>-40dB</string> | ||||||
|  |       </property> | ||||||
|  |       <property name="alignment"> | ||||||
|  |        <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> | ||||||
|  |       </property> | ||||||
|  |      </widget> | ||||||
|  |     </item> | ||||||
|  |     <item row="3" column="0"> | ||||||
|  |      <widget class="QLabel" name="label_3"> | ||||||
|  |       <property name="text"> | ||||||
|  |        <string>Volume</string> | ||||||
|  |       </property> | ||||||
|  |      </widget> | ||||||
|  |     </item> | ||||||
|  |     <item row="3" column="2"> | ||||||
|  |      <widget class="QLabel" name="volumeText"> | ||||||
|  |       <property name="minimumSize"> | ||||||
|  |        <size> | ||||||
|  |         <width>50</width> | ||||||
|  |         <height>0</height> | ||||||
|  |        </size> | ||||||
|  |       </property> | ||||||
|  |       <property name="text"> | ||||||
|  |        <string>2.0</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>12</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="0" column="2"> | ||||||
|  |      <widget class="QLabel" name="deltaUnits"> | ||||||
|  |       <property name="text"> | ||||||
|  |        <string>Hz</string> | ||||||
|  |       </property> | ||||||
|  |      </widget> | ||||||
|  |     </item> | ||||||
|  |    </layout> | ||||||
|  |   </widget> | ||||||
|  |  </widget> | ||||||
|  |  <customwidgets> | ||||||
|  |   <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> | ||||||
|  | @ -0,0 +1,54 @@ | ||||||
|  | #include "amplugin.h" | ||||||
|  | 
 | ||||||
|  | #include <QtPlugin> | ||||||
|  | #include <QAction> | ||||||
|  | #include "plugin/pluginapi.h" | ||||||
|  | #include "amdemodgui.h" | ||||||
|  | 
 | ||||||
|  | const PluginDescriptor AMPlugin::m_pluginDescriptor = { | ||||||
|  | 	QString("AM Demodulator"), | ||||||
|  | 	QString("---"), | ||||||
|  | 	QString("(c) Edouard Griffiths, F4EXB"), | ||||||
|  | 	QString("https://github.com/f4exb/rtl-sdrangelove/tree/f4exb"), | ||||||
|  | 	true, | ||||||
|  | 	QString("https://github.com/f4exb/rtl-sdrangelove/tree/f4exb") | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | AMPlugin::AMPlugin(QObject* parent) : | ||||||
|  | 	QObject(parent) | ||||||
|  | { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const PluginDescriptor& AMPlugin::getPluginDescriptor() const | ||||||
|  | { | ||||||
|  | 	return m_pluginDescriptor; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void AMPlugin::initPlugin(PluginAPI* pluginAPI) | ||||||
|  | { | ||||||
|  | 	m_pluginAPI = pluginAPI; | ||||||
|  | 
 | ||||||
|  | 	// register AM demodulator
 | ||||||
|  | 	QAction* action = new QAction(tr("&AM Demodulator"), this); | ||||||
|  | 	connect(action, SIGNAL(triggered()), this, SLOT(createInstanceAM())); | ||||||
|  | 	m_pluginAPI->registerChannel("de.maintech.sdrangelove.channel.am", this, action); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | PluginGUI* AMPlugin::createChannel(const QString& channelName) | ||||||
|  | { | ||||||
|  | 	if(channelName == "de.maintech.sdrangelove.channel.am") { | ||||||
|  | 		AMDemodGUI* gui = AMDemodGUI::create(m_pluginAPI); | ||||||
|  | 		m_pluginAPI->registerChannelInstance("de.maintech.sdrangelove.channel.am", gui); | ||||||
|  | 		m_pluginAPI->addChannelRollup(gui); | ||||||
|  | 		return gui; | ||||||
|  | 	} else { | ||||||
|  | 		return NULL; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void AMPlugin::createInstanceAM() | ||||||
|  | { | ||||||
|  | 	AMDemodGUI* gui = AMDemodGUI::create(m_pluginAPI); | ||||||
|  | 	m_pluginAPI->registerChannelInstance("de.maintech.sdrangelove.channel.am", gui); | ||||||
|  | 	m_pluginAPI->addChannelRollup(gui); | ||||||
|  | } | ||||||
|  | @ -0,0 +1,29 @@ | ||||||
|  | #ifndef INCLUDE_AMPLUGIN_H | ||||||
|  | #define INCLUDE_AMPLUGIN_H | ||||||
|  | 
 | ||||||
|  | #include <QObject> | ||||||
|  | #include "plugin/plugininterface.h" | ||||||
|  | 
 | ||||||
|  | class AMPlugin : public QObject, PluginInterface { | ||||||
|  | 	Q_OBJECT | ||||||
|  | 	Q_INTERFACES(PluginInterface) | ||||||
|  | 	Q_PLUGIN_METADATA(IID "de.maintech.sdrangelove.channel.am") | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	explicit AMPlugin(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 createInstanceAM(); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | #endif // INCLUDE_AMPLUGIN_H
 | ||||||
		Ładowanie…
	
		Reference in New Issue
	
	 f4exb
						f4exb