[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Discuss-gnuradio] Phase Locked Loop Example Part 2

From: Rick Parrish
Subject: [Discuss-gnuradio] Phase Locked Loop Example Part 2
Date: Sat, 08 Nov 2003 03:39:13 -0600
User-agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.4) Gecko/20030624


Here's the implementation. Not much to it ...a simple loop inside inputData method and some accessors for setting options. This particular version has been hacked up a bit for accepting 4 level FSK symbol sets. On some radios, the signal is inverted from what's expected so an invert flag takes care of that. For a 4 level symbol set {0, 1, 2, 3} inverting the signal means subtracting the original symbol from 3 so 0 becomes 3, 1 becomes 2, etc.

The PLL effect is actually accomplished in one line of code ... see below where the accumulator is scaled by 0.95 (see "95%" in comments). After extracting a sequence of one or symbols, the accumulator measures the number of samples remaining to be carried over into the next pass. On a perfect signal, the time remaining is zero which implies perfect phase. In reality, we'll be a little bit early (positive) or a little bit late (negative). Scaling this left-over time value has the effect of fine tuning the periodic point in time where the PLL demarks the boundary between successive symbols.


/* Copyright 2002 see the file "COPYING" for license. */

#include "PhaseLockedLoop.h"

PhaseLockedLoop::PhaseLockedLoop(unsigned long dwSamplesPerSecond, unsigned long dwSymbolsPerSecond, SymbolConsumer *pConsumer) :
 _fDamp = _fPhase = _fAccumulate = 0.0;

// Handle phase correction and delta value to discrete bit decomposition.
// As clock drifts around, calculate drift and add/subtract a small
// adjustment to the accumulator.
bool PhaseLockedLoop::inputData(char chSymbol, const long nDelta, const unsigned long dwSamplesPerSecond)
 if (!_pConsumer) return false;
// Compute new dampened/averaged phase error - using 5% correction for now.
 const double kPhaseDamper = 0.05;
 double fDelta = 0.;

 if (chSymbol == _chLastSymbol)
   _nLastCount += nDelta;
   return false;

 char chMappedSymbol = _bInvert ? 3 - _chLastSymbol : _chLastSymbol;
 fDelta = _nLastCount;
 // Compute delta time as some sub-second decimal value.
 fDelta /= dwSamplesPerSecond;
 // Add delta to our delta time accumlator.
 _fAccumulate += fDelta;
 // Decompose the delta time value into a sequence of
 // zero or more symbols all having the same value.
 while (_fAccumulate >= _fBitHalfTime)
   _pConsumer->consume(chMappedSymbol); // symbol consumer called here.
   _fAccumulate -= _fBitTime;
 // _fAccumulate is in range of -_fBitHalfTime to +_fBitHalfTime.
 // A value of zero is perfect mid-phase sampling.
 // A positive value means we are sampling early in the clock phase.
 // A negative value means we are sampling late in the clock phase.

 // Ideally the accumulator value IS our phase but we can't trust
 // this 100% due to noise so we dampen and average the phase error
 // values to tolerate an occassional noise glitch while improving
 // phase tracking over time.

 // Apply error to clock accumulator.
 // Comment out this next line to turn PLL phase correction OFF.
 _fAccumulate *= (1.0 - kPhaseDamper); // 95%

 _chLastSymbol = chSymbol;
 _nLastCount = nDelta;
 return false;

void PhaseLockedLoop::setSpeed(const unsigned long dwSymbolsPerSecond)
 _iSymbolRate = dwSymbolsPerSecond;
 // measured in seconds
 _fBitTime = 1.0 / (double)dwSymbolsPerSecond;
 _fBitHalfTime = 0.5 / (double)dwSymbolsPerSecond;
 if (_pConsumer) _pConsumer->setOption("symbolrate", _iSymbolRate);

void PhaseLockedLoop::setConsumer(SymbolConsumer *pConsumer)
 _pConsumer = pConsumer;
 if (_pConsumer) _pConsumer->setOption("symbolrate", _iSymbolRate);

reply via email to

[Prev in Thread] Current Thread [Next in Thread]