| 
									
										
										
										
											2015-12-10 08:54:57 +01:00
										 |  |  | ///////////////////////////////////////////////////////////////////////////////////
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:02:48 +01:00
										 |  |  | // Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany //
 | 
					
						
							|  |  |  | // written by Christian Daniel                                                   //
 | 
					
						
							|  |  |  | // Copyright (C) 2015-2019 Edouard Griffiths, F4EXB <f4exb06@gmail.com>          //
 | 
					
						
							|  |  |  | // Copyright (C) 2018 Jason Gerecke <killertofu@gmail.com>                       //
 | 
					
						
							|  |  |  | // Copyright (C) 2020 Kacper Michajłow <kasper93@gmail.com>                      //
 | 
					
						
							| 
									
										
										
										
											2015-12-10 08:54:57 +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 06:39:30 +02:00
										 |  |  | // (at your option) any later version.                                           //
 | 
					
						
							| 
									
										
										
										
											2015-12-10 08:54:57 +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/>.          //
 | 
					
						
							|  |  |  | ///////////////////////////////////////////////////////////////////////////////////
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-02 13:18:07 +02:00
										 |  |  | #include "../../channelrx/demodbfm/rdsdemod.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-10 15:02:16 +01:00
										 |  |  | #include <QDebug>
 | 
					
						
							| 
									
										
										
										
											2020-11-03 13:52:12 +01:00
										 |  |  | #include <cmath>
 | 
					
						
							| 
									
										
										
										
											2015-12-10 13:39:15 +01:00
										 |  |  | #include <stdio.h>
 | 
					
						
							|  |  |  | #include <stdlib.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-11 03:57:17 +01:00
										 |  |  | const Real RDSDemod::m_pllBeta = 50; | 
					
						
							| 
									
										
										
										
											2015-12-12 19:53:24 +01:00
										 |  |  | const Real RDSDemod::m_fsc = 1187.5; | 
					
						
							| 
									
										
										
										
											2015-12-11 03:57:17 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-13 09:45:29 +01:00
										 |  |  | RDSDemod::RDSDemod() | 
					
						
							|  |  |  | 	// : m_udpDebug(this, 1472, 9995) // UDP debug
 | 
					
						
							| 
									
										
										
										
											2015-12-10 08:54:57 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2015-12-11 03:57:17 +01:00
										 |  |  | 	m_srate = 250000; | 
					
						
							| 
									
										
										
										
											2015-12-12 15:14:26 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	m_parms.subcarr_phi = 0; | 
					
						
							| 
									
										
										
										
											2018-04-21 09:53:04 -07:00
										 |  |  | 	memset(m_parms.subcarr_bb, 0, sizeof(m_parms.subcarr_bb)); | 
					
						
							| 
									
										
										
										
											2015-12-12 15:14:26 +01:00
										 |  |  | 	m_parms.clock_offset = 0; | 
					
						
							|  |  |  | 	m_parms.clock_phi = 0; | 
					
						
							|  |  |  | 	m_parms.prev_clock_phi = 0; | 
					
						
							|  |  |  | 	m_parms.lo_clock = 0; | 
					
						
							| 
									
										
										
										
											2015-12-12 23:01:46 +01:00
										 |  |  | 	m_parms.prev_lo_clock = 0; | 
					
						
							| 
									
										
										
										
											2015-12-12 15:14:26 +01:00
										 |  |  | 	m_parms.prev_bb = 0; | 
					
						
							|  |  |  | 	m_parms.d_cphi = 0; | 
					
						
							|  |  |  | 	m_parms.acc = 0; | 
					
						
							|  |  |  | 	m_parms.numsamples = 0; | 
					
						
							|  |  |  | 	m_parms.prev_acc = 0; | 
					
						
							|  |  |  | 	m_parms.counter = 0; | 
					
						
							|  |  |  | 	m_parms.reading_frame = 0; | 
					
						
							| 
									
										
										
										
											2018-04-21 09:53:04 -07:00
										 |  |  | 	memset(m_parms.tot_errs, 0, sizeof(m_parms.tot_errs)); | 
					
						
							| 
									
										
										
										
											2015-12-13 02:54:22 +01:00
										 |  |  | 	m_parms.dbit = 0; | 
					
						
							| 
									
										
										
										
											2017-05-05 10:40:45 +02:00
										 |  |  | 	m_prev = 0.0f; | 
					
						
							| 
									
										
										
										
											2018-02-22 03:04:42 +01:00
										 |  |  | 	memset(m_xv, 0, 6*sizeof(Real)); | 
					
						
							|  |  |  | 	memset(m_yv, 0, 6*sizeof(Real)); | 
					
						
							|  |  |  |     memset(m_xw, 0, 2*sizeof(Real)); | 
					
						
							|  |  |  |     memset(m_yw, 0, 2*sizeof(Real)); | 
					
						
							|  |  |  |     m_report.acc = 0.0f; | 
					
						
							|  |  |  |     m_report.fclk = 0.0f; | 
					
						
							|  |  |  |     m_report.qua = 0.0f; | 
					
						
							| 
									
										
										
										
											2015-12-10 08:54:57 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | RDSDemod::~RDSDemod() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2015-12-12 23:01:46 +01:00
										 |  |  | 	//delete m_socket;
 | 
					
						
							| 
									
										
										
										
											2015-12-10 08:54:57 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-13 00:45:03 +01:00
										 |  |  | void RDSDemod::setSampleRate(int srate) /// FIXME: fix rate for now
 | 
					
						
							| 
									
										
										
										
											2015-12-10 08:54:57 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2018-11-13 00:45:03 +01:00
										 |  |  |     (void) srate; | 
					
						
							| 
									
										
										
										
											2015-12-11 03:57:17 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-13 02:54:22 +01:00
										 |  |  | bool RDSDemod::process(Real demod, bool& bit) | 
					
						
							| 
									
										
										
										
											2015-12-11 03:57:17 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2015-12-13 02:54:22 +01:00
										 |  |  | 	bool ret = false; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-13 09:45:29 +01:00
										 |  |  | 	//m_udpDebug.write(m_parms.lo_clock * m_parms.subcarr_bb[0]); // UDP debug
 | 
					
						
							| 
									
										
										
										
											2015-12-11 03:57:17 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-12 15:14:26 +01:00
										 |  |  | 	// Subcarrier downmix & phase recovery
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	m_parms.subcarr_bb[0] = filter_lp_2400_iq(demod, 0); | 
					
						
							| 
									
										
										
										
											2015-12-10 08:54:57 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-12 15:14:26 +01:00
										 |  |  | 	// 1187.5 Hz clock
 | 
					
						
							| 
									
										
										
										
											2015-12-11 19:33:10 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-12 23:01:46 +01:00
										 |  |  | 	/*
 | 
					
						
							|  |  |  | 	if (m_parms.subcarr_phi > 1e9) // ~ every 37 hours =>  not really useful
 | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		qDebug("RDSDemod::process: reset 1187.5 Hz clock"); | 
					
						
							|  |  |  | 		m_parms.subcarr_phi = 0; | 
					
						
							|  |  |  | 		m_parms.clock_offset = 0; | 
					
						
							|  |  |  | 	}*/ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-23 17:19:58 +02:00
										 |  |  |     m_parms.subcarr_phi += (2 * M_PI * m_fsc) / (Real) m_srate; | 
					
						
							| 
									
										
										
										
											2015-12-12 19:53:24 +01:00
										 |  |  | 	m_parms.clock_phi = m_parms.subcarr_phi + m_parms.clock_offset; | 
					
						
							| 
									
										
										
										
											2015-12-10 13:39:15 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Clock phase recovery
 | 
					
						
							| 
									
										
										
										
											2015-12-11 19:33:10 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-12 15:14:26 +01:00
										 |  |  | 	if (sign(m_parms.prev_bb) != sign(m_parms.subcarr_bb[0])) | 
					
						
							| 
									
										
										
										
											2015-12-10 13:39:15 +01:00
										 |  |  | 	{ | 
					
						
							| 
									
										
										
										
											2015-12-12 15:14:26 +01:00
										 |  |  | 		m_parms.d_cphi = std::fmod(m_parms.clock_phi, M_PI); | 
					
						
							| 
									
										
										
										
											2015-12-10 13:39:15 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-12 15:14:26 +01:00
										 |  |  | 		if (m_parms.d_cphi >= M_PI_2) | 
					
						
							| 
									
										
										
										
											2015-12-10 13:39:15 +01:00
										 |  |  | 		{ | 
					
						
							| 
									
										
										
										
											2015-12-12 15:14:26 +01:00
										 |  |  | 			m_parms.d_cphi -= M_PI; | 
					
						
							| 
									
										
										
										
											2015-12-10 13:39:15 +01:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-12 15:14:26 +01:00
										 |  |  | 		m_parms.clock_offset -= 0.005 * m_parms.d_cphi; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-12 19:53:24 +01:00
										 |  |  | 	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); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-12 15:14:26 +01:00
										 |  |  | 	/* 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; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-12 23:01:46 +01:00
										 |  |  | 		if (sign(m_parms.lo_clock) != sign(m_parms.prev_lo_clock)) | 
					
						
							| 
									
										
										
										
											2015-12-12 15:14:26 +01:00
										 |  |  | 		{ | 
					
						
							| 
									
										
										
										
											2015-12-13 02:54:22 +01:00
										 |  |  | 			ret = biphase(m_parms.acc, bit, m_parms.clock_phi - m_parms.prev_clock_phi); | 
					
						
							| 
									
										
										
										
											2015-12-12 15:14:26 +01:00
										 |  |  | 			m_parms.acc = 0; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-12 23:01:46 +01:00
										 |  |  | 		m_parms.prev_lo_clock = m_parms.lo_clock; | 
					
						
							| 
									
										
										
										
											2015-12-12 15:14:26 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	m_parms.numsamples++; | 
					
						
							|  |  |  | 	m_parms.prev_bb = m_parms.subcarr_bb[0]; | 
					
						
							|  |  |  | 	m_parms.prev_clock_phi = m_parms.clock_phi; | 
					
						
							|  |  |  | 	m_prev = demod; | 
					
						
							| 
									
										
										
										
											2015-12-13 02:54:22 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return ret; | 
					
						
							| 
									
										
										
										
											2015-12-12 15:14:26 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-13 02:54:22 +01:00
										 |  |  | bool RDSDemod::biphase(Real acc, bool& bit, Real d_cphi) | 
					
						
							| 
									
										
										
										
											2015-12-12 15:14:26 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2015-12-13 02:54:22 +01:00
										 |  |  | 	bool ret = false; | 
					
						
							| 
									
										
										
										
											2015-12-12 15:14:26 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	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
 | 
					
						
							|  |  |  | 	{ | 
					
						
							| 
									
										
										
										
											2015-12-13 02:54:22 +01:00
										 |  |  | 		// new bit found
 | 
					
						
							|  |  |  | 		int b = sign(m_parms.acc + m_parms.prev_acc); | 
					
						
							|  |  |  | 		bit = b ^ m_parms.dbit; | 
					
						
							|  |  |  | 		m_parms.dbit = b; | 
					
						
							|  |  |  | 		ret = true; | 
					
						
							| 
									
										
										
										
											2015-12-10 13:39:15 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-12 15:14:26 +01:00
										 |  |  | 	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; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2015-12-10 15:02:16 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-14 03:01:44 +01:00
										 |  |  | 		m_report.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; | 
					
						
							|  |  |  | 		m_report.acc = acc; | 
					
						
							|  |  |  | 		m_report.fclk = (d_cphi / (2 * M_PI)) * m_srate; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/*
 | 
					
						
							| 
									
										
										
										
											2015-12-12 19:53:24 +01:00
										 |  |  | 		qDebug("RDSDemod::biphase: frame: %d  acc: %+6.3f errs: %3d %3d  qual: %3.0f%%  clk: %7.2f", | 
					
						
							| 
									
										
										
										
											2015-12-12 15:14:26 +01:00
										 |  |  | 				m_parms.reading_frame, | 
					
						
							|  |  |  | 				acc, | 
					
						
							|  |  |  | 				m_parms.tot_errs[0], | 
					
						
							|  |  |  | 				m_parms.tot_errs[1], | 
					
						
							| 
									
										
										
										
											2015-12-14 03:01:44 +01:00
										 |  |  | 				m_report.qua, | 
					
						
							|  |  |  | 				m_report.fclk);*/ | 
					
						
							| 
									
										
										
										
											2015-12-12 15:14:26 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		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; | 
					
						
							| 
									
										
										
										
											2015-12-13 02:54:22 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return ret; | 
					
						
							| 
									
										
										
										
											2015-12-10 08:54:57 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Real RDSDemod::filter_lp_2400_iq(Real input, int iqIndex) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	/* Digital filter designed by mkfilter/mkshape/gencode A.J. Fisher
 | 
					
						
							|  |  |  | 	 Command line: /www/usr/fisher/helpers/mkfilter -Bu -Lp -o 10 | 
					
						
							|  |  |  | 	 -a 4.8000000000e-03 0.0000000000e+00 -l */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	m_xv[iqIndex][0] = m_xv[iqIndex][1]; m_xv[iqIndex][1] = m_xv[iqIndex][2]; | 
					
						
							|  |  |  | 	m_xv[iqIndex][2] = input / 4.491730007e+03; | 
					
						
							|  |  |  | 	m_yv[iqIndex][0] = m_yv[iqIndex][1]; m_yv[iqIndex][1] = m_yv[iqIndex][2]; | 
					
						
							|  |  |  | 	m_yv[iqIndex][2] =   (m_xv[iqIndex][0] + m_xv[iqIndex][2]) + 2 * m_xv[iqIndex][1] | 
					
						
							|  |  |  | 	+ ( -0.9582451124 * m_yv[iqIndex][0]) + (  1.9573545869 * m_yv[iqIndex][1]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return m_yv[iqIndex][2]; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-11 03:57:17 +01:00
										 |  |  | Real RDSDemod::filter_lp_pll(Real input) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	  m_xw[0] = m_xw[1]; | 
					
						
							|  |  |  | 	  m_xw[1] = input / 3.716236217e+01; | 
					
						
							|  |  |  | 	  m_yw[0] = m_yw[1]; | 
					
						
							|  |  |  | 	  m_yw[1] =   (m_xw[0] + m_xw[1]) | 
					
						
							|  |  |  | 	    + (  0.9461821078 * m_yw[0]); | 
					
						
							|  |  |  | 	  return m_yw[1]; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-10 13:39:15 +01:00
										 |  |  | int RDSDemod::sign(Real a) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return (a >= 0 ? 1 : 0); | 
					
						
							|  |  |  | } |