[prev in list] [next in list] [prev in thread] [next in thread] 

List:       gnuradio-discuss
Subject:    [Discuss-gnuradio] Phase Locked Loop Example Part 2
From:       Rick Parrish <rfmobile () swbell ! net>
Date:       2003-11-08 9:39:13
Message-ID: 3FACB9C1.5010305 () swbell ! net
[Download RAW message or body]

Ian;

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.

-rick

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

#include "PhaseLockedLoop.h"

PhaseLockedLoop::PhaseLockedLoop(unsigned long dwSamplesPerSecond, 
unsigned long dwSymbolsPerSecond, SymbolConsumer *pConsumer) :
  _dwSamplesPerSecond(dwSamplesPerSecond),
  _pConsumer(pConsumer),
  _bInvert(false),
  _nLastCount(0L),
  _chLastSymbol(0),
  _iSymbolRate(dwSymbolsPerSecond)
{
  _fDamp = _fPhase = _fAccumulate = 0.0;
  setSpeed(dwSymbolsPerSecond);
}

// 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);
}





[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic