コード例 #1
0
    def __init__(self, center_frequency, sample_rate, decimation, filename, sample_format=None, threshold=7.0, signal_width=40e3, offline=False, max_queue_len=500, verbose=False):
        gr.top_block.__init__(self, "Top Block")
        self._center_frequency = center_frequency
        self._signal_width = 40e3
        self._input_sample_rate = sample_rate
        self._verbose = verbose
        self._threshold = threshold
        self._filename = filename
        self._offline = offline
        self._max_queue_len = max_queue_len

        self._fft_size = int(math.pow(2, 1 + int(math.log(self._input_sample_rate / 1000, 2)))) # fft is approx 1ms long
        self._burst_pre_len = 2 * self._fft_size
        self._burst_post_len = 8 * self._fft_size
        self._burst_width= int(self._signal_width / (self._input_sample_rate / self._fft_size)) # Area to ignore around an already found signal in FFT bins
        if decimation > 1:
            self._use_pfb = True

            # We will set up a filter bank with an odd number of outputs and
            # and an over sampling ratio to still get the desired decimation.

            # The goal is to still catch signal which (due to doppler shift) end up
            # on the border of two channels.

            # For this to work the desired decimation must be even.
            if decimation % 2:
                raise RuntimeError("The desired decimation must be 1 or an even number")

            self._channels = decimation + 1
            self._pfb_over_sample_ratio = self._channels / (self._channels - 1.)
            pfb_output_sample_rate = int(round(float(self._input_sample_rate) / self._channels * self._pfb_over_sample_ratio))
            assert pfb_output_sample_rate == self._input_sample_rate / decimation


            # The over sampled region of the FIR filter contains half of the signal width and
            # the transition region of the FIR filter.
            # The bandwidth is simply increased by the signal width.
            # A signal which has its center frequency directly on the border of
            # two channels will reconstruct correclty on both channels.
            self._fir_bw = (self._input_sample_rate / self._channels + self._signal_width) / 2

            # The remaining bandwidth inside the over samples region is used to
            # contain the transistion region of the filter.
            # It can be multiplied by two as it is allowed to continue into the
            # transition region of the neighboring channel.
            # TODO: Draw a nice graphic how this works.
            self._fir_tw = (pfb_output_sample_rate / 2 - self._fir_bw) * 2

            # If the over sampling ratio is not large enough, there is not
            # enough room to construct a transition region.
            if self._fir_tw < 0:
                raise RuntimeError("PFB over sampling ratio not enough to create a working FIR filter")

            self._pfb_fir_filter = gnuradio.filter.firdes.low_pass_2(1, self._input_sample_rate, self._fir_bw, self._fir_tw, 60)

            # If the transition width approaches 0, the filter size goes up significantly.
            if len(self._pfb_fir_filter) > 200:
                print >> sys.stderr, "Warning: The PFB FIR filter has an abnormal large number of taps:", len(self._pfb_fir_filter)
                print >> sys.stderr, "Consider reducing the decimation factor or increase the over sampling ratio"


            self._burst_sample_rate = pfb_output_sample_rate
            if self._verbose:
                print >> sys.stderr, "self._channels", self._channels
                print >> sys.stderr, "len(self._pfb_fir_filter)", len(self._pfb_fir_filter)
                print >> sys.stderr, "self._pfb_over_sample_ratio", self._pfb_over_sample_ratio
                print >> sys.stderr, "self._fir_bw", self._fir_bw
                print >> sys.stderr, "self._fir_tw", self._fir_tw
                print >> sys.stderr, "self._burst_sample_rate", self._burst_sample_rate
        else:
            self._use_pfb = False
            self._burst_sample_rate = self._input_sample_rate

        # After 90 ms there needs to be a pause in the frame sturcture.
        # Let's make that the limit for a detected burst
        self._max_burst_len = int(self._burst_sample_rate * 0.09)

        if self._verbose:
            print >> sys.stderr, "require %.1f dB" % self._threshold
            print >> sys.stderr, "burst_width: %d (= %.1f Hz)" % (self._burst_width, self._burst_width*self._input_sample_rate/self._fft_size)


        if self._filename.endswith(".conf"):
            import ConfigParser
            config = ConfigParser.ConfigParser()
            config.read(self._filename)
            items = config.items("osmosdr-source")
            d = {key: value for key, value in items}

            if 'device_args' in d:
                source = osmosdr.source(args=d['device_args'])
            else:
                source = osmosdr.source()

            source.set_sample_rate(self._input_sample_rate)
            source.set_center_freq(self._center_frequency, 0)

            if 'gain' in d:
                gain = int(d['gain'])
                source.set_gain(gain, 0)
                print >> sys.stderr, "(RF) Gain:", source.get_gain(0), '(Requested %d)' % gain

            if 'if_gain' in d:
                if_gain = int(d['if_gain'])
                source.set_if_gain(if_gain, 0)
                print >> sys.stderr, "IF Gain:", source.get_gain("IF", 0), '(Requested %d)' % if_gain

            if 'mix_gain' in d:
                mix_gain = int(d['mix_gain'])
                source.set_mix_gain(mix_gain, 0)
                print >> sys.stderr, "MIX Gain:", source.get_gain("MIX", 0), '(Requested %d)' % mix_gain

            if 'bb_gain' in d:
                bb_gain = int(d['bb_gain'])
                source.set_bb_gain(bb_gain, 0)
                print >> sys.stderr, "BB Gain:", source.get_gain("BB", 0), '(Requested %d)' % bb_gain

            if 'bandwidth' in d:
                bandwidth = int(d['bandwidth'])
                source.set_bandwidth(bandwidth, 0)
                print >> sys.stderr, "Bandwidth:", source.get_bandwidth(0), '(Requested %d)' % bandwidth
            else:
                source.set_bandwidth(0, 0)
                print >> sys.stderr, "Warning: Setting bandwidth to", source.get_bandwidth(0)

            #source.set_freq_corr($corr0, 0)
            #source.set_dc_offset_mode($dc_offset_mode0, 0)
            #source.set_iq_balance_mode($iq_balance_mode0, 0)
            #source.set_gain_mode($gain_mode0, 0)
            #source.set_antenna($ant0, 0)

            converter = None
        else:
            if sample_format == "rtl":
                converter = iridium.iuchar_to_complex()
                itemsize = gr.sizeof_char
            elif sample_format == "hackrf":
                converter = blocks.interleaved_char_to_complex()
                itemsize = gr.sizeof_char
            elif sample_format == "sc16":
                converter = blocks.interleaved_short_to_complex()
                itemsize = gr.sizeof_short
            elif sample_format == "float":
                converter = None
                itemsize = gr.sizeof_gr_complex
            else:
                raise RuntimeError("Unknown sample format for offline mode given")
            source = blocks.file_source(itemsize=itemsize, filename=self._filename, repeat=False)

        # Just to keep the code below a bit more portable
        tb = self

        #fft_burst_tagger::make(float center_frequency, int fft_size, int sample_rate,
        #                    int burst_pre_len, int burst_post_len, int burst_width,
        #                    int max_bursts, float threshold, int history_size, bool debug)

        self._fft_burst_tagger = iridium.fft_burst_tagger(center_frequency=self._center_frequency,
                                fft_size=self._fft_size,
                                sample_rate=self._input_sample_rate,
                                burst_pre_len=self._burst_pre_len, burst_post_len=self._burst_post_len,
                                burst_width=self._burst_width,
                                max_bursts=100,
                                threshold=self._threshold,
                                history_size=512,
                                debug=self._verbose)

        # Initial filter to filter the detected bursts. Runs at burst_sample_rate. Used to decimate the signal.
        # TODO: Should probably be set to self._signal_width
        input_filter = gnuradio.filter.firdes.low_pass_2(1, self._burst_sample_rate, 40e3/2, 40e3, 40)
        #input_filter = gnuradio.filter.firdes.low_pass_2(1, self._burst_sample_rate, 42e3/2, 24e3, 40)
        #print len(input_filter)

        # Filter to find the start of the signal. Should be fairly narrow.
        # TODO: 250000 appears as magic number here
        start_finder_filter = gnuradio.filter.firdes.low_pass_2(1, 250000, 5e3/2, 10e3/2, 60)
        #print len(start_finder_filter)

        self._iridium_qpsk_demod = iridium.iridium_qpsk_demod_cpp()
        #self._iridium_qpsk_demod = iridium.iridium_qpsk_demod(250000)

        if self._use_pfb:
            self._burst_to_pdu_converters = []
            self._burst_downmixers = []
            sinks = []

            for channel in range(self._channels):
                center = channel if channel <= self._channels / 2 else (channel - self._channels)

                # Second and third parameters tell the block where after the PFB it sits.
                relative_center = center / float(self._channels)
                relative_span = 1. / self._channels
                relative_sample_rate = relative_span * self._pfb_over_sample_ratio
                burst_to_pdu_converter = iridium.tagged_burst_to_pdu(self._max_burst_len, relative_center,
                                            relative_span, relative_sample_rate, self._max_queue_len, not self._offline)
                burst_downmixer = iridium.burst_downmix(self._burst_sample_rate,
                                    int(0.007 * 250000), 0, (input_filter), (start_finder_filter))

                self._burst_downmixers.append(burst_downmixer)
                self._burst_to_pdu_converters.append(burst_to_pdu_converter)

            #pfb_debug_sinks = [blocks.file_sink(itemsize=gr.sizeof_gr_complex, filename="/tmp/channel-%d.f32"%i) for i in range(self._channels)]
            pfb_debug_sinks = None

            pfb = gnuradio.filter.pfb.channelizer_ccf(numchans=self._channels, taps=self._pfb_fir_filter, oversample_rate=self._pfb_over_sample_ratio)

            if converter:
                tb.connect(source, converter, self._fft_burst_tagger, pfb)
            else:
                tb.connect(source, self._fft_burst_tagger, pfb)

            for i in range(self._channels):
                tb.connect((pfb, i), self._burst_to_pdu_converters[i])
                if pfb_debug_sinks:
                    tb.connect((pfb, i), pfb_debug_sinks[i])

                tb.msg_connect((self._burst_to_pdu_converters[i], 'cpdus'), (self._burst_downmixers[i], 'cpdus'))
                tb.msg_connect((self._burst_downmixers[i], 'burst_handled'), (self._burst_to_pdu_converters[i], 'burst_handled'))

                tb.msg_connect((self._burst_downmixers[i], 'cpdus'), (self._iridium_qpsk_demod, 'cpdus'))
        else:
            burst_downmix = iridium.burst_downmix(self._burst_sample_rate, int(0.007 * 250000), 0, (input_filter), (start_finder_filter))
            burst_to_pdu = iridium.tagged_burst_to_pdu(self._max_burst_len, 0.0, 1.0, 1.0, self._max_queue_len, not self._offline)

            if converter:
                #multi = blocks.multiply_const_cc(1/128.)
                #tb.connect(source, converter, multi, self._fft_burst_tagger, burst_to_pdu)
                tb.connect(source, converter, self._fft_burst_tagger, burst_to_pdu)
            else:
                tb.connect(source, self._fft_burst_tagger, burst_to_pdu)


            tb.msg_connect((burst_to_pdu, 'cpdus'), (burst_downmix, 'cpdus'))
            tb.msg_connect((burst_downmix, 'burst_handled'), (burst_to_pdu, 'burst_handled'))

            # Final connection to the demodulator. It prints the output to stdout
            tb.msg_connect((burst_downmix, 'cpdus'), (self._iridium_qpsk_demod, 'cpdus'))

            self._burst_downmixers = [burst_downmix]
            self._burst_to_pdu_converters = [burst_to_pdu]
