summaryrefslogtreecommitdiff
path: root/gr-digital
diff options
context:
space:
mode:
authorTom Rondeau2011-10-12 17:01:44 -0400
committerTom Rondeau2011-10-12 17:01:44 -0400
commit5f0bc5a2096012d6a94f72e20190ab8b3e0b4f88 (patch)
tree465294556e9209c9a0f864e171d6823e6da11053 /gr-digital
parentd1e3108c8705289d194300ca9fad1c22f579be9b (diff)
downloadgnuradio-5f0bc5a2096012d6a94f72e20190ab8b3e0b4f88.tar.gz
gnuradio-5f0bc5a2096012d6a94f72e20190ab8b3e0b4f88.tar.bz2
gnuradio-5f0bc5a2096012d6a94f72e20190ab8b3e0b4f88.zip
digital: wip: moved all OFDM examples and blks2impl to gr-digital.
Diffstat (limited to 'gr-digital')
-rw-r--r--gr-digital/python/ofdm.py82
-rw-r--r--gr-digital/python/ofdm_receiver.py133
-rw-r--r--gr-digital/python/ofdm_sync_fixed.py50
-rw-r--r--gr-digital/python/ofdm_sync_ml.py165
-rw-r--r--gr-digital/python/ofdm_sync_pn.py123
-rw-r--r--gr-digital/python/ofdm_sync_pnac.py125
6 files changed, 639 insertions, 39 deletions
diff --git a/gr-digital/python/ofdm.py b/gr-digital/python/ofdm.py
index e05f074f4..2663f7cf8 100644
--- a/gr-digital/python/ofdm.py
+++ b/gr-digital/python/ofdm.py
@@ -21,31 +21,12 @@
#
import math
-from gnuradio import gr, ofdm_packet_utils, modulation_utils2
+from gnuradio import gr, ofdm_packet_utils
import gnuradio.gr.gr_threading as _threading
import psk, qam
from gnuradio.blks2impl.ofdm_receiver import ofdm_receiver
-def _add_common_options(normal, expert):
- """
- Adds OFDM-specific options to the Options Parser that are common
- both to the modulator and demodulator.
- """
- mods_list = ", ".join(modulation_utils2.type_1_constellations().keys())
- print dir(modulation_utils2)
- print "MODS LIST: ", mods_list
- print modulation_utils2.type_1_mods()
- normal.add_option("-m", "--modulation", type="string", default="psk",
- help="set modulation type (" + mods_list + ") [default=%default]")
- normal.add_option("-c", "--constellation-points", type="int", default=2,
- help="set number of constellation points [default=%default]")
- expert.add_option("", "--fft-length", type="intx", default=512,
- help="set the number of FFT bins [default=%default]")
- expert.add_option("", "--occupied-tones", type="intx", default=200,
- help="set the number of occupied FFT bins [default=%default]")
- expert.add_option("", "--cp-length", type="intx", default=128,
- help="set the number of bits in the cyclic prefix [default=%default]")
# /////////////////////////////////////////////////////////////////////////////
# mod/demod with packets as i/o
@@ -81,9 +62,6 @@ class ofdm_mod(gr.hier_block2):
self._occupied_tones = options.occupied_tones
self._cp_length = options.cp_length
- print (options)
- arity = options.constellation_points
-
win = [] #[1 for i in range(self._fft_length)]
# Use freq domain to get doubled-up known symbol for correlation in time domain
@@ -104,10 +82,19 @@ class ofdm_mod(gr.hier_block2):
symbol_length = options.fft_length + options.cp_length
- print modulation_utils2.type_1_constellations
- const = modulation_utils2.type_1_constellations()[self._modulation](arity).points()
-
- self._pkt_input = gr.ofdm_mapper_bcv(const, msgq_limit,
+ mods = {"bpsk": 2, "qpsk": 4, "8psk": 8, "qam8": 8, "qam16": 16, "qam64": 64, "qam256": 256}
+ arity = mods[self._modulation]
+
+ rot = 1
+ if self._modulation == "qpsk":
+ rot = (0.707+0.707j)
+
+ if(self._modulation.find("psk") >= 0):
+ rotated_const = map(lambda pt: pt * rot, psk.gray_constellation[arity])
+ elif(self._modulation.find("qam") >= 0):
+ rotated_const = map(lambda pt: pt * rot, qam.constellation[arity])
+ #print rotated_const
+ self._pkt_input = gr.ofdm_mapper_bcv(rotated_const, msgq_limit,
options.occupied_tones, options.fft_length)
self.preambles = gr.ofdm_insert_preamble(self._fft_length, padded_preambles)
@@ -153,10 +140,14 @@ class ofdm_mod(gr.hier_block2):
"""
Adds OFDM-specific options to the Options Parser
"""
- _add_common_options(normal, expert)
- for mod in modulation_utils2.type_1_mods().values():
- mod.add_options(expert)
-
+ normal.add_option("-m", "--modulation", type="string", default="bpsk",
+ help="set modulation type (bpsk, qpsk, 8psk, qam{16,64}) [default=%default]")
+ expert.add_option("", "--fft-length", type="intx", default=512,
+ help="set the number of FFT bins [default=%default]")
+ expert.add_option("", "--occupied-tones", type="intx", default=200,
+ help="set the number of occupied FFT bins [default=%default]")
+ expert.add_option("", "--cp-length", type="intx", default=128,
+ help="set the number of bits in the cyclic prefix [default=%default]")
# Make a static method to call before instantiation
add_options = staticmethod(add_options)
@@ -205,9 +196,6 @@ class ofdm_demod(gr.hier_block2):
self._cp_length = options.cp_length
self._snr = options.snr
- arity = options.constellation_points
- print("con points is %s" % options.constellation_points)
-
# Use freq domain to get doubled-up known symbol for correlation in time domain
zeros_on_left = int(math.ceil((self._fft_length - self._occupied_tones)/2.0))
ksfreq = known_symbols_4512_3[0:self._occupied_tones]
@@ -223,11 +211,22 @@ class ofdm_demod(gr.hier_block2):
self._occupied_tones, self._snr, preambles,
options.log)
- constell = modulation_utils2.type_1_constellations()[self._modulation](arity)
+ mods = {"bpsk": 2, "qpsk": 4, "8psk": 8, "qam8": 8, "qam16": 16, "qam64": 64, "qam256": 256}
+ arity = mods[self._modulation]
+
+ rot = 1
+ if self._modulation == "qpsk":
+ rot = (0.707+0.707j)
+
+ if(self._modulation.find("psk") >= 0):
+ rotated_const = map(lambda pt: pt * rot, psk.gray_constellation[arity])
+ elif(self._modulation.find("qam") >= 0):
+ rotated_const = map(lambda pt: pt * rot, qam.constellation[arity])
+ #print rotated_const
phgain = 0.25
frgain = phgain*phgain / 4.0
- self.ofdm_demod = gr.ofdm_frame_sink2(constell.base(),
+ self.ofdm_demod = gr.ofdm_frame_sink(rotated_const, range(arity),
self._rcvd_pktq,
self._occupied_tones,
phgain, frgain)
@@ -254,9 +253,14 @@ class ofdm_demod(gr.hier_block2):
"""
Adds OFDM-specific options to the Options Parser
"""
- _add_common_options(normal, expert)
- for mod in modulation_utils2.type_1_mods().values():
- mod.add_options(expert)
+ normal.add_option("-m", "--modulation", type="string", default="bpsk",
+ help="set modulation type (bpsk or qpsk) [default=%default]")
+ expert.add_option("", "--fft-length", type="intx", default=512,
+ help="set the number of FFT bins [default=%default]")
+ expert.add_option("", "--occupied-tones", type="intx", default=200,
+ help="set the number of occupied FFT bins [default=%default]")
+ expert.add_option("", "--cp-length", type="intx", default=128,
+ help="set the number of bits in the cyclic prefix [default=%default]")
# Make a static method to call before instantiation
add_options = staticmethod(add_options)
diff --git a/gr-digital/python/ofdm_receiver.py b/gr-digital/python/ofdm_receiver.py
new file mode 100644
index 000000000..56ae0c0f0
--- /dev/null
+++ b/gr-digital/python/ofdm_receiver.py
@@ -0,0 +1,133 @@
+#!/usr/bin/env python
+#
+# Copyright 2006, 2007, 2008 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+import math
+from numpy import fft
+from gnuradio import gr
+from gnuradio.blks2impl.ofdm_sync_ml import ofdm_sync_ml
+from gnuradio.blks2impl.ofdm_sync_pn import ofdm_sync_pn
+from gnuradio.blks2impl.ofdm_sync_pnac import ofdm_sync_pnac
+from gnuradio.blks2impl.ofdm_sync_fixed import ofdm_sync_fixed
+
+class ofdm_receiver(gr.hier_block2):
+ """
+ Performs receiver synchronization on OFDM symbols.
+
+ The receiver performs channel filtering as well as symbol, frequency, and phase synchronization.
+ The synchronization routines are available in three flavors: preamble correlator (Schmidl and Cox),
+ modifid preamble correlator with autocorrelation (not yet working), and cyclic prefix correlator
+ (Van de Beeks).
+ """
+
+ def __init__(self, fft_length, cp_length, occupied_tones, snr, ks, logging=False):
+ """
+ Hierarchical block for receiving OFDM symbols.
+
+ The input is the complex modulated signal at baseband.
+ Synchronized packets are sent back to the demodulator.
+
+ @param fft_length: total number of subcarriers
+ @type fft_length: int
+ @param cp_length: length of cyclic prefix as specified in subcarriers (<= fft_length)
+ @type cp_length: int
+ @param occupied_tones: number of subcarriers used for data
+ @type occupied_tones: int
+ @param snr: estimated signal to noise ratio used to guide cyclic prefix synchronizer
+ @type snr: float
+ @param ks: known symbols used as preambles to each packet
+ @type ks: list of lists
+ @param logging: turn file logging on or off
+ @type logging: bool
+ """
+
+ gr.hier_block2.__init__(self, "ofdm_receiver",
+ gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature
+ gr.io_signature2(2, 2, gr.sizeof_gr_complex*occupied_tones, gr.sizeof_char)) # Output signature
+
+ bw = (float(occupied_tones) / float(fft_length)) / 2.0
+ tb = bw*0.08
+ chan_coeffs = gr.firdes.low_pass (1.0, # gain
+ 1.0, # sampling rate
+ bw+tb, # midpoint of trans. band
+ tb, # width of trans. band
+ gr.firdes.WIN_HAMMING) # filter type
+ self.chan_filt = gr.fft_filter_ccc(1, chan_coeffs)
+
+ win = [1 for i in range(fft_length)]
+
+ zeros_on_left = int(math.ceil((fft_length - occupied_tones)/2.0))
+ ks0 = fft_length*[0,]
+ ks0[zeros_on_left : zeros_on_left + occupied_tones] = ks[0]
+
+ ks0 = fft.ifftshift(ks0)
+ ks0time = fft.ifft(ks0)
+ # ADD SCALING FACTOR
+ ks0time = ks0time.tolist()
+
+ SYNC = "pn"
+ if SYNC == "ml":
+ nco_sensitivity = -1.0/fft_length # correct for fine frequency
+ self.ofdm_sync = ofdm_sync_ml(fft_length, cp_length, snr, ks0time, logging)
+ elif SYNC == "pn":
+ nco_sensitivity = -2.0/fft_length # correct for fine frequency
+ self.ofdm_sync = ofdm_sync_pn(fft_length, cp_length, logging)
+ elif SYNC == "pnac":
+ nco_sensitivity = -2.0/fft_length # correct for fine frequency
+ self.ofdm_sync = ofdm_sync_pnac(fft_length, cp_length, ks0time, logging)
+ elif SYNC == "fixed": # for testing only; do not user over the air
+ self.chan_filt = gr.multiply_const_cc(1.0) # remove filter and filter delay for this
+ nsymbols = 18 # enter the number of symbols per packet
+ freq_offset = 0.0 # if you use a frequency offset, enter it here
+ nco_sensitivity = -2.0/fft_length # correct for fine frequency
+ self.ofdm_sync = ofdm_sync_fixed(fft_length, cp_length, nsymbols, freq_offset, logging)
+
+ # Set up blocks
+
+ self.nco = gr.frequency_modulator_fc(nco_sensitivity) # generate a signal proportional to frequency error of sync block
+ self.sigmix = gr.multiply_cc()
+ self.sampler = gr.ofdm_sampler(fft_length, fft_length+cp_length)
+ self.fft_demod = gr.fft_vcc(fft_length, True, win, True)
+ self.ofdm_frame_acq = gr.ofdm_frame_acquisition(occupied_tones, fft_length,
+ cp_length, ks[0])
+
+ self.connect(self, self.chan_filt) # filter the input channel
+ self.connect(self.chan_filt, self.ofdm_sync) # into the synchronization alg.
+ self.connect((self.ofdm_sync,0), self.nco, (self.sigmix,1)) # use sync freq. offset output to derotate input signal
+ self.connect(self.chan_filt, (self.sigmix,0)) # signal to be derotated
+ self.connect(self.sigmix, (self.sampler,0)) # sample off timing signal detected in sync alg
+ self.connect((self.ofdm_sync,1), (self.sampler,1)) # timing signal to sample at
+
+ self.connect((self.sampler,0), self.fft_demod) # send derotated sampled signal to FFT
+ self.connect(self.fft_demod, (self.ofdm_frame_acq,0)) # find frame start and equalize signal
+ self.connect((self.sampler,1), (self.ofdm_frame_acq,1)) # send timing signal to signal frame start
+ self.connect((self.ofdm_frame_acq,0), (self,0)) # finished with fine/coarse freq correction,
+ self.connect((self.ofdm_frame_acq,1), (self,1)) # frame and symbol timing, and equalization
+
+ if logging:
+ self.connect(self.chan_filt, gr.file_sink(gr.sizeof_gr_complex, "ofdm_receiver-chan_filt_c.dat"))
+ self.connect(self.fft_demod, gr.file_sink(gr.sizeof_gr_complex*fft_length, "ofdm_receiver-fft_out_c.dat"))
+ self.connect(self.ofdm_frame_acq,
+ gr.file_sink(gr.sizeof_gr_complex*occupied_tones, "ofdm_receiver-frame_acq_c.dat"))
+ self.connect((self.ofdm_frame_acq,1), gr.file_sink(1, "ofdm_receiver-found_corr_b.dat"))
+ self.connect(self.sampler, gr.file_sink(gr.sizeof_gr_complex*fft_length, "ofdm_receiver-sampler_c.dat"))
+ self.connect(self.sigmix, gr.file_sink(gr.sizeof_gr_complex, "ofdm_receiver-sigmix_c.dat"))
+ self.connect(self.nco, gr.file_sink(gr.sizeof_gr_complex, "ofdm_receiver-nco_c.dat"))
diff --git a/gr-digital/python/ofdm_sync_fixed.py b/gr-digital/python/ofdm_sync_fixed.py
new file mode 100644
index 000000000..9bac789bf
--- /dev/null
+++ b/gr-digital/python/ofdm_sync_fixed.py
@@ -0,0 +1,50 @@
+#!/usr/bin/env python
+#
+# Copyright 2007 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+import math
+from gnuradio import gr
+
+class ofdm_sync_fixed(gr.hier_block2):
+ def __init__(self, fft_length, cp_length, nsymbols, freq_offset, logging=False):
+
+ gr.hier_block2.__init__(self, "ofdm_sync_fixed",
+ gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature
+ gr.io_signature2(2, 2, gr.sizeof_float, gr.sizeof_char)) # Output signature
+
+ # Use a fixed trigger point instead of sync block
+ symbol_length = fft_length + cp_length
+ pkt_length = nsymbols*symbol_length
+ data = (pkt_length)*[0,]
+ data[(symbol_length)-1] = 1
+ self.peak_trigger = gr.vector_source_b(data, True)
+
+ # Use a pre-defined frequency offset
+ foffset = (pkt_length)*[math.pi*freq_offset,]
+ self.frequency_offset = gr.vector_source_f(foffset, True)
+
+ self.connect(self, gr.null_sink(gr.sizeof_gr_complex))
+ self.connect(self.frequency_offset, (self,0))
+ self.connect(self.peak_trigger, (self,1))
+
+ if logging:
+ self.connect(self.peak_trigger, gr.file_sink(gr.sizeof_char, "ofdm_sync_fixed-peaks_b.dat"))
+
diff --git a/gr-digital/python/ofdm_sync_ml.py b/gr-digital/python/ofdm_sync_ml.py
new file mode 100644
index 000000000..7c75d7f1d
--- /dev/null
+++ b/gr-digital/python/ofdm_sync_ml.py
@@ -0,0 +1,165 @@
+#!/usr/bin/env python
+#
+# Copyright 2007,2008 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+import math
+from gnuradio import gr
+
+class ofdm_sync_ml(gr.hier_block2):
+ def __init__(self, fft_length, cp_length, snr, kstime, logging):
+ ''' Maximum Likelihood OFDM synchronizer:
+ J. van de Beek, M. Sandell, and P. O. Borjesson, "ML Estimation
+ of Time and Frequency Offset in OFDM Systems," IEEE Trans.
+ Signal Processing, vol. 45, no. 7, pp. 1800-1805, 1997.
+ '''
+
+ gr.hier_block2.__init__(self, "ofdm_sync_ml",
+ gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature
+ gr.io_signature2(2, 2, gr.sizeof_float, gr.sizeof_char)) # Output signature
+
+ self.input = gr.add_const_cc(0)
+
+ SNR = 10.0**(snr/10.0)
+ rho = SNR / (SNR + 1.0)
+ symbol_length = fft_length + cp_length
+
+ # ML Sync
+
+ # Energy Detection from ML Sync
+
+ self.connect(self, self.input)
+
+ # Create a delay line
+ self.delay = gr.delay(gr.sizeof_gr_complex, fft_length)
+ self.connect(self.input, self.delay)
+
+ # magnitude squared blocks
+ self.magsqrd1 = gr.complex_to_mag_squared()
+ self.magsqrd2 = gr.complex_to_mag_squared()
+ self.adder = gr.add_ff()
+
+ moving_sum_taps = [rho/2 for i in range(cp_length)]
+ self.moving_sum_filter = gr.fir_filter_fff(1,moving_sum_taps)
+
+ self.connect(self.input,self.magsqrd1)
+ self.connect(self.delay,self.magsqrd2)
+ self.connect(self.magsqrd1,(self.adder,0))
+ self.connect(self.magsqrd2,(self.adder,1))
+ self.connect(self.adder,self.moving_sum_filter)
+
+
+ # Correlation from ML Sync
+ self.conjg = gr.conjugate_cc();
+ self.mixer = gr.multiply_cc();
+
+ movingsum2_taps = [1.0 for i in range(cp_length)]
+ self.movingsum2 = gr.fir_filter_ccf(1,movingsum2_taps)
+
+ # Correlator data handler
+ self.c2mag = gr.complex_to_mag()
+ self.angle = gr.complex_to_arg()
+ self.connect(self.input,(self.mixer,1))
+ self.connect(self.delay,self.conjg,(self.mixer,0))
+ self.connect(self.mixer,self.movingsum2,self.c2mag)
+ self.connect(self.movingsum2,self.angle)
+
+ # ML Sync output arg, need to find maximum point of this
+ self.diff = gr.sub_ff()
+ self.connect(self.c2mag,(self.diff,0))
+ self.connect(self.moving_sum_filter,(self.diff,1))
+
+ #ML measurements input to sampler block and detect
+ self.f2c = gr.float_to_complex()
+ self.pk_detect = gr.peak_detector_fb(0.2, 0.25, 30, 0.0005)
+ self.sample_and_hold = gr.sample_and_hold_ff()
+
+ # use the sync loop values to set the sampler and the NCO
+ # self.diff = theta
+ # self.angle = epsilon
+
+ self.connect(self.diff, self.pk_detect)
+
+ # The DPLL corrects for timing differences between CP correlations
+ use_dpll = 0
+ if use_dpll:
+ self.dpll = gr.dpll_bb(float(symbol_length),0.01)
+ self.connect(self.pk_detect, self.dpll)
+ self.connect(self.dpll, (self.sample_and_hold,1))
+ else:
+ self.connect(self.pk_detect, (self.sample_and_hold,1))
+
+ self.connect(self.angle, (self.sample_and_hold,0))
+
+ ################################
+ # correlate against known symbol
+ # This gives us the same timing signal as the PN sync block only on the preamble
+ # we don't use the signal generated from the CP correlation because we don't want
+ # to readjust the timing in the middle of the packet or we ruin the equalizer settings.
+ kstime = [k.conjugate() for k in kstime]
+ kstime.reverse()
+ self.kscorr = gr.fir_filter_ccc(1, kstime)
+ self.corrmag = gr.complex_to_mag_squared()
+ self.div = gr.divide_ff()
+
+ # The output signature of the correlation has a few spikes because the rest of the
+ # system uses the repeated preamble symbol. It needs to work that generically if
+ # anyone wants to use this against a WiMAX-like signal since it, too, repeats.
+ # The output theta of the correlator above is multiplied with this correlation to
+ # identify the proper peak and remove other products in this cross-correlation
+ self.threshold_factor = 0.1
+ self.slice = gr.threshold_ff(self.threshold_factor, self.threshold_factor, 0)
+ self.f2b = gr.float_to_char()
+ self.b2f = gr.char_to_float()
+ self.mul = gr.multiply_ff()
+
+ # Normalize the power of the corr output by the energy. This is not really needed
+ # and could be removed for performance, but it makes for a cleaner signal.
+ # if this is removed, the threshold value needs adjustment.
+ self.connect(self.input, self.kscorr, self.corrmag, (self.div,0))
+ self.connect(self.moving_sum_filter, (self.div,1))
+
+ self.connect(self.div, (self.mul,0))
+ self.connect(self.pk_detect, self.b2f, (self.mul,1))
+ self.connect(self.mul, self.slice)
+
+ # Set output signals
+ # Output 0: fine frequency correction value
+ # Output 1: timing signal
+ self.connect(self.sample_and_hold, (self,0))
+ self.connect(self.slice, self.f2b, (self,1))
+
+
+ if logging:
+ self.connect(self.moving_sum_filter, gr.file_sink(gr.sizeof_float, "ofdm_sync_ml-energy_f.dat"))
+ self.connect(self.diff, gr.file_sink(gr.sizeof_float, "ofdm_sync_ml-theta_f.dat"))
+ self.connect(self.angle, gr.file_sink(gr.sizeof_float, "ofdm_sync_ml-epsilon_f.dat"))
+ self.connect(self.corrmag, gr.file_sink(gr.sizeof_float, "ofdm_sync_ml-corrmag_f.dat"))
+ self.connect(self.kscorr, gr.file_sink(gr.sizeof_gr_complex, "ofdm_sync_ml-kscorr_c.dat"))
+ self.connect(self.div, gr.file_sink(gr.sizeof_float, "ofdm_sync_ml-div_f.dat"))
+ self.connect(self.mul, gr.file_sink(gr.sizeof_float, "ofdm_sync_ml-mul_f.dat"))
+ self.connect(self.slice, gr.file_sink(gr.sizeof_float, "ofdm_sync_ml-slice_f.dat"))
+ self.connect(self.pk_detect, gr.file_sink(gr.sizeof_char, "ofdm_sync_ml-peaks_b.dat"))
+ if use_dpll:
+ self.connect(self.dpll, gr.file_sink(gr.sizeof_char, "ofdm_sync_ml-dpll_b.dat"))
+
+ self.connect(self.sample_and_hold, gr.file_sink(gr.sizeof_float, "ofdm_sync_ml-sample_and_hold_f.dat"))
+ self.connect(self.input, gr.file_sink(gr.sizeof_gr_complex, "ofdm_sync_ml-input_c.dat"))
+
diff --git a/gr-digital/python/ofdm_sync_pn.py b/gr-digital/python/ofdm_sync_pn.py
new file mode 100644
index 000000000..05b1de2e1
--- /dev/null
+++ b/gr-digital/python/ofdm_sync_pn.py
@@ -0,0 +1,123 @@
+#!/usr/bin/env python
+#
+# Copyright 2007,2008 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+import math
+from numpy import fft
+from gnuradio import gr
+
+class ofdm_sync_pn(gr.hier_block2):
+ def __init__(self, fft_length, cp_length, logging=False):
+ """
+ OFDM synchronization using PN Correlation:
+ T. M. Schmidl and D. C. Cox, "Robust Frequency and Timing
+ Synchonization for OFDM," IEEE Trans. Communications, vol. 45,
+ no. 12, 1997.
+ """
+
+ gr.hier_block2.__init__(self, "ofdm_sync_pn",
+ gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature
+ gr.io_signature2(2, 2, gr.sizeof_float, gr.sizeof_char)) # Output signature
+
+ self.input = gr.add_const_cc(0)
+
+ # PN Sync
+
+ # Create a delay line
+ self.delay = gr.delay(gr.sizeof_gr_complex, fft_length/2)
+
+ # Correlation from ML Sync
+ self.conjg = gr.conjugate_cc();
+ self.corr = gr.multiply_cc();
+
+ # Create a moving sum filter for the corr output
+ if 1:
+ moving_sum_taps = [1.0 for i in range(fft_length//2)]
+ self.moving_sum_filter = gr.fir_filter_ccf(1,moving_sum_taps)
+ else:
+ moving_sum_taps = [complex(1.0,0.0) for i in range(fft_length//2)]
+ self.moving_sum_filter = gr.fft_filter_ccc(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)]
+
+ if 1:
+ self.inputmovingsum = gr.fir_filter_fff(1,movingsum2_taps)
+ else:
+ self.inputmovingsum = gr.fft_filter_fff(1,movingsum2_taps)
+
+ self.square = gr.multiply_ff()
+ self.normalize = gr.divide_ff()
+
+ # Get magnitude (peaks) and angle (phase/freq error)
+ self.c2mag = gr.complex_to_mag_squared()
+ self.angle = gr.complex_to_arg()
+
+ 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.pk_detect = gr.peak_detector2_fb(9)
+
+ self.connect(self, self.input)
+
+ # Calculate the frequency offset from the correlation of the preamble
+ self.connect(self.input, self.delay)
+ self.connect(self.input, (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.angle, (self.sample_and_hold,0))
+
+ # Get the power of the input signal to normalize the output of the correlation
+ self.connect(self.input, 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))
+
+ # Create a moving sum filter for the corr output
+ matched_filter_taps = [1.0/cp_length for i in range(cp_length)]
+ self.matched_filter = gr.fir_filter_fff(1,matched_filter_taps)
+ self.connect(self.normalize, self.matched_filter)
+
+ self.connect(self.matched_filter, self.sub1, self.pk_detect)
+ #self.connect(self.matched_filter, self.pk_detect)
+ self.connect(self.pk_detect, (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))
+
+ if logging:
+ self.connect(self.matched_filter, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-mf_f.dat"))
+ self.connect(self.normalize, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-theta_f.dat"))
+ self.connect(self.angle, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-epsilon_f.dat"))
+ self.connect(self.pk_detect, gr.file_sink(gr.sizeof_char, "ofdm_sync_pn-peaks_b.dat"))
+ self.connect(self.sample_and_hold, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-sample_and_hold_f.dat"))
+ self.connect(self.input, gr.file_sink(gr.sizeof_gr_complex, "ofdm_sync_pn-input_c.dat"))
+
diff --git a/gr-digital/python/ofdm_sync_pnac.py b/gr-digital/python/ofdm_sync_pnac.py
new file mode 100644
index 000000000..10a125964
--- /dev/null
+++ b/gr-digital/python/ofdm_sync_pnac.py
@@ -0,0 +1,125 @@
+#!/usr/bin/env python
+#
+# Copyright 2007 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+import math
+from numpy import fft
+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
+
+
+ self.input = gr.add_const_cc(0)
+
+ symbol_length = fft_length + cp_length
+
+ # PN Sync with cross-correlation input
+
+ # cross-correlate with the known symbol
+ kstime = [k.conjugate() for k in kstime[0:fft_length//2]]
+ kstime.reverse()
+ self.crosscorr_filter = gr.fir_filter_ccc(1, kstime)
+
+ # Create a delay line
+ self.delay = gr.delay(gr.sizeof_gr_complex, fft_length/2)
+
+ # Correlation from ML Sync
+ self.conjg = gr.conjugate_cc();
+ self.corr = gr.multiply_cc();
+
+ # Create a moving sum filter for the input
+ 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.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 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.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)
+
+ # 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.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.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.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"))