fun_ofdm  1.0
802.11a Physical Layer for USRP
 All Classes Namespaces Files Functions Variables Enumerations Enumerator Macros Pages
fun::timing_sync Class Reference

The timing_sync block. More...

#include <timing_sync.h>

Inheritance diagram for fun::timing_sync:
fun::block< tagged_sample, tagged_sample > fun::block_base

Public Member Functions

 timing_sync ()
 Constructor for timing_sync block. More...
 
virtual void work ()
 Signal processing happens here. More...
 
- Public Member Functions inherited from fun::block< tagged_sample, tagged_sample >
 block (std::string block_name)
 constructor More...
 
- Public Member Functions inherited from fun::block_base
 block_base (std::string block_name)
 block_base constructor More...
 

Private Attributes

double m_phase_offset
 The phase rotation from symbol to symbol. More...
 
double m_phase_acc
 The total phase rotation for the current symbol. More...
 
std::vector< tagged_samplem_carryover
 Vector for storing the last 160 samples from the input_buffer and carrying them over to the next call to work() More...
 

Additional Inherited Members

- Public Attributes inherited from fun::block< tagged_sample, tagged_sample >
std::vector< tagged_sampleinput_buffer
 input_buffer contains new input items to be consumed More...
 
std::vector< tagged_sampleoutput_buffer
 output_buffer is where the output items of the block should be placed More...
 

Detailed Description

The timing_sync block.

Inputs tagged samples from the frame_detector block. Outputs tagged samples to the fft_symbols block.

The timing sync block is in charge of using the two LTS symbols to align the received frame in time. It also uses the two LTS symbols to perform an initial frequency offset estimation and applying the necessary correction.

Definition at line 33 of file timing_sync.h.

Constructor & Destructor Documentation

fun::timing_sync::timing_sync ( )

Constructor for timing_sync block.

Definition at line 26 of file timing_sync.cpp.

26  :
27  block("timing_sync"),
28  m_phase_acc(0),
29  m_phase_offset(0),
30  m_carryover(CARRYOVER_LENGTH, tagged_sample())
31  {}

Member Function Documentation

void fun::timing_sync::work ( )
virtual

Signal processing happens here.

Once this block detects the STS_END flag in the input samples it begins correlating the input with the known LTS_TIME_DOMAIN_CONJ samples to find the two Long Training Sequence symbols. Once the start of the two symbols are found it counts 8 samples backwards from the true start of the first LTS symbol as LTS1. This "tricks" the fft_symbols block into thinking that each symbol begins earlier than it actually does. This is ok because each symbol is prefaced by a cyclic prefix and due to the circular property of the DFT it still works. This also aids in reliability in case the estimate of the beginning of each symbol is slightly off.

This block also uses the two LTS symbols to calculate an intial frequency offset. It then applies the offset correction to all subsequent samples until the next frame is detected and a new estimation is calculated.

Implements fun::block< tagged_sample, tagged_sample >.

Definition at line 51 of file timing_sync.cpp.

References CARRYOVER_LENGTH, fun::block< tagged_sample, tagged_sample >::input_buffer, fun::LTS1, fun::LTS2, LTS_CORR_THRESHOLD, LTS_LENGTH, fun::LTS_TIME_DOMAIN_CONJ, m_carryover, m_phase_acc, m_phase_offset, fun::block< tagged_sample, tagged_sample >::output_buffer, and fun::STS_END.

