Esempio n. 1
0
    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.

        Args:
            fft_length: total number of subcarriers (int)
            cp_length: length of cyclic prefix as specified in subcarriers (<= fft_length) (int)
            occupied_tones: number of subcarriers used for data (int)
            snr: estimated signal to noise ratio used to guide cyclic prefix synchronizer (float)
            ks: known symbols used as preambles to each packet (list of lists)
            logging: turn file logging on or off (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 = filter.firdes.low_pass (1.0,                     # gain
                                              1.0,                     # sampling rate
                                              bw+tb,                   # midpoint of trans. band
                                              tb,                      # width of trans. band
                                              filter.firdes.WIN_HAMMING)   # filter type
        self.chan_filt = filter.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)
        # for testing only; do not user over the air
        # remove filter and filter delay for this
        elif SYNC == "fixed":
            self.chan_filt = blocks.multiply_const_cc(1.0) 
            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 = analog.frequency_modulator_fc(nco_sensitivity)         # generate a signal proportional to frequency error of sync block
        self.sigmix = blocks.multiply_cc()
        self.sampler = digital.ofdm_sampler(fft_length, fft_length+cp_length)
        self.fft_demod = gr_fft.fft_vcc(fft_length, True, win, True)
        self.ofdm_frame_acq = digital.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, blocks.file_sink(gr.sizeof_gr_complex, "ofdm_receiver-chan_filt_c.dat"))
            self.connect(self.fft_demod, blocks.file_sink(gr.sizeof_gr_complex*fft_length, "ofdm_receiver-fft_out_c.dat"))
            self.connect(self.ofdm_frame_acq,
                         blocks.file_sink(gr.sizeof_gr_complex*occupied_tones, "ofdm_receiver-frame_acq_c.dat"))
            self.connect((self.ofdm_frame_acq,1), blocks.file_sink(1, "ofdm_receiver-found_corr_b.dat"))
            self.connect(self.sampler, blocks.file_sink(gr.sizeof_gr_complex*fft_length, "ofdm_receiver-sampler_c.dat"))
            self.connect(self.sigmix, blocks.file_sink(gr.sizeof_gr_complex, "ofdm_receiver-sigmix_c.dat"))
            self.connect(self.nco, blocks.file_sink(gr.sizeof_gr_complex, "ofdm_receiver-nco_c.dat"))
