kopia lustrzana https://github.com/f4exb/sdrangel
				
				
				
			Created demod constructor method in all Rx channel plugins
							rodzic
							
								
									cef9d5d7bc
								
							
						
					
					
						commit
						4ad038ed9d
					
				|  | @ -1,236 +1,237 @@ | |||
| ///////////////////////////////////////////////////////////////////////////////////
 | ||||
| // 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 "chanalyzer.h" | ||||
| 
 | ||||
| #include <QTime> | ||||
| #include <QDebug> | ||||
| #include <stdio.h> | ||||
| 
 | ||||
| #include <dsp/downchannelizer.h> | ||||
| #include "dsp/threadedbasebandsamplesink.h" | ||||
| #include "device/devicesourceapi.h" | ||||
| #include "audio/audiooutput.h" | ||||
| 
 | ||||
| 
 | ||||
| MESSAGE_CLASS_DEFINITION(ChannelAnalyzer::MsgConfigureChannelAnalyzer, Message) | ||||
| MESSAGE_CLASS_DEFINITION(ChannelAnalyzer::MsgConfigureChannelizer, Message) | ||||
| MESSAGE_CLASS_DEFINITION(ChannelAnalyzer::MsgReportChannelSampleRateChanged, Message) | ||||
| 
 | ||||
| ChannelAnalyzer::ChannelAnalyzer(DeviceSourceAPI *deviceAPI) : | ||||
|     m_deviceAPI(deviceAPI), | ||||
| 	m_sampleSink(0), | ||||
| 	m_settingsMutex(QMutex::Recursive) | ||||
| { | ||||
| 	m_Bandwidth = 5000; | ||||
| 	m_LowCutoff = 300; | ||||
| 	m_spanLog2 = 3; | ||||
| 	m_sampleRate = 96000; | ||||
| 	m_frequency = 0; | ||||
| 	m_nco.setFreq(m_frequency, m_sampleRate); | ||||
| 	m_undersampleCount = 0; | ||||
| 	m_sum = 0; | ||||
| 	m_usb = true; | ||||
| 	m_ssb = true; | ||||
| 	m_magsq = 0; | ||||
| 	SSBFilter = new fftfilt(m_LowCutoff / m_sampleRate, m_Bandwidth / m_sampleRate, ssbFftLen); | ||||
| 	DSBFilter = new fftfilt(m_Bandwidth / m_sampleRate, 2*ssbFftLen); | ||||
| 
 | ||||
|     m_channelizer = new DownChannelizer(this); | ||||
|     m_threadedChannelizer = new ThreadedBasebandSampleSink(m_channelizer, this); | ||||
|     connect(m_channelizer, SIGNAL(inputSampleRateChanged()), this, SLOT(channelSampleRateChanged())); | ||||
|     m_deviceAPI->addThreadedSink(m_threadedChannelizer); | ||||
| } | ||||
| 
 | ||||
| ChannelAnalyzer::~ChannelAnalyzer() | ||||
| { | ||||
| 	if (SSBFilter) delete SSBFilter; | ||||
| 	if (DSBFilter) delete DSBFilter; | ||||
|     m_deviceAPI->removeThreadedSink(m_threadedChannelizer); | ||||
|     delete m_threadedChannelizer; | ||||
|     delete m_channelizer; | ||||
| } | ||||
| 
 | ||||
| void ChannelAnalyzer::configure(MessageQueue* messageQueue, | ||||
| 		Real Bandwidth, | ||||
| 		Real LowCutoff, | ||||
| 		int  spanLog2, | ||||
| 		bool ssb) | ||||
| { | ||||
| 	Message* cmd = MsgConfigureChannelAnalyzer::create(Bandwidth, LowCutoff, spanLog2, ssb); | ||||
| 	messageQueue->push(cmd); | ||||
| } | ||||
| 
 | ||||