52  {
53 
54  if(input_buffer.size() == 0) return;
55  assert(input_buffer.size() > CARRYOVER_LENGTH);
56  output_buffer.resize(input_buffer.size());
57 
58  std::vector<tagged_sample> input(input_buffer.size() + CARRYOVER_LENGTH);
59 
60  memcpy(&input[0],
61  &m_carryover[0],
62  CARRYOVER_LENGTH*sizeof(tagged_sample));
63 
64  memcpy(&input[CARRYOVER_LENGTH],
65  &input_buffer[0],
66  input_buffer.size() * sizeof(tagged_sample));
67 
68  for(int x = 0; x < input.size() - CARRYOVER_LENGTH; x++)
69  {
70  // End of STS found: Look for LTS peaks
71  if(input[x].tag == STS_END)
72  {
73  // Cross correlate against the LTS
74  std::vector<std::pair<double, int> > peaks;
75  for(int p = x; p < x + CARRYOVER_LENGTH - LTS_LENGTH; p++)
76  {
77  std::complex<double> corr(0, 0);
78  double power = 0;
79  for(int s = 0; s < 64; s++)
80  {
81  corr += input[p+s].sample * LTS_TIME_DOMAIN_CONJ[s] /* complex conjugate of LTS */;
82  power += std::norm(input[p+s].sample);
83  }
84  double corr_norm = std::abs(corr) / power;
85  if(corr_norm > LTS_CORR_THRESHOLD) peaks.push_back(std::pair<double, int>(corr_norm, p));
86  }
87 
88  std::sort(peaks.begin(), peaks.end());
89  std::reverse(peaks.begin(), peaks.end());
90 
91  // Look for two peaks, 64 samples apart
92  bool found = false;
93  int jump = 5;
94  for(int s = 0; s < std::min((int)peaks.size(), 3) && !found; s+=jump)
95  {
96  for(int t = s; t < std::min((int)peaks.size(), s+jump) && !found; t++)
97  {
98  if(std::abs(peaks[s].second - peaks[t].second) == 64)
99  {
100  // Determine the LTS offset
101  found = true;
102  int lts_offset = std::min(peaks[s].second, peaks[t].second) - 32; // Start of the LTS CP
103  if(lts_offset < 0) break;
104 
105  input[lts_offset+24].tag = LTS1; // First sample in the LTS
106  input[lts_offset+24+64].tag = LTS2; // First sample in the LTS
107 
108  std::complex<double> auto_corr_acc(0.0, 0.0);
109  for(int k = LTS1; k < LTS1; k++)
110  {
111  auto_corr_acc += input[k].sample * std::conj(input[k+LTS_LENGTH].sample);
112  }
113 
114  m_phase_offset = std::arg(auto_corr_acc) / 64.0;
115  m_phase_acc = std::arg(input[lts_offset + 32 + LTS_LENGTH*2 -1].sample * LTS_TIME_DOMAIN_CONJ[63]);
116  }
117  }
118  }
119  }
120 
122  while(m_phase_acc > 2.0*M_PI) m_phase_acc -= 2.0*M_PI;
123  while(m_phase_acc < -2.0*M_PI) m_phase_acc += 2.0*M_PI;
124  std::complex<double> phase_correction(std::cos(m_phase_acc), std::sin(m_phase_acc));
125  input[x].sample *= phase_correction;
126 
127  }
128 
129  // Copy working samples to output
130  memcpy(&output_buffer[0],
131  &input[0],
132  input_buffer.size() * sizeof(tagged_sample));
133 
134  // Carryover last 160 samples from input buffer
135  memcpy(&m_carryover[0],
136  &input[input_buffer.size()],
137  CARRYOVER_LENGTH * sizeof(tagged_sample));
138 
139  }

Member Data Documentation

std::vector<tagged_sample> fun::timing_sync::m_carryover
private

Vector for storing the last 160 samples from the input_buffer and carrying them over to the next call to work()

Definition at line 51 of file timing_sync.h.

Referenced by work().

double fun::timing_sync::m_phase_acc
private

The total phase rotation for the current symbol.

Definition at line 45 of file timing_sync.h.

Referenced by work().

double fun::timing_sync::m_phase_offset
private

The phase rotation from symbol to symbol.

Definition at line 43 of file timing_sync.h.

Referenced by work().


The documentation for this class was generated from the following files: