| 
									
										
										
										
											2018-12-09 22:11:39 +01:00
										 |  |  | ///////////////////////////////////////////////////////////////////////////////////
 | 
					
						
							| 
									
										
										
										
											2023-11-19 06:43:20 +01:00
										 |  |  | // Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany //
 | 
					
						
							|  |  |  | // written by Christian Daniel                                                   //
 | 
					
						
							|  |  |  | // Copyright (C) 2014 John Greb <hexameron>                                      //
 | 
					
						
							|  |  |  | // Copyright (C) 2015-2016, 2018-2019 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
 | 
					
						
							|  |  |  | // Copyright (C) 2015 Hoernchen <la@tfc-server.de>                               //
 | 
					
						
							| 
									
										
										
										
											2018-12-09 22:11:39 +01:00
										 |  |  | //                                                                               //
 | 
					
						
							|  |  |  | // 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                  //
 | 
					
						
							| 
									
										
										
										
											2019-04-11 14:32:15 +02:00
										 |  |  | // (at your option) any later version.                                           //
 | 
					
						
							| 
									
										
										
										
											2018-12-09 22:11:39 +01:00
										 |  |  | //                                                                               //
 | 
					
						
							|  |  |  | // 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/>.          //
 | 
					
						
							|  |  |  | ///////////////////////////////////////////////////////////////////////////////////
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-18 16:52:39 +01:00
										 |  |  | #ifndef INCLUDE_INTERPOLATOR_H
 | 
					
						
							|  |  |  | #define INCLUDE_INTERPOLATOR_H
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-07 00:42:57 +01:00
										 |  |  | #ifdef USE_SSE2
 | 
					
						
							|  |  |  | #include <emmintrin.h>
 | 
					
						
							| 
									
										
										
										
											2015-07-05 17:08:06 +02:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2014-05-18 16:52:39 +01:00
										 |  |  | #include "dsp/dsptypes.h"
 | 
					
						
							| 
									
										
										
										
											2018-03-20 13:49:21 +01:00
										 |  |  | #include "export.h"
 | 
					
						
							| 
									
										
										
										
											2014-05-18 16:52:39 +01:00
										 |  |  | #include <stdio.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-03 20:23:38 +01:00
										 |  |  | class SDRBASE_API Interpolator { | 
					
						
							| 
									
										
										
										
											2014-05-18 16:52:39 +01:00
										 |  |  | public: | 
					
						
							|  |  |  | 	Interpolator(); | 
					
						
							|  |  |  | 	~Interpolator(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-31 23:40:46 +01:00
										 |  |  | 	void create(int phaseSteps, double sampleRate, double cutoff, double nbTapsPerPhase = 4.5); | 
					
						
							| 
									
										
										
										
											2014-05-18 16:52:39 +01:00
										 |  |  | 	void free(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-11-25 08:31:44 +00:00
										 |  |  | 	// Original code allowed for upsampling, but was never used that way
 | 
					
						
							| 
									
										
										
										
											2018-12-09 22:11:39 +01:00
										 |  |  | 	// The decimation factor should always be lower than 2 for proper work
 | 
					
						
							| 
									
										
										
										
											2016-10-10 01:53:32 +02:00
										 |  |  | 	bool decimate(Real *distance, const Complex& next, Complex* result) | 
					
						
							| 
									
										
										
										
											2014-05-18 16:52:39 +01:00
										 |  |  | 	{ | 
					
						
							| 
									
										
										
										
											2014-11-25 08:31:44 +00:00
										 |  |  | 		advanceFilter(next); | 
					
						
							|  |  |  | 		*distance -= 1.0; | 
					
						
							| 
									
										
										
										
											2015-08-24 00:51:27 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-09 22:11:39 +01:00
										 |  |  | 		if (*distance >= 1.0) { | 
					
						
							| 
									
										
										
										
											2014-11-25 08:31:44 +00:00
										 |  |  | 			return false; | 
					
						
							| 
									
										
										
										
											2015-08-24 00:51:27 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		doInterpolate((int) floor(*distance * (Real)m_phaseSteps), result); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-18 16:52:39 +01:00
										 |  |  | 		return true; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-28 03:18:24 +02:00
										 |  |  | 	// interpolation simplified from the generalized resampler
 | 
					
						
							| 
									
										
										
										
											2016-10-10 02:08:48 +02:00
										 |  |  | 	bool interpolate(Real *distance, const Complex& next, Complex* result) | 
					
						
							|  |  |  | 	{ | 
					
						
							| 
									
										
										
										
											2016-10-28 03:18:24 +02:00
										 |  |  | 	    bool consumed = false; | 
					
						
							| 
									
										
										
										
											2016-10-10 02:08:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-28 03:18:24 +02:00
										 |  |  |         if (*distance >= 1.0) | 
					
						
							| 
									
										
										
										
											2016-10-10 02:08:48 +02:00
										 |  |  |         { | 
					
						
							|  |  |  |             advanceFilter(next); | 
					
						
							| 
									
										
										
										
											2016-10-28 03:18:24 +02:00
										 |  |  |             *distance -= 1.0; | 
					
						
							|  |  |  |             consumed = true; | 
					
						
							| 
									
										
										
										
											2016-10-10 02:08:48 +02:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2016-10-28 03:18:24 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         doInterpolate((int)floor(*distance * (Real)m_phaseSteps), result); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return consumed; | 
					
						
							| 
									
										
										
										
											2016-10-10 02:08:48 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2014-05-18 16:52:39 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-28 03:18:24 +02:00
										 |  |  | 	// original interpolator which is actually an arbitrary rational resampler P/Q for any positive P, Q
 | 
					
						
							|  |  |  | 	// sampling frequency must be the highest of the two
 | 
					
						
							|  |  |  | 	bool resample(Real* distance, const Complex& next, bool* consumed, Complex* result) | 
					
						
							| 
									
										
										
										
											2016-10-27 18:05:40 +02:00
										 |  |  | 	{ | 
					
						
							| 
									
										
										
										
											2018-12-09 22:11:39 +01:00
										 |  |  | 		while (*distance >= 1.0) | 
					
						
							| 
									
										
										
										
											2016-10-27 18:05:40 +02:00
										 |  |  | 		{ | 
					
						
							| 
									
										
										
										
											2018-12-09 22:11:39 +01:00
										 |  |  | 			if (!(*consumed)) | 
					
						
							| 
									
										
										
										
											2016-10-27 18:05:40 +02:00
										 |  |  | 			{ | 
					
						
							|  |  |  | 				advanceFilter(next); | 
					
						
							|  |  |  | 				*distance -= 1.0; | 
					
						
							|  |  |  | 				*consumed = true; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			else | 
					
						
							|  |  |  | 			{ | 
					
						
							|  |  |  | 				return false; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		doInterpolate((int)floor(*distance * (Real)m_phaseSteps), result); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return true; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-18 16:52:39 +01:00
										 |  |  | private: | 
					
						
							|  |  |  | 	float* m_taps; | 
					
						
							|  |  |  | 	float* m_alignedTaps; | 
					
						
							|  |  |  | 	float* m_taps2; | 
					
						
							|  |  |  | 	float* m_alignedTaps2; | 
					
						
							|  |  |  | 	std::vector<Complex> m_samples; | 
					
						
							|  |  |  | 	int m_ptr; | 
					
						
							|  |  |  | 	int m_phaseSteps; | 
					
						
							|  |  |  | 	int m_nTaps; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-31 23:40:46 +01:00
										 |  |  | 	static void createPolyphaseLowPass( | 
					
						
							|  |  |  | 	    std::vector<Real>& taps, | 
					
						
							|  |  |  | 	    int phaseSteps, | 
					
						
							|  |  |  | 	    double gain, | 
					
						
							|  |  |  | 	    double sampleRateHz, | 
					
						
							|  |  |  | 	    double cutoffFreqHz, | 
					
						
							|  |  |  | 	    double transitionWidthHz, | 
					
						
							|  |  |  | 	    double oobAttenuationdB); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     static void createPolyphaseLowPass( | 
					
						
							|  |  |  |         std::vector<Real>& taps, | 
					
						
							|  |  |  |         int phaseSteps, | 
					
						
							|  |  |  |         double gain, | 
					
						
							|  |  |  |         double sampleRateHz, | 
					
						
							|  |  |  |         double cutoffFreqHz, | 
					
						
							|  |  |  |         double nbTapsPerPhase); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-18 16:52:39 +01:00
										 |  |  | 	void createTaps(int nTaps, double sampleRate, double cutoff, std::vector<Real>* taps); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	void advanceFilter(const Complex& next) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		m_ptr--; | 
					
						
							| 
									
										
										
										
											2018-12-09 22:11:39 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		if (m_ptr < 0) { | 
					
						
							|  |  |  | 		    m_ptr = m_nTaps - 1; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-18 16:52:39 +01:00
										 |  |  | 		m_samples[m_ptr] = next; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-10 02:08:48 +02:00
										 |  |  |     void advanceFilter() | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         m_ptr--; | 
					
						
							| 
									
										
										
										
											2018-12-09 22:11:39 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if (m_ptr < 0) { | 
					
						
							| 
									
										
										
										
											2016-10-10 02:08:48 +02:00
										 |  |  |             m_ptr = m_nTaps - 1; | 
					
						
							| 
									
										
										
										
											2018-12-09 22:11:39 +01:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-10 02:08:48 +02:00
										 |  |  |         m_samples[m_ptr].real(0.0); | 
					
						
							|  |  |  |         m_samples[m_ptr].imag(0.0); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-18 16:52:39 +01:00
										 |  |  | 	void doInterpolate(int phase, Complex* result) | 
					
						
							|  |  |  | 	{ | 
					
						
							| 
									
										
										
										
											2018-12-09 22:11:39 +01:00
										 |  |  | 		if (phase < 0) { | 
					
						
							|  |  |  | 		    phase = 0; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-11-07 00:42:57 +01:00
										 |  |  | #if USE_SSE2
 | 
					
						
							| 
									
										
										
										
											2014-05-18 16:52:39 +01:00
										 |  |  | 		// beware of the ringbuffer
 | 
					
						
							|  |  |  | 		if(m_ptr == 0) { | 
					
						
							|  |  |  | 			// only one straight block
 | 
					
						
							|  |  |  | 			const float* src = (const float*)&m_samples[0]; | 
					
						
							|  |  |  | 			const __m128* filter = (const __m128*)&m_alignedTaps[phase * m_nTaps * 2]; | 
					
						
							|  |  |  | 			__m128 sum = _mm_setzero_ps(); | 
					
						
							|  |  |  | 			int todo = m_nTaps / 2; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			for(int i = 0; i < todo; i++) { | 
					
						
							|  |  |  | 				sum = _mm_add_ps(sum, _mm_mul_ps(_mm_loadu_ps(src), *filter)); | 
					
						
							|  |  |  | 				src += 4; | 
					
						
							|  |  |  | 				filter += 1; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// add upper half to lower half and store
 | 
					
						
							|  |  |  | 			_mm_storel_pi((__m64*)result, _mm_add_ps(sum, _mm_shuffle_ps(sum, _mm_setzero_ps(), _MM_SHUFFLE(1, 0, 3, 2)))); | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			// two blocks
 | 
					
						
							|  |  |  | 			const float* src = (const float*)&m_samples[m_ptr]; | 
					
						
							|  |  |  | 			const __m128* filter = (const __m128*)&m_alignedTaps[phase * m_nTaps * 2]; | 
					
						
							|  |  |  | 			__m128 sum = _mm_setzero_ps(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// first block
 | 
					
						
							|  |  |  | 			int block = m_nTaps - m_ptr; | 
					
						
							|  |  |  | 			int todo = block / 2; | 
					
						
							|  |  |  | 			if(block & 1) | 
					
						
							|  |  |  | 				todo++; | 
					
						
							|  |  |  | 			for(int i = 0; i < todo; i++) { | 
					
						
							|  |  |  | 				sum = _mm_add_ps(sum, _mm_mul_ps(_mm_loadu_ps(src), *filter)); | 
					
						
							|  |  |  | 				src += 4; | 
					
						
							|  |  |  | 				filter += 1; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if(block & 1) { | 
					
						
							|  |  |  | 				// one sample beyond the end -> switch coefficient table
 | 
					
						
							|  |  |  | 				filter = (const __m128*)&m_alignedTaps2[phase * m_nTaps * 2 + todo * 4 - 4]; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			// second block
 | 
					
						
							|  |  |  | 			src = (const float*)&m_samples[0]; | 
					
						
							|  |  |  | 			block = m_ptr; | 
					
						
							|  |  |  | 			todo = block / 2; | 
					
						
							|  |  |  | 			for(int i = 0; i < todo; i++) { | 
					
						
							|  |  |  | 				sum = _mm_add_ps(sum, _mm_mul_ps(_mm_loadu_ps(src), *filter)); | 
					
						
							|  |  |  | 				src += 4; | 
					
						
							|  |  |  | 				filter += 1; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if(block & 1) { | 
					
						
							|  |  |  | 				// one sample remaining
 | 
					
						
							|  |  |  | 				sum = _mm_add_ps(sum, _mm_mul_ps(_mm_loadl_pi(_mm_setzero_ps(), (const __m64*)src), filter[0])); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// add upper half to lower half and store
 | 
					
						
							|  |  |  | 			_mm_storel_pi((__m64*)result, _mm_add_ps(sum, _mm_shuffle_ps(sum, _mm_setzero_ps(), _MM_SHUFFLE(1, 0, 3, 2)))); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | #else
 | 
					
						
							|  |  |  | 		int sample = m_ptr; | 
					
						
							|  |  |  | 		const Real* coeff = &m_alignedTaps[phase * m_nTaps * 2]; | 
					
						
							|  |  |  | 		Real rAcc = 0; | 
					
						
							|  |  |  | 		Real iAcc = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-09 22:11:39 +01:00
										 |  |  | 		for (int i = 0; i < m_nTaps; i++) { | 
					
						
							| 
									
										
										
										
											2014-05-18 16:52:39 +01:00
										 |  |  | 			rAcc += *coeff * m_samples[sample].real(); | 
					
						
							|  |  |  | 			iAcc += *coeff * m_samples[sample].imag(); | 
					
						
							|  |  |  | 			sample = (sample + 1) % m_nTaps; | 
					
						
							|  |  |  | 			coeff += 2; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-12-09 22:11:39 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-18 16:52:39 +01:00
										 |  |  | 		*result = Complex(rAcc, iAcc); | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #endif // INCLUDE_INTERPOLATOR_H
 |