コード例 #2
0
    def __init__(self,
                 center_frequency,
                 sample_rate,
                 decimation,
                 filename,
                 sample_format=None,
                 threshold=7.0,
                 signal_width=40e3,
                 offline=False,
                 max_queue_len=500,
                 handle_multiple_frames_per_burst=False,
                 raw_capture_filename=None,
                 max_bursts=0,
                 verbose=False):
        gr.top_block.__init__(self, "Top Block")
        self._center_frequency = center_frequency
        self._burst_width = 40e3
        self._input_sample_rate = sample_rate
        self._verbose = verbose
        self._threshold = threshold
        self._filename = filename
        self._offline = offline
        self._max_queue_len = max_queue_len
        self._handle_multiple_frames_per_burst = handle_multiple_frames_per_burst

        self._fft_size = int(
            math.pow(2, 1 + int(math.log(self._input_sample_rate / 1000,
                                         2))))  # fft is approx 1ms long
        self._burst_pre_len = 2 * self._fft_size
        self._burst_post_len = 8 * self._fft_size

        # Just to keep the code below a bit more portable
        tb = self

        if decimation > 1:
            self._use_pfb = True

            # We will set up a filter bank with an odd number of outputs and
            # and an over sampling ratio to still get the desired decimation.

            # The goal is to still catch signal which (due to doppler shift) end up
            # on the border of two channels.

            # For this to work the desired decimation must be even.
            if decimation % 2:
                raise RuntimeError(
                    "The desired decimation must be 1 or an even number")

            self._channels = decimation + 1
            self._pfb_over_sample_ratio = self._channels / (self._channels -
                                                            1.)
            pfb_output_sample_rate = int(
                round(
                    float(self._input_sample_rate) / self._channels *
                    self._pfb_over_sample_ratio))
            assert pfb_output_sample_rate == self._input_sample_rate / decimation

            # The over sampled region of the FIR filter contains half of the signal width and
            # the transition region of the FIR filter.
            # The bandwidth is simply increased by the signal width.
            # A signal which has its center frequency directly on the border of
            # two channels will reconstruct correclty on both channels.
            self._fir_bw = (self._input_sample_rate / self._channels +
                            self._burst_width) / 2

            # The remaining bandwidth inside the over samples region is used to
            # contain the transistion region of the filter.
            # It can be multiplied by two as it is allowed to continue into the
            # transition region of the neighboring channel.
            # TODO: Draw a nice graphic how this works.
            self._fir_tw = (pfb_output_sample_rate / 2 - self._fir_bw) * 2

            # If the over sampling ratio is not large enough, there is not
            # enough room to construct a transition region.
            if self._fir_tw < 0:
                raise RuntimeError(
                    "PFB over sampling ratio not enough to create a working FIR filter"
                )

            self._pfb_fir_filter = gnuradio.filter.firdes.low_pass_2(
                1, self._input_sample_rate, self._fir_bw, self._fir_tw, 60)

            # If the transition width approaches 0, the filter size goes up significantly.
            if len(self._pfb_fir_filter) > 200:
                print(
                    "Warning: The PFB FIR filter has an abnormal large number of taps:",
                    len(self._pfb_fir_filter),
                    file=sys.stderr)
                print(
                    "Consider reducing the decimation factor or increase the over sampling ratio",
                    file=sys.stderr)

            self._burst_sample_rate = pfb_output_sample_rate
            if self._verbose:
                print("self._channels", self._channels, file=sys.stderr)
                print("len(self._pfb_fir_filter)",
                      len(self._pfb_fir_filter),
                      file=sys.stderr)
                print("self._pfb_over_sample_ratio",
                      self._pfb_over_sample_ratio,
                      file=sys.stderr)
                print("self._fir_bw", self._fir_bw, file=sys.stderr)
                print("self._fir_tw", self._fir_tw, file=sys.stderr)
                print("self._burst_sample_rate",
                      self._burst_sample_rate,
                      file=sys.stderr)
        else:
            self._use_pfb = False
            self._burst_sample_rate = self._input_sample_rate

        # After 90 ms there needs to be a pause in the frame sturcture.
        # Let's make that the limit for a detected burst
        self._max_burst_len = int(self._burst_sample_rate * 0.09)

        if self._verbose:
            print("require %.1f dB" % self._threshold, file=sys.stderr)
            print("burst_width: %d Hz" % self._burst_width, file=sys.stderr)

        if self._filename.endswith(".conf"):
            import configparser
            config = configparser.ConfigParser()
            config.read(self._filename)
            items = config.items("osmosdr-source")
            d = {key: value for key, value in items}

            import osmosdr
            if 'device_args' in d:
                source = osmosdr.source(args=d['device_args'])
            else:
                source = osmosdr.source()

            source.set_sample_rate(self._input_sample_rate)
            source.set_center_freq(self._center_frequency, 0)

            if 'gain' in d:
                gain = int(d['gain'])
                source.set_gain(gain, 0)
                print("(RF) Gain:",
                      source.get_gain(0),
                      '(Requested %d)' % gain,
                      file=sys.stderr)

            for key, value in d.items():
                if key.endswith("_gain"):
                    gain_option_name = key.split('_')[0]
                    gain_value = int(value)

                    def match_gain(gain, gain_names):
                        for gain_name in gain_names:
                            if gain.lower() == gain_name.lower():
                                return gain_name
                        return None

                    gain_name = match_gain(gain_option_name,
                                           source.get_gain_names())

                    if gain_name is not None:
                        source.set_gain(gain_value, gain_name, 0)
                        print(gain_name,
                              "Gain:",
                              source.get_gain(gain_name, 0),
                              '(Requested %d)' % gain_value,
                              file=sys.stderr)
                    else:
                        print("WARNING: Gain",
                              gain_option_name,
                              "not supported by source!",
                              file=sys.stderr)
                        print("Supported gains:",
                              source.get_gain_names(),
                              file=sys.stderr)

            if 'bandwidth' in d:
                bandwidth = int(d['bandwidth'])
                source.set_bandwidth(bandwidth, 0)
                print("Bandwidth:",
                      source.get_bandwidth(0),
                      '(Requested %d)' % bandwidth,
                      file=sys.stderr)
            else:
                source.set_bandwidth(0, 0)
                print("Warning: Setting bandwidth to",
                      source.get_bandwidth(0),
                      file=sys.stderr)

            if 'antenna' in d:
                antenna = d['antenna']
                source.set_antenna(antenna, 0)
                print("Antenna:",
                      source.get_antenna(0),
                      '(Requested %s)' % antenna,
                      file=sys.stderr)
            else:
                print("Warning: Setting antenna to",
                      source.get_antenna(0),
                      file=sys.stderr)

            #source.set_freq_corr($corr0, 0)
            #source.set_dc_offset_mode($dc_offset_mode0, 0)
            #source.set_iq_balance_mode($iq_balance_mode0, 0)
            #source.set_gain_mode($gain_mode0, 0)
            #source.set_antenna($ant0, 0)

        else:
            if sample_format == "rtl":
                converter = iridium.iuchar_to_complex()
                itemsize = gr.sizeof_char
            elif sample_format == "hackrf":
                converter = blocks.interleaved_char_to_complex()
                itemsize = gr.sizeof_char
            elif sample_format == "sc16":
                converter = blocks.interleaved_short_to_complex()
                itemsize = gr.sizeof_short
            elif sample_format == "float":
                converter = None
                itemsize = gr.sizeof_gr_complex
            else:
                raise RuntimeError(
                    "Unknown sample format for offline mode given")

            file_source = blocks.file_source(itemsize=itemsize,
                                             filename=self._filename,
                                             repeat=False)

            if converter:
                #multi = blocks.multiply_const_cc(1/128.)
                #tb.connect(file_source, converter, multi)
                #source = multi
                tb.connect(file_source, converter)
                source = converter
            else:
                source = file_source

        #fft_burst_tagger::make(float center_frequency, int fft_size, int sample_rate,
        #                    int burst_pre_len, int burst_post_len, int burst_width,
        #                    int max_bursts, float threshold, int history_size, bool debug)

        self._fft_burst_tagger = iridium.fft_burst_tagger(
            center_frequency=self._center_frequency,
            fft_size=self._fft_size,
            sample_rate=self._input_sample_rate,
            burst_pre_len=self._burst_pre_len,
            burst_post_len=self._burst_post_len,
            burst_width=int(self._burst_width),
            max_bursts=max_bursts,
            threshold=self._threshold,
            history_size=512,
            debug=self._verbose)

        # Initial filter to filter the detected bursts. Runs at burst_sample_rate. Used to decimate the signal.
        # TODO: Should probably be set to self._burst_width
        input_filter = gnuradio.filter.firdes.low_pass_2(
            1, self._burst_sample_rate, 40e3 / 2, 40e3, 40)
        #input_filter = gnuradio.filter.firdes.low_pass_2(1, self._burst_sample_rate, 42e3/2, 24e3, 40)
        #print len(input_filter)

        # Filter to find the start of the signal. Should be fairly narrow.
        # TODO: 250000 appears as magic number here
        start_finder_filter = gnuradio.filter.firdes.low_pass_2(
            1, 250000, 5e3 / 2, 10e3 / 2, 60)
        #print len(start_finder_filter)

        self._iridium_qpsk_demod = iridium.iridium_qpsk_demod_cpp()
        self._frame_sorter = iridium.frame_sorter()
        self._iridium_frame_printer = iridium.iridium_frame_printer()

        #self._iridium_qpsk_demod = iridium.iridium_qpsk_demod(250000)

        if raw_capture_filename:
            raw_sink = blocks.file_sink(itemsize=gr.sizeof_gr_complex,
                                        filename=raw_capture_filename)
            tb.connect(source, raw_sink)
            # Enable the following if not fast enough
            #self._burst_to_pdu_converters = []
            #self._burst_downmixers = []
            #return

        tb.connect(source, self._fft_burst_tagger)

        if self._use_pfb:
            self._burst_to_pdu_converters = []
            self._burst_downmixers = []
            sinks = []

            for channel in range(self._channels):
                center = channel if channel <= self._channels / 2 else (
                    channel - self._channels)

                # Second and third parameters tell the block where after the PFB it sits.
                relative_center = center / float(self._channels)
                relative_span = 1. / self._channels
                relative_sample_rate = relative_span * self._pfb_over_sample_ratio
                burst_to_pdu_converter = iridium.tagged_burst_to_pdu(
                    self._max_burst_len, relative_center, relative_span,
                    relative_sample_rate, self._max_queue_len,
                    not self._offline)
                burst_downmixer = iridium.burst_downmix(
                    self._burst_sample_rate, int(0.007 * 250000), 0,
                    (input_filter), (start_finder_filter),
                    self._handle_multiple_frames_per_burst)

                self._burst_downmixers.append(burst_downmixer)
                self._burst_to_pdu_converters.append(burst_to_pdu_converter)

            #pfb_debug_sinks = [blocks.file_sink(itemsize=gr.sizeof_gr_complex, filename="/tmp/channel-%d.f32"%i) for i in range(self._channels)]
            pfb_debug_sinks = None

            pfb = gnuradio.filter.pfb.channelizer_ccf(
                numchans=self._channels,
                taps=self._pfb_fir_filter,
                oversample_rate=self._pfb_over_sample_ratio)

            tb.connect(self._fft_burst_tagger, pfb)

            for i in range(self._channels):
                tb.connect((pfb, i), self._burst_to_pdu_converters[i])
                if pfb_debug_sinks:
                    tb.connect((pfb, i), pfb_debug_sinks[i])

                tb.msg_connect((self._burst_to_pdu_converters[i], 'cpdus'),
                               (self._burst_downmixers[i], 'cpdus'))
                tb.msg_connect(
                    (self._burst_downmixers[i], 'burst_handled'),
                    (self._burst_to_pdu_converters[i], 'burst_handled'))

                tb.msg_connect((self._burst_downmixers[i], 'cpdus'),
                               (self._iridium_qpsk_demod, 'cpdus'))
        else:
            burst_downmix = iridium.burst_downmix(
                self._burst_sample_rate, int(0.007 * 250000), 0,
                (input_filter), (start_finder_filter),
                self._handle_multiple_frames_per_burst)
            burst_to_pdu = iridium.tagged_burst_to_pdu(self._max_burst_len,
                                                       0.0, 1.0, 1.0,
                                                       self._max_queue_len,
                                                       not self._offline)

            tb.connect(self._fft_burst_tagger, burst_to_pdu)

            tb.msg_connect((burst_to_pdu, 'cpdus'), (burst_downmix, 'cpdus'))
            tb.msg_connect((burst_downmix, 'burst_handled'),
                           (burst_to_pdu, 'burst_handled'))

            # Final connection to the demodulator. It prints the output to stdout
            tb.msg_connect((burst_downmix, 'cpdus'),
                           (self._iridium_qpsk_demod, 'cpdus'))

            self._burst_downmixers = [burst_downmix]
            self._burst_to_pdu_converters = [burst_to_pdu]

        tb.msg_connect((self._iridium_qpsk_demod, 'pdus'),
                       (self._frame_sorter, 'pdus'))
        tb.msg_connect((self._frame_sorter, 'pdus'),
                       (self._iridium_frame_printer, 'pdus'))
