def test_coerce(self):
        samp_rate = 1e6
        freq_offset = -400e3
        center_freq = 911e6

        # blocks
        self.emitter = pdu_utils.message_emitter()
        self.cf = fhss_utils.cf_estimate(fhss_utils.COERCE,
                                         [x * 1e6 for x in range(900, 930)])
        self.debug = blocks.message_debug()

        # connections
        self.tb.msg_connect((self.emitter, 'msg'), (self.cf, 'in'))
        self.tb.msg_connect((self.cf, 'out'), (self.debug, 'store'))

        # data
        in_data = (1 + 0j, ) * 2048
        i_vec = pmt.init_c32vector(len(in_data), in_data)
        out_data = np.exp(1j *
                          np.arange(0,
                                    2 * np.pi *
                                    (freq_offset / samp_rate * len(in_data)),
                                    2 * np.pi * (freq_offset / samp_rate),
                                    dtype=np.complex64))
        e_vec = pmt.init_c32vector(len(out_data), out_data.tolist(
        ))  # pmt doesn't play nice with numpy sometimes, convert to list

        meta = pmt.make_dict()
        meta = pmt.dict_add(meta, pmt.intern("sample_rate"),
                            pmt.from_float(samp_rate))
        meta = pmt.dict_add(meta, pmt.intern("center_frequency"),
                            pmt.from_float(center_freq + freq_offset))
        in_pdu = pmt.cons(meta, i_vec)
        e_pdu = pmt.cons(meta, e_vec)

        # flowgraph
        self.tb.start()
        time.sleep(.001)
        self.emitter.emit(in_pdu)
        time.sleep(.01)
        self.tb.stop()
        self.tb.wait()

        # parse output
        #print "got ", list(pmt.to_python(pmt.cdr(self.debug.get_message(0))))
        #print "got ", self.debug.get_message(0)
        rcv = self.debug.get_message(0)
        rcv_meta = pmt.car(rcv)
        rcv_data = pmt.cdr(rcv)
        rcv_cf = pmt.dict_ref(rcv_meta, pmt.intern("center_frequency"),
                              pmt.PMT_NIL)

        # asserts
        self.assertComplexTuplesAlmostEqual(
            tuple(pmt.c32vector_elements(rcv_data)), tuple(out_data), 2)
        self.assertTrue(pmt.equal(rcv_cf, pmt.from_float(911e6)))
    def test_middle_out_metadata(self):
        samp_rate = 1e6
        rot_offset = 1.0 / 16.0
        self.emitter = pdu_utils.message_emitter()
        self.cf = fhss_utils.cf_estimate(fhss_utils.MIDDLE_OUT, [])
        self.sm = self.simple_modulator()
        self.rt = self.pdu_rotate(rot_offset)
        self.debug = blocks.message_debug()

        self.tb.msg_connect((self.emitter, 'msg'), (self.sm, 'in'))
        #self.tb.msg_connect((self.cf, 'debug'), (self.debug, 'print'))
        self.tb.msg_connect((self.sm, 'out'), (self.rt, 'in'))
        self.tb.msg_connect((self.rt, 'out'), (self.cf, 'in'))
        self.tb.msg_connect((self.cf, 'out'), (self.debug, 'store'))

        # original data
        in_data = [0xAA] * 10 + [0x69] * 10 + [
            0x55
        ] * 10  # 30 bytes = 240 bits = 1920 samples
        i_vec = pmt.init_u8vector(len(in_data), in_data)
        fc = 100e6

        meta = pmt.make_dict()
        meta = pmt.dict_add(meta, pmt.intern("sample_rate"),
                            pmt.from_float(1e6))
        meta = pmt.dict_add(meta, pmt.intern("center_frequency"),
                            pmt.from_float(fc))
        meta = pmt.dict_add(meta, pmt.intern("noise_density"),
                            pmt.from_float(-105.0))
        in_pdu = pmt.cons(meta, i_vec)

        self.tb.start()
        time.sleep(.1)
        self.emitter.emit(in_pdu)
        time.sleep(.1)
        self.tb.stop()
        self.tb.wait()

        # parse output
        rcv = self.debug.get_message(0)
        rcv_meta = pmt.car(rcv)
        rcv_data = pmt.cdr(rcv)

        # asserts
        rcv_cf = pmt.to_double(
            pmt.dict_ref(rcv_meta, pmt.intern("center_frequency"),
                         pmt.PMT_NIL))
        expected_cf = 100e6 + samp_rate * rot_offset  # we rotated the burst 62500 Hz off
        max_diff = samp_rate / 256 / 2  # half an FFT bin error allowed
        #print("M-O: got ", rcv_cf - fc, " expected ", expected_cf-fc, "(diff ", rcv_cf-expected_cf, ")")
        self.assertTrue(abs(rcv_cf - expected_cf) < max_diff)
    def test_half_power_metadata(self):
        samp_rate = 1e6
        rot_offset = 1.0 / 16.0
        self.emitter = pdu_utils.message_emitter()
        self.cf = fhss_utils.cf_estimate(fhss_utils.HALF_POWER, [])
        self.sm = self.simple_modulator()
        self.rt = self.pdu_rotate(rot_offset)
        self.debug = blocks.message_debug()

        self.tb.msg_connect((self.emitter, 'msg'), (self.sm, 'in'))
        self.tb.msg_connect((self.sm, 'out'), (self.rt, 'in'))
        self.tb.msg_connect((self.rt, 'out'), (self.cf, 'in'))
        self.tb.msg_connect((self.cf, 'out'), (self.debug, 'store'))

        # original data
        in_data = [0xAA] * 10 + [0x69] * 10 + [
            0x55
        ] * 10  # 30 bytes = 240 bits = 1920 samples
        i_vec = pmt.init_u8vector(len(in_data), in_data)

        meta = pmt.make_dict()
        meta = pmt.dict_add(meta, pmt.intern("sample_rate"),
                            pmt.from_float(1e6))
        meta = pmt.dict_add(meta, pmt.intern("center_frequency"),
                            pmt.from_float(100.0e6))
        in_pdu = pmt.cons(meta, i_vec)

        self.tb.start()
        time.sleep(.1)
        self.emitter.emit(in_pdu)
        time.sleep(.1)
        self.tb.stop()
        self.tb.wait()

        # parse output
        #print("got ", list(pmt.to_python(pmt.cdr(self.debug.get_message(0)))))
        #print("got ", pmt.car(self.debug.get_message(0)))
        rcv = self.debug.get_message(0)
        rcv_meta = pmt.car(rcv)
        rcv_data = pmt.cdr(rcv)

        # asserts
        rcv_cf = pmt.to_double(
            pmt.dict_ref(rcv_meta, pmt.intern("center_frequency"),
                         pmt.PMT_NIL))
        expected_cf = 100e6 + samp_rate * rot_offset  # we rotated the burst 62500 Hz off
        self.assertTrue(
            abs(rcv_cf - expected_cf) < 1.0)  # less than 1 Hz off (no noise)
    def test_input_sanitization(self):
        samp_rate = 1e6
        freq_offset = -400e3
        center_freq = 911e6

        # blocks
        self.emitter = pdu_utils.message_emitter()
        self.cf = fhss_utils.cf_estimate(fhss_utils.COERCE,
                                         [x * 1e6 for x in range(900, 930)])
        self.debug = blocks.message_debug()

        # connections
        self.tb.msg_connect((self.emitter, 'msg'), (self.cf, 'in'))
        self.tb.msg_connect((self.cf, 'out'), (self.debug, 'store'))

        # data
        in_data = (1 + 0j, ) * 2048
        i_vec = pmt.init_c32vector(len(in_data), in_data)

        meta = pmt.make_dict()
        meta = pmt.dict_add(meta, pmt.intern("sample_rate"),
                            pmt.from_float(samp_rate))
        meta = pmt.dict_add(meta, pmt.intern("center_freq"),
                            pmt.from_float(center_freq + freq_offset))
        in_pdu = pmt.cons(meta, i_vec)

        # flowgraph
        self.tb.start()
        time.sleep(.001)
        self.emitter.emit(pmt.PMT_T)
        time.sleep(.01)
        self.emitter.emit(pmt.cons(pmt.PMT_T, pmt.PMT_NIL))
        time.sleep(.01)
        self.emitter.emit(meta)
        time.sleep(.01)
        self.emitter.emit(i_vec)
        time.sleep(.01)
        self.emitter.emit(in_pdu)
        time.sleep(.01)
        self.tb.stop()
        self.tb.wait()

        self.assertEqual(0, self.debug.num_messages())
  def __init__(self, burst_width=int(500e3), center_freq=915e6, decimation=32,
               fft_size=256, hist_time=0.004, lookahead_time=0.0005,
               max_burst_time=0.5, min_burst_time=0.001,
               output_attenuation=40, output_cutoff=0.25,
               output_trans_width=0.1, post_burst_time=0.00008,
               pre_burst_time=0.00008, samp_rate=int(16e6),
               threshold=10,
               cf_method = RMS, channel_freqs = [],
               n_threads = 3):
      gr.hier_block2.__init__(
          self, "FSK Burst Extractor Hier",
          gr.io_signature(1, 1, gr.sizeof_gr_complex*1),
          gr.io_signature(0, 0, 0),
      )
      '''
      Constructor
      
      @param burst_width - max burst bandwidth, Hz
      @param center_freq - 
      @param decimation - 
      @param fft_size - 
      @param hist_time - 
      @param lookahead_time - 
      @param max_burst_time - 
      @param min_burst_time - 
      @param output_attenuation - 
      @param output_cutoff -
      @param output_trans_width - 
      @param post_burst_time - 
      @param pre_burst_time - 
      @param samp_rate - 
      @param threshold - 
      @param cf_method - Center Frequency estimation method
      @param channel_freqs - CF Coerce freq list
      @param n_threads - 
      '''
      
      
      self.message_port_register_hier_out("pdu_out")

      ##################################################
      # Parameters
      ##################################################
      self.burst_width = burst_width
      self.center_freq = center_freq
      self.decimation = decimation
      self.fft_size = fft_size
      self.hist_time = hist_time
      self.lookahead_time = lookahead_time
      self.max_burst_time = max_burst_time
      self.min_burst_time = min_burst_time
      self.output_attenuation = output_attenuation
      self.output_cutoff = output_cutoff
      self.output_trans_width = output_trans_width
      self.post_burst_time = post_burst_time
      self.pre_burst_time = pre_burst_time
      self.samp_rate = samp_rate
      self.threshold = threshold
      self.channel_freqs = channel_freqs
      self.cf_method = cf_method
      self.n_threads = n_threads

      ##################################################
      # Blocks
      ##################################################
      # Low pass filter cutoff to half band.
      sig_taps = firdes.low_pass_2(1, 1, output_cutoff, output_trans_width, output_attenuation)
      self.pdu_utils_pdu_fir_filter_1 = pdu_utils.pdu_fir_filter(1, sig_taps)

      # This is a coarse filter,  Allow for transition band to alias onto itself.
      taps = firdes.low_pass_2(1, 1, .45 / decimation, .1 / decimation, output_attenuation)
      self.fhss_utils_tagged_burst_to_pdu_0 = fhss_utils.tagged_burst_to_pdu(decimation, taps, min_burst_time, max_burst_time, 0.0, 1.0, 1.0, samp_rate, n_threads)
      self.fhss_utils_fft_burst_tagger_0 = fhss_utils.fft_burst_tagger(center_freq, fft_size, samp_rate, int(round((float(samp_rate)/fft_size)*pre_burst_time)), int(round((float(samp_rate)/fft_size)*post_burst_time)), burst_width, 0, 0, threshold, int(round((float(samp_rate)/fft_size)*hist_time)), int(round((float(samp_rate)/fft_size)*lookahead_time)), False)
      #(self.fhss_utils_fft_burst_tagger_0).set_min_output_buffer(102400)
      self.cf_estimate = fhss_utils.cf_estimate(self.cf_method, self.channel_freqs)

      self.fine_time_measure = pdu_utils.pdu_fine_time_measure(pre_burst_time, post_burst_time, 10, 15)

      ##################################################
      # Connections
      ##################################################
      #self.msg_connect((self.pdu_utils_pdu_fir_filter_1, 'pdu_out'), (self, 'pdu_out'))
      self.msg_connect((self.fine_time_measure, 'pdu_out'), (self, 'pdu_out'))
      self.msg_connect((self.pdu_utils_pdu_fir_filter_1, 'pdu_out'), (self.fine_time_measure, 'pdu_in'))
      self.msg_connect((self.cf_estimate, 'out'), (self.pdu_utils_pdu_fir_filter_1, 'pdu_in'))
      self.msg_connect((self.fhss_utils_tagged_burst_to_pdu_0, 'cpdus'), (self.cf_estimate, 'in'))
      self.connect((self.fhss_utils_fft_burst_tagger_0, 0), (self.fhss_utils_tagged_burst_to_pdu_0, 0))
      self.connect((self, 0), (self.fhss_utils_fft_burst_tagger_0, 0))