| void ChannelAnalyzer::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool positiveOnly __attribute__((unused))) | ||||
| { | ||||
| 	fftfilt::cmplx *sideband; | ||||
| 	int n_out; | ||||
| 	int decim = 1<<m_spanLog2; | ||||
| 	unsigned char decim_mask = decim - 1; // counter LSB bit mask for decimation by 2^(m_scaleLog2 - 1)
 | ||||
| 
 | ||||
| 	m_settingsMutex.lock(); | ||||
| 
 | ||||
| 	for(SampleVector::const_iterator it = begin; it < end; ++it) | ||||
| 	{ | ||||
| 		//Complex c(it->real() / 32768.0f, it->imag() / 32768.0f);
 | ||||
| 		Complex c(it->real(), it->imag()); | ||||
| 		c *= m_nco.nextIQ(); | ||||
| 
 | ||||
| 		if (m_ssb) | ||||
| 		{ | ||||
| 			n_out = SSBFilter->runSSB(c, &sideband, m_usb); | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			n_out = DSBFilter->runDSB(c, &sideband); | ||||
| 		} | ||||
| 
 | ||||
| 		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)
 | ||||
| 
 | ||||
| 			m_sum += sideband[i]; | ||||
| 
 | ||||
| 			if (!(m_undersampleCount++ & decim_mask)) | ||||
| 			{ | ||||
| 				m_sum /= decim; | ||||
| 				m_magsq = (m_sum.real() * m_sum.real() + m_sum.imag() * m_sum.imag())/ (1<<30); | ||||
| 
 | ||||
| 				if (m_ssb & !m_usb) | ||||
| 				{ // invert spectrum for LSB
 | ||||
| 					//m_sampleBuffer.push_back(Sample(m_sum.imag() * 32768.0, m_sum.real() * 32768.0));
 | ||||
| 					m_sampleBuffer.push_back(Sample(m_sum.imag(), m_sum.real())); | ||||
| 				} | ||||
| 				else | ||||
| 				{ | ||||
| 					//m_sampleBuffer.push_back(Sample(m_sum.real() * 32768.0, m_sum.imag() * 32768.0));
 | ||||
| 					m_sampleBuffer.push_back(Sample(m_sum.real(), m_sum.imag())); | ||||
| 				} | ||||
| 
 | ||||
| 				m_sum = 0; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if(m_sampleSink != NULL) | ||||
| 	{ | ||||
| 		m_sampleSink->feed(m_sampleBuffer.begin(), m_sampleBuffer.end(), m_ssb); // m_ssb = positive only
 | ||||
| 	} | ||||
| 
 | ||||
| 	m_sampleBuffer.clear(); | ||||
| 
 | ||||
| 	m_settingsMutex.unlock(); | ||||
| } | ||||
| 
 | ||||
| void ChannelAnalyzer::start() | ||||
| { | ||||
| } | ||||
| 
 | ||||
| void ChannelAnalyzer::stop() | ||||
| { | ||||
| } | ||||
| 
 | ||||
| void ChannelAnalyzer::channelSampleRateChanged() | ||||
| { | ||||
|     MsgReportChannelSampleRateChanged *msg = MsgReportChannelSampleRateChanged::create(); | ||||
|     getMessageQueueToGUI()->push(msg); | ||||
| } | ||||
| 
 | ||||
| bool ChannelAnalyzer::handleMessage(const Message& cmd) | ||||
| { | ||||
| 	float band, lowCutoff; | ||||
| 
 | ||||
| 	qDebug() << "ChannelAnalyzer::handleMessage"; | ||||
| 
 | ||||
| 	if (DownChannelizer::MsgChannelizerNotification::match(cmd)) | ||||
| 	{ | ||||
| 		DownChannelizer::MsgChannelizerNotification& notif = (DownChannelizer::MsgChannelizerNotification&) cmd; | ||||
| 
 | ||||
| 		m_sampleRate = notif.getSampleRate(); | ||||
| 		m_nco.setFreq(-notif.getFrequencyOffset(), m_sampleRate); | ||||
| 
 | ||||
| 		qDebug() << "ChannelAnalyzer::handleMessage: MsgChannelizerNotification: m_sampleRate: " << m_sampleRate | ||||
| 				<< " frequencyOffset: " << notif.getFrequencyOffset(); | ||||
| 
 | ||||
| 		return true; | ||||
| 	} | ||||
| 	else if (MsgConfigureChannelizer::match(cmd)) | ||||
| 	{ | ||||
| 	    MsgConfigureChannelizer& cfg = (MsgConfigureChannelizer&) cmd; | ||||
| 
 | ||||
|         m_channelizer->configure(m_channelizer->getInputMessageQueue(), | ||||
|             m_channelizer->getInputSampleRate(), | ||||
|             cfg.getCenterFrequency()); | ||||
| 
 | ||||
|         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_settingsMutex.lock(); | ||||
| 
 | ||||
| 		m_Bandwidth = band; | ||||
| 		m_LowCutoff = lowCutoff; | ||||
| 
 | ||||
| 		SSBFilter->create_filter(m_LowCutoff / m_sampleRate, m_Bandwidth / m_sampleRate); | ||||
| 		DSBFilter->create_dsb_filter(m_Bandwidth / m_sampleRate); | ||||
| 
 | ||||
| 		m_spanLog2 = cfg.getSpanLog2(); | ||||
| 		m_ssb = cfg.getSSB(); | ||||
| 
 | ||||
| 		m_settingsMutex.unlock(); | ||||
| 
 | ||||
| 		qDebug() << "  - MsgConfigureChannelAnalyzer: m_Bandwidth: " << m_Bandwidth | ||||
| 				<< " m_LowCutoff: " << m_LowCutoff | ||||
| 				<< " m_spanLog2: " << m_spanLog2 | ||||
| 				<< " m_ssb: " << m_ssb; | ||||
| 
 | ||||
| 		return true; | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		if (m_sampleSink != 0) | ||||
| 		{ | ||||
| 		   return m_sampleSink->handleMessage(cmd); | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			return false; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| ///////////////////////////////////////////////////////////////////////////////////
 | ||||
| // 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 "chanalyzer.h" | ||||
| 
 | ||||
| #include <QTime> | ||||
| #include <QDebug> | ||||
| #include <stdio.h> | ||||
| 
 | ||||
| #include <dsp/downchannelizer.h> | ||||
| #include "dsp/threadedbasebandsamplesink.h" | ||||
| #include "device/devicesourceapi.h" | ||||
| #include "audio/audiooutput.h" | ||||
| 
 | ||||
| MESSAGE_CLASS_DEFINITION(ChannelAnalyzer::MsgConfigureChannelAnalyzer, Message) | ||||
| MESSAGE_CLASS_DEFINITION(ChannelAnalyzer::MsgConfigureChannelizer, Message) | ||||
| MESSAGE_CLASS_DEFINITION(ChannelAnalyzer::MsgReportChannelSampleRateChanged, Message) | ||||
| 
 | ||||
| const QString ChannelAnalyzer::m_channelID = "org.f4exb.sdrangelove.channel.chanalyzer"; | ||||
| 
 | ||||
| ChannelAnalyzer::ChannelAnalyzer(DeviceSourceAPI *deviceAPI) : | ||||
|     m_deviceAPI(deviceAPI), | ||||
| 	m_sampleSink(0), | ||||
| 	m_settingsMutex(QMutex::Recursive) | ||||
| { | ||||
| 	m_Bandwidth = 5000; | ||||
| 	m_LowCutoff = 300; | ||||
| 	m_spanLog2 = 3; | ||||
| 	m_sampleRate = 96000; | ||||
| 	m_frequency = 0; | ||||
| 	m_nco.setFreq(m_frequency, m_sampleRate); | ||||
| 	m_undersampleCount = 0; | ||||
| 	m_sum = 0; | ||||
| 	m_usb = true; | ||||
| 	m_ssb = true; | ||||
| 	m_magsq = 0; | ||||
| 	SSBFilter = new fftfilt(m_LowCutoff / m_sampleRate, m_Bandwidth / m_sampleRate, ssbFftLen); | ||||
| 	DSBFilter = new fftfilt(m_Bandwidth / m_sampleRate, 2*ssbFftLen); | ||||
| 
 | ||||
|     m_channelizer = new DownChannelizer(this); | ||||
|     m_threadedChannelizer = new ThreadedBasebandSampleSink(m_channelizer, this); | ||||
|     connect(m_channelizer, SIGNAL(inputSampleRateChanged()), this, SLOT(channelSampleRateChanged())); | ||||
|     m_deviceAPI->addThreadedSink(m_threadedChannelizer); | ||||
| } | ||||
| 
 | ||||
| ChannelAnalyzer::~ChannelAnalyzer() | ||||
| { | ||||
| 	if (SSBFilter) delete SSBFilter; | ||||
| 	if (DSBFilter) delete DSBFilter; | ||||
|     m_deviceAPI->removeThreadedSink(m_threadedChannelizer); | ||||
|     delete m_threadedChannelizer; | ||||
|     delete m_channelizer; | ||||
| } | ||||
| 
 | ||||
| void ChannelAnalyzer::configure(MessageQueue* messageQueue, | ||||
| 		Real Bandwidth, | ||||
| 		Real LowCutoff, | ||||
| 		int  spanLog2, | ||||
| 		bool ssb) | ||||
| { | ||||
| 	Message* cmd = MsgConfigureChannelAnalyzer::create(Bandwidth, LowCutoff, spanLog2, ssb); | ||||
| 	messageQueue->push(cmd); | ||||
| } | ||||
| 
 | ||||
| void ChannelAnalyzer::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool positiveOnly __attribute__((unused))) | ||||
| { | ||||
| 	fftfilt::cmplx *sideband; | ||||
| 	int n_out; | ||||
| 	int decim = 1<<m_spanLog2; | ||||
| 	unsigned char decim_mask = decim - 1; // counter LSB bit mask for decimation by 2^(m_scaleLog2 - 1)
 | ||||
| 
 | ||||
| 	m_settingsMutex.lock(); | ||||
| 
 | ||||
| 	for(SampleVector::const_iterator it = begin; it < end; ++it) | ||||
| 	{ | ||||
| 		//Complex c(it->real() / 32768.0f, it->imag() / 32768.0f);
 | ||||
| 		Complex c(it->real(), it->imag()); | ||||
| 		c *= m_nco.nextIQ(); | ||||
| 
 | ||||
| 		if (m_ssb) | ||||
| 		{ | ||||
| 			n_out = SSBFilter->runSSB(c, &sideband, m_usb); | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			n_out = DSBFilter->runDSB(c, &sideband); | ||||
| 		} | ||||
| 
 | ||||
| 		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)
 | ||||
| 
 | ||||
| 			m_sum += sideband[i]; | ||||
| 
 | ||||
| 			if (!(m_undersampleCount++ & decim_mask)) | ||||
| 			{ | ||||
| 				m_sum /= decim; | ||||
| 				m_magsq = (m_sum.real() * m_sum.real() + m_sum.imag() * m_sum.imag())/ (1<<30); | ||||
| 
 | ||||
| 				if (m_ssb & !m_usb) | ||||
| 				{ // invert spectrum for LSB
 | ||||
| 					//m_sampleBuffer.push_back(Sample(m_sum.imag() * 32768.0, m_sum.real() * 32768.0));
 | ||||
| 					m_sampleBuffer.push_back(Sample(m_sum.imag(), m_sum.real())); | ||||
| 				} | ||||
| 				else | ||||
| 				{ | ||||
| 					//m_sampleBuffer.push_back(Sample(m_sum.real() * 32768.0, m_sum.imag() * 32768.0));
 | ||||
| 					m_sampleBuffer.push_back(Sample(m_sum.real(), m_sum.imag())); | ||||
| 				} | ||||
| 
 | ||||
| 				m_sum = 0; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if(m_sampleSink != NULL) | ||||
| 	{ | ||||
| 		m_sampleSink->feed(m_sampleBuffer.begin(), m_sampleBuffer.end(), m_ssb); // m_ssb = positive only
 | ||||
| 	} | ||||
| 
 | ||||
| 	m_sampleBuffer.clear(); | ||||
| 
 | ||||
| 	m_settingsMutex.unlock(); | ||||
| } | ||||
| 
 | ||||
| void ChannelAnalyzer::start() | ||||
| { | ||||
| } | ||||
| 
 | ||||
| void ChannelAnalyzer::stop() | ||||
| { | ||||
| } | ||||
| 
 | ||||
| void ChannelAnalyzer::channelSampleRateChanged() | ||||
| { | ||||
|     MsgReportChannelSampleRateChanged *msg = MsgReportChannelSampleRateChanged::create(); | ||||
|     getMessageQueueToGUI()->push(msg); | ||||
| } | ||||
| 
 | ||||
| bool ChannelAnalyzer::handleMessage(const Message& cmd) | ||||
| { | ||||
| 	float band, lowCutoff; | ||||
| 
 | ||||
| 	qDebug() << "ChannelAnalyzer::handleMessage"; | ||||
| 
 | ||||
| 	if (DownChannelizer::MsgChannelizerNotification::match(cmd)) | ||||
| 	{ | ||||
| 		DownChannelizer::MsgChannelizerNotification& notif = (DownChannelizer::MsgChannelizerNotification&) cmd; | ||||
| 
 | ||||
| 		m_sampleRate = notif.getSampleRate(); | ||||
| 		m_nco.setFreq(-notif.getFrequencyOffset(), m_sampleRate); | ||||
| 
 | ||||
| 		qDebug() << "ChannelAnalyzer::handleMessage: MsgChannelizerNotification: m_sampleRate: " << m_sampleRate | ||||
| 				<< " frequencyOffset: " << notif.getFrequencyOffset(); | ||||
| 
 | ||||
| 		return true; | ||||
| 	} | ||||
| 	else if (MsgConfigureChannelizer::match(cmd)) | ||||
| 	{ | ||||
| 	    MsgConfigureChannelizer& cfg = (MsgConfigureChannelizer&) cmd; | ||||
| 
 | ||||
|         m_channelizer->configure(m_channelizer->getInputMessageQueue(), | ||||
|             m_channelizer->getInputSampleRate(), | ||||
|             cfg.getCenterFrequency()); | ||||
| 
 | ||||
|         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_settingsMutex.lock(); | ||||
| 
 | ||||
| 		m_Bandwidth = band; | ||||
| 		m_LowCutoff = lowCutoff; | ||||
| 
 | ||||
| 		SSBFilter->create_filter(m_LowCutoff / m_sampleRate, m_Bandwidth / m_sampleRate); | ||||
| 		DSBFilter->create_dsb_filter(m_Bandwidth / m_sampleRate); | ||||
| 
 | ||||
| 		m_spanLog2 = cfg.getSpanLog2(); | ||||
| 		m_ssb = cfg.getSSB(); | ||||
| 
 | ||||
| 		m_settingsMutex.unlock(); | ||||
| 
 | ||||
| 		qDebug() << "  - MsgConfigureChannelAnalyzer: m_Bandwidth: " << m_Bandwidth | ||||
| 				<< " m_LowCutoff: " << m_LowCutoff | ||||
| 				<< " m_spanLog2: " << m_spanLog2 | ||||
| 				<< " m_ssb: " << m_ssb; | ||||
| 
 | ||||
| 		return true; | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		if (m_sampleSink != 0) | ||||
| 		{ | ||||
| 		   return m_sampleSink->handleMessage(cmd); | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			return false; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -1,154 +1,156 @@ | |||
| ///////////////////////////////////////////////////////////////////////////////////
 | ||||
| // 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_CHANALYZER_H | ||||
| #define INCLUDE_CHANALYZER_H | ||||
| 
 | ||||
| #include <dsp/basebandsamplesink.h> | ||||
| #include <QMutex> | ||||
| #include <vector> | ||||
| #include "dsp/ncof.h" | ||||
| #include "dsp/fftfilt.h" | ||||
| #include "audio/audiofifo.h" | ||||
| #include "util/message.h" | ||||
| 
 | ||||
| #define ssbFftLen 1024 | ||||
| 
 | ||||
| class DeviceSourceAPI; | ||||
| class ThreadedBasebandSampleSink; | ||||
| class DownChannelizer; | ||||
| 
 | ||||
| class ChannelAnalyzer : public BasebandSampleSink { | ||||
| public: | ||||
|     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) | ||||
|         { } | ||||
|     }; | ||||
| 
 | ||||
|     class MsgConfigureChannelizer : public Message { | ||||
|         MESSAGE_CLASS_DECLARATION | ||||
| 
 | ||||
|     public: | ||||
|         int getCenterFrequency() const { return m_centerFrequency; } | ||||
| 
 | ||||
|         static MsgConfigureChannelizer* create(int centerFrequency) | ||||
|         { | ||||
|             return new MsgConfigureChannelizer(centerFrequency); | ||||
|         } | ||||
| 
 | ||||
|     private: | ||||
|         int  m_centerFrequency; | ||||
| 
 | ||||
|         MsgConfigureChannelizer(int centerFrequency) : | ||||
|             Message(), | ||||
|             m_centerFrequency(centerFrequency) | ||||
|         { } | ||||
|     }; | ||||
| 
 | ||||
|     class MsgReportChannelSampleRateChanged : public Message { | ||||
|         MESSAGE_CLASS_DECLARATION | ||||
| 
 | ||||
|     public: | ||||
| 
 | ||||
|         static MsgReportChannelSampleRateChanged* create() | ||||
|         { | ||||
|             return new MsgReportChannelSampleRateChanged(); | ||||
|         } | ||||
| 
 | ||||
|     private: | ||||
| 
 | ||||
|         MsgReportChannelSampleRateChanged() : | ||||
|             Message() | ||||
|         { } | ||||
|     }; | ||||
| 
 | ||||
| 	ChannelAnalyzer(DeviceSourceAPI *deviceAPI); | ||||
| 	virtual ~ChannelAnalyzer(); | ||||
| 	void setSampleSink(BasebandSampleSink* sampleSink) { m_sampleSink = sampleSink; } | ||||
| 
 | ||||
| 	void configure(MessageQueue* messageQueue, | ||||
| 			Real Bandwidth, | ||||
| 			Real LowCutoff, | ||||
| 			int spanLog2, | ||||
| 			bool ssb); | ||||
| 
 | ||||
| 	int getSampleRate() const {	return m_sampleRate; } | ||||
| 	Real getMagSq() const { return m_magsq; } | ||||
| 
 | ||||
| 	virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool positiveOnly); | ||||
| 	virtual void start(); | ||||
| 	virtual void stop(); | ||||
| 	virtual bool handleMessage(const Message& cmd); | ||||
| 
 | ||||
| private slots: | ||||
|     void channelSampleRateChanged(); | ||||
| 
 | ||||
| private: | ||||
|     DeviceSourceAPI *m_deviceAPI; | ||||
|     ThreadedBasebandSampleSink* m_threadedChannelizer; | ||||
|     DownChannelizer* m_channelizer; | ||||
| 
 | ||||
| 	Real m_Bandwidth; | ||||
| 	Real m_LowCutoff; | ||||
| 	int m_spanLog2; | ||||
| 	int m_undersampleCount; | ||||
| 	fftfilt::cmplx m_sum; | ||||
| 	int m_sampleRate; | ||||
| 	int m_frequency; | ||||
| 	bool m_usb; | ||||
| 	bool m_ssb; | ||||
| 	Real m_magsq; | ||||
| 
 | ||||
| 	NCOF m_nco; | ||||
| 	fftfilt* SSBFilter; | ||||
| 	fftfilt* DSBFilter; | ||||
| 
 | ||||
| 	BasebandSampleSink* m_sampleSink; | ||||
| 	SampleVector m_sampleBuffer; | ||||
| 	QMutex m_settingsMutex; | ||||
| }; | ||||
| 
 | ||||
| #endif // INCLUDE_CHANALYZER_H
 | ||||
| ///////////////////////////////////////////////////////////////////////////////////
 | ||||
| // 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_CHANALYZER_H | ||||
| #define INCLUDE_CHANALYZER_H | ||||
| 
 | ||||
| #include <dsp/basebandsamplesink.h> | ||||
| #include <QMutex> | ||||
| #include <vector> | ||||
| #include "dsp/ncof.h" | ||||
| #include "dsp/fftfilt.h" | ||||
| #include "audio/audiofifo.h" | ||||
| #include "util/message.h" | ||||
| 
 | ||||
| #define ssbFftLen 1024 | ||||
| 
 | ||||
| class DeviceSourceAPI; | ||||
| class ThreadedBasebandSampleSink; | ||||
| class DownChannelizer; | ||||
| 
 | ||||
| class ChannelAnalyzer : public BasebandSampleSink { | ||||
| public: | ||||
|     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) | ||||
|         { } | ||||
|     }; | ||||
| 
 | ||||
|     class MsgConfigureChannelizer : public Message { | ||||
|         MESSAGE_CLASS_DECLARATION | ||||
| 
 | ||||
|     public: | ||||
|         int getCenterFrequency() const { return m_centerFrequency; } | ||||
| 
 | ||||
|         static MsgConfigureChannelizer* create(int centerFrequency) | ||||
|         { | ||||
|             return new MsgConfigureChannelizer(centerFrequency); | ||||
|         } | ||||
| 
 | ||||
|     private: | ||||
|         int  m_centerFrequency; | ||||
| 
 | ||||
|         MsgConfigureChannelizer(int centerFrequency) : | ||||
|             Message(), | ||||
|             m_centerFrequency(centerFrequency) | ||||
|         { } | ||||
|     }; | ||||
| 
 | ||||
|     class MsgReportChannelSampleRateChanged : public Message { | ||||
|         MESSAGE_CLASS_DECLARATION | ||||
| 
 | ||||
|     public: | ||||
| 
 | ||||
|         static MsgReportChannelSampleRateChanged* create() | ||||
|         { | ||||
|             return new MsgReportChannelSampleRateChanged(); | ||||
|         } | ||||
| 
 | ||||
|     private: | ||||
| 
 | ||||
|         MsgReportChannelSampleRateChanged() : | ||||
|             Message() | ||||
|         { } | ||||
|     }; | ||||
| 
 | ||||
| 	ChannelAnalyzer(DeviceSourceAPI *deviceAPI); | ||||
| 	virtual ~ChannelAnalyzer(); | ||||
| 	void setSampleSink(BasebandSampleSink* sampleSink) { m_sampleSink = sampleSink; } | ||||
| 
 | ||||
| 	void configure(MessageQueue* messageQueue, | ||||
| 			Real Bandwidth, | ||||
| 			Real LowCutoff, | ||||
| 			int spanLog2, | ||||
| 			bool ssb); | ||||
| 
 | ||||
| 	int getSampleRate() const {	return m_sampleRate; } | ||||
| 	Real getMagSq() const { return m_magsq; } | ||||
| 
 | ||||
| 	virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool positiveOnly); | ||||
| 	virtual void start(); | ||||
| 	virtual void stop(); | ||||
| 	virtual bool handleMessage(const Message& cmd); | ||||
| 
 | ||||
|     static const QString m_channelID; | ||||
| 
 | ||||
| private slots: | ||||
|     void channelSampleRateChanged(); | ||||
| 
 | ||||
| private: | ||||
|     DeviceSourceAPI *m_deviceAPI; | ||||
|     ThreadedBasebandSampleSink* m_threadedChannelizer; | ||||
|     DownChannelizer* m_channelizer; | ||||
| 
 | ||||
| 	Real m_Bandwidth; | ||||
| 	Real m_LowCutoff; | ||||
| 	int m_spanLog2; | ||||
| 	int m_undersampleCount; | ||||
| 	fftfilt::cmplx m_sum; | ||||
| 	int m_sampleRate; | ||||
| 	int m_frequency; | ||||
| 	bool m_usb; | ||||
| 	bool m_ssb; | ||||
| 	Real m_magsq; | ||||
| 
 | ||||
| 	NCOF m_nco; | ||||
| 	fftfilt* SSBFilter; | ||||
| 	fftfilt* DSBFilter; | ||||
| 
 | ||||
| 	BasebandSampleSink* m_sampleSink; | ||||
| 	SampleVector m_sampleBuffer; | ||||
| 	QMutex m_settingsMutex; | ||||
| }; | ||||
| 
 | ||||
| #endif // INCLUDE_CHANALYZER_H
 | ||||
|  |  | |||
|  | @ -38,8 +38,6 @@ | |||
| 
 | ||||
| #include "chanalyzer.h" | ||||
| 
 | ||||
| const QString ChannelAnalyzerGUI::m_channelID = "org.f4exb.sdrangelove.channel.chanalyzer"; | ||||
| 
 | ||||
| ChannelAnalyzerGUI* ChannelAnalyzerGUI::create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet) | ||||
| { | ||||
| 	ChannelAnalyzerGUI* gui = new ChannelAnalyzerGUI(pluginAPI, deviceUISet); | ||||
|  | @ -370,7 +368,7 @@ ChannelAnalyzerGUI::ChannelAnalyzerGUI(PluginAPI* pluginAPI, DeviceUISet *device | |||
| 
 | ||||
| 	connect(&m_channelMarker, SIGNAL(changed()), this, SLOT(viewChanged())); | ||||
| 
 | ||||
| 	m_deviceUISet->registerRxChannelInstance(m_channelID, this); | ||||
| 	m_deviceUISet->registerRxChannelInstance(ChannelAnalyzer::m_channelID, this); | ||||
| 	m_deviceUISet->addChannelMarker(&m_channelMarker); | ||||
| 	m_deviceUISet->addRollupWidget(this); | ||||
| 
 | ||||
|  |  | |||
|  | @ -55,8 +55,6 @@ public: | |||
| 	virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } | ||||
| 	virtual bool handleMessage(const Message& message); | ||||
| 
 | ||||
| 	static const QString m_channelID; | ||||
| 
 | ||||
| private slots: | ||||
| 	void viewChanged(); | ||||
| 	void on_deltaFrequency_changed(quint64 value); | ||||
|  |  | |||
|  | @ -4,6 +4,7 @@ | |||
| #include "plugin/pluginapi.h" | ||||
| 
 | ||||
| #include "chanalyzergui.h" | ||||
| #include "chanalyzer.h" | ||||
| 
 | ||||
| const PluginDescriptor ChannelAnalyzerPlugin::m_pluginDescriptor = { | ||||
| 	QString("Channel Analyzer"), | ||||
|  | @ -30,12 +31,12 @@ void ChannelAnalyzerPlugin::initPlugin(PluginAPI* pluginAPI) | |||
| 	m_pluginAPI = pluginAPI; | ||||
| 
 | ||||
| 	// register demodulator
 | ||||
| 	m_pluginAPI->registerRxChannel(ChannelAnalyzerGUI::m_channelID, this); | ||||
| 	m_pluginAPI->registerRxChannel(ChannelAnalyzer::m_channelID, this); | ||||
| } | ||||
| 
 | ||||
| PluginInstanceGUI* ChannelAnalyzerPlugin::createRxChannelGUI(const QString& channelName, DeviceUISet *deviceUISet) | ||||
| { | ||||
| 	if(channelName == ChannelAnalyzerGUI::m_channelID) | ||||
| 	if(channelName == ChannelAnalyzer::m_channelID) | ||||
| 	{ | ||||
| 		ChannelAnalyzerGUI* gui = ChannelAnalyzerGUI::create(m_pluginAPI, deviceUISet); | ||||
| 		return gui; | ||||
|  | @ -43,3 +44,15 @@ PluginInstanceGUI* ChannelAnalyzerPlugin::createRxChannelGUI(const QString& chan | |||
| 		return NULL; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| BasebandSampleSink* ChannelAnalyzerPlugin::createRxChannel(const QString& channelName, DeviceSourceAPI *deviceAPI) | ||||
| { | ||||
|     if(channelName == ChannelAnalyzer::m_channelID) | ||||
|     { | ||||
|         ChannelAnalyzer* sink = new ChannelAnalyzer(deviceAPI); | ||||
|         return sink; | ||||
|     } else { | ||||
|         return 0; | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -5,6 +5,7 @@ | |||
| #include "plugin/plugininterface.h" | ||||
| 
 | ||||
| class DeviceUISet; | ||||
| class BasebandSampleSink; | ||||
| 
 | ||||
| class ChannelAnalyzerPlugin : public QObject, PluginInterface { | ||||
| 	Q_OBJECT | ||||
|  | @ -18,6 +19,7 @@ public: | |||
| 	void initPlugin(PluginAPI* pluginAPI); | ||||
| 
 | ||||
| 	PluginInstanceGUI* createRxChannelGUI(const QString& channelName, DeviceUISet *deviceUISet); | ||||
|     BasebandSampleSink* createRxChannel(const QString& channelName, DeviceSourceAPI *deviceAPI); | ||||
| 
 | ||||
| private: | ||||
| 	static const PluginDescriptor m_pluginDescriptor; | ||||
|  |  | |||
|  | @ -1,251 +1,252 @@ | |||
| ///////////////////////////////////////////////////////////////////////////////////
 | ||||
| // Copyright (C) 2017 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 "chanalyzerng.h" | ||||
| 
 | ||||
| #include <QTime> | ||||
| #include <QDebug> | ||||
| #include <stdio.h> | ||||
| 
 | ||||
| #include "device/devicesourceapi.h" | ||||
| #include "audio/audiooutput.h" | ||||
| #include "dsp/threadedbasebandsamplesink.h" | ||||
| #include "dsp/downchannelizer.h" | ||||
| 
 | ||||
| 
 | ||||
| MESSAGE_CLASS_DEFINITION(ChannelAnalyzerNG::MsgConfigureChannelAnalyzer, Message) | ||||
| MESSAGE_CLASS_DEFINITION(ChannelAnalyzerNG::MsgConfigureChannelizer, Message) | ||||
| MESSAGE_CLASS_DEFINITION(ChannelAnalyzerNG::MsgReportChannelSampleRateChanged, Message) | ||||
| 
 | ||||
| ChannelAnalyzerNG::ChannelAnalyzerNG(DeviceSourceAPI *deviceAPI) : | ||||
|     m_deviceAPI(deviceAPI), | ||||
| 	m_sampleSink(0), | ||||
| 	m_settingsMutex(QMutex::Recursive) | ||||
| { | ||||
| 	m_undersampleCount = 0; | ||||
| 	m_sum = 0; | ||||
| 	m_usb = true; | ||||
| 	m_magsq = 0; | ||||
| 	m_useInterpolator = false; | ||||
| 	m_interpolatorDistance = 1.0f; | ||||
| 	m_interpolatorDistanceRemain = 0.0f; | ||||
| 	SSBFilter = new fftfilt(m_config.m_LowCutoff / m_config.m_inputSampleRate, m_config.m_Bandwidth / m_config.m_inputSampleRate, ssbFftLen); | ||||
| 	DSBFilter = new fftfilt(m_config.m_Bandwidth / m_config.m_inputSampleRate, 2*ssbFftLen); | ||||
| 
 | ||||
|     m_channelizer = new DownChannelizer(this); | ||||
|     m_threadedChannelizer = new ThreadedBasebandSampleSink(m_channelizer, this); | ||||
|     connect(m_channelizer, SIGNAL(inputSampleRateChanged()), this, SLOT(channelizerInputSampleRateChanged())); | ||||
|     m_deviceAPI->addThreadedSink(m_threadedChannelizer); | ||||
| 
 | ||||
| 	apply(true); | ||||
| } | ||||
| 
 | ||||
| ChannelAnalyzerNG::~ChannelAnalyzerNG() | ||||
| { | ||||
| 	if (SSBFilter) delete SSBFilter; | ||||
| 	if (DSBFilter) delete DSBFilter; | ||||
|     m_deviceAPI->removeThreadedSink(m_threadedChannelizer); | ||||
|     delete m_threadedChannelizer; | ||||
|     delete m_channelizer; | ||||
| } | ||||
| 
 | ||||
| void ChannelAnalyzerNG::configure(MessageQueue* messageQueue, | ||||
| 		int channelSampleRate, | ||||
| 		Real Bandwidth, | ||||
| 		Real LowCutoff, | ||||
| 		int  spanLog2, | ||||
| 		bool ssb) | ||||
| { | ||||
|     Message* cmd = MsgConfigureChannelAnalyzer::create(channelSampleRate, Bandwidth, LowCutoff, spanLog2, ssb); | ||||
| 	messageQueue->push(cmd); | ||||
| } | ||||
| 
 | ||||
| void ChannelAnalyzerNG::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool positiveOnly __attribute__((unused))) | ||||
| { | ||||
| 	fftfilt::cmplx *sideband = 0; | ||||
| 	Complex ci; | ||||
| 
 | ||||
| 	m_settingsMutex.lock(); | ||||
| 
 | ||||
| 	for(SampleVector::const_iterator it = begin; it < end; ++it) | ||||
| 	{ | ||||
| 		Complex c(it->real(), it->imag()); | ||||
| 		c *= m_nco.nextIQ(); | ||||
| 
 | ||||
| 		if (m_useInterpolator) | ||||
| 		{ | ||||
|             if (m_interpolator.decimate(&m_interpolatorDistanceRemain, c, &ci)) | ||||
|             { | ||||
|                 processOneSample(ci, sideband); | ||||
|                 m_interpolatorDistanceRemain += m_interpolatorDistance; | ||||
|             } | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 	        processOneSample(c, sideband); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if(m_sampleSink != 0) | ||||
| 	{ | ||||
| 		m_sampleSink->feed(m_sampleBuffer.begin(), m_sampleBuffer.end(), m_running.m_ssb); // m_ssb = positive only
 | ||||
| 	} | ||||
| 
 | ||||
| 	m_sampleBuffer.clear(); | ||||
| 
 | ||||
| 	m_settingsMutex.unlock(); | ||||
| } | ||||
| 
 | ||||
| void ChannelAnalyzerNG::start() | ||||
| { | ||||
| } | ||||
| 
 | ||||
| void ChannelAnalyzerNG::stop() | ||||
| { | ||||
| } | ||||
| 
 | ||||
| void ChannelAnalyzerNG::channelizerInputSampleRateChanged() | ||||
| { | ||||
|     MsgReportChannelSampleRateChanged *msg = MsgReportChannelSampleRateChanged::create(); | ||||
|     getMessageQueueToGUI()->push(msg); | ||||
| } | ||||
| 
 | ||||
| bool ChannelAnalyzerNG::handleMessage(const Message& cmd) | ||||
| { | ||||
| 	qDebug() << "ChannelAnalyzerNG::handleMessage: " << cmd.getIdentifier(); | ||||
| 
 | ||||
| 	if (DownChannelizer::MsgChannelizerNotification::match(cmd)) | ||||
| 	{ | ||||
| 		DownChannelizer::MsgChannelizerNotification& notif = (DownChannelizer::MsgChannelizerNotification&) cmd; | ||||
| 
 | ||||
| 		m_config.m_inputSampleRate = notif.getSampleRate(); | ||||
| 		m_config.m_frequency = notif.getFrequencyOffset(); | ||||
| 
 | ||||
|         qDebug() << "ChannelAnalyzerNG::handleMessage: MsgChannelizerNotification:" | ||||
|                 << " m_sampleRate: " << m_config.m_inputSampleRate | ||||
|                 << " frequencyOffset: " << m_config.m_frequency; | ||||
| 
 | ||||
| 		apply(); | ||||
| 		return true; | ||||
| 	} | ||||
|     else if (MsgConfigureChannelizer::match(cmd)) | ||||
|     { | ||||
|         MsgConfigureChannelizer& cfg = (MsgConfigureChannelizer&) cmd; | ||||
|         m_channelizer->configure(m_channelizer->getInputMessageQueue(), | ||||
|         cfg.getSampleRate(), | ||||
|         cfg.getCenterFrequency()); | ||||
|         return true; | ||||
|     } | ||||
| 	else if (MsgConfigureChannelAnalyzer::match(cmd)) | ||||
| 	{ | ||||
| 		MsgConfigureChannelAnalyzer& cfg = (MsgConfigureChannelAnalyzer&) cmd; | ||||
| 
 | ||||
|         m_config.m_channelSampleRate = cfg.getChannelSampleRate(); | ||||
| 		m_config.m_Bandwidth = cfg.getBandwidth(); | ||||
| 		m_config.m_LowCutoff = cfg.getLoCutoff(); | ||||
| 		m_config.m_spanLog2 = cfg.getSpanLog2(); | ||||
| 		m_config.m_ssb = cfg.getSSB(); | ||||
| 
 | ||||
|         qDebug() << "ChannelAnalyzerNG::handleMessage: MsgConfigureChannelAnalyzer:" | ||||
|                 << " m_channelSampleRate: " << m_config.m_channelSampleRate | ||||
|                 << " m_Bandwidth: " << m_config.m_Bandwidth | ||||
|                 << " m_LowCutoff: " << m_config.m_LowCutoff | ||||
|                 << " m_spanLog2: " << m_config.m_spanLog2 | ||||
|                 << " m_ssb: " << m_config.m_ssb; | ||||
| 
 | ||||
|         apply(); | ||||
| 		return true; | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		if (m_sampleSink != 0) | ||||
| 		{ | ||||
| 		   return m_sampleSink->handleMessage(cmd); | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			return false; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| void ChannelAnalyzerNG::apply(bool force) | ||||
| { | ||||
|     if ((m_running.m_frequency != m_config.m_frequency) || | ||||
|         (m_running.m_inputSampleRate != m_config.m_inputSampleRate) || | ||||
|         force) | ||||
|     { | ||||
|         m_nco.setFreq(-m_config.m_frequency, m_config.m_inputSampleRate); | ||||
|     } | ||||
| 
 | ||||
|     if ((m_running.m_inputSampleRate != m_config.m_inputSampleRate) || | ||||
|         (m_running.m_channelSampleRate != m_config.m_channelSampleRate) || | ||||
|         force) | ||||
|     { | ||||
|         m_settingsMutex.lock(); | ||||
|         m_interpolator.create(16, m_config.m_inputSampleRate, m_config.m_inputSampleRate / 2.2); | ||||
|         m_interpolatorDistanceRemain = 0.0f; | ||||
|         m_interpolatorDistance =  (Real) m_config.m_inputSampleRate / (Real) m_config.m_channelSampleRate; | ||||
|         m_useInterpolator = (m_config.m_inputSampleRate != m_config.m_channelSampleRate); // optim
 | ||||
|         m_settingsMutex.unlock(); | ||||
|     } | ||||
| 
 | ||||
|     if ((m_running.m_channelSampleRate != m_config.m_channelSampleRate) || | ||||
|         (m_running.m_Bandwidth != m_config.m_Bandwidth) || | ||||
|         (m_running.m_LowCutoff != m_config.m_LowCutoff) || | ||||
|          force) | ||||
|     { | ||||
|         float bandwidth = m_config.m_Bandwidth; | ||||
|         float lowCutoff = m_config.m_LowCutoff; | ||||
| 
 | ||||
|         if (bandwidth < 0) | ||||
|         { | ||||
|             bandwidth = -bandwidth; | ||||
|             lowCutoff = -lowCutoff; | ||||
|             m_usb = false; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             m_usb = true; | ||||
|         } | ||||
| 
 | ||||
|         if (bandwidth < 100.0f) | ||||
|         { | ||||
|             bandwidth = 100.0f; | ||||
|             lowCutoff = 0; | ||||
|         } | ||||
| 
 | ||||
|         m_settingsMutex.lock(); | ||||
| 
 | ||||
|         SSBFilter->create_filter(lowCutoff / m_config.m_channelSampleRate, bandwidth / m_config.m_channelSampleRate); | ||||
|         DSBFilter->create_dsb_filter(bandwidth / m_config.m_channelSampleRate); | ||||
| 
 | ||||
|         m_settingsMutex.unlock(); | ||||
|     } | ||||
| 
 | ||||
|     m_running.m_frequency = m_config.m_frequency; | ||||
|     m_running.m_channelSampleRate = m_config.m_channelSampleRate; | ||||
|     m_running.m_inputSampleRate = m_config.m_inputSampleRate; | ||||
|     m_running.m_Bandwidth = m_config.m_Bandwidth; | ||||
|     m_running.m_LowCutoff = m_config.m_LowCutoff; | ||||
| 
 | ||||
|     //m_settingsMutex.lock();
 | ||||
|     m_running.m_spanLog2 = m_config.m_spanLog2; | ||||
|     m_running.m_ssb = m_config.m_ssb; | ||||
|     //m_settingsMutex.unlock();
 | ||||
| } | ||||
| ///////////////////////////////////////////////////////////////////////////////////
 | ||||
| // Copyright (C) 2017 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 "chanalyzerng.h" | ||||
| 
 | ||||
| #include <QTime> | ||||
| #include <QDebug> | ||||
| #include <stdio.h> | ||||
| 
 | ||||
| #include "device/devicesourceapi.h" | ||||
| #include "audio/audiooutput.h" | ||||
| #include "dsp/threadedbasebandsamplesink.h" | ||||
| #include "dsp/downchannelizer.h" | ||||
| 
 | ||||
| MESSAGE_CLASS_DEFINITION(ChannelAnalyzerNG::MsgConfigureChannelAnalyzer, Message) | ||||
| MESSAGE_CLASS_DEFINITION(ChannelAnalyzerNG::MsgConfigureChannelizer, Message) | ||||
| MESSAGE_CLASS_DEFINITION(ChannelAnalyzerNG::MsgReportChannelSampleRateChanged, Message) | ||||
| 
 | ||||
| const QString ChannelAnalyzerNG::m_channelID = "sdrangel.channel.chanalyzerng"; | ||||
| 
 | ||||
| ChannelAnalyzerNG::ChannelAnalyzerNG(DeviceSourceAPI *deviceAPI) : | ||||
|     m_deviceAPI(deviceAPI), | ||||
| 	m_sampleSink(0), | ||||
| 	m_settingsMutex(QMutex::Recursive) | ||||
| { | ||||
| 	m_undersampleCount = 0; | ||||
| 	m_sum = 0; | ||||
| 	m_usb = true; | ||||
| 	m_magsq = 0; | ||||
| 	m_useInterpolator = false; | ||||
| 	m_interpolatorDistance = 1.0f; | ||||
| 	m_interpolatorDistanceRemain = 0.0f; | ||||
| 	SSBFilter = new fftfilt(m_config.m_LowCutoff / m_config.m_inputSampleRate, m_config.m_Bandwidth / m_config.m_inputSampleRate, ssbFftLen); | ||||
| 	DSBFilter = new fftfilt(m_config.m_Bandwidth / m_config.m_inputSampleRate, 2*ssbFftLen); | ||||
| 
 | ||||
|     m_channelizer = new DownChannelizer(this); | ||||
|     m_threadedChannelizer = new ThreadedBasebandSampleSink(m_channelizer, this); | ||||
|     connect(m_channelizer, SIGNAL(inputSampleRateChanged()), this, SLOT(channelizerInputSampleRateChanged())); | ||||
|     m_deviceAPI->addThreadedSink(m_threadedChannelizer); | ||||
| 
 | ||||
| 	apply(true); | ||||
| } | ||||
| 
 | ||||
| ChannelAnalyzerNG::~ChannelAnalyzerNG() | ||||
| { | ||||
| 	if (SSBFilter) delete SSBFilter; | ||||
| 	if (DSBFilter) delete DSBFilter; | ||||
|     m_deviceAPI->removeThreadedSink(m_threadedChannelizer); | ||||
|     delete m_threadedChannelizer; | ||||
|     delete m_channelizer; | ||||
| } | ||||
| 
 | ||||
| void ChannelAnalyzerNG::configure(MessageQueue* messageQueue, | ||||
| 		int channelSampleRate, | ||||
| 		Real Bandwidth, | ||||
| 		Real LowCutoff, | ||||
| 		int  spanLog2, | ||||
| 		bool ssb) | ||||
| { | ||||
|     Message* cmd = MsgConfigureChannelAnalyzer::create(channelSampleRate, Bandwidth, LowCutoff, spanLog2, ssb); | ||||
| 	messageQueue->push(cmd); | ||||
| } | ||||
| 
 | ||||
| void ChannelAnalyzerNG::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool positiveOnly __attribute__((unused))) | ||||
| { | ||||
| 	fftfilt::cmplx *sideband = 0; | ||||
| 	Complex ci; | ||||
| 
 | ||||
| 	m_settingsMutex.lock(); | ||||
| 
 | ||||
| 	for(SampleVector::const_iterator it = begin; it < end; ++it) | ||||
| 	{ | ||||
| 		Complex c(it->real(), it->imag()); | ||||
| 		c *= m_nco.nextIQ(); | ||||
| 
 | ||||
| 		if (m_useInterpolator) | ||||
| 		{ | ||||
|             if (m_interpolator.decimate(&m_interpolatorDistanceRemain, c, &ci)) | ||||
|             { | ||||
|                 processOneSample(ci, sideband); | ||||
|                 m_interpolatorDistanceRemain += m_interpolatorDistance; | ||||
|             } | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 	        processOneSample(c, sideband); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if(m_sampleSink != 0) | ||||
| 	{ | ||||
| 		m_sampleSink->feed(m_sampleBuffer.begin(), m_sampleBuffer.end(), m_running.m_ssb); // m_ssb = positive only
 | ||||
| 	} | ||||
| 
 | ||||
| 	m_sampleBuffer.clear(); | ||||
| 
 | ||||
| 	m_settingsMutex.unlock(); | ||||
| } | ||||
| 
 | ||||
| void ChannelAnalyzerNG::start() | ||||
| { | ||||
| } | ||||
| 
 | ||||
| void ChannelAnalyzerNG::stop() | ||||
| { | ||||
| } | ||||
| 
 | ||||
| void ChannelAnalyzerNG::channelizerInputSampleRateChanged() | ||||
| { | ||||
|     MsgReportChannelSampleRateChanged *msg = MsgReportChannelSampleRateChanged::create(); | ||||
|     getMessageQueueToGUI()->push(msg); | ||||
| } | ||||
| 
 | ||||
| bool ChannelAnalyzerNG::handleMessage(const Message& cmd) | ||||
| { | ||||
| 	qDebug() << "ChannelAnalyzerNG::handleMessage: " << cmd.getIdentifier(); | ||||
| 
 | ||||
| 	if (DownChannelizer::MsgChannelizerNotification::match(cmd)) | ||||
| 	{ | ||||
| 		DownChannelizer::MsgChannelizerNotification& notif = (DownChannelizer::MsgChannelizerNotification&) cmd; | ||||
| 
 | ||||
| 		m_config.m_inputSampleRate = notif.getSampleRate(); | ||||
| 		m_config.m_frequency = notif.getFrequencyOffset(); | ||||
| 
 | ||||
|         qDebug() << "ChannelAnalyzerNG::handleMessage: MsgChannelizerNotification:" | ||||
|                 << " m_sampleRate: " << m_config.m_inputSampleRate | ||||
|                 << " frequencyOffset: " << m_config.m_frequency; | ||||
| 
 | ||||
| 		apply(); | ||||
| 		return true; | ||||
| 	} | ||||
|     else if (MsgConfigureChannelizer::match(cmd)) | ||||
|     { | ||||
|         MsgConfigureChannelizer& cfg = (MsgConfigureChannelizer&) cmd; | ||||
|         m_channelizer->configure(m_channelizer->getInputMessageQueue(), | ||||
|         cfg.getSampleRate(), | ||||
|         cfg.getCenterFrequency()); | ||||
|         return true; | ||||
|     } | ||||
| 	else if (MsgConfigureChannelAnalyzer::match(cmd)) | ||||
| 	{ | ||||
| 		MsgConfigureChannelAnalyzer& cfg = (MsgConfigureChannelAnalyzer&) cmd; | ||||
| 
 | ||||
|         m_config.m_channelSampleRate = cfg.getChannelSampleRate(); | ||||
| 		m_config.m_Bandwidth = cfg.getBandwidth(); | ||||
| 		m_config.m_LowCutoff = cfg.getLoCutoff(); | ||||
| 		m_config.m_spanLog2 = cfg.getSpanLog2(); | ||||
| 		m_config.m_ssb = cfg.getSSB(); | ||||
| 
 | ||||
|         qDebug() << "ChannelAnalyzerNG::handleMessage: MsgConfigureChannelAnalyzer:" | ||||
|                 << " m_channelSampleRate: " << m_config.m_channelSampleRate | ||||
|                 << " m_Bandwidth: " << m_config.m_Bandwidth | ||||
|                 << " m_LowCutoff: " << m_config.m_LowCutoff | ||||
|                 << " m_spanLog2: " << m_config.m_spanLog2 | ||||
|                 << " m_ssb: " << m_config.m_ssb; | ||||
| 
 | ||||
|         apply(); | ||||
| 		return true; | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		if (m_sampleSink != 0) | ||||
| 		{ | ||||
| 		   return m_sampleSink->handleMessage(cmd); | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			return false; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| void ChannelAnalyzerNG::apply(bool force) | ||||
| { | ||||
|     if ((m_running.m_frequency != m_config.m_frequency) || | ||||
|         (m_running.m_inputSampleRate != m_config.m_inputSampleRate) || | ||||
|         force) | ||||
|     { | ||||
|         m_nco.setFreq(-m_config.m_frequency, m_config.m_inputSampleRate); | ||||
|     } | ||||
| 
 | ||||
|     if ((m_running.m_inputSampleRate != m_config.m_inputSampleRate) || | ||||
|         (m_running.m_channelSampleRate != m_config.m_channelSampleRate) || | ||||
|         force) | ||||
|     { | ||||
|         m_settingsMutex.lock(); | ||||
|         m_interpolator.create(16, m_config.m_inputSampleRate, m_config.m_inputSampleRate / 2.2); | ||||
|         m_interpolatorDistanceRemain = 0.0f; | ||||
|         m_interpolatorDistance =  (Real) m_config.m_inputSampleRate / (Real) m_config.m_channelSampleRate; | ||||
|         m_useInterpolator = (m_config.m_inputSampleRate != m_config.m_channelSampleRate); // optim
 | ||||
|         m_settingsMutex.unlock(); | ||||
|     } | ||||
| 
 | ||||
|     if ((m_running.m_channelSampleRate != m_config.m_channelSampleRate) || | ||||
|         (m_running.m_Bandwidth != m_config.m_Bandwidth) || | ||||
|         (m_running.m_LowCutoff != m_config.m_LowCutoff) || | ||||
|          force) | ||||
|     { | ||||
|         float bandwidth = m_config.m_Bandwidth; | ||||
|         float lowCutoff = m_config.m_LowCutoff; | ||||
| 
 | ||||
|         if (bandwidth < 0) | ||||
|         { | ||||
|             bandwidth = -bandwidth; | ||||
|             lowCutoff = -lowCutoff; | ||||
|             m_usb = false; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             m_usb = true; | ||||
|         } | ||||
| 
 | ||||
|         if (bandwidth < 100.0f) | ||||
|         { | ||||
|             bandwidth = 100.0f; | ||||
|             lowCutoff = 0; | ||||
|         } | ||||
| 
 | ||||
|         m_settingsMutex.lock(); | ||||
| 
 | ||||
|         SSBFilter->create_filter(lowCutoff / m_config.m_channelSampleRate, bandwidth / m_config.m_channelSampleRate); | ||||
|         DSBFilter->create_dsb_filter(bandwidth / m_config.m_channelSampleRate); | ||||
| 
 | ||||
|         m_settingsMutex.unlock(); | ||||
|     } | ||||
| 
 | ||||
|     m_running.m_frequency = m_config.m_frequency; | ||||
|     m_running.m_channelSampleRate = m_config.m_channelSampleRate; | ||||
|     m_running.m_inputSampleRate = m_config.m_inputSampleRate; | ||||
|     m_running.m_Bandwidth = m_config.m_Bandwidth; | ||||
|     m_running.m_LowCutoff = m_config.m_LowCutoff; | ||||
| 
 | ||||
|     //m_settingsMutex.lock();
 | ||||
|     m_running.m_spanLog2 = m_config.m_spanLog2; | ||||
|     m_running.m_ssb = m_config.m_ssb; | ||||
|     //m_settingsMutex.unlock();
 | ||||
| } | ||||
|  |  | |||
|  | @ -1,242 +1,244 @@ | |||
| ///////////////////////////////////////////////////////////////////////////////////
 | ||||
| // Copyright (C) 2017 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_CHANALYZERNG_H | ||||
| #define INCLUDE_CHANALYZERNG_H | ||||
| 
 | ||||
| #include <dsp/basebandsamplesink.h> | ||||
| #include <QMutex> | ||||
| #include <vector> | ||||
| 
 | ||||
| #include "dsp/interpolator.h" | ||||
| #include "dsp/ncof.h" | ||||
| #include "dsp/fftfilt.h" | ||||
| #include "audio/audiofifo.h" | ||||
| #include "util/message.h" | ||||
| 
 | ||||
| #define ssbFftLen 1024 | ||||
| 
 | ||||
| class DeviceSourceAPI; | ||||
| class ThreadedBasebandSampleSink; | ||||
| class DownChannelizer; | ||||
| 
 | ||||
| class ChannelAnalyzerNG : public BasebandSampleSink { | ||||
| public: | ||||
|     class MsgConfigureChannelAnalyzer : public Message { | ||||
|         MESSAGE_CLASS_DECLARATION | ||||
| 
 | ||||
|     public: | ||||
|         int  getChannelSampleRate() const { return m_channelSampleRate; } | ||||
|         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( | ||||
|                 int channelSampleRate, | ||||
|                 Real Bandwidth, | ||||
|                 Real LowCutoff, | ||||
|                 int spanLog2, | ||||
|                 bool ssb) | ||||
|         { | ||||
|             return new MsgConfigureChannelAnalyzer( | ||||
|                     channelSampleRate, | ||||
|                     Bandwidth, | ||||
|                     LowCutoff, | ||||
|                     spanLog2, | ||||
|                     ssb); | ||||
|         } | ||||
| 
 | ||||
|     private: | ||||
|         int  m_channelSampleRate; | ||||
|         Real m_Bandwidth; | ||||
|         Real m_LowCutoff; | ||||
|         int  m_spanLog2; | ||||
|         bool m_ssb; | ||||
| 
 | ||||
|         MsgConfigureChannelAnalyzer( | ||||
|                 int channelSampleRate, | ||||
|                 Real Bandwidth, | ||||
|                 Real LowCutoff, | ||||
|                 int spanLog2, | ||||
|                 bool ssb) : | ||||
|             Message(), | ||||
|             m_channelSampleRate(channelSampleRate), | ||||
|             m_Bandwidth(Bandwidth), | ||||
|             m_LowCutoff(LowCutoff), | ||||
|             m_spanLog2(spanLog2), | ||||
|             m_ssb(ssb) | ||||
|         { } | ||||
|     }; | ||||
| 
 | ||||
|     class MsgConfigureChannelizer : public Message { | ||||
|         MESSAGE_CLASS_DECLARATION | ||||
| 
 | ||||
|     public: | ||||
|         int getSampleRate() const { return m_sampleRate; } | ||||
|         int getCenterFrequency() const { return m_centerFrequency; } | ||||
| 
 | ||||
|         static MsgConfigureChannelizer* create(int sampleRate, int centerFrequency) | ||||
|         { | ||||
|             return new MsgConfigureChannelizer(sampleRate, centerFrequency); | ||||
|         } | ||||
| 
 | ||||
|     private: | ||||
|         int  m_sampleRate; | ||||
|         int  m_centerFrequency; | ||||
| 
 | ||||
|         MsgConfigureChannelizer(int sampleRate, int centerFrequency) : | ||||
|             Message(), | ||||
|             m_sampleRate(sampleRate), | ||||
|             m_centerFrequency(centerFrequency) | ||||
|         { } | ||||
|     }; | ||||
| 
 | ||||
|     class MsgReportChannelSampleRateChanged : public Message { | ||||
|         MESSAGE_CLASS_DECLARATION | ||||
| 
 | ||||
|     public: | ||||
| 
 | ||||
|         static MsgReportChannelSampleRateChanged* create() | ||||
|         { | ||||
|             return new MsgReportChannelSampleRateChanged(); | ||||
|         } | ||||
| 
 | ||||
|     private: | ||||
| 
 | ||||
|         MsgReportChannelSampleRateChanged() : | ||||
|             Message() | ||||
|         { } | ||||
|     }; | ||||
| 
 | ||||
|     ChannelAnalyzerNG(DeviceSourceAPI *deviceAPI); | ||||
| 	virtual ~ChannelAnalyzerNG(); | ||||
| 	void setSampleSink(BasebandSampleSink* sampleSink) { m_sampleSink = sampleSink; } | ||||
| 
 | ||||
| 	void configure(MessageQueue* messageQueue, | ||||
| 			int channelSampleRate, | ||||
| 			Real Bandwidth, | ||||
| 			Real LowCutoff, | ||||
| 			int spanLog2, | ||||
| 			bool ssb); | ||||
| 
 | ||||
| 	DownChannelizer *getChannelizer() { return m_channelizer; } | ||||
| 	int getInputSampleRate() const { return m_running.m_inputSampleRate; } | ||||
|     int getChannelSampleRate() const { return m_running.m_channelSampleRate; } | ||||
| 	double getMagSq() const { return m_magsq; } | ||||
| 
 | ||||
| 	virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool positiveOnly); | ||||
| 	virtual void start(); | ||||
| 	virtual void stop(); | ||||
| 	virtual bool handleMessage(const Message& cmd); | ||||
| 
 | ||||
| private slots: | ||||
|     void channelizerInputSampleRateChanged(); | ||||
| 
 | ||||
| private: | ||||
| 
 | ||||
| 	struct Config | ||||
| 	{ | ||||
| 	    int m_frequency; | ||||
| 	    int m_inputSampleRate; | ||||
| 	    int m_channelSampleRate; | ||||
| 	    Real m_Bandwidth; | ||||
| 	    Real m_LowCutoff; | ||||
| 	    int m_spanLog2; | ||||
| 	    bool m_ssb; | ||||
| 
 | ||||
| 	    Config() : | ||||
| 	        m_frequency(0), | ||||
| 	        m_inputSampleRate(96000), | ||||
| 	        m_channelSampleRate(96000), | ||||
| 	        m_Bandwidth(5000), | ||||
| 	        m_LowCutoff(300), | ||||
| 	        m_spanLog2(3), | ||||
| 	        m_ssb(false) | ||||
| 	    {} | ||||
| 	}; | ||||
| 
 | ||||
| 	Config m_config; | ||||
| 	Config m_running; | ||||
| 
 | ||||
| 	DeviceSourceAPI *m_deviceAPI; | ||||
|     ThreadedBasebandSampleSink* m_threadedChannelizer; | ||||
|     DownChannelizer* m_channelizer; | ||||
| 
 | ||||
| 	int m_undersampleCount; | ||||
| 	fftfilt::cmplx m_sum; | ||||
| 	bool m_usb; | ||||
| 	double m_magsq; | ||||
| 	bool m_useInterpolator; | ||||
| 
 | ||||
| 	NCOF m_nco; | ||||
|     Interpolator m_interpolator; | ||||
|     Real m_interpolatorDistance; | ||||
|     Real m_interpolatorDistanceRemain; | ||||
| 
 | ||||
| 	fftfilt* SSBFilter; | ||||
| 	fftfilt* DSBFilter; | ||||
| 
 | ||||
| 	BasebandSampleSink* m_sampleSink; | ||||
| 	SampleVector m_sampleBuffer; | ||||
| 	QMutex m_settingsMutex; | ||||
| 
 | ||||
| 	void apply(bool force = false); | ||||
| 
 | ||||
| 	void processOneSample(Complex& c, fftfilt::cmplx *sideband) | ||||
| 	{ | ||||
| 	    int n_out; | ||||
| 	    int decim = 1<<m_running.m_spanLog2; | ||||
| 
 | ||||
|         if (m_running.m_ssb) | ||||
|         { | ||||
|             n_out = SSBFilter->runSSB(c, &sideband, m_usb); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             n_out = DSBFilter->runDSB(c, &sideband); | ||||
|         } | ||||
| 
 | ||||
|         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)
 | ||||
| 
 | ||||
|             m_sum += sideband[i]; | ||||
| 
 | ||||
|             if (!(m_undersampleCount++ & (decim - 1))) // counter LSB bit mask for decimation by 2^(m_scaleLog2 - 1)
 | ||||
|             { | ||||
|                 m_sum /= decim; | ||||
|                 m_magsq = (m_sum.real() * m_sum.real() + m_sum.imag() * m_sum.imag())/ (1<<30); | ||||
| 
 | ||||
|                 if (m_running.m_ssb & !m_usb) | ||||
|                 { // invert spectrum for LSB
 | ||||
|                     //m_sampleBuffer.push_back(Sample(m_sum.imag() * 32768.0, m_sum.real() * 32768.0));
 | ||||
|                     m_sampleBuffer.push_back(Sample(m_sum.imag(), m_sum.real())); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     //m_sampleBuffer.push_back(Sample(m_sum.real() * 32768.0, m_sum.imag() * 32768.0));
 | ||||
|                     m_sampleBuffer.push_back(Sample(m_sum.real(), m_sum.imag())); | ||||
|                 } | ||||
| 
 | ||||
|                 m_sum = 0; | ||||
|             } | ||||
|         } | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| #endif // INCLUDE_CHANALYZERNG_H
 | ||||
| ///////////////////////////////////////////////////////////////////////////////////
 | ||||
| // Copyright (C) 2017 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_CHANALYZERNG_H | ||||
| #define INCLUDE_CHANALYZERNG_H | ||||
| 
 | ||||
| #include <dsp/basebandsamplesink.h> | ||||
| #include <QMutex> | ||||
| #include <vector> | ||||
| 
 | ||||
| #include "dsp/interpolator.h" | ||||
| #include "dsp/ncof.h" | ||||
| #include "dsp/fftfilt.h" | ||||
| #include "audio/audiofifo.h" | ||||
| #include "util/message.h" | ||||
| 
 | ||||
| #define ssbFftLen 1024 | ||||
| 
 | ||||
| class DeviceSourceAPI; | ||||
| class ThreadedBasebandSampleSink; | ||||
| class DownChannelizer; | ||||
| 
 | ||||
| class ChannelAnalyzerNG : public BasebandSampleSink { | ||||
| public: | ||||
|     class MsgConfigureChannelAnalyzer : public Message { | ||||
|         MESSAGE_CLASS_DECLARATION | ||||
| 
 | ||||
|     public: | ||||
|         int  getChannelSampleRate() const { return m_channelSampleRate; } | ||||
|         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( | ||||
|                 int channelSampleRate, | ||||
|                 Real Bandwidth, | ||||
|                 Real LowCutoff, | ||||
|                 int spanLog2, | ||||
|                 bool ssb) | ||||
|         { | ||||
|             return new MsgConfigureChannelAnalyzer( | ||||
|                     channelSampleRate, | ||||
|                     Bandwidth, | ||||
|                     LowCutoff, | ||||
|                     spanLog2, | ||||
|                     ssb); | ||||
|         } | ||||
| 
 | ||||
|     private: | ||||
|         int  m_channelSampleRate; | ||||
|         Real m_Bandwidth; | ||||
|         Real m_LowCutoff; | ||||
|         int  m_spanLog2; | ||||
|         bool m_ssb; | ||||
| 
 | ||||
|         MsgConfigureChannelAnalyzer( | ||||
|                 int channelSampleRate, | ||||
|                 Real Bandwidth, | ||||
|                 Real LowCutoff, | ||||
|                 int spanLog2, | ||||
|                 bool ssb) : | ||||
|             Message(), | ||||
|             m_channelSampleRate(channelSampleRate), | ||||
|             m_Bandwidth(Bandwidth), | ||||
|             m_LowCutoff(LowCutoff), | ||||
|             m_spanLog2(spanLog2), | ||||
|             m_ssb(ssb) | ||||
|         { } | ||||
|     }; | ||||
| 
 | ||||
|     class MsgConfigureChannelizer : public Message { | ||||
|         MESSAGE_CLASS_DECLARATION | ||||
| 
 | ||||
|     public: | ||||
|         int getSampleRate() const { return m_sampleRate; } | ||||
|         int getCenterFrequency() const { return m_centerFrequency; } | ||||
| 
 | ||||
|         static MsgConfigureChannelizer* create(int sampleRate, int centerFrequency) | ||||
|         { | ||||
|             return new MsgConfigureChannelizer(sampleRate, centerFrequency); | ||||
|         } | ||||
| 
 | ||||
|     private: | ||||
|         int  m_sampleRate; | ||||
|         int  m_centerFrequency; | ||||
| 
 | ||||
|         MsgConfigureChannelizer(int sampleRate, int centerFrequency) : | ||||
|             Message(), | ||||
|             m_sampleRate(sampleRate), | ||||
|             m_centerFrequency(centerFrequency) | ||||
|         { } | ||||
|     }; | ||||
| 
 | ||||
|     class MsgReportChannelSampleRateChanged : public Message { | ||||
|         MESSAGE_CLASS_DECLARATION | ||||
| 
 | ||||
|     public: | ||||
| 
 | ||||
|         static MsgReportChannelSampleRateChanged* create() | ||||
|         { | ||||
|             return new MsgReportChannelSampleRateChanged(); | ||||
|         } | ||||
| 
 | ||||
|     private: | ||||
| 
 | ||||
|         MsgReportChannelSampleRateChanged() : | ||||
|             Message() | ||||
|         { } | ||||
|     }; | ||||
| 
 | ||||
|     ChannelAnalyzerNG(DeviceSourceAPI *deviceAPI); | ||||
| 	virtual ~ChannelAnalyzerNG(); | ||||
| 	void setSampleSink(BasebandSampleSink* sampleSink) { m_sampleSink = sampleSink; } | ||||
| 
 | ||||
| 	void configure(MessageQueue* messageQueue, | ||||
| 			int channelSampleRate, | ||||
| 			Real Bandwidth, | ||||
| 			Real LowCutoff, | ||||
| 			int spanLog2, | ||||
| 			bool ssb); | ||||
| 
 | ||||
| 	DownChannelizer *getChannelizer() { return m_channelizer; } | ||||
| 	int getInputSampleRate() const { return m_running.m_inputSampleRate; } | ||||
|     int getChannelSampleRate() const { return m_running.m_channelSampleRate; } | ||||
| 	double getMagSq() const { return m_magsq; } | ||||
| 
 | ||||
| 	virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool positiveOnly); | ||||
| 	virtual void start(); | ||||
| 	virtual void stop(); | ||||
| 	virtual bool handleMessage(const Message& cmd); | ||||
| 
 | ||||
|     static const QString m_channelID; | ||||
| 
 | ||||
| private slots: | ||||
|     void channelizerInputSampleRateChanged(); | ||||
| 
 | ||||
| private: | ||||
| 
 | ||||
| 	struct Config | ||||
| 	{ | ||||
| 	    int m_frequency; | ||||
| 	    int m_inputSampleRate; | ||||
| 	    int m_channelSampleRate; | ||||
| 	    Real m_Bandwidth; | ||||
| 	    Real m_LowCutoff; | ||||
| 	    int m_spanLog2; | ||||
| 	    bool m_ssb; | ||||
| 
 | ||||
| 	    Config() : | ||||
| 	        m_frequency(0), | ||||
| 	        m_inputSampleRate(96000), | ||||
| 	        m_channelSampleRate(96000), | ||||
| 	        m_Bandwidth(5000), | ||||
| 	        m_LowCutoff(300), | ||||
| 	        m_spanLog2(3), | ||||
| 	        m_ssb(false) | ||||
| 	    {} | ||||
| 	}; | ||||
| 
 | ||||
| 	Config m_config; | ||||
| 	Config m_running; | ||||
| 
 | ||||
| 	DeviceSourceAPI *m_deviceAPI; | ||||
|     ThreadedBasebandSampleSink* m_threadedChannelizer; | ||||
|     DownChannelizer* m_channelizer; | ||||
| 
 | ||||
| 	int m_undersampleCount; | ||||
| 	fftfilt::cmplx m_sum; | ||||
| 	bool m_usb; | ||||
| 	double m_magsq; | ||||
| 	bool m_useInterpolator; | ||||
| 
 | ||||
| 	NCOF m_nco; | ||||
|     Interpolator m_interpolator; | ||||
|     Real m_interpolatorDistance; | ||||
|     Real m_interpolatorDistanceRemain; | ||||
| 
 | ||||
| 	fftfilt* SSBFilter; | ||||
| 	fftfilt* DSBFilter; | ||||
| 
 | ||||
| 	BasebandSampleSink* m_sampleSink; | ||||
| 	SampleVector m_sampleBuffer; | ||||
| 	QMutex m_settingsMutex; | ||||
| 
 | ||||
| 	void apply(bool force = false); | ||||
| 
 | ||||
| 	void processOneSample(Complex& c, fftfilt::cmplx *sideband) | ||||
| 	{ | ||||
| 	    int n_out; | ||||
| 	    int decim = 1<<m_running.m_spanLog2; | ||||
| 
 | ||||
|         if (m_running.m_ssb) | ||||
|         { | ||||
|             n_out = SSBFilter->runSSB(c, &sideband, m_usb); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             n_out = DSBFilter->runDSB(c, &sideband); | ||||
|         } | ||||
| 
 | ||||
|         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)
 | ||||
| 
 | ||||
|             m_sum += sideband[i]; | ||||
| 
 | ||||
|             if (!(m_undersampleCount++ & (decim - 1))) // counter LSB bit mask for decimation by 2^(m_scaleLog2 - 1)
 | ||||
|             { | ||||
|                 m_sum /= decim; | ||||
|                 m_magsq = (m_sum.real() * m_sum.real() + m_sum.imag() * m_sum.imag())/ (1<<30); | ||||
| 
 | ||||
|                 if (m_running.m_ssb & !m_usb) | ||||
|                 { // invert spectrum for LSB
 | ||||
|                     //m_sampleBuffer.push_back(Sample(m_sum.imag() * 32768.0, m_sum.real() * 32768.0));
 | ||||
|                     m_sampleBuffer.push_back(Sample(m_sum.imag(), m_sum.real())); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     //m_sampleBuffer.push_back(Sample(m_sum.real() * 32768.0, m_sum.imag() * 32768.0));
 | ||||
|                     m_sampleBuffer.push_back(Sample(m_sum.real(), m_sum.imag())); | ||||
|                 } | ||||
| 
 | ||||
|                 m_sum = 0; | ||||
|             } | ||||
|         } | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| #endif // INCLUDE_CHANALYZERNG_H
 | ||||
|  |  | |||
|  | @ -38,8 +38,6 @@ | |||
| 
 | ||||
| #include "chanalyzerng.h" | ||||
| 
 | ||||
| const QString ChannelAnalyzerNGGUI::m_channelID = "sdrangel.channel.chanalyzerng"; | ||||
| 
 | ||||
| ChannelAnalyzerNGGUI* ChannelAnalyzerNGGUI::create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet) | ||||
| { | ||||
|     ChannelAnalyzerNGGUI* gui = new ChannelAnalyzerNGGUI(pluginAPI, deviceUISet); | ||||
|  | @ -407,7 +405,7 @@ ChannelAnalyzerNGGUI::ChannelAnalyzerNGGUI(PluginAPI* pluginAPI, DeviceUISet *de | |||
| 
 | ||||
| 	connect(&m_channelMarker, SIGNAL(changed()), this, SLOT(viewChanged())); | ||||
| 
 | ||||
| 	m_deviceUISet->registerRxChannelInstance(m_channelID, this); | ||||
|     m_deviceUISet->registerRxChannelInstance(ChannelAnalyzerNG::m_channelID, this); | ||||
| 	m_deviceUISet->addChannelMarker(&m_channelMarker); | ||||
| 	m_deviceUISet->addRollupWidget(this); | ||||
| 
 | ||||
|  |  | |||
|  | @ -55,8 +55,6 @@ public: | |||
| 	virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } | ||||
| 	virtual bool handleMessage(const Message& message); | ||||
| 
 | ||||
| 	static const QString m_channelID; | ||||
| 
 | ||||
| private slots: | ||||
| 	void viewChanged(); | ||||
| //	void channelizerInputSampleRateChanged();
 | ||||
|  |  | |||
|  | @ -19,10 +19,11 @@ | |||
| #include "plugin/pluginapi.h" | ||||
| #include "chanalyzerngplugin.h" | ||||
| #include "chanalyzernggui.h" | ||||
| #include "chanalyzerng.h" | ||||
| 
 | ||||
| const PluginDescriptor ChannelAnalyzerNGPlugin::m_pluginDescriptor = { | ||||
| 	QString("Channel Analyzer NG"), | ||||
| 	QString("3.8.0"), | ||||
| 	QString("3.8.2"), | ||||
| 	QString("(c) Edouard Griffiths, F4EXB"), | ||||
| 	QString("https://github.com/f4exb/sdrangel"), | ||||
| 	true, | ||||
|  | @ -45,16 +46,28 @@ void ChannelAnalyzerNGPlugin::initPlugin(PluginAPI* pluginAPI) | |||
| 	m_pluginAPI = pluginAPI; | ||||
| 
 | ||||
| 	// register demodulator
 | ||||
| 	m_pluginAPI->registerRxChannel(ChannelAnalyzerNGGUI::m_channelID, this); | ||||
| 	m_pluginAPI->registerRxChannel(ChannelAnalyzerNG::m_channelID, this); | ||||
| } | ||||
| 
 | ||||
| PluginInstanceGUI* ChannelAnalyzerNGPlugin::createRxChannelGUI(const QString& channelName, DeviceUISet *deviceUISet) | ||||
| { | ||||
| 	if(channelName == ChannelAnalyzerNGGUI::m_channelID) | ||||
| 	if(channelName == ChannelAnalyzerNG::m_channelID) | ||||
| 	{ | ||||
| 	    ChannelAnalyzerNGGUI* gui = ChannelAnalyzerNGGUI::create(m_pluginAPI, deviceUISet); | ||||
| 		return gui; | ||||
| 	} else { | ||||
| 		return NULL; | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| BasebandSampleSink* ChannelAnalyzerNGPlugin::createRxChannel(const QString& channelName, DeviceSourceAPI *deviceAPI) | ||||
| { | ||||
|     if(channelName == ChannelAnalyzerNG::m_channelID) | ||||
|     { | ||||
|         ChannelAnalyzerNG* sink = new ChannelAnalyzerNG(deviceAPI); | ||||
|         return sink; | ||||
|     } else { | ||||
|         return 0; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -22,6 +22,7 @@ | |||
| #include "plugin/plugininterface.h" | ||||
| 
 | ||||
| class DeviceUISet; | ||||
| class BasebandSampleSink; | ||||
| 
 | ||||
| class ChannelAnalyzerNGPlugin : public QObject, PluginInterface { | ||||
| 	Q_OBJECT | ||||
|  | @ -35,6 +36,7 @@ public: | |||
| 	void initPlugin(PluginAPI* pluginAPI); | ||||
| 
 | ||||
| 	PluginInstanceGUI* createRxChannelGUI(const QString& channelName, DeviceUISet *deviceUISet); | ||||
|     BasebandSampleSink* createRxChannel(const QString& channelName, DeviceSourceAPI *deviceAPI); | ||||
| 
 | ||||
| private: | ||||
| 	static const PluginDescriptor m_pluginDescriptor; | ||||
|  |  | |||
|  | @ -32,6 +32,7 @@ | |||
| MESSAGE_CLASS_DEFINITION(AMDemod::MsgConfigureAMDemod, Message) | ||||
| MESSAGE_CLASS_DEFINITION(AMDemod::MsgConfigureChannelizer, Message) | ||||
| 
 | ||||
| const QString AMDemod::m_channelID = "de.maintech.sdrangelove.channel.am"; | ||||
| const int AMDemod::m_udpBlockSize = 512; | ||||
| 
 | ||||
| AMDemod::AMDemod(DeviceSourceAPI *deviceAPI) : | ||||
|  |  | |||
|  | @ -1,227 +1,229 @@ | |||
| ///////////////////////////////////////////////////////////////////////////////////
 | ||||
| // 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 <dsp/basebandsamplesink.h> | ||||
| #include <QMutex> | ||||
| #include <vector> | ||||
| #include "dsp/nco.h" | ||||
| #include "dsp/interpolator.h" | ||||
| #include "dsp/movingaverage.h" | ||||
| #include "dsp/agc.h" | ||||
| #include "dsp/bandpass.h" | ||||
| #include "audio/audiofifo.h" | ||||
| #include "util/message.h" | ||||
| #include "amdemodsettings.h" | ||||
| 
 | ||||
| class DeviceSourceAPI; | ||||
| class DownChannelizer; | ||||
| class ThreadedBasebandSampleSink; | ||||
| 
 | ||||
| class AMDemod : public BasebandSampleSink { | ||||
| 	Q_OBJECT | ||||
| public: | ||||
|     class MsgConfigureAMDemod : public Message { | ||||
|         MESSAGE_CLASS_DECLARATION | ||||
| 
 | ||||
|     public: | ||||
|         const AMDemodSettings& getSettings() const { return m_settings; } | ||||
|         bool getForce() const { return m_force; } | ||||
| 
 | ||||
|         static MsgConfigureAMDemod* create(const AMDemodSettings& settings, bool force) | ||||
|         { | ||||
|             return new MsgConfigureAMDemod(settings, force); | ||||
|         } | ||||
| 
 | ||||
|     private: | ||||
|         AMDemodSettings m_settings; | ||||
|         bool m_force; | ||||
| 
 | ||||
|         MsgConfigureAMDemod(const AMDemodSettings& settings, bool force) : | ||||
|             Message(), | ||||
|             m_settings(settings), | ||||
|             m_force(force) | ||||
|         { } | ||||
|     }; | ||||
| 
 | ||||
|     class MsgConfigureChannelizer : public Message { | ||||
|         MESSAGE_CLASS_DECLARATION | ||||
| 
 | ||||
|     public: | ||||
|         int getSampleRate() const { return m_sampleRate; } | ||||
|         int getCenterFrequency() const { return m_centerFrequency; } | ||||
| 
 | ||||
|         static MsgConfigureChannelizer* create(int sampleRate, int centerFrequency) | ||||
|         { | ||||
|             return new MsgConfigureChannelizer(sampleRate, centerFrequency); | ||||
|         } | ||||
| 
 | ||||
|     private: | ||||
|         int m_sampleRate; | ||||
|         int  m_centerFrequency; | ||||
| 
 | ||||
|         MsgConfigureChannelizer(int sampleRate, int centerFrequency) : | ||||
|             Message(), | ||||
|             m_sampleRate(sampleRate), | ||||
|             m_centerFrequency(centerFrequency) | ||||
|         { } | ||||
|     }; | ||||
| 
 | ||||
|     AMDemod(DeviceSourceAPI *deviceAPI); | ||||
| 	~AMDemod(); | ||||
| 
 | ||||
| 	virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool po); | ||||
| 	virtual void start(); | ||||
| 	virtual void stop(); | ||||
| 	virtual bool handleMessage(const Message& cmd); | ||||
| 
 | ||||
| 	double getMagSq() const { return m_magsq; } | ||||
| 	bool getSquelchOpen() const { return m_squelchOpen; } | ||||
| 
 | ||||
| 	void getMagSqLevels(double& avg, double& peak, int& nbSamples) | ||||
| 	{ | ||||
| 	    avg = m_magsqCount == 0 ? 1e-10 : m_magsqSum / m_magsqCount; | ||||
| 	    peak = m_magsqPeak == 0.0 ? 1e-10 : m_magsqPeak; | ||||
| 	    nbSamples = m_magsqCount == 0 ? 1 : m_magsqCount; | ||||
| 	    m_magsqSum = 0.0f; | ||||
|         m_magsqPeak = 0.0f; | ||||
|         m_magsqCount = 0; | ||||
| 	} | ||||
| 
 | ||||
| private: | ||||
| 	enum RateState { | ||||
| 		RSInitialFill, | ||||
| 		RSRunning | ||||
| 	}; | ||||
| 
 | ||||
| 	DeviceSourceAPI *m_deviceAPI; | ||||
|     ThreadedBasebandSampleSink* m_threadedChannelizer; | ||||
|     DownChannelizer* m_channelizer; | ||||
| 
 | ||||
|     AMDemodSettings m_settings; | ||||
| 
 | ||||
| 	NCO m_nco; | ||||
| 	Interpolator m_interpolator; | ||||
| 	Real m_interpolatorDistance; | ||||
| 	Real m_interpolatorDistanceRemain; | ||||
| 
 | ||||
| 	Real m_squelchLevel; | ||||
| 	uint32_t m_squelchCount; | ||||
| 	bool m_squelchOpen; | ||||
| 	double m_magsq; | ||||
| 	double m_magsqSum; | ||||
| 	double m_magsqPeak; | ||||
| 	int  m_magsqCount; | ||||
| 
 | ||||
| 	MovingAverage<double> m_movingAverage; | ||||
| 	SimpleAGC m_volumeAGC; | ||||
|     Bandpass<Real> m_bandpass; | ||||
| 
 | ||||
| 	AudioVector m_audioBuffer; | ||||
| 	uint32_t m_audioBufferFill; | ||||
| 	AudioFifo m_audioFifo; | ||||
|     UDPSink<qint16> *m_udpBufferAudio; | ||||
| 
 | ||||
|     static const int m_udpBlockSize; | ||||
| 
 | ||||
| 	QMutex m_settingsMutex; | ||||
| 
 | ||||
| //	void apply(bool force = false);
 | ||||
|     void applySettings(const AMDemodSettings& settings, bool force = false); | ||||
| 
 | ||||
| 	void processOneSample(Complex &ci) | ||||
| 	{ | ||||
|         Real magsq = ci.real() * ci.real() + ci.imag() * ci.imag(); | ||||
|         magsq /= (1<<30); | ||||
|         m_movingAverage.feed(magsq); | ||||
|         m_magsq = m_movingAverage.average(); | ||||
|         m_magsqSum += magsq; | ||||
| 
 | ||||
|         if (magsq > m_magsqPeak) | ||||
|         { | ||||
|             m_magsqPeak = magsq; | ||||
|         } | ||||
| 
 | ||||
|         m_magsqCount++; | ||||
| 
 | ||||
|         if (m_magsq >= m_squelchLevel) | ||||
|         { | ||||
|             if (m_squelchCount <= m_settings.m_audioSampleRate / 10) | ||||
|             { | ||||
|                 if (m_squelchCount == m_settings.m_audioSampleRate / 20) { | ||||
|                     m_volumeAGC.fill(1.0); | ||||
|                 } | ||||
| 
 | ||||
|                 m_squelchCount++; | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             if (m_squelchCount > 1) | ||||
|             { | ||||
|                 m_squelchCount -= 2; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         qint16 sample; | ||||
| 
 | ||||
|         if ((m_squelchCount >= m_settings.m_audioSampleRate / 20) && !m_settings.m_audioMute) | ||||
|         { | ||||
|             Real demod = sqrt(magsq); | ||||
|             m_volumeAGC.feed(demod); | ||||
|             demod = (demod - m_volumeAGC.getValue()) / m_volumeAGC.getValue(); | ||||
| 
 | ||||
|             if (m_settings.m_bandpassEnable) | ||||
|             { | ||||
|                 demod = m_bandpass.filter(demod); | ||||
|                 demod /= 301.0f; | ||||
|             } | ||||
| 
 | ||||
|             Real attack = (m_squelchCount - 0.05f * m_settings.m_audioSampleRate) / (0.05f * m_settings.m_audioSampleRate); | ||||
|             sample = demod * attack * 2048 * m_settings.m_volume; | ||||
|             if (m_settings.m_copyAudioToUDP) m_udpBufferAudio->write(demod * attack * 32768); | ||||
| 
 | ||||
|             m_squelchOpen = true; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             sample = 0; | ||||
|             if (m_settings.m_copyAudioToUDP) m_udpBufferAudio->write(0); | ||||
|             m_squelchOpen = false; | ||||
|         } | ||||
| 
 | ||||
|         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, 10); | ||||
| 
 | ||||
|             if (res != m_audioBufferFill) | ||||
|             { | ||||
|                 qDebug("AMDemod::feed: %u/%u audio samples written", res, m_audioBufferFill); | ||||
|             } | ||||
| 
 | ||||
|             m_audioBufferFill = 0; | ||||
|         } | ||||
| 	} | ||||
| 
 | ||||
| }; | ||||
| 
 | ||||
| #endif // INCLUDE_AMDEMOD_H
 | ||||
| ///////////////////////////////////////////////////////////////////////////////////
 | ||||
| // 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 <dsp/basebandsamplesink.h> | ||||
| #include <QMutex> | ||||
| #include <vector> | ||||
| #include "dsp/nco.h" | ||||
| #include "dsp/interpolator.h" | ||||
| #include "dsp/movingaverage.h" | ||||
| #include "dsp/agc.h" | ||||
| #include "dsp/bandpass.h" | ||||
| #include "audio/audiofifo.h" | ||||
| #include "util/message.h" | ||||
| #include "amdemodsettings.h" | ||||
| 
 | ||||
| class DeviceSourceAPI; | ||||
| class DownChannelizer; | ||||
| class ThreadedBasebandSampleSink; | ||||
| 
 | ||||
| class AMDemod : public BasebandSampleSink { | ||||
| 	Q_OBJECT | ||||
| public: | ||||
|     class MsgConfigureAMDemod : public Message { | ||||
|         MESSAGE_CLASS_DECLARATION | ||||
| 
 | ||||
|     public: | ||||
|         const AMDemodSettings& getSettings() const { return m_settings; } | ||||
|         bool getForce() const { return m_force; } | ||||
| 
 | ||||
|         static MsgConfigureAMDemod* create(const AMDemodSettings& settings, bool force) | ||||
|         { | ||||
|             return new MsgConfigureAMDemod(settings, force); | ||||
|         } | ||||
| 
 | ||||
|     private: | ||||
|         AMDemodSettings m_settings; | ||||
|         bool m_force; | ||||
| 
 | ||||
|         MsgConfigureAMDemod(const AMDemodSettings& settings, bool force) : | ||||
|             Message(), | ||||
|             m_settings(settings), | ||||
|             m_force(force) | ||||
|         { } | ||||
|     }; | ||||
| 
 | ||||
|     class MsgConfigureChannelizer : public Message { | ||||
|         MESSAGE_CLASS_DECLARATION | ||||
| 
 | ||||
|     public: | ||||
|         int getSampleRate() const { return m_sampleRate; } | ||||
|         int getCenterFrequency() const { return m_centerFrequency; } | ||||
| 
 | ||||
|         static MsgConfigureChannelizer* create(int sampleRate, int centerFrequency) | ||||
|         { | ||||
|             return new MsgConfigureChannelizer(sampleRate, centerFrequency); | ||||
|         } | ||||
| 
 | ||||
|     private: | ||||
|         int m_sampleRate; | ||||
|         int  m_centerFrequency; | ||||
| 
 | ||||
|         MsgConfigureChannelizer(int sampleRate, int centerFrequency) : | ||||
|             Message(), | ||||
|             m_sampleRate(sampleRate), | ||||
|             m_centerFrequency(centerFrequency) | ||||
|         { } | ||||
|     }; | ||||
| 
 | ||||
|     AMDemod(DeviceSourceAPI *deviceAPI); | ||||
| 	~AMDemod(); | ||||
| 
 | ||||
| 	virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool po); | ||||
| 	virtual void start(); | ||||
| 	virtual void stop(); | ||||
| 	virtual bool handleMessage(const Message& cmd); | ||||
| 
 | ||||
| 	double getMagSq() const { return m_magsq; } | ||||
| 	bool getSquelchOpen() const { return m_squelchOpen; } | ||||
| 
 | ||||
| 	void getMagSqLevels(double& avg, double& peak, int& nbSamples) | ||||
| 	{ | ||||
| 	    avg = m_magsqCount == 0 ? 1e-10 : m_magsqSum / m_magsqCount; | ||||
| 	    peak = m_magsqPeak == 0.0 ? 1e-10 : m_magsqPeak; | ||||
| 	    nbSamples = m_magsqCount == 0 ? 1 : m_magsqCount; | ||||
| 	    m_magsqSum = 0.0f; | ||||
|         m_magsqPeak = 0.0f; | ||||
|         m_magsqCount = 0; | ||||
| 	} | ||||
| 
 | ||||
|     static const QString m_channelID; | ||||
| 
 | ||||
| private: | ||||
| 	enum RateState { | ||||
| 		RSInitialFill, | ||||
| 		RSRunning | ||||
| 	}; | ||||
| 
 | ||||
| 	DeviceSourceAPI *m_deviceAPI; | ||||
|     ThreadedBasebandSampleSink* m_threadedChannelizer; | ||||
|     DownChannelizer* m_channelizer; | ||||
| 
 | ||||
|     AMDemodSettings m_settings; | ||||
| 
 | ||||
| 	NCO m_nco; | ||||
| 	Interpolator m_interpolator; | ||||
| 	Real m_interpolatorDistance; | ||||
| 	Real m_interpolatorDistanceRemain; | ||||
| 
 | ||||
| 	Real m_squelchLevel; | ||||
| 	uint32_t m_squelchCount; | ||||
| 	bool m_squelchOpen; | ||||
| 	double m_magsq; | ||||
| 	double m_magsqSum; | ||||
| 	double m_magsqPeak; | ||||
| 	int  m_magsqCount; | ||||
| 
 | ||||
| 	MovingAverage<double> m_movingAverage; | ||||
| 	SimpleAGC m_volumeAGC; | ||||
|     Bandpass<Real> m_bandpass; | ||||
| 
 | ||||
| 	AudioVector m_audioBuffer; | ||||
| 	uint32_t m_audioBufferFill; | ||||
| 	AudioFifo m_audioFifo; | ||||
|     UDPSink<qint16> *m_udpBufferAudio; | ||||
| 
 | ||||
|     static const int m_udpBlockSize; | ||||
| 
 | ||||
| 	QMutex m_settingsMutex; | ||||
| 
 | ||||
| //	void apply(bool force = false);
 | ||||
|     void applySettings(const AMDemodSettings& settings, bool force = false); | ||||
| 
 | ||||
| 	void processOneSample(Complex &ci) | ||||
| 	{ | ||||
|         Real magsq = ci.real() * ci.real() + ci.imag() * ci.imag(); | ||||
|         magsq /= (1<<30); | ||||
|         m_movingAverage.feed(magsq); | ||||
|         m_magsq = m_movingAverage.average(); | ||||
|         m_magsqSum += magsq; | ||||
| 
 | ||||
|         if (magsq > m_magsqPeak) | ||||
|         { | ||||
|             m_magsqPeak = magsq; | ||||
|         } | ||||
| 
 | ||||
|         m_magsqCount++; | ||||
| 
 | ||||
|         if (m_magsq >= m_squelchLevel) | ||||
|         { | ||||
|             if (m_squelchCount <= m_settings.m_audioSampleRate / 10) | ||||
|             { | ||||
|                 if (m_squelchCount == m_settings.m_audioSampleRate / 20) { | ||||
|                     m_volumeAGC.fill(1.0); | ||||
|                 } | ||||
| 
 | ||||
|                 m_squelchCount++; | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             if (m_squelchCount > 1) | ||||
|             { | ||||
|                 m_squelchCount -= 2; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         qint16 sample; | ||||
| 
 | ||||
|         if ((m_squelchCount >= m_settings.m_audioSampleRate / 20) && !m_settings.m_audioMute) | ||||
|         { | ||||
|             Real demod = sqrt(magsq); | ||||
|             m_volumeAGC.feed(demod); | ||||
|             demod = (demod - m_volumeAGC.getValue()) / m_volumeAGC.getValue(); | ||||
| 
 | ||||
|             if (m_settings.m_bandpassEnable) | ||||
|             { | ||||
|                 demod = m_bandpass.filter(demod); | ||||
|                 demod /= 301.0f; | ||||
|             } | ||||
| 
 | ||||
|             Real attack = (m_squelchCount - 0.05f * m_settings.m_audioSampleRate) / (0.05f * m_settings.m_audioSampleRate); | ||||
|             sample = demod * attack * 2048 * m_settings.m_volume; | ||||
|             if (m_settings.m_copyAudioToUDP) m_udpBufferAudio->write(demod * attack * 32768); | ||||
| 
 | ||||
|             m_squelchOpen = true; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             sample = 0; | ||||
|             if (m_settings.m_copyAudioToUDP) m_udpBufferAudio->write(0); | ||||
|             m_squelchOpen = false; | ||||
|         } | ||||
| 
 | ||||
|         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, 10); | ||||
| 
 | ||||
|             if (res != m_audioBufferFill) | ||||
|             { | ||||
|                 qDebug("AMDemod::feed: %u/%u audio samples written", res, m_audioBufferFill); | ||||
|             } | ||||
| 
 | ||||
|             m_audioBufferFill = 0; | ||||
|         } | ||||
| 	} | ||||
| 
 | ||||
| }; | ||||
| 
 | ||||
| #endif // INCLUDE_AMDEMOD_H
 | ||||
|  |  | |||
|  | @ -34,8 +34,6 @@ | |||
| 
 | ||||
| #include "amdemod.h" | ||||
| 
 | ||||
| const QString AMDemodGUI::m_channelID = "de.maintech.sdrangelove.channel.am"; | ||||
| 
 | ||||
| AMDemodGUI* AMDemodGUI::create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet) | ||||
| { | ||||
| 	AMDemodGUI* gui = new AMDemodGUI(pluginAPI, deviceUISet); | ||||
|  | @ -206,7 +204,7 @@ AMDemodGUI::AMDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, QWidget* | |||
| 
 | ||||
| 	connect(&m_channelMarker, SIGNAL(changed()), this, SLOT(channelMarkerChanged())); | ||||
| 
 | ||||
| 	m_deviceUISet->registerRxChannelInstance(m_channelID, this); | ||||
|     m_deviceUISet->registerRxChannelInstance(AMDemod::m_channelID, this); | ||||
| 	m_deviceUISet->addChannelMarker(&m_channelMarker); | ||||
| 	m_deviceUISet->addRollupWidget(this); | ||||
| 
 | ||||
|  |  | |||
|  | @ -37,8 +37,6 @@ public: | |||
| 	virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } | ||||
| 	virtual bool handleMessage(const Message& message); | ||||
| 
 | ||||
| 	static const QString m_channelID; | ||||
| 
 | ||||
| private slots: | ||||
| 	void channelMarkerChanged(); | ||||
| 	void on_deltaFrequency_changed(qint64 value); | ||||
|  |  | |||
|  | @ -31,12 +31,12 @@ void AMDemodPlugin::initPlugin(PluginAPI* pluginAPI) | |||
| 	m_pluginAPI = pluginAPI; | ||||
| 
 | ||||
| 	// register AM demodulator
 | ||||
| 	m_pluginAPI->registerRxChannel(AMDemodGUI::m_channelID, this); | ||||
| 	m_pluginAPI->registerRxChannel(AMDemod::m_channelID, this); | ||||
| } | ||||
| 
 | ||||
| PluginInstanceGUI* AMDemodPlugin::createRxChannelGUI(const QString& channelName, DeviceUISet *deviceUISet) | ||||
| { | ||||
| 	if(channelName == AMDemodGUI::m_channelID) | ||||
| 	if(channelName == AMDemod::m_channelID) | ||||
| 	{ | ||||
| 		AMDemodGUI* gui = AMDemodGUI::create(m_pluginAPI, deviceUISet); | ||||
| 		return gui; | ||||
|  | @ -47,7 +47,7 @@ PluginInstanceGUI* AMDemodPlugin::createRxChannelGUI(const QString& channelName, | |||
| 
 | ||||
| BasebandSampleSink* AMDemodPlugin::createRxChannel(const QString& channelName, DeviceSourceAPI *deviceAPI) | ||||
| { | ||||
|     if(channelName == AMDemodGUI::m_channelID) | ||||
|     if(channelName == AMDemod::m_channelID) | ||||
|     { | ||||
|         AMDemod* sink = new AMDemod(deviceAPI); | ||||
|         return sink; | ||||
|  |  | |||
|  | @ -35,6 +35,7 @@ MESSAGE_CLASS_DEFINITION(ATVDemod::MsgReportEffectiveSampleRate, Message) | |||
| MESSAGE_CLASS_DEFINITION(ATVDemod::MsgConfigureChannelizer, Message) | ||||
| MESSAGE_CLASS_DEFINITION(ATVDemod::MsgReportChannelSampleRateChanged, Message) | ||||
| 
 | ||||
| const QString ATVDemod::m_channelID = "sdrangel.channel.demodatv"; | ||||
| const int ATVDemod::m_ssbFftLen = 1024; | ||||
| 
 | ||||
| ATVDemod::ATVDemod(DeviceSourceAPI *deviceAPI) : | ||||
|  |  | |||
|  | @ -227,6 +227,8 @@ public: | |||
|     double getMagSq() const { return m_objMagSqAverage.average(); } //!< Beware this is scaled to 2^30
 | ||||
|     bool getBFOLocked(); | ||||
| 
 | ||||
|     static const QString m_channelID; | ||||
| 
 | ||||
| private slots: | ||||
|     void channelSampleRateChanged(); | ||||
| 
 | ||||
|  |  | |||
|  | @ -36,8 +36,6 @@ | |||
| 
 | ||||
| #include "atvdemod.h" | ||||
| 
 | ||||
| const QString ATVDemodGUI::m_strChannelID = "sdrangel.channel.demodatv"; | ||||
| 
 | ||||
| ATVDemodGUI* ATVDemodGUI::create(PluginAPI* objPluginAPI, | ||||
|         DeviceUISet *deviceUISet) | ||||
| { | ||||
|  | @ -312,7 +310,7 @@ ATVDemodGUI::ATVDemodGUI(PluginAPI* objPluginAPI, DeviceUISet *deviceUISet, | |||
| 
 | ||||
|     connect(&m_channelMarker, SIGNAL(changed()), this, SLOT(viewChanged())); | ||||
| 
 | ||||
|     m_deviceUISet->registerRxChannelInstance(m_strChannelID, this); | ||||
|     m_deviceUISet->registerRxChannelInstance(ATVDemod::m_channelID, this); | ||||
|     m_deviceUISet->addChannelMarker(&m_channelMarker); | ||||
|     m_deviceUISet->addRollupWidget(this); | ||||
| 
 | ||||
|  |  | |||
|  | @ -54,8 +54,6 @@ public: | |||
|     virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } | ||||
|     virtual bool handleMessage(const Message& objMessage); | ||||
| 
 | ||||
|     static const QString m_strChannelID; | ||||
| 
 | ||||
| private slots: | ||||
| 	void viewChanged(); | ||||
|     void handleSourceMessages(); | ||||
|  |  | |||
|  | @ -21,6 +21,7 @@ | |||
| #include "plugin/pluginapi.h" | ||||
| 
 | ||||
| #include "atvdemodgui.h" | ||||
| #include "atvdemod.h" | ||||
| #include "atvdemodplugin.h" | ||||
| 
 | ||||
| const PluginDescriptor ATVDemodPlugin::m_ptrPluginDescriptor = | ||||
|  | @ -50,18 +51,30 @@ void ATVDemodPlugin::initPlugin(PluginAPI* ptrPluginAPI) | |||
|     m_ptrPluginAPI = ptrPluginAPI; | ||||
| 
 | ||||
| 	// register ATV demodulator
 | ||||
|     m_ptrPluginAPI->registerRxChannel(ATVDemodGUI::m_strChannelID, this); | ||||
|     m_ptrPluginAPI->registerRxChannel(ATVDemod::m_channelID, this); | ||||
| } | ||||
| 
 | ||||
| PluginInstanceGUI* ATVDemodPlugin::createRxChannelGUI(const QString& strChannelName, DeviceUISet *deviceUISet) | ||||
| { | ||||
|     if(strChannelName == ATVDemodGUI::m_strChannelID) | ||||
|     if(strChannelName == ATVDemod::m_channelID) | ||||
| 	{ | ||||
|         ATVDemodGUI* ptrGui = ATVDemodGUI::create(m_ptrPluginAPI, deviceUISet); | ||||
|         return ptrGui; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
| 		return NULL; | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| BasebandSampleSink* ATVDemodPlugin::createRxChannel(const QString& channelName, DeviceSourceAPI *deviceAPI) | ||||
| { | ||||
|     if(channelName == ATVDemod::m_channelID) | ||||
|     { | ||||
|         ATVDemod* sink = new ATVDemod(deviceAPI); | ||||
|         return sink; | ||||
|     } else { | ||||
|         return 0; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -22,6 +22,7 @@ | |||
| #include "plugin/plugininterface.h" | ||||
| 
 | ||||
| class DeviceUISet; | ||||
| class BasebandSampleSink; | ||||
| 
 | ||||
| class ATVDemodPlugin : public QObject, PluginInterface | ||||
| { | ||||
|  | @ -36,6 +37,7 @@ public: | |||
|     void initPlugin(PluginAPI* ptrPluginAPI); | ||||
| 
 | ||||
|     PluginInstanceGUI* createRxChannelGUI(const QString& strChannelName, DeviceUISet *deviceUISet); | ||||
|     BasebandSampleSink* createRxChannel(const QString& channelName, DeviceSourceAPI *deviceAPI); | ||||
| 
 | ||||
| private: | ||||
|     static const PluginDescriptor m_ptrPluginDescriptor; | ||||
|  |  | |||
|  | @ -34,6 +34,7 @@ MESSAGE_CLASS_DEFINITION(BFMDemod::MsgConfigureChannelizer, Message) | |||
| MESSAGE_CLASS_DEFINITION(BFMDemod::MsgReportChannelSampleRateChanged, Message) | ||||
| MESSAGE_CLASS_DEFINITION(BFMDemod::MsgConfigureBFMDemod, Message) | ||||
| 
 | ||||
| const QString BFMDemod::m_channelID = "sdrangel.channel.bfm"; | ||||
| const Real BFMDemod::default_deemphasis = 50.0; // 50 us
 | ||||
| const int BFMDemod::m_udpBlockSize = 512; | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,219 +1,221 @@ | |||
| ///////////////////////////////////////////////////////////////////////////////////
 | ||||
| // Copyright (C) 2015 F4EXB                                                      //
 | ||||
| // written by Edouard Griffiths                                                  //
 | ||||
| //                                                                               //
 | ||||
| // 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_BFMDEMOD_H | ||||
| #define INCLUDE_BFMDEMOD_H | ||||
| 
 | ||||
| #include <dsp/basebandsamplesink.h> | ||||
| #include <QMutex> | ||||
| #include <vector> | ||||
| #include "dsp/nco.h" | ||||
| #include "dsp/interpolator.h" | ||||
| #include "dsp/lowpass.h" | ||||
| #include "dsp/movingaverage.h" | ||||
| #include "dsp/fftfilt.h" | ||||
| #include "dsp/phaselock.h" | ||||
| #include "dsp/filterrc.h" | ||||
| #include "dsp/phasediscri.h" | ||||
| #include "audio/audiofifo.h" | ||||
| #include "util/message.h" | ||||
| #include "util/udpsink.h" | ||||
| 
 | ||||
| #include "rdsparser.h" | ||||
| #include "rdsdecoder.h" | ||||
| #include "rdsdemod.h" | ||||
| #include "bfmdemodsettings.h" | ||||
| 
 | ||||
| class DeviceSourceAPI; | ||||
| class ThreadedBasebandSampleSink; | ||||
| class DownChannelizer; | ||||
| 
 | ||||
| class BFMDemod : public BasebandSampleSink { | ||||
| public: | ||||
|     class MsgConfigureBFMDemod : public Message { | ||||
|         MESSAGE_CLASS_DECLARATION | ||||
| 
 | ||||
|     public: | ||||
|         const BFMDemodSettings& getSettings() const { return m_settings; } | ||||
|         bool getForce() const { return m_force; } | ||||
| 
 | ||||
|         static MsgConfigureBFMDemod* create(const BFMDemodSettings& settings, bool force) | ||||
|         { | ||||
|             return new MsgConfigureBFMDemod(settings, force); | ||||
|         } | ||||
| 
 | ||||
|     private: | ||||
|         BFMDemodSettings m_settings; | ||||
|         bool m_force; | ||||
| 
 | ||||
|         MsgConfigureBFMDemod(const BFMDemodSettings& settings, bool force) : | ||||
|             Message(), | ||||
|             m_settings(settings), | ||||
|             m_force(force) | ||||
|         { } | ||||
|     }; | ||||
| 
 | ||||
|     class MsgConfigureChannelizer : public Message { | ||||
|         MESSAGE_CLASS_DECLARATION | ||||
| 
 | ||||
|     public: | ||||
|         int getSampleRate() const { return m_sampleRate; } | ||||
|         int getCenterFrequency() const { return m_centerFrequency; } | ||||
| 
 | ||||
|         static MsgConfigureChannelizer* create(int sampleRate, int centerFrequency) | ||||
|         { | ||||
|             return new MsgConfigureChannelizer(sampleRate, centerFrequency); | ||||
|         } | ||||
| 
 | ||||
|     private: | ||||
|         int m_sampleRate; | ||||
|         int m_centerFrequency; | ||||
| 
 | ||||
|         MsgConfigureChannelizer(int sampleRate, int centerFrequency) : | ||||
|             Message(), | ||||
|             m_sampleRate(sampleRate), | ||||
|             m_centerFrequency(centerFrequency) | ||||
|         { } | ||||
|     }; | ||||
| 
 | ||||
|     class MsgReportChannelSampleRateChanged : public Message { | ||||
|         MESSAGE_CLASS_DECLARATION | ||||
| 
 | ||||
|     public: | ||||
|         int getSampleRate() const { return m_sampleRate; } | ||||
| 
 | ||||
|         static MsgReportChannelSampleRateChanged* create(int sampleRate) | ||||
|         { | ||||
|             return new MsgReportChannelSampleRateChanged(sampleRate); | ||||
|         } | ||||
| 
 | ||||
|     private: | ||||
|         int m_sampleRate; | ||||
| 
 | ||||
|         MsgReportChannelSampleRateChanged(int sampleRate) : | ||||
|             Message(), | ||||
|             m_sampleRate(sampleRate) | ||||
|         { } | ||||
|     }; | ||||
| 
 | ||||
| 	BFMDemod(DeviceSourceAPI *deviceAPI); | ||||
| 	virtual ~BFMDemod(); | ||||
| 	void setSampleSink(BasebandSampleSink* sampleSink) { m_sampleSink = sampleSink; } | ||||
| 
 | ||||
| 	int getSampleRate() const { return m_settings.m_inputSampleRate; } | ||||
| 	virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool po); | ||||
| 	virtual void start(); | ||||
| 	virtual void stop(); | ||||
| 	virtual bool handleMessage(const Message& cmd); | ||||
| 
 | ||||
| 	double getMagSq() const { return m_magsq; } | ||||
| 
 | ||||
| 	bool getPilotLock() const { return m_pilotPLL.locked(); } | ||||
| 	Real getPilotLevel() const { return m_pilotPLL.get_pilot_level(); } | ||||
| 
 | ||||
| 	Real getDecoderQua() const { return m_rdsDecoder.m_qua; } | ||||
| 	bool getDecoderSynced() const { return m_rdsDecoder.synced(); } | ||||
| 	Real getDemodAcc() const { return m_rdsDemod.m_report.acc; } | ||||
| 	Real getDemodQua() const { return m_rdsDemod.m_report.qua; } | ||||
| 	Real getDemodFclk() const { return m_rdsDemod.m_report.fclk; } | ||||
| 
 | ||||
|     void getMagSqLevels(double& avg, double& peak, int& nbSamples) | ||||
|     { | ||||
|         avg = m_magsqCount == 0 ? 1e-10 : m_magsqSum / m_magsqCount; | ||||
|         m_magsq = avg; | ||||
|         peak = m_magsqPeak == 0.0 ? 1e-10 : m_magsqPeak; | ||||
|         nbSamples = m_magsqCount == 0 ? 1 : m_magsqCount; | ||||
|         m_magsqSum = 0.0f; | ||||
|         m_magsqPeak = 0.0f; | ||||
|         m_magsqCount = 0; | ||||
|     } | ||||
| 
 | ||||
|     RDSParser& getRDSParser() { return m_rdsParser; } | ||||
| 
 | ||||
| private slots: | ||||
|     void channelSampleRateChanged(); | ||||
| 
 | ||||
| private: | ||||
| 	enum RateState { | ||||
| 		RSInitialFill, | ||||
| 		RSRunning | ||||
| 	}; | ||||
| 
 | ||||
| 	DeviceSourceAPI *m_deviceAPI; | ||||
|     ThreadedBasebandSampleSink* m_threadedChannelizer; | ||||
|     DownChannelizer* m_channelizer; | ||||
| 
 | ||||
|     BFMDemodSettings m_settings; | ||||
| 
 | ||||
| 	NCO m_nco; | ||||
| 	Interpolator m_interpolator; //!< Interpolator between fixed demod bandwidth and audio bandwidth (rational)
 | ||||
| 	Real m_interpolatorDistance; | ||||
| 	Real m_interpolatorDistanceRemain; | ||||
| 
 | ||||
| 	Interpolator m_interpolatorStereo; //!< Twin Interpolator for stereo subcarrier
 | ||||
| 	Real m_interpolatorStereoDistance; | ||||
| 	Real m_interpolatorStereoDistanceRemain; | ||||
| 
 | ||||
| 	Interpolator m_interpolatorRDS; //!< Twin Interpolator for stereo subcarrier
 | ||||
| 	Real m_interpolatorRDSDistance; | ||||
| 	Real m_interpolatorRDSDistanceRemain; | ||||
| 
 | ||||
| 	Lowpass<Real> m_lowpass; | ||||
| 	fftfilt* m_rfFilter; | ||||
| 	static const int filtFftLen = 1024; | ||||
| 
 | ||||
| 	Real m_squelchLevel; | ||||
| 	int m_squelchState; | ||||
| 
 | ||||
| 	Real m_m1Arg; //!> x^-1 real sample
 | ||||
| 
 | ||||
|     double m_magsq; | ||||
|     double m_magsqSum; | ||||
|     double m_magsqPeak; | ||||
|     int    m_magsqCount; | ||||
| 
 | ||||
| 	AudioVector m_audioBuffer; | ||||
| 	uint m_audioBufferFill; | ||||
| 
 | ||||
| 	BasebandSampleSink* m_sampleSink; | ||||
| 	AudioFifo m_audioFifo; | ||||
| 	SampleVector m_sampleBuffer; | ||||
| 	QMutex m_settingsMutex; | ||||
| 
 | ||||
| 	RDSPhaseLock m_pilotPLL; | ||||
| 	Real m_pilotPLLSamples[4]; | ||||
| 
 | ||||
| 	RDSDemod m_rdsDemod; | ||||
| 	RDSDecoder m_rdsDecoder; | ||||
| 	RDSParser m_rdsParser; | ||||
| 
 | ||||
| 	LowPassFilterRC m_deemphasisFilterX; | ||||
| 	LowPassFilterRC m_deemphasisFilterY; | ||||
|     static const Real default_deemphasis; | ||||
| 
 | ||||
| 	Real m_fmExcursion; | ||||
| 	static const int default_excursion = 750000; // +/- 75 kHz
 | ||||
| 
 | ||||
| 	PhaseDiscriminators m_phaseDiscri; | ||||
|     UDPSink<AudioSample> *m_udpBufferAudio; | ||||
| 
 | ||||
|     static const int m_udpBlockSize; | ||||
| 
 | ||||
| 	void applySettings(const BFMDemodSettings& settings, bool force = false); | ||||
| }; | ||||
| 
 | ||||
| #endif // INCLUDE_BFMDEMOD_H
 | ||||
| ///////////////////////////////////////////////////////////////////////////////////
 | ||||
| // Copyright (C) 2015 F4EXB                                                      //
 | ||||
| // written by Edouard Griffiths                                                  //
 | ||||
| //                                                                               //
 | ||||
| // 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_BFMDEMOD_H | ||||
| #define INCLUDE_BFMDEMOD_H | ||||
| 
 | ||||
| #include <dsp/basebandsamplesink.h> | ||||
| #include <QMutex> | ||||
| #include <vector> | ||||
| #include "dsp/nco.h" | ||||
| #include "dsp/interpolator.h" | ||||
| #include "dsp/lowpass.h" | ||||
| #include "dsp/movingaverage.h" | ||||
| #include "dsp/fftfilt.h" | ||||
| #include "dsp/phaselock.h" | ||||
| #include "dsp/filterrc.h" | ||||
| #include "dsp/phasediscri.h" | ||||
| #include "audio/audiofifo.h" | ||||
| #include "util/message.h" | ||||
| #include "util/udpsink.h" | ||||
| 
 | ||||
| #include "rdsparser.h" | ||||
| #include "rdsdecoder.h" | ||||
| #include "rdsdemod.h" | ||||
| #include "bfmdemodsettings.h" | ||||
| 
 | ||||
| class DeviceSourceAPI; | ||||
| class ThreadedBasebandSampleSink; | ||||
| class DownChannelizer; | ||||
| 
 | ||||
| class BFMDemod : public BasebandSampleSink { | ||||
| public: | ||||
|     class MsgConfigureBFMDemod : public Message { | ||||
|         MESSAGE_CLASS_DECLARATION | ||||
| 
 | ||||
|     public: | ||||
|         const BFMDemodSettings& getSettings() const { return m_settings; } | ||||
|         bool getForce() const { return m_force; } | ||||
| 
 | ||||
|         static MsgConfigureBFMDemod* create(const BFMDemodSettings& settings, bool force) | ||||
|         { | ||||
|             return new MsgConfigureBFMDemod(settings, force); | ||||
|         } | ||||
| 
 | ||||
|     private: | ||||
|         BFMDemodSettings m_settings; | ||||
|         bool m_force; | ||||
| 
 | ||||
|         MsgConfigureBFMDemod(const BFMDemodSettings& settings, bool force) : | ||||
|             Message(), | ||||
|             m_settings(settings), | ||||
|             m_force(force) | ||||
|         { } | ||||
|     }; | ||||
| 
 | ||||
|     class MsgConfigureChannelizer : public Message { | ||||
|         MESSAGE_CLASS_DECLARATION | ||||
| 
 | ||||
|     public: | ||||
|         int getSampleRate() const { return m_sampleRate; } | ||||
|         int getCenterFrequency() const { return m_centerFrequency; } | ||||
| 
 | ||||
|         static MsgConfigureChannelizer* create(int sampleRate, int centerFrequency) | ||||
|         { | ||||
|             return new MsgConfigureChannelizer(sampleRate, centerFrequency); | ||||
|         } | ||||
| 
 | ||||
|     private: | ||||
|         int m_sampleRate; | ||||
|         int m_centerFrequency; | ||||
| 
 | ||||
|         MsgConfigureChannelizer(int sampleRate, int centerFrequency) : | ||||
|             Message(), | ||||
|             m_sampleRate(sampleRate), | ||||
|             m_centerFrequency(centerFrequency) | ||||
|         { } | ||||
|     }; | ||||
| 
 | ||||
|     class MsgReportChannelSampleRateChanged : public Message { | ||||
|         MESSAGE_CLASS_DECLARATION | ||||
| 
 | ||||
|     public: | ||||
|         int getSampleRate() const { return m_sampleRate; } | ||||
| 
 | ||||
|         static MsgReportChannelSampleRateChanged* create(int sampleRate) | ||||
|         { | ||||
|             return new MsgReportChannelSampleRateChanged(sampleRate); | ||||
|         } | ||||
| 
 | ||||
|     private: | ||||
|         int m_sampleRate; | ||||
| 
 | ||||
|         MsgReportChannelSampleRateChanged(int sampleRate) : | ||||
|             Message(), | ||||
|             m_sampleRate(sampleRate) | ||||
|         { } | ||||
|     }; | ||||
| 
 | ||||
| 	BFMDemod(DeviceSourceAPI *deviceAPI); | ||||
| 	virtual ~BFMDemod(); | ||||
| 	void setSampleSink(BasebandSampleSink* sampleSink) { m_sampleSink = sampleSink; } | ||||
| 
 | ||||
| 	int getSampleRate() const { return m_settings.m_inputSampleRate; } | ||||
| 	virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool po); | ||||
| 	virtual void start(); | ||||
| 	virtual void stop(); | ||||
| 	virtual bool handleMessage(const Message& cmd); | ||||
| 
 | ||||
| 	double getMagSq() const { return m_magsq; } | ||||
| 
 | ||||
| 	bool getPilotLock() const { return m_pilotPLL.locked(); } | ||||
| 	Real getPilotLevel() const { return m_pilotPLL.get_pilot_level(); } | ||||
| 
 | ||||
| 	Real getDecoderQua() const { return m_rdsDecoder.m_qua; } | ||||
| 	bool getDecoderSynced() const { return m_rdsDecoder.synced(); } | ||||
| 	Real getDemodAcc() const { return m_rdsDemod.m_report.acc; } | ||||
| 	Real getDemodQua() const { return m_rdsDemod.m_report.qua; } | ||||
| 	Real getDemodFclk() const { return m_rdsDemod.m_report.fclk; } | ||||
| 
 | ||||
|     void getMagSqLevels(double& avg, double& peak, int& nbSamples) | ||||
|     { | ||||
|         avg = m_magsqCount == 0 ? 1e-10 : m_magsqSum / m_magsqCount; | ||||
|         m_magsq = avg; | ||||
|         peak = m_magsqPeak == 0.0 ? 1e-10 : m_magsqPeak; | ||||
|         nbSamples = m_magsqCount == 0 ? 1 : m_magsqCount; | ||||
|         m_magsqSum = 0.0f; | ||||
|         m_magsqPeak = 0.0f; | ||||
|         m_magsqCount = 0; | ||||
|     } | ||||
| 
 | ||||
|     RDSParser& getRDSParser() { return m_rdsParser; } | ||||
| 
 | ||||
|     static const QString m_channelID; | ||||
| 
 | ||||
| private slots: | ||||
|     void channelSampleRateChanged(); | ||||
| 
 | ||||
| private: | ||||
| 	enum RateState { | ||||
| 		RSInitialFill, | ||||
| 		RSRunning | ||||
| 	}; | ||||
| 
 | ||||
| 	DeviceSourceAPI *m_deviceAPI; | ||||
|     ThreadedBasebandSampleSink* m_threadedChannelizer; | ||||
|     DownChannelizer* m_channelizer; | ||||
| 
 | ||||
|     BFMDemodSettings m_settings; | ||||
| 
 | ||||
| 	NCO m_nco; | ||||
| 	Interpolator m_interpolator; //!< Interpolator between fixed demod bandwidth and audio bandwidth (rational)
 | ||||
| 	Real m_interpolatorDistance; | ||||
| 	Real m_interpolatorDistanceRemain; | ||||
| 
 | ||||
| 	Interpolator m_interpolatorStereo; //!< Twin Interpolator for stereo subcarrier
 | ||||
| 	Real m_interpolatorStereoDistance; | ||||
| 	Real m_interpolatorStereoDistanceRemain; | ||||
| 
 | ||||
| 	Interpolator m_interpolatorRDS; //!< Twin Interpolator for stereo subcarrier
 | ||||
| 	Real m_interpolatorRDSDistance; | ||||
| 	Real m_interpolatorRDSDistanceRemain; | ||||
| 
 | ||||
| 	Lowpass<Real> m_lowpass; | ||||
| 	fftfilt* m_rfFilter; | ||||
| 	static const int filtFftLen = 1024; | ||||
| 
 | ||||
| 	Real m_squelchLevel; | ||||
| 	int m_squelchState; | ||||
| 
 | ||||
| 	Real m_m1Arg; //!> x^-1 real sample
 | ||||
| 
 | ||||
|     double m_magsq; | ||||
|     double m_magsqSum; | ||||
|     double m_magsqPeak; | ||||
|     int    m_magsqCount; | ||||
| 
 | ||||
| 	AudioVector m_audioBuffer; | ||||
| 	uint m_audioBufferFill; | ||||
| 
 | ||||
| 	BasebandSampleSink* m_sampleSink; | ||||
| 	AudioFifo m_audioFifo; | ||||
| 	SampleVector m_sampleBuffer; | ||||
| 	QMutex m_settingsMutex; | ||||
| 
 | ||||
| 	RDSPhaseLock m_pilotPLL; | ||||
| 	Real m_pilotPLLSamples[4]; | ||||
| 
 | ||||
| 	RDSDemod m_rdsDemod; | ||||
| 	RDSDecoder m_rdsDecoder; | ||||
| 	RDSParser m_rdsParser; | ||||
| 
 | ||||
| 	LowPassFilterRC m_deemphasisFilterX; | ||||
| 	LowPassFilterRC m_deemphasisFilterY; | ||||
|     static const Real default_deemphasis; | ||||
| 
 | ||||
| 	Real m_fmExcursion; | ||||
| 	static const int default_excursion = 750000; // +/- 75 kHz
 | ||||
| 
 | ||||
| 	PhaseDiscriminators m_phaseDiscri; | ||||
|     UDPSink<AudioSample> *m_udpBufferAudio; | ||||
| 
 | ||||
|     static const int m_udpBlockSize; | ||||
| 
 | ||||
| 	void applySettings(const BFMDemodSettings& settings, bool force = false); | ||||
| }; | ||||
| 
 | ||||
| #endif // INCLUDE_BFMDEMOD_H
 | ||||
|  |  | |||
|  | @ -43,8 +43,6 @@ | |||
| #include "rdstmc.h" | ||||
| #include "ui_bfmdemodgui.h" | ||||
| 
 | ||||
| const QString BFMDemodGUI::m_channelID = "sdrangel.channel.bfm"; | ||||
| 
 | ||||
| BFMDemodGUI* BFMDemodGUI::create(PluginAPI* pluginAPI, DeviceUISet *deviceUIset) | ||||
| { | ||||
| 	BFMDemodGUI* gui = new BFMDemodGUI(pluginAPI, deviceUIset); | ||||
|  | @ -364,7 +362,7 @@ BFMDemodGUI::BFMDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, QWidget | |||
| 
 | ||||
| 	connect(&m_channelMarker, SIGNAL(changed()), this, SLOT(channelMarkerChanged())); | ||||
| 
 | ||||
| 	m_deviceUISet->registerRxChannelInstance(m_channelID, this); | ||||
| 	m_deviceUISet->registerRxChannelInstance(BFMDemod::m_channelID, this); | ||||
| 	m_deviceUISet->addChannelMarker(&m_channelMarker); | ||||
| 	m_deviceUISet->addRollupWidget(this); | ||||
| 
 | ||||
|  |  | |||
|  | @ -56,8 +56,6 @@ public: | |||
| 	virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } | ||||
| 	virtual bool handleMessage(const Message& message); | ||||
| 
 | ||||
| 	static const QString m_channelID; | ||||
| 
 | ||||
| private slots: | ||||
| 	void on_deltaFrequency_changed(qint64 value); | ||||
| 	void on_rfBW_valueChanged(int value); | ||||
|  |  | |||
|  | @ -21,6 +21,7 @@ | |||
| #include "plugin/pluginapi.h" | ||||
| 
 | ||||
| #include "bfmdemodgui.h" | ||||
| #include "bfmdemod.h" | ||||
| 
 | ||||
| const PluginDescriptor BFMPlugin::m_pluginDescriptor = { | ||||
| 	QString("Broadcast FM Demodulator"), | ||||
|  | @ -47,12 +48,12 @@ void BFMPlugin::initPlugin(PluginAPI* pluginAPI) | |||
| 	m_pluginAPI = pluginAPI; | ||||
| 
 | ||||
| 	// register BFM demodulator
 | ||||
| 	m_pluginAPI->registerRxChannel(BFMDemodGUI::m_channelID, this); | ||||
| 	m_pluginAPI->registerRxChannel(BFMDemod::m_channelID, this); | ||||
| } | ||||
| 
 | ||||
| PluginInstanceGUI* BFMPlugin::createRxChannelGUI(const QString& channelName, DeviceUISet *deviceUISet) | ||||
| { | ||||
| 	if(channelName == BFMDemodGUI::m_channelID) | ||||
| 	if(channelName == BFMDemod::m_channelID) | ||||
| 	{ | ||||
| 		BFMDemodGUI* gui = BFMDemodGUI::create(m_pluginAPI, deviceUISet); | ||||
| 		return gui; | ||||
|  | @ -60,3 +61,15 @@ PluginInstanceGUI* BFMPlugin::createRxChannelGUI(const QString& channelName, Dev | |||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| BasebandSampleSink* BFMPlugin::createRxChannel(const QString& channelName, DeviceSourceAPI *deviceAPI) | ||||
| { | ||||
|     if(channelName == BFMDemod::m_channelID) | ||||
|     { | ||||
|         BFMDemod* sink = new BFMDemod(deviceAPI); | ||||
|         return sink; | ||||
|     } else { | ||||
|         return 0; | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -35,6 +35,7 @@ public: | |||
| 	void initPlugin(PluginAPI* pluginAPI); | ||||
| 
 | ||||
| 	PluginInstanceGUI* createRxChannelGUI(const QString& channelName, DeviceUISet *deviceUISet); | ||||
|     BasebandSampleSink* createRxChannel(const QString& channelName, DeviceSourceAPI *deviceAPI); | ||||
| 
 | ||||
| private: | ||||
| 	static const PluginDescriptor m_pluginDescriptor; | ||||
|  |  | |||
|  | @ -35,6 +35,7 @@ MESSAGE_CLASS_DEFINITION(DSDDemod::MsgConfigureChannelizer, Message) | |||
| MESSAGE_CLASS_DEFINITION(DSDDemod::MsgConfigureDSDDemod, Message) | ||||
| MESSAGE_CLASS_DEFINITION(DSDDemod::MsgConfigureMyPosition, Message) | ||||
| 
 | ||||
| const QString DSDDemod::m_channelID = "sdrangel.channel.dsddemod"; | ||||
| const int DSDDemod::m_udpBlockSize = 512; | ||||
| 
 | ||||
| DSDDemod::DSDDemod(DeviceSourceAPI *deviceAPI) : | ||||
|  |  | |||
|  | @ -1,194 +1,195 @@ | |||
| ///////////////////////////////////////////////////////////////////////////////////
 | ||||
| // Copyright (C) 2016 F4EXB                                                      //
 | ||||
| // written by Edouard Griffiths                                                  //
 | ||||
| //                                                                               //
 | ||||
| // 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_DSDDEMOD_H | ||||
| #define INCLUDE_DSDDEMOD_H | ||||
| 
 | ||||
| #include <dsp/basebandsamplesink.h> | ||||
| #include <dsp/phasediscri.h> | ||||
| #include <QMutex> | ||||
| #include <vector> | ||||
| #include "dsp/nco.h" | ||||
| #include "dsp/interpolator.h" | ||||
| #include "dsp/lowpass.h" | ||||
| #include "dsp/bandpass.h" | ||||
| #include "dsp/afsquelch.h" | ||||
| #include "dsp/movingaverage.h" | ||||
| #include "dsp/afsquelch.h" | ||||
| #include "audio/audiofifo.h" | ||||
| #include "util/message.h" | ||||
| #include "util/udpsink.h" | ||||
| 
 | ||||
| #include "dsddemodsettings.h" | ||||
| #include "dsddecoder.h" | ||||
| 
 | ||||
| class DeviceSourceAPI; | ||||
| class ThreadedBasebandSampleSink; | ||||
| class DownChannelizer; | ||||
| 
 | ||||
| class DSDDemod : public BasebandSampleSink { | ||||
| public: | ||||
|     class MsgConfigureDSDDemod : public Message { | ||||
|         MESSAGE_CLASS_DECLARATION | ||||
| 
 | ||||
|     public: | ||||
|         const DSDDemodSettings& getSettings() const { return m_settings; } | ||||
|         bool getForce() const { return m_force; } | ||||
| 
 | ||||
|         static MsgConfigureDSDDemod* create(const DSDDemodSettings& settings, bool force) | ||||
|         { | ||||
|             return new MsgConfigureDSDDemod(settings, force); | ||||
|         } | ||||
| 
 | ||||
|     private: | ||||
|         DSDDemodSettings m_settings; | ||||
|         bool m_force; | ||||
| 
 | ||||
|         MsgConfigureDSDDemod(const DSDDemodSettings& settings, bool force) : | ||||
|             Message(), | ||||
|             m_settings(settings), | ||||
|             m_force(force) | ||||
|         { } | ||||
|     }; | ||||
| 
 | ||||
|     class MsgConfigureChannelizer : public Message { | ||||
|         MESSAGE_CLASS_DECLARATION | ||||
| 
 | ||||
|     public: | ||||
|         int getSampleRate() const { return m_sampleRate; } | ||||
|         int getCenterFrequency() const { return m_centerFrequency; } | ||||
| 
 | ||||
|         static MsgConfigureChannelizer* create(int sampleRate, int centerFrequency) | ||||
|         { | ||||
|             return new MsgConfigureChannelizer(sampleRate, centerFrequency); | ||||
|         } | ||||
| 
 | ||||
|     private: | ||||
|         int m_sampleRate; | ||||
|         int  m_centerFrequency; | ||||
| 
 | ||||
|         MsgConfigureChannelizer(int sampleRate, int centerFrequency) : | ||||
|             Message(), | ||||
|             m_sampleRate(sampleRate), | ||||
|             m_centerFrequency(centerFrequency) | ||||
|         { } | ||||
|     }; | ||||
| 
 | ||||
|     DSDDemod(DeviceSourceAPI *deviceAPI); | ||||
| 	~DSDDemod(); | ||||
| 	void setScopeSink(BasebandSampleSink* sampleSink) { m_scope = sampleSink; } | ||||
| 
 | ||||
| 	void configureMyPosition(MessageQueue* messageQueue, float myLatitude, float myLongitude); | ||||
| 
 | ||||
| 	virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool po); | ||||
| 	virtual void start(); | ||||
| 	virtual void stop(); | ||||
| 	virtual bool handleMessage(const Message& cmd); | ||||
| 
 | ||||
| 	double getMagSq() { return m_magsq; } | ||||
| 	bool getSquelchOpen() const { return m_squelchOpen; } | ||||
| 
 | ||||
| 	const DSDDecoder& getDecoder() const { return m_dsdDecoder; } | ||||
| 
 | ||||
|     void getMagSqLevels(double& avg, double& peak, int& nbSamples) | ||||
|     { | ||||
|         avg = m_magsqCount == 0 ? 1e-10 : m_magsqSum / m_magsqCount; | ||||
|         m_magsq = avg; | ||||
|         peak = m_magsqPeak == 0.0 ? 1e-10 : m_magsqPeak; | ||||
|         nbSamples = m_magsqCount == 0 ? 1 : m_magsqCount; | ||||
|         m_magsqSum = 0.0f; | ||||
|         m_magsqPeak = 0.0f; | ||||
|         m_magsqCount = 0; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| private: | ||||
| 	class MsgConfigureMyPosition : public Message { | ||||
| 		MESSAGE_CLASS_DECLARATION | ||||
| 
 | ||||
| 	public: | ||||
| 		float getMyLatitude() const { return m_myLatitude; } | ||||
| 		float getMyLongitude() const { return m_myLongitude; } | ||||
| 
 | ||||
| 		static MsgConfigureMyPosition* create(float myLatitude, float myLongitude) | ||||
| 		{ | ||||
| 			return new MsgConfigureMyPosition(myLatitude, myLongitude); | ||||
| 		} | ||||
| 
 | ||||
| 	private: | ||||
| 		float m_myLatitude; | ||||
| 		float m_myLongitude; | ||||
| 
 | ||||
| 		MsgConfigureMyPosition(float myLatitude, float myLongitude) : | ||||
| 			m_myLatitude(myLatitude), | ||||
| 			m_myLongitude(myLongitude) | ||||
| 		{} | ||||
| 	}; | ||||
| 
 | ||||
| 	enum RateState { | ||||
| 		RSInitialFill, | ||||
| 		RSRunning | ||||
| 	}; | ||||
| 
 | ||||
| 	DSDDemodSettings m_settings; | ||||
| 
 | ||||
| 	DeviceSourceAPI *m_deviceAPI; | ||||
|     ThreadedBasebandSampleSink* m_threadedChannelizer; | ||||
|     DownChannelizer* m_channelizer; | ||||
| 
 | ||||
| 	NCO m_nco; | ||||
| 	Interpolator m_interpolator; | ||||
| 	Real m_interpolatorDistance; | ||||
| 	Real m_interpolatorDistanceRemain; | ||||
| 	int m_sampleCount; | ||||
| 	int m_squelchCount; | ||||
| 	int m_squelchGate; | ||||
| 
 | ||||
| 	double m_squelchLevel; | ||||
| 	bool m_squelchOpen; | ||||
| 
 | ||||
|     MovingAverage<double> m_movingAverage; | ||||
|     double m_magsq; | ||||
|     double m_magsqSum; | ||||
|     double m_magsqPeak; | ||||
|     int  m_magsqCount; | ||||
| 
 | ||||
| 	Real m_fmExcursion; | ||||
| 
 | ||||
| 	SampleVector m_scopeSampleBuffer; | ||||
| 	AudioVector m_audioBuffer; | ||||
| 	uint m_audioBufferFill; | ||||
| 	qint16 *m_sampleBuffer; //!< samples ring buffer
 | ||||
| 	int m_sampleBufferIndex; | ||||
| 
 | ||||
| 	AudioFifo m_audioFifo1; | ||||
|     AudioFifo m_audioFifo2; | ||||
| 	BasebandSampleSink* m_scope; | ||||
| 	bool m_scopeEnabled; | ||||
| 
 | ||||
| 	DSDDecoder m_dsdDecoder; | ||||
| 	QMutex m_settingsMutex; | ||||
| 
 | ||||
|     PhaseDiscriminators m_phaseDiscri; | ||||
|     UDPSink<AudioSample> *m_udpBufferAudio; | ||||
| 
 | ||||
|     static const int m_udpBlockSize; | ||||
| 
 | ||||
| 	void applySettings(DSDDemodSettings& settings, bool force = false); | ||||
| }; | ||||
| 
 | ||||
| #endif // INCLUDE_DSDDEMOD_H
 | ||||
| ///////////////////////////////////////////////////////////////////////////////////
 | ||||
| // Copyright (C) 2016 F4EXB                                                      //
 | ||||
| // written by Edouard Griffiths                                                  //
 | ||||
| //                                                                               //
 | ||||
| // 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_DSDDEMOD_H | ||||
| #define INCLUDE_DSDDEMOD_H | ||||
| 
 | ||||
| #include <dsp/basebandsamplesink.h> | ||||
| #include <dsp/phasediscri.h> | ||||
| #include <QMutex> | ||||
| #include <vector> | ||||
| #include "dsp/nco.h" | ||||
| #include "dsp/interpolator.h" | ||||
| #include "dsp/lowpass.h" | ||||
| #include "dsp/bandpass.h" | ||||
| #include "dsp/afsquelch.h" | ||||
| #include "dsp/movingaverage.h" | ||||
| #include "dsp/afsquelch.h" | ||||
| #include "audio/audiofifo.h" | ||||
| #include "util/message.h" | ||||
| #include "util/udpsink.h" | ||||
| 
 | ||||
| #include "dsddemodsettings.h" | ||||
| #include "dsddecoder.h" | ||||
| 
 | ||||
| class DeviceSourceAPI; | ||||
| class ThreadedBasebandSampleSink; | ||||
| class DownChannelizer; | ||||
| 
 | ||||
| class DSDDemod : public BasebandSampleSink { | ||||
| public: | ||||
|     class MsgConfigureDSDDemod : public Message { | ||||
|         MESSAGE_CLASS_DECLARATION | ||||
| 
 | ||||
|     public: | ||||
|         const DSDDemodSettings& getSettings() const { return m_settings; } | ||||
|         bool getForce() const { return m_force; } | ||||
| 
 | ||||
|         static MsgConfigureDSDDemod* create(const DSDDemodSettings& settings, bool force) | ||||
|         { | ||||
|             return new MsgConfigureDSDDemod(settings, force); | ||||
|         } | ||||
| 
 | ||||
|     private: | ||||
|         DSDDemodSettings m_settings; | ||||
|         bool m_force; | ||||
| 
 | ||||
|         MsgConfigureDSDDemod(const DSDDemodSettings& settings, bool force) : | ||||
|             Message(), | ||||
|             m_settings(settings), | ||||
|             m_force(force) | ||||
|         { } | ||||
|     }; | ||||
| 
 | ||||
|     class MsgConfigureChannelizer : public Message { | ||||
|         MESSAGE_CLASS_DECLARATION | ||||
| 
 | ||||
|     public: | ||||
|         int getSampleRate() const { return m_sampleRate; } | ||||
|         int getCenterFrequency() const { return m_centerFrequency; } | ||||
| 
 | ||||
|         static MsgConfigureChannelizer* create(int sampleRate, int centerFrequency) | ||||
|         { | ||||
|             return new MsgConfigureChannelizer(sampleRate, centerFrequency); | ||||
|         } | ||||
| 
 | ||||
|     private: | ||||
|         int m_sampleRate; | ||||
|         int  m_centerFrequency; | ||||
| 
 | ||||
|         MsgConfigureChannelizer(int sampleRate, int centerFrequency) : | ||||
|             Message(), | ||||
|             m_sampleRate(sampleRate), | ||||
|             m_centerFrequency(centerFrequency) | ||||
|         { } | ||||
|     }; | ||||
| 
 | ||||
|     DSDDemod(DeviceSourceAPI *deviceAPI); | ||||
| 	~DSDDemod(); | ||||
| 	void setScopeSink(BasebandSampleSink* sampleSink) { m_scope = sampleSink; } | ||||
| 
 | ||||
| 	void configureMyPosition(MessageQueue* messageQueue, float myLatitude, float myLongitude); | ||||
| 
 | ||||
| 	virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool po); | ||||
| 	virtual void start(); | ||||
| 	virtual void stop(); | ||||
| 	virtual bool handleMessage(const Message& cmd); | ||||
| 
 | ||||
| 	double getMagSq() { return m_magsq; } | ||||
| 	bool getSquelchOpen() const { return m_squelchOpen; } | ||||
| 
 | ||||
| 	const DSDDecoder& getDecoder() const { return m_dsdDecoder; } | ||||
| 
 | ||||
|     void getMagSqLevels(double& avg, double& peak, int& nbSamples) | ||||
|     { | ||||
|         avg = m_magsqCount == 0 ? 1e-10 : m_magsqSum / m_magsqCount; | ||||
|         m_magsq = avg; | ||||
|         peak = m_magsqPeak == 0.0 ? 1e-10 : m_magsqPeak; | ||||
|         nbSamples = m_magsqCount == 0 ? 1 : m_magsqCount; | ||||
|         m_magsqSum = 0.0f; | ||||
|         m_magsqPeak = 0.0f; | ||||
|         m_magsqCount = 0; | ||||
|     } | ||||
| 
 | ||||
|     static const QString m_channelID; | ||||
| 
 | ||||
| private: | ||||
| 	class MsgConfigureMyPosition : public Message { | ||||
| 		MESSAGE_CLASS_DECLARATION | ||||
| 
 | ||||
| 	public: | ||||
| 		float getMyLatitude() const { return m_myLatitude; } | ||||
| 		float getMyLongitude() const { return m_myLongitude; } | ||||
| 
 | ||||
| 		static MsgConfigureMyPosition* create(float myLatitude, float myLongitude) | ||||
| 		{ | ||||
| 			return new MsgConfigureMyPosition(myLatitude, myLongitude); | ||||
| 		} | ||||
| 
 | ||||
| 	private: | ||||
| 		float m_myLatitude; | ||||
| 		float m_myLongitude; | ||||
| 
 | ||||
| 		MsgConfigureMyPosition(float myLatitude, float myLongitude) : | ||||
| 			m_myLatitude(myLatitude), | ||||
| 			m_myLongitude(myLongitude) | ||||
| 		{} | ||||
| 	}; | ||||
| 
 | ||||
| 	enum RateState { | ||||
| 		RSInitialFill, | ||||
| 		RSRunning | ||||
| 	}; | ||||
| 
 | ||||
| 	DSDDemodSettings m_settings; | ||||
| 
 | ||||
| 	DeviceSourceAPI *m_deviceAPI; | ||||
|     ThreadedBasebandSampleSink* m_threadedChannelizer; | ||||
|     DownChannelizer* m_channelizer; | ||||
| 
 | ||||
| 	NCO m_nco; | ||||
| 	Interpolator m_interpolator; | ||||
| 	Real m_interpolatorDistance; | ||||
| 	Real m_interpolatorDistanceRemain; | ||||
| 	int m_sampleCount; | ||||
| 	int m_squelchCount; | ||||
| 	int m_squelchGate; | ||||
| 
 | ||||
| 	double m_squelchLevel; | ||||
| 	bool m_squelchOpen; | ||||
| 
 | ||||
|     MovingAverage<double> m_movingAverage; | ||||
|     double m_magsq; | ||||
|     double m_magsqSum; | ||||
|     double m_magsqPeak; | ||||
|     int  m_magsqCount; | ||||
| 
 | ||||
| 	Real m_fmExcursion; | ||||
| 
 | ||||
| 	SampleVector m_scopeSampleBuffer; | ||||
| 	AudioVector m_audioBuffer; | ||||
| 	uint m_audioBufferFill; | ||||
| 	qint16 *m_sampleBuffer; //!< samples ring buffer
 | ||||
| 	int m_sampleBufferIndex; | ||||
| 
 | ||||
| 	AudioFifo m_audioFifo1; | ||||
|     AudioFifo m_audioFifo2; | ||||
| 	BasebandSampleSink* m_scope; | ||||
| 	bool m_scopeEnabled; | ||||
| 
 | ||||
| 	DSDDecoder m_dsdDecoder; | ||||
| 	QMutex m_settingsMutex; | ||||
| 
 | ||||
|     PhaseDiscriminators m_phaseDiscri; | ||||
|     UDPSink<AudioSample> *m_udpBufferAudio; | ||||
| 
 | ||||
|     static const int m_udpBlockSize; | ||||
| 
 | ||||
| 	void applySettings(DSDDemodSettings& settings, bool force = false); | ||||
| }; | ||||
| 
 | ||||
| #endif // INCLUDE_DSDDEMOD_H
 | ||||
|  |  | |||
|  | @ -38,8 +38,6 @@ | |||
| #include "dsddemodbaudrates.h" | ||||
| #include "dsddemod.h" | ||||
| 
 | ||||
| const QString DSDDemodGUI::m_channelID = "sdrangel.channel.dsddemod"; | ||||
| 
 | ||||
| DSDDemodGUI* DSDDemodGUI::create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet) | ||||
| { | ||||
|     DSDDemodGUI* gui = new DSDDemodGUI(pluginAPI, deviceUISet); | ||||
|  | @ -281,7 +279,7 @@ DSDDemodGUI::DSDDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, QWidget | |||
| 
 | ||||
| 	connect(&m_channelMarker, SIGNAL(changed()), this, SLOT(channelMarkerChanged())); | ||||
| 
 | ||||
| 	m_deviceUISet->registerRxChannelInstance(m_channelID, this); | ||||
| 	m_deviceUISet->registerRxChannelInstance(DSDDemod::m_channelID, this); | ||||
| 	m_deviceUISet->addChannelMarker(&m_channelMarker); | ||||
| 	m_deviceUISet->addRollupWidget(this); | ||||
| 
 | ||||
|  |  | |||
|  | @ -57,8 +57,6 @@ public: | |||
| 	virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } | ||||
| 	virtual bool handleMessage(const Message& message); | ||||
| 
 | ||||
| 	static const QString m_channelID; | ||||
| 
 | ||||
| private slots: | ||||
| 	void formatStatusText(); | ||||
| 	void channelMarkerChanged(); | ||||
|  |  | |||
|  | @ -21,6 +21,7 @@ | |||
| #include <QtPlugin> | ||||
| #include "plugin/pluginapi.h" | ||||
| #include "dsddemodgui.h" | ||||
| #include "dsddemod.h" | ||||
| 
 | ||||
| const PluginDescriptor DSDDemodPlugin::m_pluginDescriptor = { | ||||
| 	QString("DSD Demodulator"), | ||||
|  | @ -47,16 +48,27 @@ void DSDDemodPlugin::initPlugin(PluginAPI* pluginAPI) | |||
| 	m_pluginAPI = pluginAPI; | ||||
| 
 | ||||
| 	// register DSD demodulator
 | ||||
| 	m_pluginAPI->registerRxChannel(DSDDemodGUI::m_channelID, this); | ||||
| 	m_pluginAPI->registerRxChannel(DSDDemod::m_channelID, this); | ||||
| } | ||||
| 
 | ||||
| PluginInstanceGUI* DSDDemodPlugin::createRxChannelGUI(const QString& channelName, DeviceUISet *deviceUISet) | ||||
| { | ||||
| 	if(channelName == DSDDemodGUI::m_channelID) | ||||
| 	if(channelName == DSDDemod::m_channelID) | ||||
| 	{ | ||||
| 		DSDDemodGUI* gui = DSDDemodGUI::create(m_pluginAPI, deviceUISet); | ||||
| 		return gui; | ||||
| 	} else { | ||||
| 		return NULL; | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| BasebandSampleSink* DSDDemodPlugin::createRxChannel(const QString& channelName, DeviceSourceAPI *deviceAPI) | ||||
| { | ||||
|     if(channelName == DSDDemod::m_channelID) | ||||
|     { | ||||
|         DSDDemod* sink = new DSDDemod(deviceAPI); | ||||
|         return sink; | ||||
|     } else { | ||||
|         return 0; | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -22,6 +22,7 @@ | |||
| #include "plugin/plugininterface.h" | ||||
| 
 | ||||
| class DeviceUISet; | ||||
| class BasebandSampleSink; | ||||
| 
 | ||||
| class DSDDemodPlugin : public QObject, PluginInterface { | ||||
| 	Q_OBJECT | ||||
|  | @ -35,6 +36,7 @@ public: | |||
| 	void initPlugin(PluginAPI* pluginAPI); | ||||
| 
 | ||||
| 	PluginInstanceGUI* createRxChannelGUI(const QString& channelName, DeviceUISet *deviceUISet); | ||||
|     BasebandSampleSink* createRxChannel(const QString& channelName, DeviceSourceAPI *deviceAPI); | ||||
| 
 | ||||
| private: | ||||
| 	static const PluginDescriptor m_pluginDescriptor; | ||||
|  |  | |||
|  | @ -31,6 +31,8 @@ | |||
| MESSAGE_CLASS_DEFINITION(LoRaDemod::MsgConfigureLoRaDemod, Message) | ||||
| MESSAGE_CLASS_DEFINITION(LoRaDemod::MsgConfigureChannelizer, Message) | ||||
| 
 | ||||
| const QString LoRaDemod::m_channelID = "de.maintech.sdrangelove.channel.lora"; | ||||
| 
 | ||||
| LoRaDemod::LoRaDemod(DeviceSourceAPI* deviceAPI) : | ||||
|     m_deviceAPI(deviceAPI), | ||||
| 	m_sampleSink(0), | ||||
|  |  | |||
|  | @ -96,6 +96,8 @@ public: | |||
| 	virtual void stop(); | ||||
| 	virtual bool handleMessage(const Message& cmd); | ||||
| 
 | ||||
|     static const QString m_channelID; | ||||
| 
 | ||||
| private: | ||||
| 	int  detect(Complex sample, Complex angle); | ||||
| 	void dumpRaw(void); | ||||
|  |  | |||
|  | @ -16,8 +16,6 @@ | |||
| #include "lorademod.h" | ||||
| #include "lorademodgui.h" | ||||
| 
 | ||||
| const QString LoRaDemodGUI::m_channelID = "de.maintech.sdrangelove.channel.lora"; | ||||
| 
 | ||||
| LoRaDemodGUI* LoRaDemodGUI::create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet) | ||||
| { | ||||
| 	LoRaDemodGUI* gui = new LoRaDemodGUI(pluginAPI, deviceUISet); | ||||
|  | @ -153,7 +151,7 @@ LoRaDemodGUI::LoRaDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, QWidg | |||
| 
 | ||||
| 	connect(&m_channelMarker, SIGNAL(changed()), this, SLOT(viewChanged())); | ||||
| 
 | ||||
| 	m_deviceUISet->registerRxChannelInstance(m_channelID, this); | ||||
| 	m_deviceUISet->registerRxChannelInstance(LoRaDemod::m_channelID, this); | ||||
| 	m_deviceUISet->addChannelMarker(&m_channelMarker); | ||||
| 	m_deviceUISet->addRollupWidget(this); | ||||
| 
 | ||||
|  |  | |||
|  | @ -35,8 +35,6 @@ public: | |||
| 	virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } | ||||
| 	virtual bool handleMessage(const Message& message); | ||||
| 
 | ||||
| 	static const QString m_channelID; | ||||
| 
 | ||||
| private slots: | ||||
| 	void viewChanged(); | ||||
| 	void on_BW_valueChanged(int value); | ||||
|  |  | |||
|  | @ -3,6 +3,7 @@ | |||
| 
 | ||||
| #include "loraplugin.h" | ||||
| #include "lorademodgui.h" | ||||
| #include "lorademod.h" | ||||
| 
 | ||||
| const PluginDescriptor LoRaPlugin::m_pluginDescriptor = { | ||||
| 	QString("LoRa Demodulator"), | ||||
|  | @ -29,16 +30,27 @@ void LoRaPlugin::initPlugin(PluginAPI* pluginAPI) | |||
| 	m_pluginAPI = pluginAPI; | ||||
| 
 | ||||
| 	// register demodulator
 | ||||
| 	m_pluginAPI->registerRxChannel(LoRaDemodGUI::m_channelID, this); | ||||
| 	m_pluginAPI->registerRxChannel(LoRaDemod::m_channelID, this); | ||||
| } | ||||
| 
 | ||||
| PluginInstanceGUI* LoRaPlugin::createRxChannelGUI(const QString& channelName, DeviceUISet *deviceUISet) | ||||
| { | ||||
| 	if(channelName == LoRaDemodGUI::m_channelID) | ||||
| 	if(channelName == LoRaDemod::m_channelID) | ||||
| 	{ | ||||
| 		LoRaDemodGUI* gui = LoRaDemodGUI::create(m_pluginAPI, deviceUISet); | ||||
| 		return gui; | ||||
| 	} else { | ||||
| 		return NULL; | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| BasebandSampleSink* LoRaPlugin::createRxChannel(const QString& channelName, DeviceSourceAPI *deviceAPI) | ||||
| { | ||||
|     if(channelName == LoRaDemod::m_channelID) | ||||
|     { | ||||
|         LoRaDemod* sink = new LoRaDemod(deviceAPI); | ||||
|         return sink; | ||||
|     } else { | ||||
|         return 0; | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -5,6 +5,7 @@ | |||
| #include "plugin/plugininterface.h" | ||||
| 
 | ||||
| class DeviceUISet; | ||||
| class BasebandSampleSink; | ||||
| 
 | ||||
| class LoRaPlugin : public QObject, PluginInterface { | ||||
| 	Q_OBJECT | ||||
|  | @ -18,6 +19,7 @@ public: | |||
| 	void initPlugin(PluginAPI* pluginAPI); | ||||
| 
 | ||||
| 	PluginInstanceGUI* createRxChannelGUI(const QString& channelName, DeviceUISet *deviceUISet); | ||||
|     BasebandSampleSink* createRxChannel(const QString& channelName, DeviceSourceAPI *deviceAPI); | ||||
| 
 | ||||
| private: | ||||
| 	static const PluginDescriptor m_pluginDescriptor; | ||||
|  |  | |||
|  | @ -35,6 +35,8 @@ MESSAGE_CLASS_DEFINITION(NFMDemod::MsgConfigureNFMDemod, Message) | |||
| MESSAGE_CLASS_DEFINITION(NFMDemod::MsgConfigureChannelizer, Message) | ||||
| MESSAGE_CLASS_DEFINITION(NFMDemod::MsgReportCTCSSFreq, Message) | ||||
| 
 | ||||
| const QString NFMDemod::m_channelID = "de.maintech.sdrangelove.channel.nfm"; | ||||
| 
 | ||||
| static const double afSqTones[2] = {1000.0, 6000.0}; // {1200.0, 8000.0};
 | ||||
| const int NFMDemod::m_udpBlockSize = 512; | ||||
| 
 | ||||
|  |  | |||
|  | @ -138,6 +138,8 @@ public: | |||
|         m_magsqCount = 0; | ||||
|     } | ||||
| 
 | ||||
|     static const QString m_channelID; | ||||
| 
 | ||||
| private: | ||||
| 	enum RateState { | ||||
| 		RSInitialFill, | ||||
|  |  | |||
|  | @ -16,8 +16,6 @@ | |||
| #include "mainwindow.h" | ||||
| #include "nfmdemod.h" | ||||
| 
 | ||||
| const QString NFMDemodGUI::m_channelID = "de.maintech.sdrangelove.channel.nfm"; | ||||
| 
 | ||||
| NFMDemodGUI* NFMDemodGUI::create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet) | ||||
| { | ||||
| 	NFMDemodGUI* gui = new NFMDemodGUI(pluginAPI, deviceUISet); | ||||
|  | @ -277,7 +275,7 @@ NFMDemodGUI::NFMDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, QWidget | |||
| 
 | ||||
| 	connect(&m_channelMarker, SIGNAL(changed()), this, SLOT(channelMarkerChanged())); | ||||
| 
 | ||||
| 	m_deviceUISet->registerRxChannelInstance(m_channelID, this); | ||||
|     m_deviceUISet->registerRxChannelInstance(NFMDemod::m_channelID, this); | ||||
| 	m_deviceUISet->addChannelMarker(&m_channelMarker); | ||||
| 	m_deviceUISet->addRollupWidget(this); | ||||
| 
 | ||||
|  |  | |||
|  | @ -38,8 +38,6 @@ public: | |||
| 	virtual bool handleMessage(const Message& message); | ||||
| 	void setCtcssFreq(Real ctcssFreq); | ||||
| 
 | ||||
| 	static const QString m_channelID; | ||||
| 
 | ||||
| private slots: | ||||
| 	void channelMarkerChanged(); | ||||
| 	void on_deltaFrequency_changed(qint64 value); | ||||
|  |  | |||
|  | @ -3,6 +3,7 @@ | |||
| 
 | ||||
| #include "nfmplugin.h" | ||||
| #include "nfmdemodgui.h" | ||||
| #include "nfmdemod.h" | ||||
| 
 | ||||
| const PluginDescriptor NFMPlugin::m_pluginDescriptor = { | ||||
| 	QString("NFM Demodulator"), | ||||
|  | @ -29,15 +30,26 @@ void NFMPlugin::initPlugin(PluginAPI* pluginAPI) | |||
| 	m_pluginAPI = pluginAPI; | ||||
| 
 | ||||
| 	// register NFM demodulator
 | ||||
| 	m_pluginAPI->registerRxChannel(NFMDemodGUI::m_channelID, this); | ||||
| 	m_pluginAPI->registerRxChannel(NFMDemod::m_channelID, this); | ||||
| } | ||||
| 
 | ||||
| PluginInstanceGUI* NFMPlugin::createRxChannelGUI(const QString& channelName, DeviceUISet *deviceUISet) | ||||
| { | ||||
| 	if(channelName == NFMDemodGUI::m_channelID) { | ||||
| 	if(channelName == NFMDemod::m_channelID) { | ||||
| 		NFMDemodGUI* gui = NFMDemodGUI::create(m_pluginAPI, deviceUISet); | ||||
| 		return gui; | ||||
| 	} else { | ||||
| 		return NULL; | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| BasebandSampleSink* NFMPlugin::createRxChannel(const QString& channelName, DeviceSourceAPI *deviceAPI) | ||||
| { | ||||
|     if(channelName == NFMDemod::m_channelID) | ||||
|     { | ||||
|         NFMDemod* sink = new NFMDemod(deviceAPI); | ||||
|         return sink; | ||||
|     } else { | ||||
|         return 0; | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -5,6 +5,7 @@ | |||
| #include "plugin/plugininterface.h" | ||||
| 
 | ||||
| class DeviceUISet; | ||||
| class BasebandSampleSink; | ||||
| 
 | ||||
| class NFMPlugin : public QObject, PluginInterface { | ||||
| 	Q_OBJECT | ||||
|  | @ -18,6 +19,7 @@ public: | |||
| 	void initPlugin(PluginAPI* pluginAPI); | ||||
| 
 | ||||
| 	PluginInstanceGUI* createRxChannelGUI(const QString& channelName, DeviceUISet *deviceUISet); | ||||
|     BasebandSampleSink* createRxChannel(const QString& channelName, DeviceSourceAPI *deviceAPI); | ||||
| 
 | ||||
| private: | ||||
| 	static const PluginDescriptor m_pluginDescriptor; | ||||
|  |  | |||
|  | @ -34,6 +34,8 @@ MESSAGE_CLASS_DEFINITION(SSBDemod::MsgConfigureSSBDemod, Message) | |||
| MESSAGE_CLASS_DEFINITION(SSBDemod::MsgConfigureSSBDemodPrivate, Message) | ||||
| MESSAGE_CLASS_DEFINITION(SSBDemod::MsgConfigureChannelizer, Message) | ||||
| 
 | ||||
| const QString SSBDemod::m_channelID = "de.maintech.sdrangelove.channel.ssb"; | ||||
| 
 | ||||
| SSBDemod::SSBDemod(DeviceSourceAPI *deviceAPI) : | ||||
|     m_deviceAPI(deviceAPI), | ||||
| 	m_audioBinaual(false), | ||||
|  |  | |||
|  | @ -1,270 +1,272 @@ | |||
| ///////////////////////////////////////////////////////////////////////////////////
 | ||||
| // 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_SSBDEMOD_H | ||||
| #define INCLUDE_SSBDEMOD_H | ||||
| 
 | ||||
| #include <QMutex> | ||||
| #include <vector> | ||||
| 
 | ||||
| #include <dsp/basebandsamplesink.h> | ||||
| #include "dsp/ncof.h" | ||||
| #include "dsp/interpolator.h" | ||||
| #include "dsp/fftfilt.h" | ||||
| #include "dsp/agc.h" | ||||
| #include "audio/audiofifo.h" | ||||
| #include "util/message.h" | ||||
| 
 | ||||
| #include "ssbdemodsettings.h" | ||||
| 
 | ||||
| #define ssbFftLen 1024 | ||||
| #define agcTarget 3276.8 // -10 dB amplitude => -20 dB power: center of normal signal
 | ||||
| 
 | ||||
| class DeviceSourceAPI; | ||||
| class ThreadedBasebandSampleSink; | ||||
| class DownChannelizer; | ||||
| 
 | ||||
| class SSBDemod : public BasebandSampleSink { | ||||
| public: | ||||
|     class MsgConfigureSSBDemod : public Message { | ||||
|         MESSAGE_CLASS_DECLARATION | ||||
| 
 | ||||
|     public: | ||||
|         const SSBDemodSettings& getSettings() const { return m_settings; } | ||||
|         bool getForce() const { return m_force; } | ||||
| 
 | ||||
|         static MsgConfigureSSBDemod* create(const SSBDemodSettings& settings, bool force) | ||||
|         { | ||||
|             return new MsgConfigureSSBDemod(settings, force); | ||||
|         } | ||||
| 
 | ||||
|     private: | ||||
|         SSBDemodSettings m_settings; | ||||
|         bool m_force; | ||||
| 
 | ||||
|         MsgConfigureSSBDemod(const SSBDemodSettings& settings, bool force) : | ||||
|             Message(), | ||||
|             m_settings(settings), | ||||
|             m_force(force) | ||||
|         { } | ||||
|     }; | ||||
| 
 | ||||
|     class MsgConfigureChannelizer : public Message { | ||||
|         MESSAGE_CLASS_DECLARATION | ||||
| 
 | ||||
|     public: | ||||
|         int getSampleRate() const { return m_sampleRate; } | ||||
|         int getCenterFrequency() const { return m_centerFrequency; } | ||||
| 
 | ||||
|         static MsgConfigureChannelizer* create(int sampleRate, int centerFrequency) | ||||
|         { | ||||
|             return new MsgConfigureChannelizer(sampleRate, centerFrequency); | ||||
|         } | ||||
| 
 | ||||
|     private: | ||||
|         int m_sampleRate; | ||||
|         int  m_centerFrequency; | ||||
| 
 | ||||
|         MsgConfigureChannelizer(int sampleRate, int centerFrequency) : | ||||
|             Message(), | ||||
|             m_sampleRate(sampleRate), | ||||
|             m_centerFrequency(centerFrequency) | ||||
|         { } | ||||
|     }; | ||||
| 
 | ||||
| 	SSBDemod(DeviceSourceAPI *deviceAPI); | ||||
| 	virtual ~SSBDemod(); | ||||
| 	void setSampleSink(BasebandSampleSink* sampleSink) { m_sampleSink = sampleSink; } | ||||
| 
 | ||||
| 	void configure(MessageQueue* messageQueue, | ||||
| 			Real Bandwidth, | ||||
| 			Real LowCutoff, | ||||
| 			Real volume, | ||||
| 			int spanLog2, | ||||
| 			bool audioBinaural, | ||||
| 			bool audioFlipChannels, | ||||
| 			bool dsb, | ||||
| 			bool audioMute, | ||||
| 			bool agc, | ||||
| 			bool agcClamping, | ||||
| 			int agcTimeLog2, | ||||
| 			int agcPowerThreshold, | ||||
| 			int agcThresholdGate); | ||||
| 
 | ||||
| 	virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool positiveOnly); | ||||
| 	virtual void start(); | ||||
| 	virtual void stop(); | ||||
| 	virtual bool handleMessage(const Message& cmd); | ||||
| 
 | ||||
| 	double getMagSq() const { return m_magsq; } | ||||
| 	bool getAudioActive() const { return m_audioActive; } | ||||
| 
 | ||||
|     void getMagSqLevels(double& avg, double& peak, int& nbSamples) | ||||
|     { | ||||
|         avg = m_magsqCount == 0 ? 1e-10 : m_magsqSum / m_magsqCount; | ||||
|         m_magsq = avg; | ||||
|         peak = m_magsqPeak == 0.0 ? 1e-10 : m_magsqPeak; | ||||
|         nbSamples = m_magsqCount == 0 ? 1 : m_magsqCount; | ||||
|         m_magsqSum = 0.0f; | ||||
|         m_magsqPeak = 0.0f; | ||||
|         m_magsqCount = 0; | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
| 	class MsgConfigureSSBDemodPrivate : public Message { | ||||
| 		MESSAGE_CLASS_DECLARATION | ||||
| 
 | ||||
| 	public: | ||||
| 		Real getBandwidth() const { return m_Bandwidth; } | ||||
| 		Real getLoCutoff() const { return m_LowCutoff; } | ||||
| 		Real getVolume() const { return m_volume; } | ||||
| 		int  getSpanLog2() const { return m_spanLog2; } | ||||
| 		bool getAudioBinaural() const { return m_audioBinaural; } | ||||
| 		bool getAudioFlipChannels() const { return m_audioFlipChannels; } | ||||
| 		bool getDSB() const { return m_dsb; } | ||||
| 		bool getAudioMute() const { return m_audioMute; } | ||||
| 		bool getAGC() const { return m_agc; } | ||||
| 		bool getAGCClamping() const { return m_agcClamping; } | ||||
| 		int  getAGCTimeLog2() const { return m_agcTimeLog2; } | ||||
| 		int  getAGCPowerThershold() const { return m_agcPowerThreshold; } | ||||
|         int  getAGCThersholdGate() const { return m_agcThresholdGate; } | ||||
| 
 | ||||
| 		static MsgConfigureSSBDemodPrivate* create(Real Bandwidth, | ||||
| 				Real LowCutoff, | ||||
| 				Real volume, | ||||
| 				int spanLog2, | ||||
| 				bool audioBinaural, | ||||
| 				bool audioFlipChannels, | ||||
| 				bool dsb, | ||||
| 				bool audioMute, | ||||
|                 bool agc, | ||||
|                 bool agcClamping, | ||||
|                 int  agcTimeLog2, | ||||
|                 int  agcPowerThreshold, | ||||
|                 int  agcThresholdGate) | ||||
| 		{ | ||||
| 			return new MsgConfigureSSBDemodPrivate( | ||||
| 			        Bandwidth, | ||||
| 			        LowCutoff, | ||||
| 			        volume, | ||||
| 			        spanLog2, | ||||
| 			        audioBinaural, | ||||
| 			        audioFlipChannels, | ||||
| 			        dsb, | ||||
| 			        audioMute, | ||||
| 			        agc, | ||||
| 			        agcClamping, | ||||
| 			        agcTimeLog2, | ||||
| 			        agcPowerThreshold, | ||||
| 			        agcThresholdGate); | ||||
| 		} | ||||
| 
 | ||||
| 	private: | ||||
| 		Real m_Bandwidth; | ||||
| 		Real m_LowCutoff; | ||||
| 		Real m_volume; | ||||
| 		int  m_spanLog2; | ||||
| 		bool m_audioBinaural; | ||||
| 		bool m_audioFlipChannels; | ||||
| 		bool m_dsb; | ||||
| 		bool m_audioMute; | ||||
| 		bool m_agc; | ||||
| 		bool m_agcClamping; | ||||
| 		int  m_agcTimeLog2; | ||||
| 		int  m_agcPowerThreshold; | ||||
| 		int  m_agcThresholdGate; | ||||
| 
 | ||||
| 		MsgConfigureSSBDemodPrivate(Real Bandwidth, | ||||
| 				Real LowCutoff, | ||||
| 				Real volume, | ||||
| 				int spanLog2, | ||||
| 				bool audioBinaural, | ||||
| 				bool audioFlipChannels, | ||||
| 				bool dsb, | ||||
| 				bool audioMute, | ||||
| 				bool agc, | ||||
| 				bool agcClamping, | ||||
| 				int  agcTimeLog2, | ||||
| 				int  agcPowerThreshold, | ||||
| 				int  agcThresholdGate) : | ||||
| 			Message(), | ||||
| 			m_Bandwidth(Bandwidth), | ||||
| 			m_LowCutoff(LowCutoff), | ||||
| 			m_volume(volume), | ||||
| 			m_spanLog2(spanLog2), | ||||
| 			m_audioBinaural(audioBinaural), | ||||
| 			m_audioFlipChannels(audioFlipChannels), | ||||
| 			m_dsb(dsb), | ||||
| 			m_audioMute(audioMute), | ||||
| 			m_agc(agc), | ||||
| 			m_agcClamping(agcClamping), | ||||
| 			m_agcTimeLog2(agcTimeLog2), | ||||
| 			m_agcPowerThreshold(agcPowerThreshold), | ||||
| 			m_agcThresholdGate(agcThresholdGate) | ||||
| 		{ } | ||||
| 	}; | ||||
| 
 | ||||
| 	DeviceSourceAPI *m_deviceAPI; | ||||
|     ThreadedBasebandSampleSink* m_threadedChannelizer; | ||||
|     DownChannelizer* m_channelizer; | ||||
|     SSBDemodSettings m_settings; | ||||
| 
 | ||||
| 	Real m_Bandwidth; | ||||
| 	Real m_LowCutoff; | ||||
| 	Real m_volume; | ||||
| 	int m_spanLog2; | ||||
| 	fftfilt::cmplx m_sum; | ||||
| 	int m_undersampleCount; | ||||
| 	int m_sampleRate; | ||||
| 	int m_frequency; | ||||
| 	bool m_audioBinaual; | ||||
| 	bool m_audioFlipChannels; | ||||
| 	bool m_usb; | ||||
| 	bool m_dsb; | ||||
| 	bool m_audioMute; | ||||
| 	double m_magsq; | ||||
| 	double m_magsqSum; | ||||
| 	double m_magsqPeak; | ||||
|     int  m_magsqCount; | ||||
|     MagAGC m_agc; | ||||
|     bool m_agcActive; | ||||
|     bool m_agcClamping; | ||||
|     int m_agcNbSamples;         //!< number of audio (48 kHz) samples for AGC averaging
 | ||||
|     double m_agcPowerThreshold; //!< AGC power threshold (linear)
 | ||||
|     int m_agcThresholdGate;     //!< Gate length in number of samples befor threshold triggers
 | ||||
|     bool m_audioActive;         //!< True if an audio signal is produced (no AGC or AGC and above threshold)
 | ||||
| 
 | ||||
| 	NCOF m_nco; | ||||
| 	Interpolator m_interpolator; | ||||
| 	Real m_sampleDistanceRemain; | ||||
| 	fftfilt* SSBFilter; | ||||
| 	fftfilt* DSBFilter; | ||||
| 
 | ||||
| 	BasebandSampleSink* m_sampleSink; | ||||
| 	SampleVector m_sampleBuffer; | ||||
| 
 | ||||
| 	AudioVector m_audioBuffer; | ||||
| 	uint m_audioBufferFill; | ||||
| 	AudioFifo m_audioFifo; | ||||
| 	quint32 m_audioSampleRate; | ||||
| 
 | ||||
| 	QMutex m_settingsMutex; | ||||
| 
 | ||||
| 	void applySettings(const SSBDemodSettings& settings, bool force = false); | ||||
| }; | ||||
| 
 | ||||
| #endif // INCLUDE_SSBDEMOD_H
 | ||||
| ///////////////////////////////////////////////////////////////////////////////////
 | ||||
| // 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_SSBDEMOD_H | ||||
| #define INCLUDE_SSBDEMOD_H | ||||
| 
 | ||||
| #include <QMutex> | ||||
| #include <vector> | ||||
| 
 | ||||
| #include <dsp/basebandsamplesink.h> | ||||
| #include "dsp/ncof.h" | ||||
| #include "dsp/interpolator.h" | ||||
| #include "dsp/fftfilt.h" | ||||
| #include "dsp/agc.h" | ||||
| #include "audio/audiofifo.h" | ||||
| #include "util/message.h" | ||||
| 
 | ||||
| #include "ssbdemodsettings.h" | ||||
| 
 | ||||
| #define ssbFftLen 1024 | ||||
| #define agcTarget 3276.8 // -10 dB amplitude => -20 dB power: center of normal signal
 | ||||
| 
 | ||||
| class DeviceSourceAPI; | ||||
| class ThreadedBasebandSampleSink; | ||||
| class DownChannelizer; | ||||
| 
 | ||||
| class SSBDemod : public BasebandSampleSink { | ||||
| public: | ||||
|     class MsgConfigureSSBDemod : public Message { | ||||
|         MESSAGE_CLASS_DECLARATION | ||||
| 
 | ||||
|     public: | ||||
|         const SSBDemodSettings& getSettings() const { return m_settings; } | ||||
|         bool getForce() const { return m_force; } | ||||
| 
 | ||||
|         static MsgConfigureSSBDemod* create(const SSBDemodSettings& settings, bool force) | ||||
|         { | ||||
|             return new MsgConfigureSSBDemod(settings, force); | ||||
|         } | ||||
| 
 | ||||
|     private: | ||||
|         SSBDemodSettings m_settings; | ||||
|         bool m_force; | ||||
| 
 | ||||
|         MsgConfigureSSBDemod(const SSBDemodSettings& settings, bool force) : | ||||
|             Message(), | ||||
|             m_settings(settings), | ||||
|             m_force(force) | ||||
|         { } | ||||
|     }; | ||||
| 
 | ||||
|     class MsgConfigureChannelizer : public Message { | ||||
|         MESSAGE_CLASS_DECLARATION | ||||
| 
 | ||||
|     public: | ||||
|         int getSampleRate() const { return m_sampleRate; } | ||||
|         int getCenterFrequency() const { return m_centerFrequency; } | ||||
| 
 | ||||
|         static MsgConfigureChannelizer* create(int sampleRate, int centerFrequency) | ||||
|         { | ||||
|             return new MsgConfigureChannelizer(sampleRate, centerFrequency); | ||||
|         } | ||||
| 
 | ||||
|     private: | ||||
|         int m_sampleRate; | ||||
|         int  m_centerFrequency; | ||||
| 
 | ||||
|         MsgConfigureChannelizer(int sampleRate, int centerFrequency) : | ||||
|             Message(), | ||||
|             m_sampleRate(sampleRate), | ||||
|             m_centerFrequency(centerFrequency) | ||||
|         { } | ||||
|     }; | ||||
| 
 | ||||
| 	SSBDemod(DeviceSourceAPI *deviceAPI); | ||||
| 	virtual ~SSBDemod(); | ||||
| 	void setSampleSink(BasebandSampleSink* sampleSink) { m_sampleSink = sampleSink; } | ||||
| 
 | ||||
| 	void configure(MessageQueue* messageQueue, | ||||
| 			Real Bandwidth, | ||||
| 			Real LowCutoff, | ||||
| 			Real volume, | ||||
| 			int spanLog2, | ||||
| 			bool audioBinaural, | ||||
| 			bool audioFlipChannels, | ||||
| 			bool dsb, | ||||
| 			bool audioMute, | ||||
| 			bool agc, | ||||
| 			bool agcClamping, | ||||
| 			int agcTimeLog2, | ||||
| 			int agcPowerThreshold, | ||||
| 			int agcThresholdGate); | ||||
| 
 | ||||
| 	virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool positiveOnly); | ||||
| 	virtual void start(); | ||||
| 	virtual void stop(); | ||||
| 	virtual bool handleMessage(const Message& cmd); | ||||
| 
 | ||||
| 	double getMagSq() const { return m_magsq; } | ||||
| 	bool getAudioActive() const { return m_audioActive; } | ||||
| 
 | ||||
|     void getMagSqLevels(double& avg, double& peak, int& nbSamples) | ||||
|     { | ||||
|         avg = m_magsqCount == 0 ? 1e-10 : m_magsqSum / m_magsqCount; | ||||
|         m_magsq = avg; | ||||
|         peak = m_magsqPeak == 0.0 ? 1e-10 : m_magsqPeak; | ||||
|         nbSamples = m_magsqCount == 0 ? 1 : m_magsqCount; | ||||
|         m_magsqSum = 0.0f; | ||||
|         m_magsqPeak = 0.0f; | ||||
|         m_magsqCount = 0; | ||||
|     } | ||||
| 
 | ||||
|     static const QString m_channelID; | ||||
| 
 | ||||
| private: | ||||
| 	class MsgConfigureSSBDemodPrivate : public Message { | ||||
| 		MESSAGE_CLASS_DECLARATION | ||||
| 
 | ||||
| 	public: | ||||
| 		Real getBandwidth() const { return m_Bandwidth; } | ||||
| 		Real getLoCutoff() const { return m_LowCutoff; } | ||||
| 		Real getVolume() const { return m_volume; } | ||||
| 		int  getSpanLog2() const { return m_spanLog2; } | ||||
| 		bool getAudioBinaural() const { return m_audioBinaural; } | ||||
| 		bool getAudioFlipChannels() const { return m_audioFlipChannels; } | ||||
| 		bool getDSB() const { return m_dsb; } | ||||
| 		bool getAudioMute() const { return m_audioMute; } | ||||
| 		bool getAGC() const { return m_agc; } | ||||
| 		bool getAGCClamping() const { return m_agcClamping; } | ||||
| 		int  getAGCTimeLog2() const { return m_agcTimeLog2; } | ||||
| 		int  getAGCPowerThershold() const { return m_agcPowerThreshold; } | ||||
|         int  getAGCThersholdGate() const { return m_agcThresholdGate; } | ||||
| 
 | ||||
| 		static MsgConfigureSSBDemodPrivate* create(Real Bandwidth, | ||||
| 				Real LowCutoff, | ||||
| 				Real volume, | ||||
| 				int spanLog2, | ||||
| 				bool audioBinaural, | ||||
| 				bool audioFlipChannels, | ||||
| 				bool dsb, | ||||
| 				bool audioMute, | ||||
|                 bool agc, | ||||
|                 bool agcClamping, | ||||
|                 int  agcTimeLog2, | ||||
|                 int  agcPowerThreshold, | ||||
|                 int  agcThresholdGate) | ||||
| 		{ | ||||
| 			return new MsgConfigureSSBDemodPrivate( | ||||
| 			        Bandwidth, | ||||
| 			        LowCutoff, | ||||
| 			        volume, | ||||
| 			        spanLog2, | ||||
| 			        audioBinaural, | ||||
| 			        audioFlipChannels, | ||||
| 			        dsb, | ||||
| 			        audioMute, | ||||
| 			        agc, | ||||
| 			        agcClamping, | ||||
| 			        agcTimeLog2, | ||||
| 			        agcPowerThreshold, | ||||
| 			        agcThresholdGate); | ||||
| 		} | ||||
| 
 | ||||
| 	private: | ||||
| 		Real m_Bandwidth; | ||||
| 		Real m_LowCutoff; | ||||
| 		Real m_volume; | ||||
| 		int  m_spanLog2; | ||||
| 		bool m_audioBinaural; | ||||
| 		bool m_audioFlipChannels; | ||||
| 		bool m_dsb; | ||||
| 		bool m_audioMute; | ||||
| 		bool m_agc; | ||||
| 		bool m_agcClamping; | ||||
| 		int  m_agcTimeLog2; | ||||
| 		int  m_agcPowerThreshold; | ||||
| 		int  m_agcThresholdGate; | ||||
| 
 | ||||
| 		MsgConfigureSSBDemodPrivate(Real Bandwidth, | ||||
| 				Real LowCutoff, | ||||
| 				Real volume, | ||||
| 				int spanLog2, | ||||
| 				bool audioBinaural, | ||||
| 				bool audioFlipChannels, | ||||
| 				bool dsb, | ||||
| 				bool audioMute, | ||||
| 				bool agc, | ||||
| 				bool agcClamping, | ||||
| 				int  agcTimeLog2, | ||||
| 				int  agcPowerThreshold, | ||||
| 				int  agcThresholdGate) : | ||||
| 			Message(), | ||||
| 			m_Bandwidth(Bandwidth), | ||||
| 			m_LowCutoff(LowCutoff), | ||||
| 			m_volume(volume), | ||||
| 			m_spanLog2(spanLog2), | ||||
| 			m_audioBinaural(audioBinaural), | ||||
| 			m_audioFlipChannels(audioFlipChannels), | ||||
| 			m_dsb(dsb), | ||||
| 			m_audioMute(audioMute), | ||||
| 			m_agc(agc), | ||||
| 			m_agcClamping(agcClamping), | ||||
| 			m_agcTimeLog2(agcTimeLog2), | ||||
| 			m_agcPowerThreshold(agcPowerThreshold), | ||||
| 			m_agcThresholdGate(agcThresholdGate) | ||||
| 		{ } | ||||
| 	}; | ||||
| 
 | ||||
| 	DeviceSourceAPI *m_deviceAPI; | ||||
|     ThreadedBasebandSampleSink* m_threadedChannelizer; | ||||
|     DownChannelizer* m_channelizer; | ||||
|     SSBDemodSettings m_settings; | ||||
| 
 | ||||
| 	Real m_Bandwidth; | ||||
| 	Real m_LowCutoff; | ||||
| 	Real m_volume; | ||||
| 	int m_spanLog2; | ||||
| 	fftfilt::cmplx m_sum; | ||||
| 	int m_undersampleCount; | ||||
| 	int m_sampleRate; | ||||
| 	int m_frequency; | ||||
| 	bool m_audioBinaual; | ||||
| 	bool m_audioFlipChannels; | ||||
| 	bool m_usb; | ||||
| 	bool m_dsb; | ||||
| 	bool m_audioMute; | ||||
| 	double m_magsq; | ||||
| 	double m_magsqSum; | ||||
| 	double m_magsqPeak; | ||||
|     int  m_magsqCount; | ||||
|     MagAGC m_agc; | ||||
|     bool m_agcActive; | ||||
|     bool m_agcClamping; | ||||
|     int m_agcNbSamples;         //!< number of audio (48 kHz) samples for AGC averaging
 | ||||
|     double m_agcPowerThreshold; //!< AGC power threshold (linear)
 | ||||
|     int m_agcThresholdGate;     //!< Gate length in number of samples befor threshold triggers
 | ||||
|     bool m_audioActive;         //!< True if an audio signal is produced (no AGC or AGC and above threshold)
 | ||||
| 
 | ||||
| 	NCOF m_nco; | ||||
| 	Interpolator m_interpolator; | ||||
| 	Real m_sampleDistanceRemain; | ||||
| 	fftfilt* SSBFilter; | ||||
| 	fftfilt* DSBFilter; | ||||
| 
 | ||||
| 	BasebandSampleSink* m_sampleSink; | ||||
| 	SampleVector m_sampleBuffer; | ||||
| 
 | ||||
| 	AudioVector m_audioBuffer; | ||||
| 	uint m_audioBufferFill; | ||||
| 	AudioFifo m_audioFifo; | ||||
| 	quint32 m_audioSampleRate; | ||||
| 
 | ||||
| 	QMutex m_settingsMutex; | ||||
| 
 | ||||
| 	void applySettings(const SSBDemodSettings& settings, bool force = false); | ||||
| }; | ||||
| 
 | ||||
| #endif // INCLUDE_SSBDEMOD_H
 | ||||
|  |  | |||
|  | @ -17,8 +17,6 @@ | |||
| #include "mainwindow.h" | ||||
| #include "ssbdemod.h" | ||||
| 
 | ||||
| const QString SSBDemodGUI::m_channelID = "de.maintech.sdrangelove.channel.ssb"; | ||||
| 
 | ||||
| SSBDemodGUI* SSBDemodGUI::create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet) | ||||
| { | ||||
| 	SSBDemodGUI* gui = new SSBDemodGUI(pluginAPI, deviceUISet); | ||||
|  | @ -253,7 +251,7 @@ SSBDemodGUI::SSBDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, QWidget | |||
| 
 | ||||
| 	connect(&m_channelMarker, SIGNAL(changed()), this, SLOT(viewChanged())); | ||||
| 
 | ||||
| 	m_deviceUISet->registerRxChannelInstance(m_channelID, this); | ||||
| 	m_deviceUISet->registerRxChannelInstance(SSBDemod::m_channelID, this); | ||||
| 	m_deviceUISet->addChannelMarker(&m_channelMarker); | ||||
| 	m_deviceUISet->addRollupWidget(this); | ||||
| 
 | ||||
|  |  | |||
|  | @ -37,8 +37,6 @@ public: | |||
| 	virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } | ||||
| 	virtual bool handleMessage(const Message& message); | ||||
| 
 | ||||
| 	static const QString m_channelID; | ||||
| 
 | ||||
| private slots: | ||||
| 	void viewChanged(); | ||||
| 	void on_deltaFrequency_changed(qint64 value); | ||||
|  |  | |||
|  | @ -4,6 +4,7 @@ | |||
| #include <QtPlugin> | ||||
| #include "plugin/pluginapi.h" | ||||
| #include "ssbdemodgui.h" | ||||
| #include "ssbdemod.h" | ||||
| 
 | ||||
| const PluginDescriptor SSBPlugin::m_pluginDescriptor = { | ||||
| 	QString("SSB Demodulator"), | ||||
|  | @ -30,16 +31,27 @@ void SSBPlugin::initPlugin(PluginAPI* pluginAPI) | |||
| 	m_pluginAPI = pluginAPI; | ||||
| 
 | ||||
| 	// register demodulator
 | ||||
| 	m_pluginAPI->registerRxChannel(SSBDemodGUI::m_channelID, this); | ||||
| 	m_pluginAPI->registerRxChannel(SSBDemod::m_channelID, this); | ||||
| } | ||||
| 
 | ||||
| PluginInstanceGUI* SSBPlugin::createRxChannelGUI(const QString& channelName, DeviceUISet *deviceUISet) | ||||
| { | ||||
| 	if(channelName == SSBDemodGUI::m_channelID) | ||||
| 	if(channelName == SSBDemod::m_channelID) | ||||
| 	{ | ||||
| 		SSBDemodGUI* gui = SSBDemodGUI::create(m_pluginAPI, deviceUISet); | ||||
| 		return gui; | ||||
| 	} else { | ||||
| 		return NULL; | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| BasebandSampleSink* SSBPlugin::createRxChannel(const QString& channelName, DeviceSourceAPI *deviceAPI) | ||||
| { | ||||
|     if(channelName == SSBDemod::m_channelID) | ||||
|     { | ||||
|         SSBDemod* sink = new SSBDemod(deviceAPI); | ||||
|         return sink; | ||||
|     } else { | ||||
|         return 0; | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -5,6 +5,7 @@ | |||
| #include "plugin/plugininterface.h" | ||||
| 
 | ||||
| class DeviceUISet; | ||||
| class BasebandSampleSink; | ||||
| 
 | ||||
| class SSBPlugin : public QObject, PluginInterface { | ||||
| 	Q_OBJECT | ||||
|  | @ -18,6 +19,7 @@ public: | |||
| 	void initPlugin(PluginAPI* pluginAPI); | ||||
| 
 | ||||
| 	PluginInstanceGUI* createRxChannelGUI(const QString& channelName, DeviceUISet *deviceUISet); | ||||
|     BasebandSampleSink* createRxChannel(const QString& channelName, DeviceSourceAPI *deviceAPI); | ||||
| 
 | ||||
| private: | ||||
| 	static const PluginDescriptor m_pluginDescriptor; | ||||
|  |  | |||
|  | @ -33,6 +33,8 @@ | |||
| MESSAGE_CLASS_DEFINITION(WFMDemod::MsgConfigureWFMDemod, Message) | ||||
| MESSAGE_CLASS_DEFINITION(WFMDemod::MsgConfigureChannelizer, Message) | ||||
| 
 | ||||
| const QString WFMDemod::m_channelID = "de.maintech.sdrangelove.channel.wfm"; | ||||
| 
 | ||||
| WFMDemod::WFMDemod(DeviceSourceAPI* deviceAPI) : | ||||
|     m_deviceAPI(deviceAPI), | ||||
|     m_squelchOpen(false), | ||||
|  |  | |||
|  | @ -109,6 +109,8 @@ public: | |||
|         m_magsqCount = 0; | ||||
|     } | ||||
| 
 | ||||
|     static const QString m_channelID; | ||||
| 
 | ||||
| private: | ||||
| 	enum RateState { | ||||
| 		RSInitialFill, | ||||
|  |  | |||
|  | @ -18,8 +18,6 @@ | |||
| 
 | ||||
| #include "wfmdemod.h" | ||||
| 
 | ||||
| const QString WFMDemodGUI::m_channelID = "de.maintech.sdrangelove.channel.wfm"; | ||||
| 
 | ||||
| WFMDemodGUI* WFMDemodGUI::create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet) | ||||
| { | ||||
| 	WFMDemodGUI* gui = new WFMDemodGUI(pluginAPI, deviceUISet); | ||||
|  | @ -187,7 +185,7 @@ WFMDemodGUI::WFMDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, QWidget | |||
| 
 | ||||
| 	connect(&m_channelMarker, SIGNAL(changed()), this, SLOT(channelMarkerChanged())); | ||||
| 
 | ||||
| 	m_deviceUISet->registerRxChannelInstance(m_channelID, this); | ||||
| 	m_deviceUISet->registerRxChannelInstance(WFMDemod::m_channelID, this); | ||||
| 	m_deviceUISet->addChannelMarker(&m_channelMarker); | ||||
| 	m_deviceUISet->addRollupWidget(this); | ||||
| 
 | ||||
|  |  | |||
|  | @ -36,8 +36,6 @@ public: | |||
| 	virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } | ||||
| 	virtual bool handleMessage(const Message& message); | ||||
| 
 | ||||
| 	static const QString m_channelID; | ||||
| 
 | ||||
| private slots: | ||||
| 	void channelMarkerChanged(); | ||||
| 	void on_deltaFrequency_changed(qint64 value); | ||||
|  |  | |||
|  | @ -4,6 +4,7 @@ | |||
| #include "plugin/pluginapi.h" | ||||
| 
 | ||||
| #include "wfmdemodgui.h" | ||||
| #include "wfmdemod.h" | ||||
| 
 | ||||
| const PluginDescriptor WFMPlugin::m_pluginDescriptor = { | ||||
| 	QString("WFM Demodulator"), | ||||
|  | @ -30,16 +31,27 @@ void WFMPlugin::initPlugin(PluginAPI* pluginAPI) | |||
| 	m_pluginAPI = pluginAPI; | ||||
| 
 | ||||
| 	// register WFM demodulator
 | ||||
| 	m_pluginAPI->registerRxChannel(WFMDemodGUI::m_channelID, this); | ||||
| 	m_pluginAPI->registerRxChannel(WFMDemod::m_channelID, this); | ||||
| } | ||||
| 
 | ||||
| PluginInstanceGUI* WFMPlugin::createRxChannelGUI(const QString& channelName, DeviceUISet *deviceUISet) | ||||
| { | ||||
| 	if(channelName == WFMDemodGUI::m_channelID) | ||||
| 	if(channelName == WFMDemod::m_channelID) | ||||
| 	{ | ||||
| 		WFMDemodGUI* gui = WFMDemodGUI::create(m_pluginAPI, deviceUISet); | ||||
| 		return gui; | ||||
| 	} else { | ||||
| 		return NULL; | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| BasebandSampleSink* WFMPlugin::createRxChannel(const QString& channelName, DeviceSourceAPI *deviceAPI) | ||||
| { | ||||
|     if(channelName == WFMDemod::m_channelID) | ||||
|     { | ||||
|         WFMDemod* sink = new WFMDemod(deviceAPI); | ||||
|         return sink; | ||||
|     } else { | ||||
|         return 0; | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -5,6 +5,7 @@ | |||
| #include "plugin/plugininterface.h" | ||||
| 
 | ||||
| class DeviceUISet; | ||||
| class BasebandSampleSink; | ||||
| 
 | ||||
| class WFMPlugin : public QObject, PluginInterface { | ||||
| 	Q_OBJECT | ||||
|  | @ -18,6 +19,7 @@ public: | |||
| 	void initPlugin(PluginAPI* pluginAPI); | ||||
| 
 | ||||
| 	PluginInstanceGUI* createRxChannelGUI(const QString& channelName, DeviceUISet *deviceUISet); | ||||
|     BasebandSampleSink* createRxChannel(const QString& channelName, DeviceSourceAPI *deviceAPI); | ||||
| 
 | ||||
| private: | ||||
| 	static const PluginDescriptor m_pluginDescriptor; | ||||
|  |  | |||
|  | @ -30,6 +30,8 @@ MESSAGE_CLASS_DEFINITION(TCPSrc::MsgConfigureChannelizer, Message) | |||
| MESSAGE_CLASS_DEFINITION(TCPSrc::MsgTCPSrcConnection, Message) | ||||
| MESSAGE_CLASS_DEFINITION(TCPSrc::MsgTCPSrcSpectrum, Message) | ||||
| 
 | ||||
| const QString TCPSrc::m_channelID = "sdrangel.channel.tcpsrc"; | ||||
| 
 | ||||
| TCPSrc::TCPSrc(DeviceSourceAPI* deviceAPI) : | ||||
|     m_deviceAPI(deviceAPI), | ||||
| 	m_settingsMutex(QMutex::Recursive) | ||||
|  |  | |||
|  | @ -113,6 +113,8 @@ public: | |||
| 	virtual void stop(); | ||||
| 	virtual bool handleMessage(const Message& cmd); | ||||
| 
 | ||||
|     static const QString m_channelID; | ||||
| 
 | ||||
| protected: | ||||
| 	class MsgTCPSrcSpectrum : public Message { | ||||
| 		MESSAGE_CLASS_DECLARATION | ||||
|  |  | |||
|  | @ -12,8 +12,6 @@ | |||
| #include "mainwindow.h" | ||||
| #include "tcpsrc.h" | ||||
| 
 | ||||
| const QString TCPSrcGUI::m_channelID = "sdrangel.channel.tcpsrc"; | ||||
| 
 | ||||
| TCPSrcGUI* TCPSrcGUI::create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet) | ||||
| { | ||||
| 	TCPSrcGUI* gui = new TCPSrcGUI(pluginAPI, deviceUISet); | ||||
|  | @ -165,7 +163,7 @@ TCPSrcGUI::TCPSrcGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, QWidget* pa | |||
| 
 | ||||
| 	connect(&m_channelMarker, SIGNAL(changed()), this, SLOT(channelMarkerChanged())); | ||||
| 
 | ||||
| 	m_deviceUISet->registerRxChannelInstance(m_channelID, this); | ||||
| 	m_deviceUISet->registerRxChannelInstance(TCPSrc::m_channelID, this); | ||||
| 	m_deviceUISet->addChannelMarker(&m_channelMarker); | ||||
| 	m_deviceUISet->addRollupWidget(this); | ||||
| 
 | ||||
|  |  | |||
|  | @ -39,8 +39,6 @@ public: | |||
| 	virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } | ||||
| 	virtual bool handleMessage(const Message& message); | ||||
| 
 | ||||
| 	static const QString m_channelID; | ||||
| 
 | ||||
| private slots: | ||||
| 	void channelMarkerChanged(); | ||||
| 	void on_deltaFrequency_changed(qint64 value); | ||||
|  |  | |||
|  | @ -4,6 +4,7 @@ | |||
| #include "plugin/pluginapi.h" | ||||
| 
 | ||||
| #include "tcpsrcgui.h" | ||||
| #include "tcpsrc.h" | ||||
| 
 | ||||
| const PluginDescriptor TCPSrcPlugin::m_pluginDescriptor = { | ||||
| 	QString("TCP Channel Source"), | ||||
|  | @ -30,18 +31,29 @@ void TCPSrcPlugin::initPlugin(PluginAPI* pluginAPI) | |||
| 	m_pluginAPI = pluginAPI; | ||||
| 
 | ||||
| 	// register TCP Channel Source
 | ||||
| 	m_pluginAPI->registerRxChannel(TCPSrcGUI::m_channelID, this); | ||||
| 	m_pluginAPI->registerRxChannel(TCPSrc::m_channelID, this); | ||||
| } | ||||
| 
 | ||||
| PluginInstanceGUI* TCPSrcPlugin::createRxChannelGUI(const QString& channelName, DeviceUISet *deviceUISet) | ||||
| { | ||||
| 	if(channelName == TCPSrcGUI::m_channelID) | ||||
| 	if(channelName == TCPSrc::m_channelID) | ||||
| 	{ | ||||
| 		TCPSrcGUI* gui = TCPSrcGUI::create(m_pluginAPI, deviceUISet); | ||||
| //		deviceAPI->registerChannelInstance("sdrangel.channel.tcpsrc", gui);
 | ||||
| //		m_pluginAPI->addChannelRollup(gui);
 | ||||
| 		return gui; | ||||
| 	} else { | ||||
| 		return NULL; | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| BasebandSampleSink* TCPSrcPlugin::createRxChannel(const QString& channelName, DeviceSourceAPI *deviceAPI) | ||||
| { | ||||
|     if(channelName == TCPSrc::m_channelID) | ||||
|     { | ||||
|         TCPSrc* sink = new TCPSrc(deviceAPI); | ||||
|         return sink; | ||||
|     } else { | ||||
|         return 0; | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -5,6 +5,7 @@ | |||
| #include "plugin/plugininterface.h" | ||||
| 
 | ||||
| class DeviceUISet; | ||||
| class BasebandSampleSink; | ||||
| 
 | ||||
| class TCPSrcPlugin : public QObject, PluginInterface { | ||||
| 	Q_OBJECT | ||||
|  | @ -18,6 +19,7 @@ public: | |||
| 	void initPlugin(PluginAPI* pluginAPI); | ||||
| 
 | ||||
| 	PluginInstanceGUI* createRxChannelGUI(const QString& channelName, DeviceUISet *deviceUISet); | ||||
|     BasebandSampleSink* createRxChannel(const QString& channelName, DeviceSourceAPI *deviceAPI); | ||||
| 
 | ||||
| private: | ||||
| 	static const PluginDescriptor m_pluginDescriptor; | ||||
|  |  | |||
|  | @ -33,6 +33,8 @@ MESSAGE_CLASS_DEFINITION(UDPSrc::MsgConfigureUDPSrc, Message) | |||
| MESSAGE_CLASS_DEFINITION(UDPSrc::MsgConfigureChannelizer, Message) | ||||
| MESSAGE_CLASS_DEFINITION(UDPSrc::MsgUDPSrcSpectrum, Message) | ||||
| 
 | ||||
| const QString UDPSrc::m_channelID = "sdrangel.channel.udpsrc"; | ||||
| 
 | ||||
| UDPSrc::UDPSrc(DeviceSourceAPI *deviceAPI) : | ||||
|     m_deviceAPI(deviceAPI), | ||||
|     m_outMovingAverage(480, 1e-10), | ||||
|  |  | |||
|  | @ -104,6 +104,7 @@ public: | |||
| 	virtual void stop(); | ||||
| 	virtual bool handleMessage(const Message& cmd); | ||||
| 
 | ||||
|     static const QString m_channelID; | ||||
| 	static const int udpBlockSize = 512; // UDP block size in number of bytes
 | ||||
| 
 | ||||
| public slots: | ||||
|  |  | |||
|  | @ -30,8 +30,6 @@ | |||
| 
 | ||||
| #include "udpsrc.h" | ||||
| 
 | ||||
| const QString UDPSrcGUI::m_channelID = "sdrangel.channel.udpsrc"; | ||||
| 
 | ||||
| UDPSrcGUI* UDPSrcGUI::create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet) | ||||
| { | ||||
| 	UDPSrcGUI* gui = new UDPSrcGUI(pluginAPI, deviceUISet); | ||||
|  | @ -185,7 +183,7 @@ UDPSrcGUI::UDPSrcGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, QWidget* pa | |||
| 
 | ||||
| 	connect(&m_channelMarker, SIGNAL(changed()), this, SLOT(channelMarkerChanged())); | ||||
| 
 | ||||
| 	m_deviceUISet->registerRxChannelInstance(m_channelID, this); | ||||
| 	m_deviceUISet->registerRxChannelInstance(UDPSrc::m_channelID, this); | ||||
| 	m_deviceUISet->addChannelMarker(&m_channelMarker); | ||||
| 	m_deviceUISet->addRollupWidget(this); | ||||
| 
 | ||||
|  |  | |||
|  | @ -55,8 +55,6 @@ public: | |||
| 	virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } | ||||
| 	virtual bool handleMessage(const Message& message); | ||||
| 
 | ||||
| 	static const QString m_channelID; | ||||
| 
 | ||||
| private slots: | ||||
| 	void channelMarkerChanged(); | ||||
| 	void on_deltaFrequency_changed(qint64 value); | ||||
|  |  | |||
|  | @ -21,6 +21,7 @@ | |||
| #include "plugin/pluginapi.h" | ||||
| 
 | ||||
| #include "udpsrcgui.h" | ||||
| #include "udpsrc.h" | ||||
| 
 | ||||
| const PluginDescriptor UDPSrcPlugin::m_pluginDescriptor = { | ||||
| 	QString("UDP Channel Source"), | ||||
|  | @ -47,12 +48,12 @@ void UDPSrcPlugin::initPlugin(PluginAPI* pluginAPI) | |||
| 	m_pluginAPI = pluginAPI; | ||||
| 
 | ||||
| 	// register TCP Channel Source
 | ||||
| 	m_pluginAPI->registerRxChannel(UDPSrcGUI::m_channelID, this); | ||||
| 	m_pluginAPI->registerRxChannel(UDPSrc::m_channelID, this); | ||||
| } | ||||
| 
 | ||||
| PluginInstanceGUI* UDPSrcPlugin::createRxChannelGUI(const QString& channelName, DeviceUISet *deviceUISet) | ||||
| { | ||||
| 	if(channelName == UDPSrcGUI::m_channelID) | ||||
| 	if(channelName == UDPSrc::m_channelID) | ||||
| 	{ | ||||
| 		UDPSrcGUI* gui = UDPSrcGUI::create(m_pluginAPI, deviceUISet); | ||||
| //		deviceAPI->registerChannelInstance("sdrangel.channel.udpsrc", gui);
 | ||||
|  | @ -62,3 +63,14 @@ PluginInstanceGUI* UDPSrcPlugin::createRxChannelGUI(const QString& channelName, | |||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| BasebandSampleSink* UDPSrcPlugin::createRxChannel(const QString& channelName, DeviceSourceAPI *deviceAPI) | ||||
| { | ||||
|     if(channelName == UDPSrc::m_channelID) | ||||
|     { | ||||
|         UDPSrc* sink = new UDPSrc(deviceAPI); | ||||
|         return sink; | ||||
|     } else { | ||||
|         return 0; | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -22,6 +22,7 @@ | |||
| #include "plugin/plugininterface.h" | ||||
| 
 | ||||
| class DeviceUISet; | ||||
| class BasebandSampleSink; | ||||
| 
 | ||||
| class UDPSrcPlugin : public QObject, PluginInterface { | ||||
| 	Q_OBJECT | ||||
|  | @ -35,6 +36,7 @@ public: | |||
| 	void initPlugin(PluginAPI* pluginAPI); | ||||
| 
 | ||||
| 	PluginInstanceGUI* createRxChannelGUI(const QString& channelName, DeviceUISet *deviceUISet); | ||||
|     BasebandSampleSink* createRxChannel(const QString& channelName, DeviceSourceAPI *deviceAPI); | ||||
| 
 | ||||
| private: | ||||
| 	static const PluginDescriptor m_pluginDescriptor; | ||||
|  |  | |||
		Ładowanie…
	
		Reference in New Issue
	
	 f4exb
						f4exb