Esempio n. 2
0
    def __init__(
        self, fft_length, cp_length, occupied_tones, snr, ks, threshold, options, logging=False
    ):  # apurv++: added num_symbols, use_chan_filt
        """
    Hierarchical block for receiving OFDM symbols.

    The input is the complex modulated signal at baseband.
    Synchronized packets are sent back to the demodulator.

    @param params: Raw OFDM parameters
    @type  params: ofdm_params
    @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_signature3(
                3, 3, gr.sizeof_gr_complex * occupied_tones, gr.sizeof_char, gr.sizeof_gr_complex * occupied_tones
            ),
        )  # Output signature

        # low-pass filter the input channel
        bw = (float(occupied_tones) / float(fft_length)) / 2.0
        tb = bw * 0.08
        lpf_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, lpf_coeffs)
        self.connect(self, self.chan_filt)
        self.connect(self.chan_filt, gr.file_sink(gr.sizeof_gr_complex, "rx-filt.dat"))

        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)
        ks0time = ks0time.tolist()

        ks1 = fft_length * [0]
        ks1[zeros_on_left : zeros_on_left + occupied_tones] = ks[1]
        ks1 = fft.ifftshift(ks1)
        ks1time = fft.ifft(ks1)
        ks1time = ks1time.tolist()

        # sync = ofdm_sync_pn(fft_length, cp_length, True, logging)		# raw
        sync = ofdm_sync_pn(fft_length, cp_length, True, ks0time, ks1time, threshold, logging)  # crosscorr version

        use_chan_filt = 1
        if use_chan_filt == 0:
            self.connect(gr.file_source(gr.sizeof_gr_complex, "chan-filt.dat"), sync)
        else:
            self.connect(self.chan_filt, sync)

        # correct for fine frequency offset computed in sync (up to +-pi/fft_length)
        nco_sensitivity = 2.0 / fft_length

        nco = gr.frequency_modulator_fc(nco_sensitivity)
        sigmix = gr.multiply_cc()

        # sample at symbol boundaries
        # NOTE: (sync,2) indicates the first sample of the symbol!
        sampler = digital_swig.ofdm_sampler(fft_length, fft_length + cp_length, len(ks) + 1, timeout=100)  # apurv--

        # frequency offset correction #
        self.connect((sync, 0), (sigmix, 0))
        self.connect((sync, 1), nco, (sigmix, 1))
        self.connect(sigmix, (sampler, 0))
        self.connect((sync, 2), (sampler, 1))

        """
    self.connect((sync,0), (sampler,0))
    self.connect((sync,2), (sampler,1))  # timing signal to sample at
    self.connect((sync,1), gr.file_sink(gr.sizeof_float, "offset.dat"))
    """

        self.connect((sampler, 1), gr.file_sink(gr.sizeof_char, "sampler_timing.dat"))
        # self.connect((sampler, 2), gr.file_sink(gr.sizeof_char*fft_length, "sampler_timing_fft.dat"))

        # fft on the symbols
        win = [1 for i in range(fft_length)]
        # see gr_fft_vcc_fftw that it works differently if win = []
        fft1 = gr.fft_vcc(fft_length, True, win, True)
        self.connect((sampler, 0), fft1)

        # use the preamble to correct the coarse frequency offset and initial equalizer
        ###frame_acq = raw.ofdm_frame_acquisition(fft_length, cp_length, preambles_raw, carriers)
        frame_acq = digital_swig.ofdm_frame_acquisition(occupied_tones, fft_length, cp_length, ks)

        self.frame_acq = frame_acq

        # normal operation #
        self.connect((fft1, 0), gr.file_sink(gr.sizeof_gr_complex * options.fft_length, "fft-out.dat"))
        self.connect((sampler, 1), gr.file_sink(gr.sizeof_char, "sampler_timing.dat"))
        self.connect(fft1, (frame_acq, 0))
        self.connect((sampler, 1), (frame_acq, 1))

        """ 
    # enable to have manual input to frame_acq # 
    self.connect(fft1, gr.null_sink(gr.sizeof_gr_complex*options.fft_length))
    self.connect(gr.file_source(gr.sizeof_gr_complex*options.fft_length, "combined.dat"), (frame_acq,0))
    self.connect(gr.file_source(gr.sizeof_char, "combined_t.dat"), (frame_acq,1))
    """

        # self.connect(fft, gr.null_sink(gr.sizeof_gr_complex*options.fft_length))
        # self.connect((sampler, 1), gr.null_sink(gr.sizeof_char))
        # self.connect(gr.file_source(gr.sizeof_gr_complex*options.fft_length, "symbols_src.dat"), (frame_acq, 0))
        # self.connect(gr.file_source(gr.sizeof_char, "timing.dat"), (frame_acq, 1))

        self.connect((frame_acq, 0), (self, 0))  # finished with fine/coarse freq correction
        self.connect((frame_acq, 1), (self, 1))  # frame and symbol timing
        self.connect((frame_acq, 2), (self, 2))  # hestimates

        self.connect((frame_acq, 0), gr.file_sink(gr.sizeof_gr_complex * occupied_tones, "rx-acq.dat"))
        self.connect((frame_acq, 1), gr.file_sink(gr.sizeof_char, "timing-acq.dat"))

        if logging:
            self.connect(self.chan_filt, gr.file_sink(gr.sizeof_gr_complex, "rx-filt.dat"))
            self.connect(fft1, gr.file_sink(gr.sizeof_gr_complex * fft_length, "rx-fft.dat"))
            self.connect((frame_acq, 0), gr.file_sink(gr.sizeof_gr_complex * occupied_tones, "rx-acq.dat"))
            self.connect((frame_acq, 1), gr.file_sink(1, "rx-detect.datb"))
            self.connect(sampler, gr.file_sink(gr.sizeof_gr_complex * fft_length, "rx-sampler.dat"))
            self.connect(sigmix, gr.file_sink(gr.sizeof_gr_complex, "rx-sigmix.dat"))
            self.connect(nco, gr.file_sink(gr.sizeof_gr_complex, "rx-nco.dat"))
Esempio n. 3
0
    def __init__(self,
                 fft_length,
                 cp_length,
                 occupied_tones,
                 snr,
                 ks,
                 threshold,
                 options,
                 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 apurv--
            gr.io_signature3(3, 3, gr.sizeof_gr_complex * occupied_tones,
                             gr.sizeof_char,
                             gr.sizeof_gr_complex * occupied_tones
                             ))  # apurv++, goes into frame sink for hestimates
        #gr.io_signature4(4, 4, gr.sizeof_gr_complex*occupied_tones, gr.sizeof_char, gr.sizeof_gr_complex*occupied_tones, gr.sizeof_gr_complex*fft_length))     # apurv++, goes into frame sink for hestimates

        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()

        nco_sensitivity = -2.0 / fft_length  # correct for fine frequency
        self.ofdm_sync = ofdm_sync_pn(fft_length, cp_length, ks0time,
                                      threshold, options.threshold_type,
                                      options.threshold_gap,
                                      logging)  # apurv++

        # 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 = digital_swig.ofdm_sampler(
            fft_length, fft_length + cp_length,
            len(ks) + 1, 100
        )  # 1 for the extra preamble which ofdm_rx doesn't know about (check frame_sink)
        self.fft_demod = gr.fft_vcc(fft_length, True, win, True)
        self.ofdm_frame_acq = digital_swig.ofdm_frame_acquisition(
            occupied_tones, fft_length, cp_length, ks)

        if options.verbose:
            self._print_verbage(options)

# apurv++ modified to allow collected time domain data to artifically pass through the rx chain #

# to replay the input manually, use this #
#self.connect(self, gr.null_sink(gr.sizeof_gr_complex))
#self.connect(gr.file_source(gr.sizeof_gr_complex, "input.dat"), self.chan_filt)

############# input -> chan_filt ##############
        self.connect(self, self.chan_filt)

        use_chan_filt = options.use_chan_filt
        correct_freq_offset = 0

        if use_chan_filt == 1:
            ##### chan_filt -> SYNC, chan_filt -> SIGMIX ####
            self.connect(self.chan_filt, self.ofdm_sync)
            if correct_freq_offset == 1:
                # enable if frequency offset correction is required #
                self.connect(self.chan_filt,
                             gr.delay(gr.sizeof_gr_complex, (fft_length)),
                             (self.sigmix, 0))  # apurv++ follow freq offset
            else:
                self.connect(self.chan_filt, (self.sampler, 0))
                ###self.connect(self.chan_filt, gr.delay(gr.sizeof_gr_complex, (fft_length)), (self.sampler, 0))		## extra delay

                #self.connect(self.chan_filt, gr.file_sink(gr.sizeof_gr_complex, "ofdm_receiver-chan_filt_c.dat"))
        elif use_chan_filt == 2:
            #### alternative: chan_filt-> NULL, file_source -> SYNC, file_source -> SIGMIX ####
            self.connect(self.chan_filt, gr.null_sink(gr.sizeof_gr_complex))
            if correct_freq_offset == 1:
                self.connect(
                    gr.file_source(gr.sizeof_gr_complex, "chan_filt.dat"),
                    self.ofdm_sync)
                self.connect(
                    gr.file_source(gr.sizeof_gr_complex, "chan_filt.dat"),
                    gr.delay(gr.sizeof_gr_complex, (fft_length)),
                    (self.sigmix, 0))
            else:
                self.connect(
                    gr.file_source(gr.sizeof_gr_complex, "chan_filt.dat"),
                    self.ofdm_sync)
                self.connect(
                    gr.file_source(gr.sizeof_gr_complex, "chan_filt.dat"),
                    (self.sampler, 0))
        else:
            # chan_filt->NULL #
            self.connect(self.chan_filt, gr.null_sink(gr.sizeof_gr_complex))

        method = options.method
        if method == -1:
            ################## for offline analysis, dump sampler input till the frame_sink, using io_signature4 #################
            if correct_freq_offset == 1:
                # enable if frequency offset correction is required #
                self.connect((self.ofdm_sync, 0), self.nco,
                             (self.sigmix, 1))  # freq offset (0'ed :/)
                self.connect(self.sigmix,
                             (self.sampler, 0))  # corrected output (0'ed FF)
                self.connect((self.ofdm_sync, 1),
                             gr.delay(gr.sizeof_char, fft_length),
                             (self.sampler, 1))  # timing signal

            else:
                # disable frequency offset correction completely #
                self.connect((self.ofdm_sync, 0),
                             gr.null_sink(gr.sizeof_float))
                self.connect((self.ofdm_sync, 1),
                             (self.sampler, 1))  # timing signal,

                #self.connect((self.ofdm_sync,0), (self.sampler, 2))						##added
                ##self.connect((self.ofdm_sync,1), gr.delay(gr.sizeof_char, fft_length+cp_length), (self.sampler, 1))           # timing signal, ##extra delay

                # route received time domain to sink (all-the-way) for offline analysis #
            self.connect((self.sampler, 0), (self.ofdm_frame_acq, 2))
            #self.connect((self.sampler, 1), gr.file_sink(gr.sizeof_char*fft_length, "sampler_timing.dat"))

        elif method == 0:
            # NORMAL functioning #
            if correct_freq_offset == 1:
                self.connect(
                    (self.ofdm_sync, 0), self.nco, (self.sigmix, 1)
                )  # use sync freq. offset output to derotate input signal
                self.connect(
                    self.sigmix,
                    (self.sampler,
                     0))  # sample off timing signal detected in sync alg
                self.connect((self.ofdm_sync, 1),
                             gr.delay(gr.sizeof_char, fft_length),
                             (self.sampler, 1))  # delay?
            else:
                self.connect((self.ofdm_sync, 1), (self.sampler, 1))

#self.connect((self.sampler, 2), (self.ofdm_frame_acq, 2))
#######################################################################

        use_default = options.use_default
        if use_default == 0:  #(set method == 0)
            # sampler-> NULL, replay trace->fft_demod, ofdm_frame_acq (time domain) #
            #self.connect((self.sampler, 0), gr.null_sink(gr.sizeof_gr_complex*fft_length))
            #self.connect((self.sampler, 1), gr.null_sink(gr.sizeof_char*fft_length))

            self.connect(
                gr.file_source(gr.sizeof_gr_complex * fft_length,
                               "symbols_src.dat"), self.fft_demod)
            self.connect(
                gr.file_source(gr.sizeof_char * fft_length, "timing_src.dat"),
                (self.ofdm_frame_acq, 1))
            self.connect(self.fft_demod, (self.ofdm_frame_acq, 0))
        elif use_default == 1:  #(set method == -1)
            # normal functioning #
            self.connect(
                (self.sampler, 0),
                self.fft_demod)  # send derotated sampled signal to FFT
            self.connect((self.sampler, 1),
                         (self.ofdm_frame_acq,
                          1))  # send timing signal to signal frame start
            self.connect(self.fft_demod, (self.ofdm_frame_acq, 0))
        elif use_default == 2:
            # replay directly to ofdm_frame_acq (frequency domain) #
            self.connect(
                gr.file_source(gr.sizeof_gr_complex * fft_length,
                               "symbols_src.dat"), (self.ofdm_frame_acq, 0))
            self.connect(
                gr.file_source(gr.sizeof_char * fft_length, "timing_src.dat"),
                (self.ofdm_frame_acq, 1))

########################### some logging start ##############################
#self.connect((self.ofdm_sync,1), gr.delay(gr.sizeof_char, fft_length), gr.file_sink(gr.sizeof_char, "ofdm_sync_pn-peaks_b.dat"))
#self.connect((self.sampler, 0), gr.file_sink(gr.sizeof_gr_complex*fft_length, "ofdm_receiver-sampler_c.dat"))
#self.connect((self.sampler, 1), gr.file_sink(gr.sizeof_char*fft_length, "ofdm_timing_sampler_c.dat"))
############################ some logging end ###############################

        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
        self.connect((self.ofdm_frame_acq, 2),
                     (self, 2))  # equalizer: hestimates

        #self.connect((self.ofdm_frame_acq,3), (self,3))           # ref sampler above

        # apurv++ ends #

        #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"))

        # apurv++ log the fine frequency offset corrected symbols #
        #self.connect((self.ofdm_frame_acq, 1), gr.file_sink(gr.sizeof_char, "ofdm_timing_frame_acq_c.dat"))
        #self.connect((self.ofdm_frame_acq, 2), gr.file_sink(gr.sizeof_gr_complex*occupied_tones, "ofdm_hestimates_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.chan_filt, gr.file_sink(gr.sizeof_gr_complex, "ofdm_receiver-chan_filt_c.dat"))
        #self.connect(self, gr.file_sink(gr.sizeof_gr_complex, "ofdm_input_c.dat"))
        # apurv++ end #

        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"))
    def __init__(self, fft_length, cp_length, occupied_tones, snr, ks, threshold, options, 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 apurv--
            gr.io_signature3(
                3, 3, gr.sizeof_gr_complex * occupied_tones, gr.sizeof_char, gr.sizeof_gr_complex * occupied_tones
            ),
        )  # apurv++, goes into frame sink for hestimates
        # gr.io_signature4(4, 4, gr.sizeof_gr_complex*occupied_tones, gr.sizeof_char, gr.sizeof_gr_complex*occupied_tones, gr.sizeof_gr_complex*fft_length))     # apurv++, goes into frame sink for hestimates

        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()

        nco_sensitivity = -2.0 / fft_length  # correct for fine frequency
        self.ofdm_sync = ofdm_sync_pn(
            fft_length, cp_length, ks0time, threshold, options.threshold_type, options.threshold_gap, logging
        )  # apurv++

        # 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 = digital_swig.ofdm_sampler(
            fft_length, fft_length + cp_length, len(ks) + 1, 100
        )  # 1 for the extra preamble which ofdm_rx doesn't know about (check frame_sink)
        self.fft_demod = gr.fft_vcc(fft_length, True, win, True)
        self.ofdm_frame_acq = digital_swig.ofdm_frame_acquisition(occupied_tones, fft_length, cp_length, ks)

        if options.verbose:
            self._print_verbage(options)

        # apurv++ modified to allow collected time domain data to artifically pass through the rx chain #

        # to replay the input manually, use this #
        # self.connect(self, gr.null_sink(gr.sizeof_gr_complex))
        # self.connect(gr.file_source(gr.sizeof_gr_complex, "input.dat"), self.chan_filt)

        ############# input -> chan_filt ##############
        self.connect(self, self.chan_filt)

        use_chan_filt = options.use_chan_filt
        correct_freq_offset = 0

        if use_chan_filt == 1:
            ##### chan_filt -> SYNC, chan_filt -> SIGMIX ####
            self.connect(self.chan_filt, self.ofdm_sync)
            if correct_freq_offset == 1:
                # enable if frequency offset correction is required #
                self.connect(
                    self.chan_filt, gr.delay(gr.sizeof_gr_complex, (fft_length)), (self.sigmix, 0)
                )  # apurv++ follow freq offset
            else:
                self.connect(self.chan_filt, (self.sampler, 0))
                ###self.connect(self.chan_filt, gr.delay(gr.sizeof_gr_complex, (fft_length)), (self.sampler, 0))		## extra delay

                # self.connect(self.chan_filt, gr.file_sink(gr.sizeof_gr_complex, "ofdm_receiver-chan_filt_c.dat"))
        elif use_chan_filt == 2:
            #### alternative: chan_filt-> NULL, file_source -> SYNC, file_source -> SIGMIX ####
            self.connect(self.chan_filt, gr.null_sink(gr.sizeof_gr_complex))
            if correct_freq_offset == 1:
                self.connect(gr.file_source(gr.sizeof_gr_complex, "chan_filt.dat"), self.ofdm_sync)
                self.connect(
                    gr.file_source(gr.sizeof_gr_complex, "chan_filt.dat"),
                    gr.delay(gr.sizeof_gr_complex, (fft_length)),
                    (self.sigmix, 0),
                )
            else:
                self.connect(gr.file_source(gr.sizeof_gr_complex, "chan_filt.dat"), self.ofdm_sync)
                self.connect(gr.file_source(gr.sizeof_gr_complex, "chan_filt.dat"), (self.sampler, 0))
        else:
            # chan_filt->NULL #
            self.connect(self.chan_filt, gr.null_sink(gr.sizeof_gr_complex))

        method = options.method
        if method == -1:
            ################## for offline analysis, dump sampler input till the frame_sink, using io_signature4 #################
            if correct_freq_offset == 1:
                # enable if frequency offset correction is required #
                self.connect((self.ofdm_sync, 0), self.nco, (self.sigmix, 1))  # freq offset (0'ed :/)
                self.connect(self.sigmix, (self.sampler, 0))  # corrected output (0'ed FF)
                self.connect(
                    (self.ofdm_sync, 1), gr.delay(gr.sizeof_char, fft_length), (self.sampler, 1)
                )  # timing signal

            else:
                # disable frequency offset correction completely #
                self.connect((self.ofdm_sync, 0), gr.null_sink(gr.sizeof_float))
                self.connect((self.ofdm_sync, 1), (self.sampler, 1))  # timing signal,

                # self.connect((self.ofdm_sync,0), (self.sampler, 2))						##added
                ##self.connect((self.ofdm_sync,1), gr.delay(gr.sizeof_char, fft_length+cp_length), (self.sampler, 1))           # timing signal, ##extra delay

                # route received time domain to sink (all-the-way) for offline analysis #
            self.connect((self.sampler, 0), (self.ofdm_frame_acq, 2))
            # self.connect((self.sampler, 1), gr.file_sink(gr.sizeof_char*fft_length, "sampler_timing.dat"))

        elif method == 0:
            # NORMAL functioning #
            if correct_freq_offset == 1:
                self.connect(
                    (self.ofdm_sync, 0), self.nco, (self.sigmix, 1)
                )  # use sync freq. offset output to derotate input signal
                self.connect(self.sigmix, (self.sampler, 0))  # sample off timing signal detected in sync alg
                self.connect((self.ofdm_sync, 1), gr.delay(gr.sizeof_char, fft_length), (self.sampler, 1))  # delay?
            else:
                self.connect((self.ofdm_sync, 1), (self.sampler, 1))

                # self.connect((self.sampler, 2), (self.ofdm_frame_acq, 2))
                #######################################################################

        use_default = options.use_default
        if use_default == 0:  # (set method == 0)
            # sampler-> NULL, replay trace->fft_demod, ofdm_frame_acq (time domain) #
            # self.connect((self.sampler, 0), gr.null_sink(gr.sizeof_gr_complex*fft_length))
            # self.connect((self.sampler, 1), gr.null_sink(gr.sizeof_char*fft_length))

            self.connect(gr.file_source(gr.sizeof_gr_complex * fft_length, "symbols_src.dat"), self.fft_demod)
            self.connect(gr.file_source(gr.sizeof_char * fft_length, "timing_src.dat"), (self.ofdm_frame_acq, 1))
            self.connect(self.fft_demod, (self.ofdm_frame_acq, 0))
        elif use_default == 1:  # (set method == -1)
            # normal functioning #
            self.connect((self.sampler, 0), self.fft_demod)  # send derotated sampled signal to FFT
            self.connect((self.sampler, 1), (self.ofdm_frame_acq, 1))  # send timing signal to signal frame start
            self.connect(self.fft_demod, (self.ofdm_frame_acq, 0))
        elif use_default == 2:
            # replay directly to ofdm_frame_acq (frequency domain) #
            self.connect(gr.file_source(gr.sizeof_gr_complex * fft_length, "symbols_src.dat"), (self.ofdm_frame_acq, 0))
            self.connect(gr.file_source(gr.sizeof_char * fft_length, "timing_src.dat"), (self.ofdm_frame_acq, 1))

        ########################### some logging start ##############################
        # self.connect((self.ofdm_sync,1), gr.delay(gr.sizeof_char, fft_length), gr.file_sink(gr.sizeof_char, "ofdm_sync_pn-peaks_b.dat"))
        # self.connect((self.sampler, 0), gr.file_sink(gr.sizeof_gr_complex*fft_length, "ofdm_receiver-sampler_c.dat"))
        # self.connect((self.sampler, 1), gr.file_sink(gr.sizeof_char*fft_length, "ofdm_timing_sampler_c.dat"))
        ############################ some logging end ###############################

        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
        self.connect((self.ofdm_frame_acq, 2), (self, 2))  # equalizer: hestimates

        # self.connect((self.ofdm_frame_acq,3), (self,3))           # ref sampler above

        # apurv++ ends #

        # 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"))

        # apurv++ log the fine frequency offset corrected symbols #
        # self.connect((self.ofdm_frame_acq, 1), gr.file_sink(gr.sizeof_char, "ofdm_timing_frame_acq_c.dat"))
        # self.connect((self.ofdm_frame_acq, 2), gr.file_sink(gr.sizeof_gr_complex*occupied_tones, "ofdm_hestimates_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.chan_filt, gr.file_sink(gr.sizeof_gr_complex, "ofdm_receiver-chan_filt_c.dat"))
        # self.connect(self, gr.file_sink(gr.sizeof_gr_complex, "ofdm_input_c.dat"))
        # apurv++ end #

        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"))