mirror of
				https://github.com/f4exb/sdrangel.git
				synced 2025-10-30 20:40:20 -04:00 
			
		
		
		
	BFM demod: apply de-emphasis
This commit is contained in:
		
							parent
							
								
									2f8fda7137
								
							
						
					
					
						commit
						e533997dbe
					
				| @ -79,6 +79,7 @@ set(sdrbase_SOURCES | |||||||
| 	sdrbase/dsp/fftengine.cpp | 	sdrbase/dsp/fftengine.cpp | ||||||
| 	sdrbase/dsp/fftfilt.cxx | 	sdrbase/dsp/fftfilt.cxx | ||||||
| 	sdrbase/dsp/fftwindow.cpp | 	sdrbase/dsp/fftwindow.cpp | ||||||
|  | 	sdrbase/dsp/filterrc.cpp | ||||||
| 	sdrbase/dsp/filesink.cpp | 	sdrbase/dsp/filesink.cpp | ||||||
| 	sdrbase/dsp/interpolator.cpp | 	sdrbase/dsp/interpolator.cpp | ||||||
| 	sdrbase/dsp/inthalfbandfilter.cpp | 	sdrbase/dsp/inthalfbandfilter.cpp | ||||||
| @ -155,6 +156,7 @@ set(sdrbase_HEADERS | |||||||
| 	include/dsp/fftfilt.h | 	include/dsp/fftfilt.h | ||||||
| 	include/dsp/fftwengine.h | 	include/dsp/fftwengine.h | ||||||
| 	include/dsp/fftwindow.h | 	include/dsp/fftwindow.h | ||||||
|  | 	include/dsp/filterrc.h | ||||||
| 	include/dsp/filesink.h | 	include/dsp/filesink.h | ||||||
| 	include/dsp/gfft.h | 	include/dsp/gfft.h | ||||||
| 	include/dsp/interpolator.h | 	include/dsp/interpolator.h | ||||||
|  | |||||||
							
								
								
									
										50
									
								
								include/dsp/filterrc.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								include/dsp/filterrc.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,50 @@ | |||||||
|  | ///////////////////////////////////////////////////////////////////////////////////
 | ||||||
|  | // 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_DSP_FILTERRC_H_ | ||||||
|  | #define INCLUDE_DSP_FILTERRC_H_ | ||||||
|  | 
 | ||||||
|  | #include "dsp/dsptypes.h" | ||||||
|  | 
 | ||||||
|  | /** First order low-pass IIR filter for real-valued signals. */ | ||||||
|  | class LowPassFilterRC | ||||||
|  | { | ||||||
|  | public: | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Construct 1st order low-pass IIR filter. | ||||||
|  |      * | ||||||
|  |      * timeconst :: RC time constant in seconds (1 / (2 * PI * cutoff_freq) | ||||||
|  |      */ | ||||||
|  |     LowPassFilterRC(Real timeconst); | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Reconfigure filter with new time constant | ||||||
|  |      */ | ||||||
|  |     void configure(Real timeout); | ||||||
|  | 
 | ||||||
|  |     /** Process samples. */ | ||||||
|  |     void process(const Real& sample_in, Real& sample_out); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     Real m_timeconst; | ||||||
|  |     Real m_y1; | ||||||
|  |     Real m_a1; | ||||||
|  |     Real m_b0; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | #endif /* INCLUDE_DSP_FILTERRC_H_ */ | ||||||
| @ -32,7 +32,9 @@ BFMDemod::BFMDemod(SampleSink* sampleSink) : | |||||||
| 	m_sampleSink(sampleSink), | 	m_sampleSink(sampleSink), | ||||||
| 	m_audioFifo(4, 250000), | 	m_audioFifo(4, 250000), | ||||||
| 	m_settingsMutex(QMutex::Recursive), | 	m_settingsMutex(QMutex::Recursive), | ||||||
| 	m_pilotPLL(19000/384000, 50/384000, 0.01) | 	m_pilotPLL(19000/384000, 50/384000, 0.01), | ||||||
|  | 	m_deemphasisFilterX(default_deemphasis * 48000 * 1.0e-6), | ||||||
|  | 	m_deemphasisFilterY(default_deemphasis * 48000 * 1.0e-6) | ||||||
| { | { | ||||||
| 	setObjectName("BFMDemod"); | 	setObjectName("BFMDemod"); | ||||||
| 
 | 
 | ||||||
| @ -43,6 +45,8 @@ BFMDemod::BFMDemod(SampleSink* sampleSink) : | |||||||
| 	m_config.m_squelch = -60.0; | 	m_config.m_squelch = -60.0; | ||||||
| 	m_config.m_volume = 2.0; | 	m_config.m_volume = 2.0; | ||||||
| 	m_config.m_audioSampleRate = DSPEngine::instance()->getAudioSampleRate(); // normally 48 kHz
 | 	m_config.m_audioSampleRate = DSPEngine::instance()->getAudioSampleRate(); // normally 48 kHz
 | ||||||
|  | 	m_deemphasisFilterX.configure(default_deemphasis * m_config.m_audioSampleRate * 1.0e-6); | ||||||
|  | 	m_deemphasisFilterY.configure(default_deemphasis * m_config.m_audioSampleRate * 1.0e-6); | ||||||
| 	m_rfFilter = new fftfilt(-50000.0 / 384000.0, 50000.0 / 384000.0, rfFilterFftLength); | 	m_rfFilter = new fftfilt(-50000.0 / 384000.0, 50000.0 / 384000.0, rfFilterFftLength); | ||||||
| 
 | 
 | ||||||
| 	apply(); | 	apply(); | ||||||
| @ -120,7 +124,7 @@ void BFMDemod::feed(const SampleVector::const_iterator& begin, const SampleVecto | |||||||
| 			m_m1Sample = rf[i]; | 			m_m1Sample = rf[i]; | ||||||
| 
 | 
 | ||||||
| 			m_sampleBuffer.push_back(Sample(demod * (1<<15), 0.0)); | 			m_sampleBuffer.push_back(Sample(demod * (1<<15), 0.0)); | ||||||
| 			quint16 sampleStereo; | 			Real sampleStereo; | ||||||
| 
 | 
 | ||||||
| 			// Process stereo if stereo mode is selected
 | 			// Process stereo if stereo mode is selected
 | ||||||
| 
 | 
 | ||||||
| @ -135,7 +139,7 @@ void BFMDemod::feed(const SampleVector::const_iterator& begin, const SampleVecto | |||||||
| 
 | 
 | ||||||
| 				if (m_interpolatorStereo.interpolate(&m_interpolatorStereoDistanceRemain, s, &cs)) | 				if (m_interpolatorStereo.interpolate(&m_interpolatorStereoDistanceRemain, s, &cs)) | ||||||
| 				{ | 				{ | ||||||
| 					sampleStereo = (qint16)(cs.real() * 3000 * m_running.m_volume); | 					sampleStereo = cs.real(); | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| @ -143,15 +147,19 @@ void BFMDemod::feed(const SampleVector::const_iterator& begin, const SampleVecto | |||||||
| 
 | 
 | ||||||
| 			if (m_interpolator.interpolate(&m_interpolatorDistanceRemain, e, &ci)) | 			if (m_interpolator.interpolate(&m_interpolatorDistanceRemain, e, &ci)) | ||||||
| 			{ | 			{ | ||||||
| 				quint16 sample = (qint16)(ci.real() * 3000 * m_running.m_volume); |  | ||||||
| 
 |  | ||||||
| 				if (m_running.m_audioStereo) | 				if (m_running.m_audioStereo) | ||||||
| 				{ | 				{ | ||||||
| 					m_audioBuffer[m_audioBufferFill].l = sample + sampleStereo; | 					Real deemph_l, deemph_r; // Pre-emphasis is applied on each channel before multiplexing
 | ||||||
| 					m_audioBuffer[m_audioBufferFill].r = sample - sampleStereo; | 					m_deemphasisFilterX.process(ci.real() + sampleStereo, deemph_l); | ||||||
|  | 					m_deemphasisFilterY.process(ci.real() - sampleStereo, deemph_r); | ||||||
|  | 					m_audioBuffer[m_audioBufferFill].l = (qint16)(deemph_l * 3000 * m_running.m_volume); | ||||||
|  | 					m_audioBuffer[m_audioBufferFill].r = (qint16)(deemph_r * 3000 * m_running.m_volume); | ||||||
| 				} | 				} | ||||||
| 				else | 				else | ||||||
| 				{ | 				{ | ||||||
|  | 					Real deemph; | ||||||
|  | 					m_deemphasisFilterX.process(ci.real() + sampleStereo, deemph); | ||||||
|  | 					quint16 sample = (qint16)(deemph * 3000 * m_running.m_volume); | ||||||
| 					m_audioBuffer[m_audioBufferFill].l = sample; | 					m_audioBuffer[m_audioBufferFill].l = sample; | ||||||
| 					m_audioBuffer[m_audioBufferFill].r = sample; | 					m_audioBuffer[m_audioBufferFill].r = sample; | ||||||
| 				} | 				} | ||||||
| @ -319,6 +327,12 @@ void BFMDemod::apply() | |||||||
| 		m_squelchLevel *= m_squelchLevel; | 		m_squelchLevel *= m_squelchLevel; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	if (m_config.m_audioSampleRate != m_running.m_audioSampleRate) | ||||||
|  | 	{ | ||||||
|  | 		m_deemphasisFilterX.configure(default_deemphasis * m_config.m_audioSampleRate * 1.0e-6); | ||||||
|  | 		m_deemphasisFilterY.configure(default_deemphasis * m_config.m_audioSampleRate * 1.0e-6); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	m_running.m_inputSampleRate = m_config.m_inputSampleRate; | 	m_running.m_inputSampleRate = m_config.m_inputSampleRate; | ||||||
| 	m_running.m_inputFrequencyOffset = m_config.m_inputFrequencyOffset; | 	m_running.m_inputFrequencyOffset = m_config.m_inputFrequencyOffset; | ||||||
| 	m_running.m_rfBandwidth = m_config.m_rfBandwidth; | 	m_running.m_rfBandwidth = m_config.m_rfBandwidth; | ||||||
|  | |||||||
| @ -27,6 +27,7 @@ | |||||||
| #include "dsp/movingaverage.h" | #include "dsp/movingaverage.h" | ||||||
| #include "dsp/fftfilt.h" | #include "dsp/fftfilt.h" | ||||||
| #include "dsp/phaselock.h" | #include "dsp/phaselock.h" | ||||||
|  | #include "dsp/filterrc.h" | ||||||
| #include "audio/audiofifo.h" | #include "audio/audiofifo.h" | ||||||
| #include "util/message.h" | #include "util/message.h" | ||||||
| 
 | 
 | ||||||
| @ -147,6 +148,10 @@ private: | |||||||
| 	StereoPhaseLock m_pilotPLL; | 	StereoPhaseLock m_pilotPLL; | ||||||
| 	Real m_pilotPLLSamples[2]; | 	Real m_pilotPLLSamples[2]; | ||||||
| 
 | 
 | ||||||
|  | 	LowPassFilterRC m_deemphasisFilterX; | ||||||
|  | 	LowPassFilterRC m_deemphasisFilterY; | ||||||
|  | 	static const Real default_deemphasis = 50.0; // 50 us
 | ||||||
|  | 
 | ||||||
| 	void apply(); | 	void apply(); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										58
									
								
								sdrbase/dsp/filterrc.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								sdrbase/dsp/filterrc.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,58 @@ | |||||||
|  | ///////////////////////////////////////////////////////////////////////////////////
 | ||||||
|  | // 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/>.          //
 | ||||||
|  | ///////////////////////////////////////////////////////////////////////////////////
 | ||||||
|  | 
 | ||||||
|  | #include <QDebug> | ||||||
|  | #include "dsp/filterrc.h" | ||||||
|  | 
 | ||||||
|  | // Construct 1st order low-pass IIR filter.
 | ||||||
|  | LowPassFilterRC::LowPassFilterRC(Real timeconst) : | ||||||
|  |     m_timeconst(timeconst), | ||||||
|  | 	m_y1(0) | ||||||
|  | { | ||||||
|  | 	m_a1 = - exp(-1/m_timeconst); | ||||||
|  | 	m_b0 = 1 + m_a1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Reconfigure
 | ||||||
|  | void LowPassFilterRC::configure(Real timeconst) | ||||||
|  | { | ||||||
|  | 	m_timeconst = timeconst; | ||||||
|  | 	m_y1 = 0; | ||||||
|  | 	m_a1 = - exp(-1/m_timeconst); | ||||||
|  | 	m_b0 = 1 + m_a1; | ||||||
|  | 
 | ||||||
|  | 	qDebug() << "LowPassFilterRC::configure: t: " << m_timeconst | ||||||
|  | 			<< " a1: " << m_a1 | ||||||
|  | 			<< " b0: " << m_b0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Process samples.
 | ||||||
|  | void LowPassFilterRC::process(const Real& sample_in, Real& sample_out) | ||||||
|  | { | ||||||
|  |     /*
 | ||||||
|  |      * Continuous domain: | ||||||
|  |      *   H(s) = 1 / (1 - s * timeconst) | ||||||
|  |      * | ||||||
|  |      * Discrete domain: | ||||||
|  |      *   H(z) = (1 - exp(-1/timeconst)) / (1 - exp(-1/timeconst) / z) | ||||||
|  |      */ | ||||||
|  | 
 | ||||||
|  | 	m_y1 = (sample_in * m_b0) - (m_y1 * m_a1); | ||||||
|  | 	sample_out = m_y1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| @ -81,6 +81,7 @@ PhaseLock::PhaseLock(Real freq, Real bandwidth, Real minsignal) | |||||||
|     m_sample_cnt    = 0; |     m_sample_cnt    = 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| void PhaseLock::configure(Real freq, Real bandwidth, Real minsignal) | void PhaseLock::configure(Real freq, Real bandwidth, Real minsignal) | ||||||
| { | { | ||||||
| 	qDebug("PhaseLock::configure: freq: %f bandwidth: %f minsignal: %f", freq, bandwidth, minsignal); | 	qDebug("PhaseLock::configure: freq: %f bandwidth: %f minsignal: %f", freq, bandwidth, minsignal); | ||||||
| @ -142,6 +143,7 @@ void PhaseLock::configure(Real freq, Real bandwidth, Real minsignal) | |||||||
|     m_sample_cnt    = 0; |     m_sample_cnt    = 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| // Process samples. Bufferized version
 | // Process samples. Bufferized version
 | ||||||
| void PhaseLock::process(const std::vector<Real>& samples_in, std::vector<Real>& samples_out) | void PhaseLock::process(const std::vector<Real>& samples_in, std::vector<Real>& samples_out) | ||||||
| { | { | ||||||
| @ -247,21 +249,6 @@ void PhaseLock::process(const std::vector<Real>& samples_in, std::vector<Real>& | |||||||
|     m_sample_cnt += n; |     m_sample_cnt += n; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /*
 |  | ||||||
| void PhaseLock::process(const Real& sample_in, Real& sample_out) |  | ||||||
| { |  | ||||||
| 	m_phase += m_freq; |  | ||||||
| 
 |  | ||||||
| 	if (m_phase > 2.0 * M_PI) { |  | ||||||
| 		m_phase -= 2.0 * M_PI; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	Real psin = sin(m_phase); |  | ||||||
| 	Real pcos = cos(m_phase); |  | ||||||
| 
 |  | ||||||
| 	sample_out = 2 * psin * pcos; |  | ||||||
| }*/ |  | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
| // Process samples. Multiple output
 | // Process samples. Multiple output
 | ||||||
| void PhaseLock::process(const Real& sample_in, Real *samples_out) | void PhaseLock::process(const Real& sample_in, Real *samples_out) | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user