コード例 #3
0
    def __init__(self,
                 center_frequency,
                 sample_rate,
                 decimation,
                 filename,
                 sample_format=None,
                 threshold=7.0,
                 burst_width=40e3,
                 offline=False,
                 max_queue_len=500,
                 handle_multiple_frames_per_burst=False,
                 raw_capture_filename=None,
                 debug_id=None,
                 max_bursts=0,
                 verbose=False,
                 file_info=None,
                 samples_per_symbol=10,
                 config={}):
        gr.top_block.__init__(self, "Top Block")
        self.handle_sigint = False
        self._center_frequency = center_frequency
        self._burst_width = burst_width
        self._input_sample_rate = sample_rate
        self._verbose = verbose
        self._threshold = threshold
        self._filename = filename
        self._offline = offline
        self._max_queue_len = max_queue_len
        self._handle_multiple_frames_per_burst = handle_multiple_frames_per_burst

        # Sample rate of the bursts exiting the burst downmix block
        self._burst_sample_rate = 25000 * samples_per_symbol
        assert (self._input_sample_rate /
                decimation) % self._burst_sample_rate == 0

        self._fft_size = 2**round(math.log(self._input_sample_rate / 1000,
                                           2))  # FFT is approx. 1 ms long
        self._burst_pre_len = 2 * self._fft_size

        # Keep 16 ms of signal after the FFT loses track
        self._burst_post_len = int(self._input_sample_rate * 16e-3)

        # Just to keep the code below a bit more portable
        tb = self

        if decimation > 1:
            self._use_pfb = True

            # We will set up a filter bank with an odd number of outputs and
            # and an over sampling ratio to still get the desired decimation.

            # The goal is to reconstruct signals which (due to Doppler shift) end up
            # on the border of two channels.

            # For this to work the desired decimation must be even.
            if decimation % 2:
                raise RuntimeError(
                    "The desired decimation must be 1 or an even number")

            self._channels = decimation + 1
            self._pfb_over_sample_ratio = self._channels / (self._channels -
                                                            1.)
            pfb_output_sample_rate = int(
                round(
                    float(self._input_sample_rate) / self._channels *
                    self._pfb_over_sample_ratio))
            assert pfb_output_sample_rate == self._input_sample_rate / decimation
            assert pfb_output_sample_rate % self._burst_sample_rate == 0

            # The over sampled region of the FIR filter contains half of the signal width and
            # the transition region of the FIR filter.
            # The bandwidth is simply increased by the signal width.
            # A signal which has its center frequency directly on the border of
            # two channels will reconstruct correctly on both channels.
            self._fir_bw = (self._input_sample_rate / self._channels +
                            self._burst_width) / 2

            # The remaining bandwidth inside the over sampled region is used to
            # contain the transition region of the filter.
            # It can be multiplied by two as it is allowed to continue into the
            # transition region of the neighboring channel.
            # Some details can be found here: https://youtu.be/6ngYp8W-AX0?t=2289
            self._fir_tw = (pfb_output_sample_rate / 2 - self._fir_bw) * 2

            # Real world data shows only a minor degradation in performance when
            # doubling the transition width.
            self._fir_tw *= 2

            # If the over sampling ratio is not large enough, there is not
            # enough room to construct a transition region.
            if self._fir_tw < 0:
                raise RuntimeError(
                    "PFB over sampling ratio not enough to create a working FIR filter"
                )

            self._pfb_fir_filter = gnuradio.filter.firdes.low_pass_2(
                1, self._input_sample_rate, self._fir_bw, self._fir_tw, 60)

            # If the transition width approaches 0, the filter size goes up significantly.
            if len(self._pfb_fir_filter) > 300:
                print(
                    "Warning: The PFB FIR filter has an abnormal large number of taps:",
                    len(self._pfb_fir_filter),
                    file=sys.stderr)
                print("Consider reducing the decimation factor",
                      file=sys.stderr)

            pfb_input_delay = (
                len(self._pfb_fir_filter) +
                1) // 2 - self._channels / self._pfb_over_sample_ratio
            self._pfb_delay = pfb_input_delay / decimation
            self._channel_sample_rate = pfb_output_sample_rate
            if self._verbose:
                print("self._channels", self._channels, file=sys.stderr)
                print("len(self._pfb_fir_filter)",
                      len(self._pfb_fir_filter),
                      file=sys.stderr)
                print("self._pfb_over_sample_ratio",
                      self._pfb_over_sample_ratio,
                      file=sys.stderr)
                print("self._fir_bw", self._fir_bw, file=sys.stderr)
                print("self._fir_tw", self._fir_tw, file=sys.stderr)
                print("self._channel_sample_rate",
                      self._channel_sample_rate,
                      file=sys.stderr)
        else:
            self._use_pfb = False
            self._channel_sample_rate = self._input_sample_rate
            self._channels = 1

        # After 90 ms there needs to be a pause in the frame sturcture.
        # Let's make that the limit for a detected burst
        self._max_burst_len = int(self._channel_sample_rate * 0.09)

        if self._verbose:
            print("require %.1f dB" % self._threshold, file=sys.stderr)
            print("burst_width: %d Hz" % self._burst_width, file=sys.stderr)

        if config['source'] == 'osmosdr':
            d = config["osmosdr-source"]

            # work around https://github.com/gnuradio/gnuradio/issues/5121
            sys.path.append('/usr/local/lib/python3/dist-packages')
            import osmosdr
            if 'device_args' in d:
                source = osmosdr.source(args=d['device_args'])
            else:
                source = osmosdr.source()

            source.set_time_now(osmosdr.time_spec_t.get_system_time())

            source.set_sample_rate(self._input_sample_rate)
            source.set_center_freq(self._center_frequency, 0)

            if 'gain' in d:
                gain = int(d['gain'])
                source.set_gain(gain, 0)
                print("(RF) Gain:",
                      source.get_gain(0),
                      '(Requested %d)' % gain,
                      file=sys.stderr)

            for key, value in d.items():
                if key.endswith("_gain"):
                    gain_option_name = key.split('_')[0]
                    gain_value = int(value)

                    def match_gain(gain, gain_names):
                        for gain_name in gain_names:
                            if gain.lower() == gain_name.lower():
                                return gain_name
                        return None

                    gain_name = match_gain(gain_option_name,
                                           source.get_gain_names())

                    if gain_name is not None:
                        source.set_gain(gain_value, gain_name, 0)
                        print(gain_name,
                              "Gain:",
                              source.get_gain(gain_name, 0),
                              '(Requested %d)' % gain_value,
                              file=sys.stderr)
                    else:
                        print("WARNING: Gain",
                              gain_option_name,
                              "not supported by source!",
                              file=sys.stderr)
                        print("Supported gains:",
                              source.get_gain_names(),
                              file=sys.stderr)

            if 'bandwidth' in d:
                bandwidth = int(d['bandwidth'])
                source.set_bandwidth(bandwidth, 0)
                print("Bandwidth:",
                      source.get_bandwidth(0),
                      '(Requested %d)' % bandwidth,
                      file=sys.stderr)
            else:
                source.set_bandwidth(0, 0)
                print("Warning: Setting bandwidth to",
                      source.get_bandwidth(0),
                      file=sys.stderr)

            if 'antenna' in d:
                antenna = d['antenna']
                source.set_antenna(antenna, 0)
                print("Antenna:",
                      source.get_antenna(0),
                      '(Requested %s)' % antenna,
                      file=sys.stderr)
            else:
                print("Warning: Setting antenna to",
                      source.get_antenna(0),
                      file=sys.stderr)

            #source.set_freq_corr($corr0, 0)
            #source.set_dc_offset_mode($dc_offset_mode0, 0)
            #source.set_iq_balance_mode($iq_balance_mode0, 0)
            #source.set_gain_mode($gain_mode0, 0)
            #source.set_antenna($ant0, 0)

        else:
            if sample_format == "cu8":
                converter = iridium.iuchar_to_complex()
                itemsize = gr.sizeof_char
                scale = 1
                itemtype = np.uint8
            elif sample_format == "ci8":
                converter = blocks.interleaved_char_to_complex()
                itemsize = gr.sizeof_char
                scale = 1 / 128.
                itemtype = np.int8
            elif sample_format == "ci16_le":
                converter = blocks.interleaved_short_to_complex()
                itemsize = gr.sizeof_short
                scale = 1 / 32768.
                itemtype = np.int16
            elif sample_format == "cf32_le":
                converter = None
                itemsize = gr.sizeof_gr_complex
                itemtype = np.complex64
            else:
                raise RuntimeError(
                    "Unknown sample format for offline mode given")

            if config['source'] == 'stdin':
                file_source = blocks.file_descriptor_source(itemsize=itemsize,
                                                            fd=0,
                                                            repeat=False)
            elif config['source'] == 'object':
                from iridium.file_object_source import file_object_source
                file_source = file_object_source(fileobject=config['object'],
                                                 itemtype=itemtype)
            else:
                file_source = blocks.file_source(itemsize=itemsize,
                                                 filename=config['file'],
                                                 repeat=False)

            self.source = file_source  # XXX: keep reference

            if converter:
                multi = blocks.multiply_const_cc(scale)
                tb.connect(file_source, converter, multi)
                source = multi
            else:
                source = file_source

        self._fft_burst_tagger = iridium.fft_burst_tagger(
            center_frequency=self._center_frequency,
            fft_size=self._fft_size,
            sample_rate=self._input_sample_rate,
            burst_pre_len=self._burst_pre_len,
            burst_post_len=self._burst_post_len,
            burst_width=int(self._burst_width),
            max_bursts=max_bursts,
            threshold=self._threshold,
            history_size=512,
            offline=self._offline,
            debug=self._verbose)

        # Initial filter to filter the detected bursts. Runs at burst_sample_rate. Used to decimate the signal.
        input_filter = gnuradio.filter.firdes.low_pass_2(
            1, self._channel_sample_rate, self._burst_width / 2,
            self._burst_width, 40)
        #input_filter = gnuradio.filter.firdes.low_pass_2(1, self._channel_sample_rate, 42e3/2, 24e3, 40)
        #print len(input_filter)

        # Filter to find the start of the signal. Should be fairly narrow.
        start_finder_filter = gnuradio.filter.firdes.low_pass_2(
            1, self._burst_sample_rate, 5e3 / 2, 10e3 / 2, 60)
        #print len(start_finder_filter)

        self._iridium_qpsk_demod = iridium.iridium_qpsk_demod_cpp(
            self._channels)
        self._frame_sorter = iridium.frame_sorter()
        self._iridium_frame_printer = iridium.iridium_frame_printer(file_info)

        if raw_capture_filename:
            multi = blocks.multiply_const_cc(32768)
            converter = blocks.complex_to_interleaved_short()
            raw_sink = blocks.file_sink(itemsize=gr.sizeof_short,
                                        filename=raw_capture_filename)
            tb.connect(source, multi, converter, raw_sink)

            # Enable the following if not fast enough
            #self._burst_to_pdu_converters = []
            #self._burst_downmixers = []
            #return

        tb.connect(source, self._fft_burst_tagger)

        if self._use_pfb:
            self._burst_to_pdu_converters = []
            self._burst_downmixers = []
            sinks = []

            for channel in range(self._channels):
                center = channel if channel <= self._channels / 2 else (
                    channel - self._channels)

                # Second and third parameters tell the block where after the PFB it sits.
                relative_center = center / float(self._channels)
                relative_span = 1. / self._channels
                relative_sample_rate = relative_span * self._pfb_over_sample_ratio
                burst_to_pdu_converter = iridium.tagged_burst_to_pdu(
                    self._max_burst_len, relative_center, relative_span,
                    relative_sample_rate, -self._pfb_delay,
                    self._max_queue_len, not self._offline)
                burst_downmixer = iridium.burst_downmix(
                    self._burst_sample_rate,
                    int(0.007 * self._burst_sample_rate), 0, (input_filter),
                    (start_finder_filter),
                    self._handle_multiple_frames_per_burst)

                if debug_id is not None: burst_downmixer.debug_id(debug_id)

                self._burst_downmixers.append(burst_downmixer)
                self._burst_to_pdu_converters.append(burst_to_pdu_converter)

            #pfb_debug_sinks = [blocks.file_sink(itemsize=gr.sizeof_gr_complex, filename="/tmp/channel-%d.f32"%i) for i in range(self._channels)]
            pfb_debug_sinks = None

            pfb = gnuradio.filter.pfb.channelizer_ccf(
                numchans=self._channels,
                taps=self._pfb_fir_filter,
                oversample_rate=self._pfb_over_sample_ratio)

            tb.connect(self._fft_burst_tagger, pfb)

            for i in range(self._channels):
                tb.connect((pfb, i), self._burst_to_pdu_converters[i])
                if pfb_debug_sinks:
                    tb.connect((pfb, i), pfb_debug_sinks[i])

                tb.msg_connect((self._burst_to_pdu_converters[i], 'cpdus'),
                               (self._burst_downmixers[i], 'cpdus'))
                tb.msg_connect(
                    (self._burst_downmixers[i], 'burst_handled'),
                    (self._burst_to_pdu_converters[i], 'burst_handled'))

                tb.msg_connect((self._burst_downmixers[i], 'cpdus'),
                               (self._iridium_qpsk_demod, 'cpdus%d' % i))
        else:
            burst_downmix = iridium.burst_downmix(
                self._burst_sample_rate, int(0.007 * self._burst_sample_rate),
                0, (input_filter), (start_finder_filter),
                self._handle_multiple_frames_per_burst)
            if debug_id is not None: burst_downmix.debug_id(debug_id)

            burst_to_pdu = iridium.tagged_burst_to_pdu(self._max_burst_len,
                                                       0.0, 1.0, 1.0, 0,
                                                       self._max_queue_len,
                                                       not self._offline)

            tb.connect(self._fft_burst_tagger, burst_to_pdu)

            tb.msg_connect((burst_to_pdu, 'cpdus'), (burst_downmix, 'cpdus'))
            tb.msg_connect((burst_downmix, 'burst_handled'),
                           (burst_to_pdu, 'burst_handled'))

            # Final connection to the demodulator. It prints the output to stdout
            tb.msg_connect((burst_downmix, 'cpdus'),
                           (self._iridium_qpsk_demod, 'cpdus'))

            self._burst_downmixers = [burst_downmix]
            self._burst_to_pdu_converters = [burst_to_pdu]

        tb.msg_connect((self._iridium_qpsk_demod, 'pdus'),
                       (self._frame_sorter, 'pdus'))
        tb.msg_connect((self._frame_sorter, 'pdus'),
                       (self._iridium_frame_printer, 'pdus'))