mirror of
				https://github.com/f4exb/sdrangel.git
				synced 2025-10-31 13:00:26 -04:00 
			
		
		
		
	Implemented phase lock with configurable outputs
This commit is contained in:
		
							parent
							
								
									6d3e18f79c
								
							
						
					
					
						commit
						ea5cdb034f
					
				| @ -15,6 +15,7 @@ | ||||
| // along with this program. If not, see <http://www.gnu.org/licenses/>.          //
 | ||||
| ///////////////////////////////////////////////////////////////////////////////////
 | ||||
| 
 | ||||
| #include <vector> | ||||
| #include "dsp/dsptypes.h" | ||||
| 
 | ||||
| /** Phase-locked loop mainly for broadcadt FM stereo pilot. */ | ||||
| @ -43,6 +44,9 @@ public: | ||||
|      */ | ||||
|     PhaseLock(Real freq, Real bandwidth, Real minsignal); | ||||
| 
 | ||||
|     virtual ~PhaseLock() | ||||
|     {} | ||||
| 
 | ||||
|     /**
 | ||||
|      * Change phase locked loop parameters | ||||
|      * | ||||
| @ -61,12 +65,21 @@ public: | ||||
|     void process(const std::vector<Real>& samples_in, std::vector<Real>& samples_out); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Process samples and extract 19 kHz pilot tone. | ||||
|      * Generate phase-locked 38 kHz tone with unit amplitude. | ||||
|      * Process samples and extract pilot tone. Generate phase-locked twice | ||||
|      * the frequency tone with unit amplitude. Mostly useful for 19 kHz stereo | ||||
|      * pilot tone on broadcast FM. | ||||
|      * In flow version | ||||
|      */ | ||||
|     void process(const Real& sample_in, Real& sample_out); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Process samples and track a pilot tone. Generate samples for multiple phase-locked | ||||
|      * signals. Implement the processPhase virtual method to produce the output samples. | ||||
|      * In flow version. Ex: Use 19 kHz stereo pilot tone to generate 38 kHz (stereo) and 57 kHz | ||||
|      * pilots (see RDSPhaseLock class below). | ||||
|      */ | ||||
|     void process(const Real& sample_in, std::vector<Real>& samples_out); | ||||
| 
 | ||||
|     /** Return true if the phase-locked loop is locked. */ | ||||
|     bool locked() const | ||||
|     { | ||||
| @ -79,13 +92,23 @@ public: | ||||
|         return 2 * m_pilot_level; | ||||
|     } | ||||
| 
 | ||||
| protected: | ||||
|     Real    m_phase; | ||||
|     Real    m_psin; | ||||
|     Real    m_pcos; | ||||
|     /**
 | ||||
|      * Callback method to produce multiple outputs from the current phase value in m_phase | ||||
|      * and/or the sin and cos values in m_psin and m_pcos | ||||
|      */ | ||||
|     virtual void processPhase(std::vector<Real>& samples_out) const {}; | ||||
| 
 | ||||
| private: | ||||
|     Real    m_minfreq, m_maxfreq; | ||||
|     Real    m_phasor_b0, m_phasor_a1, m_phasor_a2; | ||||
|     Real    m_phasor_i1, m_phasor_i2, m_phasor_q1, m_phasor_q2; | ||||
|     Real    m_loopfilter_b0, m_loopfilter_b1; | ||||
|     Real    m_loopfilter_x1; | ||||
|     Real    m_freq, m_phase; | ||||
|     Real    m_freq; | ||||
|     Real    m_minsignal; | ||||
|     Real    m_pilot_level; | ||||
|     int     m_lock_delay; | ||||
| @ -97,3 +120,45 @@ private: | ||||
|     quint64 m_sample_cnt; | ||||
|     std::vector<PpsEvent> m_pps_events; | ||||
| }; | ||||
| 
 | ||||
