mirror of
				https://github.com/f4exb/sdrangel.git
				synced 2025-10-26 02:20:26 -04:00 
			
		
		
		
	BFM demod: working RDS demod
This commit is contained in:
		
							parent
							
								
									8fa44a4ddb
								
							
						
					
					
						commit
						1a9dca1632
					
				| @ -92,7 +92,7 @@ void BFMDemod::configure(MessageQueue* messageQueue, | ||||
| 
 | ||||
| void BFMDemod::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool firstOfBurst) | ||||
| { | ||||
| 	Complex ci, cs; | ||||
| 	Complex ci, cs, cr; | ||||
| 	fftfilt::cmplx *rf; | ||||
| 	int rf_out; | ||||
| 	Real msq, demod; | ||||
| @ -136,8 +136,15 @@ void BFMDemod::feed(const SampleVector::const_iterator& begin, const SampleVecto | ||||
| 
 | ||||
| 			if (m_running.m_rdsActive) | ||||
| 			{ | ||||
| 				m_rdsDemod.process(demod * 2.0 * cos(3.0 * m_pilotPLLSamples[2]), m_pilotPLLSamples[2]); | ||||
| 				//m_rdsDemod.process(demod, m_pilotPLLSamples[2]);
 | ||||
| 				//Complex r(demod * 2.0 * std::cos(3.0 * m_pilotPLLSamples[2]), 0.0);
 | ||||
| 				Complex r(demod * 2.0 * std::cos(3.0 * m_pilotPLLSamples[2]), m_pilotPLLSamples[2]); | ||||
| 
 | ||||
| 				if (m_interpolatorRDS.interpolate(&m_interpolatorRDSDistanceRemain, r, &cr)) | ||||
| 				{ | ||||
| 					m_rdsDemod.process(cr.real(), cr.imag()); | ||||
| 					m_interpolatorRDSDistanceRemain += m_interpolatorRDSDistance; | ||||
| 				} | ||||
| 
 | ||||
| 			} | ||||
| 
 | ||||
| 			Real sampleStereo; | ||||
| @ -158,6 +165,7 @@ void BFMDemod::feed(const SampleVector::const_iterator& begin, const SampleVecto | ||||
| 				if (m_interpolatorStereo.interpolate(&m_interpolatorStereoDistanceRemain, s, &cs)) | ||||
| 				{ | ||||
| 					sampleStereo = cs.real(); | ||||
| 					m_interpolatorStereoDistanceRemain += m_interpolatorStereoDistance; | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| @ -291,11 +299,6 @@ bool BFMDemod::handleMessage(const Message& cmd) | ||||
| 
 | ||||
| void BFMDemod::apply() | ||||
| { | ||||
| 	if (m_config.m_inputSampleRate != m_running.m_inputSampleRate) | ||||
| 	{ | ||||
| 		m_rdsDemod.setSampleRate(m_config.m_inputSampleRate); | ||||
| 	} | ||||
| 
 | ||||
| 	if ((m_config.m_inputSampleRate != m_running.m_inputSampleRate) | ||||
| 		|| (m_config.m_audioStereo && (m_config.m_audioStereo != m_running.m_audioStereo))) | ||||
| 	{ | ||||
| @ -314,12 +317,19 @@ void BFMDemod::apply() | ||||
| 	{ | ||||
| 		m_settingsMutex.lock(); | ||||
| 		qDebug() << "BFMDemod::handleMessage: m_interpolator.create"; | ||||
| 
 | ||||
| 		m_interpolator.create(16, m_config.m_inputSampleRate, m_config.m_afBandwidth); | ||||
| 		m_interpolatorDistanceRemain = (Real) m_config.m_inputSampleRate / m_config.m_audioSampleRate; | ||||
| 		m_interpolatorDistance =  (Real) m_config.m_inputSampleRate / (Real) m_config.m_audioSampleRate; | ||||
| 
 | ||||
| 		m_interpolatorStereo.create(16, m_config.m_inputSampleRate, m_config.m_afBandwidth); | ||||
| 		m_interpolatorStereoDistanceRemain = (Real) m_config.m_inputSampleRate / m_config.m_audioSampleRate; | ||||
| 		m_interpolatorStereoDistance =  (Real) m_config.m_inputSampleRate / (Real) m_config.m_audioSampleRate; | ||||
| 
 | ||||
| 		m_interpolatorRDS.create(4, m_config.m_inputSampleRate, 600.0); | ||||
| 		m_interpolatorRDSDistanceRemain = (Real) m_config.m_inputSampleRate / 250000.0; | ||||
| 		m_interpolatorRDSDistance =  (Real) m_config.m_inputSampleRate / 250000.0; | ||||
| 
 | ||||
| 		m_settingsMutex.unlock(); | ||||
| 	} | ||||
| 
 | ||||
|  | ||||
| @ -159,9 +159,15 @@ private: | ||||
| 	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; | ||||
| 
 | ||||
|  | ||||
| @ -23,77 +23,164 @@ | ||||
| #include "rdsdemod.h" | ||||
| 
 | ||||
| const Real RDSDemod::m_pllBeta = 50; | ||||
| const int RDSDemod::m_udpSize = 1472; | ||||
| 
 | ||||
| RDSDemod::RDSDemod() | ||||
| { | ||||
| 	m_srate = 250000; | ||||
| 	m_fsc = 57000.0; | ||||
| 	m_subcarrPhi_1 = 0; | ||||
| 	m_dPhiSc = 0; | ||||
| 	m_subcarrBB_1 = 0.0; | ||||
| 	m_rdsClockPhase = 0.0; | ||||
| 	m_rdsClockPhase_1 = 0.0; | ||||
| 	m_rdsClockOffset = 0.0; | ||||
| 	m_rdsClockLO = 0.0; | ||||
| 	m_rdsClockLO_1 = 0.0; | ||||
| 	m_numSamples = 0; | ||||
| 	m_acc = 0.0; | ||||
| 	m_acc_1 = 0.0; | ||||
| 	m_counter = 0; | ||||
| 	m_readingFrame = 0; | ||||
| 	m_totErrors[0] = 0; | ||||
| 	m_totErrors[1] = 0; | ||||
| 	m_dbit = 0; | ||||
| 
 | ||||
| 	m_parms.subcarr_phi = 0; | ||||
| 	m_parms.clock_offset = 0; | ||||
| 	m_parms.clock_phi = 0; | ||||
| 	m_parms.prev_clock_phi = 0; | ||||
| 	m_parms.lo_clock = 0; | ||||
| 	m_parms.prevclock = 0; | ||||
| 	m_parms.prev_bb = 0; | ||||
| 	m_parms.d_phi_sc = 0; | ||||
| 	m_parms.d_cphi = 0; | ||||
| 	m_parms.acc = 0; | ||||
| 	m_parms.subcarr_sample = 0; | ||||
| 	m_parms.c = 0; | ||||
| 	m_parms.fmfreq = 0; | ||||
| 	m_parms.bytesread; | ||||
| 	m_parms.numsamples = 0; | ||||
| 	m_parms.loop_out = 0; | ||||
| 	m_parms.prev_loop = 0; | ||||
| 
 | ||||
| 	m_parms.prev_acc = 0; | ||||
| 	m_parms.counter = 0; | ||||
| 	m_parms.reading_frame = 0; | ||||
| 
 | ||||
| 	m_socket = new QUdpSocket(this); | ||||
| 	m_sampleBufferIndex = 0; | ||||
| } | ||||
| 
 | ||||
| RDSDemod::~RDSDemod() | ||||
| { | ||||
| 	delete m_socket; | ||||
| } | ||||
| 
 | ||||
| void RDSDemod::setSampleRate(int srate) | ||||
| { | ||||
| 	m_srate = srate; | ||||
| 	m_fsc = 57000.0; | ||||
| } | ||||
| 
 | ||||
| void RDSDemod::process(Real demod, Real pilotPhaseSample) | ||||
| void RDSDemod::process(Real demod, Real pilot) | ||||
| { | ||||
| 	pilotPhaseSample = fmod(pilotPhaseSample, M_PI); | ||||
| 
 | ||||
| 	m_dPhiSc = pilotPhaseSample - m_subcarrPhi_1; | ||||
| 	double fsc = 57000; | ||||
| 
 | ||||
| 	if (m_dPhiSc < 0) | ||||
| 	Real dPilot = pilot - m_pilotPrev; | ||||
| 
 | ||||
| 	if (dPilot < 0) | ||||
| 	{ | ||||
| 		m_dPhiSc += M_PI; | ||||
| 		dPilot += 2 * M_PI; | ||||
| 	} | ||||
| 
 | ||||
| 	m_subcarrBB[0] = filter_lp_2400_iq(demod, 0); // working on real part only
 | ||||
| 	if (m_sampleBufferIndex < m_udpSize) | ||||
| 	{ | ||||
| 		m_sampleBufferIndex++; | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		m_socket->writeDatagram((const char*)&m_sampleBuffer[0], (qint64 ) (m_udpSize * sizeof(Real)), QHostAddress::LocalHost, 9995); | ||||
| 		m_sampleBufferIndex = 0; | ||||
| 	} | ||||
| 
 | ||||
| 	// 1187.5 Hz clock from 19 kHz pilot
 | ||||
| 	// Subcarrier downmix & phase recovery
 | ||||
| 
 | ||||
| 	m_rdsClockPhase += (m_dPhiSc / 16.0) + m_rdsClockOffset; | ||||
| 	m_rdsClockPhase = fmod(m_rdsClockPhase, M_PI); | ||||
| 	m_rdsClockLO = (m_rdsClockPhase < 0.0 ? -1.0 : 1.0); | ||||
| 	m_parms.subcarr_phi += 2 * M_PI * fsc * (1.0 / m_srate); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	if (m_parms.subcarr_phi > 48 * 2 * M_PI) | ||||
| 	{ | ||||
| 		m_parms.subcarr_phi -= 48 * 2 * M_PI; | ||||
| 	}*/ | ||||
| 
 | ||||
| 	m_parms.subcarr_bb[0] = filter_lp_2400_iq(demod, 0); | ||||
| 	//m_parms.subcarr_bb[1] = filter_lp_2400_iq(demod * std::sin(m_parms.subcarr_phi), 1);
 | ||||
| 
 | ||||
| 	m_parms.clock_phi += dPilot / 16.0; | ||||
| 	m_parms.clock_phi = std::fmod(m_parms.clock_phi, 2 * M_PI); | ||||
| 	m_parms.lo_clock = (m_parms.clock_phi < M_PI ? 1 : -1); | ||||
| 
 | ||||
| 	m_sampleBuffer[m_sampleBufferIndex] =  m_parms.lo_clock * m_parms.subcarr_bb[0]; // UDP debug
 | ||||
| 
 | ||||
| 	// 1187.5 Hz clock
 | ||||
| 
 | ||||
| 	m_parms.clock_phi = (m_parms.subcarr_phi / 48.0) + m_parms.clock_offset; | ||||
| 
 | ||||
| 	// Clock phase recovery
 | ||||
| 
 | ||||
| 	if (sign(m_subcarrBB_1) != sign(m_subcarrBB[0])) | ||||
| 	if (sign(m_parms.prev_bb) != sign(m_parms.subcarr_bb[0])) | ||||
| 	{ | ||||
| 		Real d_cphi = m_rdsClockPhase; | ||||
| 		m_parms.d_cphi = std::fmod(m_parms.clock_phi, M_PI); | ||||
| 
 | ||||
| 		if (d_cphi >= M_PI_2) | ||||
| 		if (m_parms.d_cphi >= M_PI_2) | ||||
| 		{ | ||||
| 			d_cphi -= M_PI; | ||||
| 			m_parms.d_cphi -= M_PI; | ||||
| 		} | ||||
| 
 | ||||
| 		m_rdsClockOffset -= 0.005 * d_cphi; | ||||
| 		m_parms.clock_offset -= 0.005 * m_parms.d_cphi; | ||||
| 	} | ||||
| 
 | ||||
| 	biphase(m_acc, m_rdsClockPhase - m_rdsClockPhase_1); | ||||
| 	/* Decimate band-limited signal */ | ||||
| 	if (m_parms.numsamples % 8 == 0) | ||||
| 	{ | ||||
| 		/* biphase symbol integrate & dump */ | ||||
| 		m_parms.acc += m_parms.subcarr_bb[0] * m_parms.lo_clock; | ||||
| 
 | ||||
| 	m_numSamples++; | ||||
| 	m_subcarrPhi_1 = pilotPhaseSample; | ||||
| 	m_rdsClockPhase_1 = m_rdsClockPhase; | ||||
| 		if (sign(m_parms.lo_clock) != sign(m_parms.prevclock)) | ||||
| 		{ | ||||
| 			biphase(m_parms.acc, m_parms.clock_phi - m_parms.prev_clock_phi); | ||||
| 			m_parms.acc = 0; | ||||
| 		} | ||||
| 
 | ||||
| 		m_parms.prevclock = m_parms.lo_clock; | ||||
| 	} | ||||
| 
 | ||||
| 	m_parms.numsamples++; | ||||
| 	m_parms.prev_bb = m_parms.subcarr_bb[0]; | ||||
| 	m_parms.prev_clock_phi = m_parms.clock_phi; | ||||
| 	m_prev = demod; | ||||
| 	m_pilotPrev - pilot; | ||||
| } | ||||
| 
 | ||||
| void RDSDemod::biphase(Real acc, Real d_cphi) | ||||
| { | ||||
| 
 | ||||
| 	if (sign(acc) != sign(m_parms.prev_acc)) // two successive of different sign: error detected
 | ||||
| 	{ | ||||
| 		m_parms.tot_errs[m_parms.counter % 2]++; | ||||
| 	} | ||||
| 
 | ||||
| 	if (m_parms.counter % 2 == m_parms.reading_frame) // two successive of the same sing: OK
 | ||||
| 	{ | ||||
| 		// TODO: take action print_delta(sign(acc + prev_acc));
 | ||||
| 	} | ||||
| 
 | ||||
| 	if (m_parms.counter == 0) | ||||
| 	{ | ||||
| 		if (m_parms.tot_errs[1 - m_parms.reading_frame] < m_parms.tot_errs[m_parms.reading_frame]) | ||||
| 		{ | ||||
| 			m_parms.reading_frame = 1 - m_parms.reading_frame; | ||||
| 		} | ||||
| 
 | ||||
| 		Real qua = (1.0 * abs(m_parms.tot_errs[0] - m_parms.tot_errs[1]) / (m_parms.tot_errs[0] + m_parms.tot_errs[1])) * 100; | ||||
| 		qDebug("RDSDemod::biphase: frame: %d  acc: %+6.3f errs: %3d %3d  qual: %3.0f%%  clk: %7.2f\n", | ||||
| 				m_parms.reading_frame, | ||||
| 				acc, | ||||
| 				m_parms.tot_errs[0], | ||||
| 				m_parms.tot_errs[1], | ||||
| 				qua, | ||||
| 				(d_cphi / (2 * M_PI)) * m_srate); | ||||
| 
 | ||||
| 		m_parms.tot_errs[0] = 0; | ||||
| 		m_parms.tot_errs[1] = 0; | ||||
| 	} | ||||
| 
 | ||||
| 	m_parms.prev_acc = acc; // memorize (z^-1)
 | ||||
| 	m_parms.counter = (m_parms.counter + 1) % 800; | ||||
| } | ||||
| 
 | ||||
| Real RDSDemod::filter_lp_2400_iq(Real input, int iqIndex) | ||||
| @ -125,38 +212,3 @@ int RDSDemod::sign(Real a) | ||||
| { | ||||
| 	return (a >= 0 ? 1 : 0); | ||||
| } | ||||
| 
 | ||||
| void RDSDemod::biphase(Real acc, Real clockDPhi) | ||||
| { | ||||
| 	if (clockDPhi < 0) | ||||
| 	{ | ||||
| 		clockDPhi += M_PI; | ||||
| 	} | ||||
| 
 | ||||
| 	double fsc = (m_dPhiSc / (2.0 * M_PI)) * m_srate; | ||||
| 	double fclk = (clockDPhi / (2.0 * M_PI)) * m_srate; | ||||
| 
 | ||||
| 	if (m_counter == 0) | ||||
| 	{ | ||||
| 		qDebug("RDSDemod::biphase: frame: %d pll: %.3f (ppm: %+8.3f) clk: %8.1f", | ||||
| 				m_readingFrame, | ||||
| 				fsc, | ||||
| 				((fsc - 19000.0) / 19000.0) * 1000000, | ||||
| 				fclk); | ||||
| 	} | ||||
| 
 | ||||
| 	m_counter = (m_counter + 1) % 800; | ||||
| } | ||||
| 
 | ||||
| void RDSDemod::print_delta(char b) | ||||
| { | ||||
| 	output_bit(b ^ m_dbit); | ||||
| 	m_dbit = b; | ||||
| } | ||||
| 
 | ||||
| void RDSDemod::output_bit(char b) | ||||
| { | ||||
| 	// TODO: return value instead of spitting out
 | ||||
| 	//printf("%d", b);
 | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -19,49 +19,70 @@ | ||||
| #ifndef PLUGINS_CHANNEL_BFM_RDSDEMOD_H_ | ||||
| #define PLUGINS_CHANNEL_BFM_RDSDEMOD_H_ | ||||
| 
 | ||||
| #include <QObject> | ||||
| #include <QHostAddress> | ||||
| #include <QUdpSocket> | ||||
| 
 | ||||
| #include "dsp/dsptypes.h" | ||||
| 
 | ||||
| class RDSDemod | ||||
| class RDSDemod : public QObject | ||||
| { | ||||
|     Q_OBJECT | ||||
| public: | ||||
| 	RDSDemod(); | ||||
| 	~RDSDemod(); | ||||
| 
 | ||||
| 	void setSampleRate(int srate); | ||||
| 	void process(Real rdsSample, Real pilotPhaseSample); | ||||
| 	void process(Real rdsSample, Real pilotSample); | ||||
| 
 | ||||
| protected: | ||||
| 	void biphase(Real acc, Real d_cphi); | ||||
| 	Real filter_lp_2400_iq(Real in, int iqIndex); | ||||
| 	Real filter_lp_pll(Real input); | ||||
| 	int sign(Real a); | ||||
| 	void biphase(Real acc, Real dPhiClock); | ||||
| 	void print_delta(char b); | ||||
| 	void output_bit(char b); | ||||
| 
 | ||||
| private: | ||||
| 	int m_srate; | ||||
| 	Real m_fsc; | ||||
| 	struct | ||||
| 	{ | ||||
| 		double subcarr_phi; | ||||
| 		double subcarr_bb[2]; | ||||
| 		double clock_offset; | ||||
| 		double clock_phi; | ||||
| 		double prev_clock_phi; | ||||
| 		double lo_clock; | ||||
| 		double prevclock; | ||||
| 		double prev_bb; | ||||
| 		double d_phi_sc; | ||||
| 		double d_cphi; | ||||
| 		double acc; | ||||
| 		double subcarr_sample; | ||||
| 		int c; | ||||
| 		int fmfreq; | ||||
| 		int bytesread; | ||||
| 		int numsamples; | ||||
| 		double loop_out; | ||||
| 		double prev_loop; | ||||
| 		double prev_acc; | ||||
| 		int counter; | ||||
| 		int reading_frame; | ||||
| 		int tot_errs[2]; | ||||
| 	} m_parms; | ||||
| 
 | ||||
| 	Real m_xv[2][2+1]; | ||||
| 	Real m_yv[2][2+1]; | ||||
| 	Real m_xw[2]; | ||||
| 	Real m_yw[2]; | ||||
| 	Real m_subcarrPhi_1; | ||||
| 	Real m_subcarrBB[2]; | ||||
| 	Real m_dPhiSc; | ||||
| 	Real m_subcarrBB_1; | ||||
| 	Real m_rdsClockPhase; | ||||
| 	Real m_rdsClockPhase_1; | ||||
| 	Real m_rdsClockOffset; | ||||
| 	Real m_rdsClockLO; | ||||
| 	Real m_rdsClockLO_1; | ||||
| 	int m_numSamples; | ||||
| 	Real m_acc; | ||||
| 	Real m_acc_1; | ||||
| 	int m_counter; | ||||
| 	int m_readingFrame; | ||||
| 	int m_totErrors[2]; | ||||
| 	int m_dbit; | ||||
| 	Real m_xw[1+1]; | ||||
| 	Real m_yw[1+1]; | ||||
| 	Real m_prev; | ||||
| 
 | ||||
| 	Real m_pilotPrev; | ||||
| 
 | ||||
| 	int m_srate; | ||||
| 
 | ||||
| 	QUdpSocket *m_socket; | ||||
| 	Real m_sampleBuffer[1<<12]; | ||||
| 	int m_sampleBufferIndex; | ||||
| 
 | ||||
| 	static const int m_udpSize; | ||||
| 	static const Real m_pllBeta; | ||||
| }; | ||||
| 
 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user