summaryrefslogtreecommitdiff
path: root/gnuradio-core
diff options
context:
space:
mode:
authortrondeau2008-04-17 14:37:19 +0000
committertrondeau2008-04-17 14:37:19 +0000
commitb96d58fda01e00e8a9f710824dffe9123d93e35f (patch)
tree42955f45614f575974cf5266d4bbc76b7c22c105 /gnuradio-core
parent8bce0d9e3f61b4af7799ed519109b83b926d4e12 (diff)
downloadgnuradio-b96d58fda01e00e8a9f710824dffe9123d93e35f.tar.gz
gnuradio-b96d58fda01e00e8a9f710824dffe9123d93e35f.tar.bz2
gnuradio-b96d58fda01e00e8a9f710824dffe9123d93e35f.zip
Improved the pnac ofdm sync block. This is based on a VTC'99 paper by Tufvesson, et al. that does a bit more work than the Schmidl and Cox to produce a more identifiable peak for the timing. This seems to work well in the simulation for low frequency errors. The correlation doesn't seem to track well, though. See the comments for more info. Also, the peak detection requires unity amplitude for the threshold detection. So, who wants to make an OFDM AGC?
git-svn-id: http://gnuradio.org/svn/gnuradio/trunk@8217 221aa14e-8319-0410-a670-987f0aec2ac5
Diffstat (limited to 'gnuradio-core')
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/ofdm_sync_pnac.py74
1 files changed, 43 insertions, 31 deletions
diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/ofdm_sync_pnac.py b/gnuradio-core/src/python/gnuradio/blks2impl/ofdm_sync_pnac.py
index 89f70ed2b..10a125964 100644
--- a/gnuradio-core/src/python/gnuradio/blks2impl/ofdm_sync_pnac.py
+++ b/gnuradio-core/src/python/gnuradio/blks2impl/ofdm_sync_pnac.py
@@ -26,7 +26,25 @@ from gnuradio import gr
class ofdm_sync_pnac(gr.hier_block2):
def __init__(self, fft_length, cp_length, kstime, logging=False):
-
+ """
+ OFDM synchronization using PN Correlation and initial cross-correlation:
+ F. Tufvesson, O. Edfors, and M. Faulkner, "Time and Frequency Synchronization for OFDM using
+ PN-Sequency Preambles," IEEE Proc. VTC, 1999, pp. 2203-2207.
+
+ This implementation is meant to be a more robust version of the Schmidl and Cox receiver design.
+ By correlating against the preamble and using that as the input to the time-delayed correlation,
+ this circuit produces a very clean timing signal at the end of the preamble. The timing is
+ more accurate and does not have the problem associated with determining the timing from the
+ plateau structure in the Schmidl and Cox.
+
+ This implementation appears to require that the signal is received with a normalized power or signal
+ scalling factor to reduce ambiguities intorduced from partial correlation of the cyclic prefix and
+ the peak detection. A better peak detection block might fix this.
+
+ Also, the cross-correlation falls apart as the frequency offset gets larger and completely fails
+ when an integer offset is introduced. Another thing to look at.
+ """
+
gr.hier_block2.__init__(self, "ofdm_sync_pnac",
gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature
gr.io_signature2(2, 2, gr.sizeof_float, gr.sizeof_char)) # Output signature
@@ -42,7 +60,6 @@ class ofdm_sync_pnac(gr.hier_block2):
kstime = [k.conjugate() for k in kstime[0:fft_length//2]]
kstime.reverse()
self.crosscorr_filter = gr.fir_filter_ccc(1, kstime)
- self.connect(self.crosscorr_filter, gr.file_sink(gr.sizeof_gr_complex, "crosscorr.dat"))
# Create a delay line
self.delay = gr.delay(gr.sizeof_gr_complex, fft_length/2)
@@ -51,63 +68,58 @@ class ofdm_sync_pnac(gr.hier_block2):
self.conjg = gr.conjugate_cc();
self.corr = gr.multiply_cc();
- # Create a moving sum filter for the corr output
- moving_sum_taps = [1.0 for i in range(fft_length//2)]
- self.moving_sum_filter = gr.fir_filter_ccf(1,moving_sum_taps)
-
# Create a moving sum filter for the input
- self.inputmag2 = gr.complex_to_mag_squared()
- movingsum2_taps = [1.0 for i in range(fft_length//2)]
- self.inputmovingsum = gr.fir_filter_fff(1,movingsum2_taps)
- self.square = gr.multiply_ff()
- self.normalize = gr.divide_ff()
+ self.mag = gr.complex_to_mag_squared()
+ movingsum_taps = (fft_length//1)*[1.0,]
+ self.power = gr.fir_filter_fff(1,movingsum_taps)
# Get magnitude (peaks) and angle (phase/freq error)
self.c2mag = gr.complex_to_mag_squared()
self.angle = gr.complex_to_arg()
-
+ self.compare = gr.sub_ff()
+
self.sample_and_hold = gr.sample_and_hold_ff()
#ML measurements input to sampler block and detect
- self.sub1 = gr.add_const_ff(-1)
- self.pk_detect = gr.peak_detector_fb(0.20, 0.20, 30, 0.001)
+ self.threshold = gr.threshold_ff(0,0,0) # threshold detection might need to be tweaked
+ self.peaks = gr.float_to_char()
self.connect(self, self.input)
# Cross-correlate input signal with known preamble
self.connect(self.input, self.crosscorr_filter)
- # use the output of the cross-correlation as input to Schmidl&Cox
+ # use the output of the cross-correlation as input time-shifted correlation
self.connect(self.crosscorr_filter, self.delay)
self.connect(self.crosscorr_filter, (self.corr,0))
self.connect(self.delay, self.conjg)
self.connect(self.conjg, (self.corr,1))
- self.connect(self.corr, self.moving_sum_filter)
- self.connect(self.moving_sum_filter, self.c2mag)
- self.connect(self.moving_sum_filter, self.angle)
+ self.connect(self.corr, self.c2mag)
+ self.connect(self.corr, self.angle)
self.connect(self.angle, (self.sample_and_hold,0))
+
+ # Get the power of the input signal to compare against the correlation
+ self.connect(self.crosscorr_filter, self.mag, self.power)
- # Get the power of the input signal to normalize the output of the correlation
- self.connect(self.crosscorr_filter, self.inputmag2, self.inputmovingsum)
- self.connect(self.inputmovingsum, (self.square,0))
- self.connect(self.inputmovingsum, (self.square,1))
- self.connect(self.square, (self.normalize,1))
- self.connect(self.c2mag, (self.normalize,0))
-
- self.connect(self.normalize, self.sub1, self.pk_detect)
- self.connect(self.pk_detect, (self.sample_and_hold,1))
+ # Compare the power to the correlator output to determine timing peak
+ # When the peak occurs, it peaks above zero, so the thresholder detects this
+ self.connect(self.c2mag, (self.compare,0))
+ self.connect(self.power, (self.compare,1))
+ self.connect(self.compare, self.threshold)
+ self.connect(self.threshold, self.peaks, (self.sample_and_hold,1))
# Set output signals
# Output 0: fine frequency correction value
# Output 1: timing signal
self.connect(self.sample_and_hold, (self,0))
- self.connect(self.pk_detect, (self,1))
+ self.connect(self.peaks, (self,1))
if logging:
+ self.connect(self.compare, gr.file_sink(gr.sizeof_float, "ofdm_sync_pnac-compare_f.dat"))
self.connect(self.c2mag, gr.file_sink(gr.sizeof_float, "ofdm_sync_pnac-theta_f.dat"))
- self.connect(self.normalize, gr.file_sink(gr.sizeof_float, "ofdm_sync_pnac-normalized_f.dat"))
- self.connect(self.sub1, gr.file_sink(gr.sizeof_float, "ofdm_sync_pnac-sub1_f.dat"))
+ self.connect(self.power, gr.file_sink(gr.sizeof_float, "ofdm_sync_pnac-inputpower_f.dat"))
self.connect(self.angle, gr.file_sink(gr.sizeof_float, "ofdm_sync_pnac-epsilon_f.dat"))
- self.connect(self.pk_detect, gr.file_sink(gr.sizeof_char, "ofdm_sync_pnac-peaks_b.dat"))
+ self.connect(self.threshold, gr.file_sink(gr.sizeof_float, "ofdm_sync_pnac-threshold_f.dat"))
+ self.connect(self.peaks, gr.file_sink(gr.sizeof_char, "ofdm_sync_pnac-peaks_b.dat"))
self.connect(self.sample_and_hold, gr.file_sink(gr.sizeof_float, "ofdm_sync_pnac-sample_and_hold_f.dat"))
self.connect(self.input, gr.file_sink(gr.sizeof_gr_complex, "ofdm_sync_pnac-input_c.dat"))