| class StereoPhaseLock : public PhaseLock | ||||
| { | ||||
| public: | ||||
| 	StereoPhaseLock(Real freq, Real bandwidth, Real minsignal) : | ||||
| 		PhaseLock(freq, bandwidth, minsignal) | ||||
|     {} | ||||
| 
 | ||||
|     virtual ~StereoPhaseLock() | ||||
|     {} | ||||
| 
 | ||||
| protected: | ||||
|     virtual void processPhase(std::vector<Real>& samples_out) const | ||||
|     { | ||||
|     	samples_out[0] = m_psin; // f Pilot
 | ||||
|         // Generate double-frequency output.
 | ||||
|         // sin(2*x) = 2 * sin(x) * cos(x)
 | ||||
|     	samples_out[1] = 2.0 * m_psin * m_pcos; // 2f Pilot
 | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| class RDSPhaseLock : public PhaseLock | ||||
| { | ||||
| public: | ||||
| 	RDSPhaseLock(Real freq, Real bandwidth, Real minsignal) : | ||||
| 		PhaseLock(freq, bandwidth, minsignal) | ||||
|     {} | ||||
| 
 | ||||
|     virtual ~RDSPhaseLock() | ||||
|     {} | ||||
| 
 | ||||
| protected: | ||||
|     virtual void processPhase(std::vector<Real>& samples_out) const | ||||
|     { | ||||
|     	samples_out[0] = m_psin; // f Pilot
 | ||||
|         // Generate double-frequency output.
 | ||||
|         // sin(2*x) = 2 * sin(x) * cos(x)
 | ||||
|     	samples_out[1] = 2.0 * m_psin * m_pcos; // 2f Pilot
 | ||||
|     	samples_out[2] = sin(3.0 * m_phase); // 3f pilot
 | ||||
|     } | ||||
| }; | ||||
|  | ||||
| @ -45,6 +45,8 @@ PhaseLock::PhaseLock(Real freq, Real bandwidth, Real minsignal) | ||||
|     m_lock_cnt   = 0; | ||||
|     m_unlock_cnt = 0; | ||||
|     m_pilot_level = 0; | ||||
|     m_psin = 0.0; | ||||
|     m_pcos = 1.0; | ||||
| 
 | ||||
|     // Create 2nd order filter for I/Q representation of phase error.
 | ||||
|     // Filter has two poles, unit DC gain.
 | ||||
| @ -377,3 +379,108 @@ void PhaseLock::process(const Real& sample_in, Real& sample_out) | ||||
|     // Update sample counter.
 | ||||
|     m_sample_cnt += 1; // n
 | ||||
| } | ||||
| 
 | ||||
| // Process samples. Multiple output
 | ||||
| void PhaseLock::process(const Real& sample_in, std::vector<Real>& samples_out) | ||||
| { | ||||
|     bool was_locked = (m_lock_cnt >= m_lock_delay); | ||||
|     m_pps_events.clear(); | ||||
| 
 | ||||
| 	// Generate locked pilot tone.
 | ||||
| 	m_psin = sin(m_phase); | ||||
| 	m_pcos = cos(m_phase); | ||||
| 
 | ||||
| 	// Generate output
 | ||||
| 	processPhase(samples_out); | ||||
| 
 | ||||
| 	// Multiply locked tone with input.
 | ||||
| 	Real x = sample_in; | ||||
| 	Real phasor_i = m_psin * x; | ||||
| 	Real phasor_q = m_pcos * x; | ||||
| 
 | ||||
| 	// Run IQ phase error through low-pass filter.
 | ||||
| 	phasor_i = m_phasor_b0 * phasor_i | ||||
| 			   - m_phasor_a1 * m_phasor_i1 | ||||
| 			   - m_phasor_a2 * m_phasor_i2; | ||||
| 	phasor_q = m_phasor_b0 * phasor_q | ||||
| 			   - m_phasor_a1 * m_phasor_q1 | ||||
| 			   - m_phasor_a2 * m_phasor_q2; | ||||
| 	m_phasor_i2 = m_phasor_i1; | ||||
| 	m_phasor_i1 = phasor_i; | ||||
| 	m_phasor_q2 = m_phasor_q1; | ||||
| 	m_phasor_q1 = phasor_q; | ||||
| 
 | ||||
| 	// Convert I/Q ratio to estimate of phase error.
 | ||||
| 	Real phase_err; | ||||
| 	if (phasor_i > abs(phasor_q)) { | ||||
| 		// We are within +/- 45 degrees from lock.
 | ||||
| 		// Use simple linear approximation of arctan.
 | ||||
| 		phase_err = phasor_q / phasor_i; | ||||
| 	} else if (phasor_q > 0) { | ||||
| 		// We are lagging more than 45 degrees behind the input.
 | ||||
| 		phase_err = 1; | ||||
| 	} else { | ||||
| 		// We are more than 45 degrees ahead of the input.
 | ||||
| 		phase_err = -1; | ||||
| 	} | ||||
| 
 | ||||
| 	// Detect pilot level (conservative).
 | ||||
| 	// m_pilot_level = std::min(m_pilot_level, phasor_i);
 | ||||
| 	m_pilot_level = phasor_i; | ||||
| 
 | ||||
| 	// Run phase error through loop filter and update frequency estimate.
 | ||||
| 	m_freq += m_loopfilter_b0 * phase_err | ||||
| 			  + m_loopfilter_b1 * m_loopfilter_x1; | ||||
| 	m_loopfilter_x1 = phase_err; | ||||
| 
 | ||||
| 	// Limit frequency to allowable range.
 | ||||
| 	m_freq = std::max(m_minfreq, std::min(m_maxfreq, m_freq)); | ||||
| 
 | ||||
| 	// Update locked phase.
 | ||||
| 	m_phase += m_freq; | ||||
| 	if (m_phase > 2.0 * M_PI) | ||||
| 	{ | ||||
| 		m_phase -= 2.0 * M_PI; | ||||
| 		m_pilot_periods++; | ||||
| 
 | ||||
| 		// Generate pulse-per-second.
 | ||||
| 		if (m_pilot_periods == pilot_frequency) | ||||
| 		{ | ||||
| 			m_pilot_periods = 0; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
|     // Update lock status.
 | ||||
|     if (2 * m_pilot_level > m_minsignal) | ||||
|     { | ||||
|         if (m_lock_cnt < m_lock_delay) | ||||
|         { | ||||
|             m_lock_cnt += 1; // n
 | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|         	m_unlock_cnt = 0; | ||||
|         } | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|     	if (m_unlock_cnt < m_unlock_delay) | ||||
|     	{ | ||||
|     		m_unlock_cnt += 1; | ||||
|     	} | ||||
|     	else | ||||
|     	{ | ||||
|     		m_lock_cnt = 0; | ||||
|     	} | ||||
|     } | ||||
| 
 | ||||
|     // Drop PPS events when pilot not locked.
 | ||||
|     if (m_lock_cnt < m_lock_delay) { | ||||
|         m_pilot_periods = 0; | ||||
|         m_pps_cnt = 0; | ||||
|         m_pps_events.clear(); | ||||
|     } | ||||
| 
 | ||||
|     // Update sample counter.
 | ||||
|     m_sample_cnt += 1; // n
 | ||||
| } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user