def __init__(self): gr.top_block.__init__(self, "Borip Eb500") ################################################## # Variables ################################################## self.udp_port = udp_port = 19855 self.samp_rate = samp_rate = 640000 self.freq = freq = 14200000 ################################################## # Blocks ################################################## self.sink = baz.udp_sink(gr.sizeof_short * 1, '127.0.0.1', 28888, 1472, False, True) self.eb500_control_0 = eb500.EB500Control(freq, samp_rate, 'eb500', udp_port) self.eb200_if_stream_decode_0 = eb200.if_stream_decode(False) self.blocks_udp = blocks.udp_source(gr.sizeof_char * 1, '192.168.1.32', udp_port, 32796, False) self.blocks_complex_to_interleaved_short_0 = blocks.complex_to_interleaved_short( False) ################################################## # Connections ################################################## self.connect((self.blocks_complex_to_interleaved_short_0, 0), (self.sink, 0)) self.connect((self.blocks_udp, 0), (self.eb200_if_stream_decode_0, 0)) self.connect((self.eb200_if_stream_decode_0, 0), (self.blocks_complex_to_interleaved_short_0, 0))
def test_stacked_tx_commands(): # Crimson TNG setup. channels = range(4) sample_rate = 20e6 sample_count = int(sample_rate) # Waveform setup. wave_center = 15e6 wave_freq = 1.0e6 wave_ampl = 2.0e4 # Generates complex samples. sigs = [ analog.sig_source_c(sample_rate, analog.GR_SIN_WAVE, wave_freq, wave_ampl, 0.0) for ch in channels ] # Stops flowgraph when sample size is reached. heds = [blocks.head(gr.sizeof_gr_complex, sample_count) for ch in channels] # Converts complex floats to interleaved shorts. c2ss = [blocks.complex_to_interleaved_short(True) for ch in channels] # Takes interleaved shorts and outputs to Crimson TNG. csnk = crimson.get_snk_s(channels, sample_rate, wave_center, 0.0) """ +-----------+ +---------+ +---------+ +---------+ | | | sigs[0] |-->| heds[0] |-->| c2ss[0] |-->|ch[0] | +---------+ +---------+ +---------+ | | +---------+ +---------+ +---------+ | | | sigs[1] |-->| heds[1] |-->| c2ss[1] |-->|ch[1] | +---------+ +---------+ +---------+ | | | | +---------+ +---------+ +---------+ | | | sigs[N] |-->| heds[N] |-->| c2ss[N] |-->|ch[N] | +---------+ +---------+ +---------+ | csnk | +-----------+ """ # Connects flowgraph. flowgraph = gr.top_block() for ch in channels: flowgraph.connect(sigs[ch], heds[ch]) flowgraph.connect(heds[ch], c2ss[ch]) flowgraph.connect(c2ss[ch], (csnk, ch)) # Runs each TX command at specified start times. csnk.set_time_now(uhd.time_spec(0.0)) for second in range(5, 25, 5): csnk.set_start_time(uhd.time_spec(second)) # Flowgraph stop running when head count is full. flowgraph.run() # Sets head count to zero for next TX command start time. for ch in channels: heds[ch].reset()
def __init__(self, filename, dev_addrs, onebit, gain, digital_gain, fs, fc, sync_pps): gr.top_block.__init__(self) if onebit: raise NotImplementedError("TODO: 1-bit mode not implemented.") uhd_srcs = [ uhd.usrp_source(",".join( [addr, "num_recv_frames=256,recv_frame_size=16384"]), uhd.stream_args( cpu_format="fc32", otwformat="sc16", channels=[0])) for addr in dev_addrs] str2vec = blocks.streams_to_vector(2, len(uhd_srcs)) self.connect(str2vec, blocks.stream_to_vector(2 * len(uhd_srcs), 16*1024*1024), blocks.file_sink(2 * len(uhd_srcs) * 16 * 1024 * 1024, filename, False)) for ix, src in enumerate(uhd_srcs): src.set_clock_rate(fs*2, uhd.ALL_MBOARDS) src.set_samp_rate(fs) src.set_center_freq(uhd.tune_request(fc, 3e6)) src.set_gain(gain, 0) # TODO Use offset tuning? if sync_pps: src.set_clock_source("external") # 10 MHz src.set_time_source("external") # PPS self.connect(src, # [-1.0, 1.0] blocks.multiply_const_cc(32767 * digital_gain[ix]), # [-32767.0, 32767.0] blocks.complex_to_interleaved_short(), #[-32768, 32767] blocks.short_to_char(), #[-128, 127] blocks.stream_to_vector(1, 2), # I,Q,I,Q -> IQ, IQ (str2vec, ix)) print "Setting clocks..." if sync_pps: time.sleep(1.1) # Ensure there's been an edge. TODO: necessary? last_pps_time = uhd_srcs[0].get_time_last_pps() while last_pps_time == uhd_srcs[0].get_time_last_pps(): time.sleep(0.1) print "Got edge" [src.set_time_next_pps(uhd.time_spec(round(time.time())+1)) for src in uhd_srcs] time.sleep(1.0) # Wait for edge to set the clocks else: # No external PPS/10 MHz. Just set each clock and accept some skew. t = time.time() [src.set_time_now(uhd.time_spec(time.time())) for src in uhd_srcs] if len(uhd_srcs) > 1: print "Uncabled; loosely synced only. Initial skew ~ %.1f ms" % ( (time.time()-t) * 1000) t_start = uhd.time_spec(time.time() + 1.5) [src.set_start_time(t_start) for src in uhd_srcs] print "ready"
def test_complex_to_interleaved_short(self): src_data = (1+2j, 3+4j, 5+6j, 7+8j, 9+10j) expected_data = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10) src = blocks.vector_source_c(src_data) op = blocks.complex_to_interleaved_short() dst = blocks.vector_sink_s() self.tb.connect(src, op, dst) self.tb.run() self.assertEqual(expected_data, dst.data())
def test_complex_to_interleaved_short(self): src_data = (1 + 2j, 3 + 4j, 5 + 6j, 7 + 8j, 9 + 10j) expected_data = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10) src = blocks.vector_source_c(src_data) op = blocks.complex_to_interleaved_short() dst = blocks.vector_sink_s() self.tb.connect(src, op, dst) self.tb.run() self.assertEqual(expected_data, dst.data())
def __init__(self): gr.hier_block2.__init__(self, self.__class__.__name__, gr.io_signature(1, 1, gr.sizeof_gr_complex), gr.io_signature(1, 1, gr.sizeof_short)) self.clipper = Clipper(-1.0, 1.0) self.scaler = blocks.multiply_const_cc(C2IS_SCALE_FACTOR) self.c2is = blocks.complex_to_interleaved_short(vector=False) self.connect(self, self.clipper, self.scaler, self.c2is, self)
def __init__(self, config): gr.top_block.__init__(self) ## Vars ## ## Interleaved Short input (sc16) if config['intype'] == SNAME_TO_ENUM['sc16']: fsrc = blocks.file_source(gr.sizeof_short * 1, config['infile'], False) # SEEK_SET is offset from beginning, sample_offset is in samples # Factor of 2 because vector of length 2 (since interleaved) fsrc.seek(config['sample_offset'] * 2, os.SEEK_SET) # sc16->fc32 if config['outtype'] == SNAME_TO_ENUM['fc32']: ishort_to_cpxfloat = blocks.interleaved_short_to_complex( False, False) # Copies sample_len then exits head = blocks.head(gr.sizeof_gr_complex * 1, config['sample_len']) fsink = blocks.file_sink(gr.sizeof_gr_complex * 1, config['outfile']) self.connect(fsrc, ishort_to_cpxfloat, head, fsink) # sc16->sc16 elif config['outtype'] == SNAME_TO_ENUM['sc16']: head = blocks.head(gr.sizeof_short, config['sample_len']) fsink = blocks.file_sink(gr.sizeof_short, config['outfile']) self.connect(fsrc, head, fsink) ## Complex float input (fc32) elif config['intype'] == SNAME_TO_ENUM['fc32']: fsrc = blocks.file_source(gr.sizeof_gr_complex * 1, config['infile'], False) # SEEK_SET is offset from beginning, sample_offset is in samples fsrc.seek(config['sample_offset'], os.SEEK_SET) # fc32->fc32 if config['outtype'] == SNAME_TO_ENUM['fc32']: head = blocks.head(gr.sizeof_gr_complex * 1, config['sample_len']) fsink = blocks.file_sink(gr.sizeof_gr_complex * 1, config['outfile']) self.connect(fsrc, head, fsink) # fc32->sc16 elif config['outtype'] == SNAME_TO_ENUM['sc16']: cpxfloat_to_ishort = blocks.complex_to_interleaved_short(False) head = blocks.head(gr.sizeof_gr_complex * 1, config['sample_len']) fsink = blocks.file_sink(gr.sizeof_short, config['outfile']) self.connect(fsrc, head, cpxfloat_to_ishort, fsink)
def run_tx(csnk, channels, stack, sample_rate, wave_freq): """ +-----------+ +---------+ +---------+ +---------+ | | | sigs[0] |-->| heds[0] |-->| c2ss[0] |-->|ch[0] | +---------+ +---------+ +---------+ | | +---------+ +---------+ +---------+ | | | sigs[1] |-->| heds[1] |-->| c2ss[1] |-->|ch[1] | +---------+ +---------+ +---------+ | | | | +---------+ +---------+ +---------+ | | | sigs[N] |-->| heds[N] |-->| c2ss[N] |-->|ch[N] | +---------+ +---------+ +---------+ | csnk | +-----------+ """ for frame in stack: #in fund_freq.py this is tx_stack # Connect. sigs = [ analog.sig_source_c(sample_rate, analog.GR_SIN_WAVE, wave_freq, 2.0e4, 0.0) for ch in channels ] heds = [ blocks.head( gr.sizeof_gr_complex, frame[1] ) #block_head=(sizeofstream_dataformat, nitems)= (sizeofgr_complex, frame[1]) = (16bit I & 16bit Q, tx_stack[ , it["sample_rate"]) in fund_freq test for ch in channels ] c2ss = [blocks.complex_to_interleaved_short(True) for ch in channels] flowgraph = gr.top_block() for ch in channels: flowgraph.connect(sigs[ch], heds[ch]) flowgraph.connect(heds[ch], c2ss[ch]) flowgraph.connect(c2ss[ch], (csnk, ch)) # Run. csnk.set_start_time(uhd.time_spec( frame[0])) #frame[0]= tx_stack[10, ] in fund_freq test flowgraph.run() #print("tx time spec is:", uhd.time_spec(frame[0])) for hed in heds: hed.reset()
def __init__(self): gr.top_block.__init__(self, "Vdl2") ################################################## # Variables ################################################## self.center_freq = center_freq = 133e6 ################################################## # Blocks ################################################## self.rational_resampler_xxx_0 = filter.rational_resampler_ccc( interpolation=105, decimation=100, taps=None, fractional_bw=None, ) self.freq_xlating_fir_filter_xxx_0_1 = filter.freq_xlating_fir_filter_ccc( 50, (firdes.complex_band_pass(1, 10e6, -50e3, 50e3, 5000)), 136.975e6 - center_freq, 10e6) self.blocks_multiply_const_vxx_0 = blocks.multiply_const_vcc((32767, )) self.blocks_file_source_0 = blocks.file_source( gr.sizeof_gr_complex * 1, "/tmp/decompress.iq", False) self.blocks_file_sink_0 = blocks.file_sink(gr.sizeof_short * 1, "/tmp/vdl2.iq", False) self.blocks_file_sink_0.set_unbuffered(False) self.blocks_complex_to_interleaved_short_0 = blocks.complex_to_interleaved_short( False) ################################################## # Connections ################################################## self.connect((self.blocks_complex_to_interleaved_short_0, 0), (self.blocks_file_sink_0, 0)) self.connect((self.blocks_file_source_0, 0), (self.freq_xlating_fir_filter_xxx_0_1, 0)) self.connect((self.blocks_multiply_const_vxx_0, 0), (self.blocks_complex_to_interleaved_short_0, 0)) self.connect((self.freq_xlating_fir_filter_xxx_0_1, 0), (self.rational_resampler_xxx_0, 0)) self.connect((self.rational_resampler_xxx_0, 0), (self.blocks_multiply_const_vxx_0, 0))
def __init__(self, input_file, output_file): gr.top_block.__init__(self, "Uhd Shifter") ################################################## # Variables ################################################## self.samp_rate = samp_rate = 256000 self.offset = offset = -25e3 ################################################## # Blocks ################################################## self.signal_source = analog.sig_source_c(samp_rate, analog.GR_COS_WAVE, offset, 1, 0) self.resampler = filter.rational_resampler_ccc( interpolation=96, decimation=256, taps=None, fractional_bw=None, ) self.multiply_const = blocks.multiply_const_vcc((32768, )) self.multi = blocks.multiply_vcc(1) self.file_source = blocks.file_source(gr.sizeof_gr_complex * 1, input_file, False) self.file_sink = blocks.file_sink(gr.sizeof_short * 1, output_file, False) self.file_sink.set_unbuffered(True) self.c2is = blocks.complex_to_interleaved_short() ################################################## # Connections ################################################## self.connect((self.multi, 0), (self.resampler, 0)) self.connect((self.file_source, 0), (self.multi, 0)) self.connect((self.signal_source, 0), (self.multi, 1)) self.connect((self.c2is, 0), (self.file_sink, 0)) self.connect((self.multiply_const, 0), (self.c2is, 0)) self.connect((self.resampler, 0), (self.multiply_const, 0))
def __init__(self, config): gr.top_block.__init__(self) ## Vars ## ## Interleaved Short input (sc16) if config["intype"] == SNAME_TO_ENUM["sc16"]: fsrc = blocks.file_source(gr.sizeof_short * 1, config["infile"], False) # SEEK_SET is offset from beginning, sample_offset is in samples # Factor of 2 because vector of length 2 (since interleaved) fsrc.seek(config["sample_offset"] * 2, os.SEEK_SET) # sc16->fc32 if config["outtype"] == SNAME_TO_ENUM["fc32"]: ishort_to_cpxfloat = blocks.interleaved_short_to_complex(False, False) # Copies sample_len then exits head = blocks.head(gr.sizeof_gr_complex * 1, config["sample_len"]) fsink = blocks.file_sink(gr.sizeof_gr_complex * 1, config["outfile"]) self.connect(fsrc, ishort_to_cpxfloat, head, fsink) # sc16->sc16 elif config["outtype"] == SNAME_TO_ENUM["sc16"]: head = blocks.head(gr.sizeof_short, config["sample_len"]) fsink = blocks.file_sink(gr.sizeof_short, config["outfile"]) self.connect(fsrc, head, fsink) ## Complex float input (fc32) elif config["intype"] == SNAME_TO_ENUM["fc32"]: fsrc = blocks.file_source(gr.sizeof_gr_complex * 1, config["infile"], False) # SEEK_SET is offset from beginning, sample_offset is in samples fsrc.seek(config["sample_offset"], os.SEEK_SET) # fc32->fc32 if config["outtype"] == SNAME_TO_ENUM["fc32"]: head = blocks.head(gr.sizeof_gr_complex * 1, config["sample_len"]) fsink = blocks.file_sink(gr.sizeof_gr_complex * 1, config["outfile"]) self.connect(fsrc, head, fsink) # fc32->sc16 elif config["outtype"] == SNAME_TO_ENUM["sc16"]: cpxfloat_to_ishort = blocks.complex_to_interleaved_short(False) head = blocks.head(gr.sizeof_gr_complex * 1, config["sample_len"]) fsink = blocks.file_sink(gr.sizeof_short, config["outfile"]) self.connect(fsrc, head, cpxfloat_to_ishort, fsink)
def tx_run(csnk, channels, sample_count, start_time_specs, sample_rate): """ +-----------+ +---------+ +---------+ +---------+ | | | sigs[0] |-->| heds[0] |-->| c2ss[0] |-->|ch[0] | +---------+ +---------+ +---------+ | | +---------+ +---------+ +---------+ | | | sigs[1] |-->| heds[1] |-->| c2ss[1] |-->|ch[1] | +---------+ +---------+ +---------+ | | | | +---------+ +---------+ +---------+ | | | sigs[N] |-->| heds[N] |-->| c2ss[N] |-->|ch[N] | +---------+ +---------+ +---------+ | csnk | +-----------+ """ sigs = [ analog.sig_source_c(sample_rate, analog.GR_SIN_WAVE, 1.0e6, 2.0e4, 0.0) for ch in channels ] heds = [blocks.head(gr.sizeof_gr_complex, sample_count) for ch in channels] c2ss = [blocks.complex_to_interleaved_short(True) for ch in channels] flowgraph = gr.top_block() for ch in channels: flowgraph.connect(sigs[ch], heds[ch]) flowgraph.connect(heds[ch], c2ss[ch]) flowgraph.connect(c2ss[ch], (csnk, ch)) for start_time_spec in start_time_specs: csnk.set_start_time(start_time_spec) flowgraph.run() for hed in heds: hed.reset()
def __init__(self, input_file, output_file): gr.top_block.__init__(self, "Uhd Shifter") ################################################## # Variables ################################################## self.samp_rate = samp_rate = 256000 self.offset = offset = -25e3 ################################################## # Blocks ################################################## self.signal_source = analog.sig_source_c(samp_rate, analog.GR_COS_WAVE, offset, 1, 0) self.resampler = filter.rational_resampler_ccc( interpolation=96, decimation=256, taps=None, fractional_bw=None, ) self.multiply_const = blocks.multiply_const_vcc((32768, )) self.multi = blocks.multiply_vcc(1) self.file_source = blocks.file_source(gr.sizeof_gr_complex*1, input_file, False) self.file_sink = blocks.file_sink(gr.sizeof_short*1, output_file, False) self.file_sink.set_unbuffered(True) self.c2is = blocks.complex_to_interleaved_short() ################################################## # Connections ################################################## self.connect((self.multi, 0), (self.resampler, 0)) self.connect((self.file_source, 0), (self.multi, 0)) self.connect((self.signal_source, 0), (self.multi, 1)) self.connect((self.c2is, 0), (self.file_sink, 0)) self.connect((self.multiply_const, 0), (self.c2is, 0)) self.connect((self.resampler, 0), (self.multiply_const, 0))
def __init__(self): gr.top_block.__init__(self, "Shipping Passing App") Qt.QWidget.__init__(self) self.setWindowTitle("Shipping Passing App") qtgui.util.check_set_qss() try: self.setWindowIcon(Qt.QIcon.fromTheme('gnuradio-grc')) except: pass self.top_scroll_layout = Qt.QVBoxLayout() self.setLayout(self.top_scroll_layout) self.top_scroll = Qt.QScrollArea() self.top_scroll.setFrameStyle(Qt.QFrame.NoFrame) self.top_scroll_layout.addWidget(self.top_scroll) self.top_scroll.setWidgetResizable(True) self.top_widget = Qt.QWidget() self.top_scroll.setWidget(self.top_widget) self.top_layout = Qt.QVBoxLayout(self.top_widget) self.top_grid_layout = Qt.QGridLayout() self.top_layout.addLayout(self.top_grid_layout) self.settings = Qt.QSettings("GNU Radio", "audioTxRx") self.restoreGeometry(self.settings.value("geometry").toByteArray()) ################################################## # Variables ################################################## self.samp_rate = samp_rate = 32000 self.msg_str = msg_str = "CSQ305438" self.QPSK = QPSK = digital.constellation_calcdist( ([1 + 1j, -1 + 1j, 1 - 1j, -1 - 1j]), ([0, 1, 3, 2]), 4, 1).base() ################################################## # Blocks ################################################## self._msg_str_tool_bar = Qt.QToolBar(self) self._msg_str_tool_bar.addWidget(Qt.QLabel("msg_str" + ": ")) self._msg_str_line_edit = Qt.QLineEdit(str(self.msg_str)) self._msg_str_tool_bar.addWidget(self._msg_str_line_edit) self._msg_str_line_edit.returnPressed.connect(lambda: self.set_msg_str( str(str(self._msg_str_line_edit.text().toAscii())))) self.top_grid_layout.addWidget(self._msg_str_tool_bar) self.tutorial_my_qpsk_demod_cb_0 = tutorial.my_qpsk_demod_cb(True) self.tutorial_chat_sanitizer_0 = tutorial.chat_sanitizer('', '') self.gnuShipping_shippingReciever_0 = gnuShipping.shippingReciever( msg_str) self.digital_crc32_async_bb_1 = digital.crc32_async_bb(False) self.digital_crc32_async_bb_0 = digital.crc32_async_bb(True) self.digital_clock_recovery_mm_xx_0 = digital.clock_recovery_mm_cc( 1, 0.25 * 0.175 * 0.175, 0.5, 0.175, 0.005) self.digital_chunks_to_symbols_xx_0 = digital.chunks_to_symbols_bc( (QPSK.points()), 1) self.blocks_throttle_0_0 = blocks.throttle(gr.sizeof_char * 1, samp_rate, True) self.blocks_throttle_0 = blocks.throttle(gr.sizeof_char * 1, samp_rate, True) self.blocks_tagged_stream_to_pdu_0 = blocks.tagged_stream_to_pdu( blocks.byte_t, 'pdu_length') self.blocks_tag_debug_0 = blocks.tag_debug(gr.sizeof_char * 1, '', "") self.blocks_tag_debug_0.set_display(True) self.blocks_short_to_float_0 = blocks.short_to_float(1, 1) self.blocks_repack_bits_bb_1 = blocks.repack_bits_bb( 2, 8, 'pdu_length', True, gr.GR_LSB_FIRST) self.blocks_repack_bits_bb_0 = blocks.repack_bits_bb( 8, 2, 'pdu_length', False, gr.GR_LSB_FIRST) self.blocks_pdu_to_tagged_stream_0 = blocks.pdu_to_tagged_stream( blocks.byte_t, 'pdu_length') self.blocks_message_debug_0 = blocks.message_debug() self.blocks_interleaved_short_to_complex_0 = blocks.interleaved_short_to_complex( False, False) self.blocks_float_to_short_0 = blocks.float_to_short(1, 1) self.blocks_complex_to_interleaved_short_0 = blocks.complex_to_interleaved_short( False) self.audio_source_0 = audio.source(samp_rate, '', True) self.audio_sink_0 = audio.sink(samp_rate, '', True) ################################################## # Connections ################################################## self.msg_connect((self.blocks_tagged_stream_to_pdu_0, 'pdus'), (self.digital_crc32_async_bb_0, 'in')) self.msg_connect((self.digital_crc32_async_bb_0, 'out'), (self.gnuShipping_shippingReciever_0, 'in')) self.msg_connect((self.digital_crc32_async_bb_1, 'out'), (self.blocks_message_debug_0, 'print_pdu')) self.msg_connect((self.digital_crc32_async_bb_1, 'out'), (self.blocks_pdu_to_tagged_stream_0, 'pdus')) self.msg_connect((self.tutorial_chat_sanitizer_0, 'out'), (self.digital_crc32_async_bb_1, 'in')) self.connect((self.audio_source_0, 0), (self.blocks_float_to_short_0, 0)) self.connect((self.blocks_complex_to_interleaved_short_0, 0), (self.blocks_short_to_float_0, 0)) self.connect((self.blocks_float_to_short_0, 0), (self.blocks_interleaved_short_to_complex_0, 0)) self.connect((self.blocks_interleaved_short_to_complex_0, 0), (self.digital_clock_recovery_mm_xx_0, 0)) self.connect((self.blocks_pdu_to_tagged_stream_0, 0), (self.blocks_tag_debug_0, 0)) self.connect((self.blocks_pdu_to_tagged_stream_0, 0), (self.blocks_throttle_0, 0)) self.connect((self.blocks_repack_bits_bb_0, 0), (self.digital_chunks_to_symbols_xx_0, 0)) self.connect((self.blocks_repack_bits_bb_1, 0), (self.blocks_tagged_stream_to_pdu_0, 0)) self.connect((self.blocks_short_to_float_0, 0), (self.audio_sink_0, 0)) self.connect((self.blocks_throttle_0, 0), (self.blocks_repack_bits_bb_0, 0)) self.connect((self.blocks_throttle_0_0, 0), (self.blocks_repack_bits_bb_1, 0)) self.connect((self.digital_chunks_to_symbols_xx_0, 0), (self.blocks_complex_to_interleaved_short_0, 0)) self.connect((self.digital_clock_recovery_mm_xx_0, 0), (self.tutorial_my_qpsk_demod_cb_0, 0)) self.connect((self.tutorial_my_qpsk_demod_cb_0, 0), (self.blocks_throttle_0_0, 0))
def __init__(self, baudrate=1200.0, raw_file='', udp_dest_audio='127.0.0.1', udp_dest_iq='127.0.0.1', udp_port_audio=7355, udp_port_iq=7356): gr.top_block.__init__(self, "satnogs raw to udp") ################################################## # Parameters ################################################## self.baudrate = baudrate self.raw_file = raw_file self.udp_dest_audio = udp_dest_audio self.udp_dest_iq = udp_dest_iq self.udp_port_audio = udp_port_audio self.udp_port_iq = udp_port_iq ################################################## # Variables ################################################## self.sps = sps = 4 self.audio_samp_rate = audio_samp_rate = 48000 self.if_freq = if_freq = 12000 self.decimation = decimation = satnogs.find_decimation( baudrate, 2, audio_samp_rate, sps) ################################################## # Blocks ################################################## self.pfb_arb_resampler_xxx_0 = pfb.arb_resampler_ccf( audio_samp_rate / (baudrate * decimation), taps=None, flt_size=32) self.pfb_arb_resampler_xxx_0.declare_sample_delay(0) self.low_pass_filter_0_0 = filter.fir_filter_ccf( 1, firdes.low_pass(1, audio_samp_rate, 0.42 * audio_samp_rate / 2.0, 0.05 * audio_samp_rate, firdes.WIN_HAMMING, 6.76)) self.blocks_udp_sink_0_0 = blocks.udp_sink(gr.sizeof_short * 1, udp_dest_iq, udp_port_iq, 1472, True) self.blocks_udp_sink_0 = blocks.udp_sink(gr.sizeof_short * 1, udp_dest_audio, udp_port_audio, 1472, True) self.blocks_throttle_1 = blocks.throttle(gr.sizeof_gr_complex * 1, baudrate * decimation, True) self.blocks_rotator_cc_0_0 = blocks.rotator_cc( 2.0 * math.pi * (if_freq / audio_samp_rate)) self.blocks_multiply_const_vxx_1 = blocks.multiply_const_cc(16383) self.blocks_multiply_const_vxx_0 = blocks.multiply_const_cc(1 / 16768) self.blocks_interleaved_short_to_complex_0 = blocks.interleaved_short_to_complex( False, False) self.blocks_float_to_short_0 = blocks.float_to_short(1, 16383.0) self.blocks_file_source_0 = blocks.file_source(gr.sizeof_short * 1, raw_file, False, 0, 0) self.blocks_file_source_0.set_begin_tag(pmt.PMT_NIL) self.blocks_complex_to_real_0 = blocks.complex_to_real(1) self.blocks_complex_to_interleaved_short_0 = blocks.complex_to_interleaved_short( False) self.analog_agc2_xx_0_1 = analog.agc2_cc(1e-3, 1e-3, 0.5, 1.0) self.analog_agc2_xx_0_1.set_max_gain(65536) self.analog_agc2_xx_0_0 = analog.agc2_cc(0.01, 0.001, 0.015, 1.0) self.analog_agc2_xx_0_0.set_max_gain(65536) self.analog_agc2_xx_0 = analog.agc2_cc(1e-2, 1e-3, 1.5e-2, 1.0) self.analog_agc2_xx_0.set_max_gain(65536) ################################################## # Connections ################################################## self.connect((self.analog_agc2_xx_0, 0), (self.blocks_multiply_const_vxx_1, 0)) self.connect((self.analog_agc2_xx_0_0, 0), (self.low_pass_filter_0_0, 0)) self.connect((self.analog_agc2_xx_0_1, 0), (self.pfb_arb_resampler_xxx_0, 0)) self.connect((self.blocks_complex_to_interleaved_short_0, 0), (self.blocks_udp_sink_0_0, 0)) self.connect((self.blocks_complex_to_real_0, 0), (self.blocks_float_to_short_0, 0)) self.connect((self.blocks_file_source_0, 0), (self.blocks_interleaved_short_to_complex_0, 0)) self.connect((self.blocks_float_to_short_0, 0), (self.blocks_udp_sink_0, 0)) self.connect((self.blocks_interleaved_short_to_complex_0, 0), (self.blocks_multiply_const_vxx_0, 0)) self.connect((self.blocks_multiply_const_vxx_0, 0), (self.blocks_throttle_1, 0)) self.connect((self.blocks_multiply_const_vxx_1, 0), (self.blocks_complex_to_interleaved_short_0, 0)) self.connect((self.blocks_rotator_cc_0_0, 0), (self.blocks_complex_to_real_0, 0)) self.connect((self.blocks_throttle_1, 0), (self.analog_agc2_xx_0, 0)) self.connect((self.blocks_throttle_1, 0), (self.analog_agc2_xx_0_1, 0)) self.connect((self.low_pass_filter_0_0, 0), (self.blocks_rotator_cc_0_0, 0)) self.connect((self.pfb_arb_resampler_xxx_0, 0), (self.analog_agc2_xx_0_0, 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="", 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 self._decimation = decimation # Sample rate of the bursts exiting the burst downmix block self._burst_sample_rate = 25000 * samples_per_symbol if (self._input_sample_rate / self._decimation) % self._burst_sample_rate != 0: raise RuntimeError("Selected sample rate and decimation can not be matched. Please try a different combination. Sample rate divided by decimation must be a multiple of %d." % self._burst_sample_rate) 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 self._decimation > 1: self._use_channelizer = 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 self._decimation % 2: raise RuntimeError("The desired decimation must be 1 or an even number.") self._channels = self._decimation + 1 if self._decimation >= 8: self._use_fft_channelizer = True if 2**int(math.log(self._decimation, 2)) != self._decimation: raise RuntimeError("Decimations >= 8 must be a power of two.") self._channel_sample_rate = self._input_sample_rate // self._decimation # On low end ARM machines we only create a single burst downmixer to not # overload the CPU. Rather drop bursts than samples. if platform.machine() == 'aarch64' and multiprocessing.cpu_count() == 4: self._n_burst_downmixers = 1 else: self._n_burst_downmixers = 2 else: self._use_fft_channelizer = False self._n_burst_downmixers = self._channels self._channelizer_over_sample_ratio = self._channels / (self._channels - 1.) channelizer_output_sample_rate = int(round(float(self._input_sample_rate) / self._channels * self._channelizer_over_sample_ratio)) self._channel_sample_rate = channelizer_output_sample_rate assert channelizer_output_sample_rate == self._input_sample_rate / self._decimation assert channelizer_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 = (channelizer_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. Please try a different decimation.") 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 or use a decimation >= 8.", file=sys.stderr) pfb_input_delay = (len(self._pfb_fir_filter) + 1) // 2 - self._channels / self._channelizer_over_sample_ratio self._channelizer_delay = pfb_input_delay / self._decimation 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._channelizer_over_sample_ratio", self._channelizer_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_channelizer = 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) print("source:", config['source'], 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_sample_rate(self._input_sample_rate) source.set_center_freq(self._center_frequency, 0) # Set a rough time estimate for potential rx_time tags from USRP devices # This prevents the output from having bogous time stamps if no GPSDO is available source.set_time_now(osmosdr.time_spec_t(time.time())) 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) if 'clock_source' in d: print("Setting clock source to:", d['clock_source'], file=sys.stderr) source.set_clock_source(d['clock_source'], 0) if 'time_source' in d: print("Setting time source to:", d['time_source'], file=sys.stderr) source.set_time_source(d['time_source'], 0) while (time.time() % 1) < 0.4 or (time.time() % 1) > 0.6: pass t = time.time() source.set_time_next_pps(osmosdr.time_spec_t(int(t) + 1)) time.sleep(1) #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) elif config['source'] == 'soapy': d = config["soapy-source"] try: from gnuradio import soapy except ImportError: raise ImportError("gr-soapy not found. Make sure you are running GNURadio >= 3.9.2.0") if 'driver' not in d: print("No driver specified for soapy", file=sys.stderr) print("Run 'SoapySDRUtil -i' to see available drivers(factories)", file=sys.stderr) exit(1) dev = 'driver=' + d['driver'] # Strip quotes def sanitize(s): if s.startswith('"') and s.endswith('"'): return s.strip('""') if s.startswith("'") and s.endswith("'"): return s.strip("''") return s # Remove all outer quotes from the args if they are present in the config if 'device_args' in d: dev_args = sanitize(d['device_args']) elif 'dev_args' in d: dev_args = sanitize(d['dev_args']) else: dev_args = '' stream_args = sanitize(d['stream_args']) if 'stream_args' in d else '' tune_args = sanitize(d['tune_args']) if 'tune_args' in d else '' other_settings = sanitize(d['other_settings']) if 'other_settings' in d else '' # We only support a single channel. Apply tune_args and other_settings to that channel. source = soapy.source(dev, "fc32", 1, dev_args, stream_args, [tune_args], [other_settings]) source.set_sample_rate(0, self._input_sample_rate) source.set_frequency(0, self._center_frequency) if 'gain' in d: gain = int(d['gain']) source.set_gain_mode(0, False) # AGC: OFF source.set_gain(0, gain) print("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.list_gains(0)) if gain_name is not None: source.set_gain(0, gain_name, gain_value) print(gain_name, "Gain:", source.get_gain(0, gain_name), '(Requested %d)' % gain_value, source.get_gain_range(0, gain_name), file=sys.stderr) else: print("WARNING: Gain", gain_option_name, "not supported by source!", file=sys.stderr) print("Supported gains:", source.list_gains(0), file=sys.stderr) if 'bandwidth' in d: bandwidth = int(d['bandwidth']) source.set_bandwidth(0, bandwidth) 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(0, antenna) 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_frequency_correction(0, f_corr) #source.set_dc_offset_mode(0, True) #source.set_dc_offset(0, dc_off) #source.set_iq_balance(0, iq_bal) elif config['source'] == 'zeromq-sub': d = config["zeromq-sub-source"] from gnuradio import zeromq if 'address' not in d: print("No address specified for zeromq sub", file=sys.stderr) exit(1) pass_tags = False if 'pass_tags' in d: pass_tags = bool(distutils.util.strtobool(d['pass_tags'])) timeout = 100 if 'timeout' in d: timeout = int(d['timeout']) high_water_mark = -1 if 'high_water_mark' in d: high_water_mark = int(d['high_water_mark']) source = zeromq.sub_source(gr.sizeof_gr_complex, 1, d['address'], timeout, pass_tags, high_water_mark, '') elif config['source'] == 'uhd': d = config["uhd-source"] from gnuradio import uhd dev_addr = "" if "device_addr" in d: dev_addr = d['device_addr'] dev_args = d['device_args'] cpu_format = 'fc32' wire_format = 'sc16' stream_args = "" stream_args = uhd.stream_args(cpu_format, wire_format, args=stream_args) source = uhd.usrp_source(dev_addr + "," + dev_args, stream_args) source.set_samp_rate(self._input_sample_rate) source.set_center_freq(self._center_frequency) if 'gain' in d: gain = int(d['gain']) source.set_gain(gain, 0) print("Gain:", source.get_gain(0), '(Requested %d)' % gain, 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) 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) print("mboard sensors:", source.get_mboard_sensor_names(0), file=sys.stderr) #for sensor in source.get_mboard_sensor_names(0): # print(sensor, source.get_mboard_sensor(sensor, 0)) gpsdo_sources = ('gpsdo', 'jacksonlabs') time_source = None if 'time_source' in d: time_source = d['time_source'] if time_source in gpsdo_sources: source.set_time_source("gpsdo", 0) else: source.set_time_source(time_source, 0) clock_source = None if 'clock_source' in d: clock_source = d['time_source'] if clock_source in gpsdo_sources: source.set_clock_source("gpsdo", 0) else: source.set_clock_source(clock_source, 0) if time_source in gpsdo_sources or clock_source in gpsdo_sources: print("Waiting for gps_locked...", file=sys.stderr) while True: try: if d['time_source'] == "jacksonlabs": servo = source.get_mboard_sensor("gps_servo", 0) # See https://lists.ettus.com/empathy/thread/6ZOCFQSKLHSG2IH3ID7XPWVKHVHZXPBP gps_locked = str(servo).split()[8] == "6" else: gps_locked = source.get_mboard_sensor("gps_locked", 0).to_bool() if gps_locked: break except ValueError as e: print(e, file=sys.stderr) pass time.sleep(1) print("gps_locked!", file=sys.stderr) if clock_source: print("Waiting for ref_locked...", file=sys.stderr) while True: try: ref_locked = source.get_mboard_sensor("ref_locked", 0) if ref_locked.to_bool(): break except ValueError as e: print(e, file=sys.stderr) pass time.sleep(1) print("ref_locked!", file=sys.stderr) if time_source: if time_source in gpsdo_sources: while True: try: gps_time = uhd.time_spec_t(source.get_mboard_sensor("gps_time").to_int()) break except ValueError as e: print(e, file=sys.stderr) pass time.sleep(1) next_pps_time = gps_time + 1 else: system_time = uhd.time_spec_t(int(time.time())) next_pps_time = system_time + 1 source.set_time_next_pps(next_pps_time) print("Next PPS at", next_pps_time.get_real_secs(), file=sys.stderr) print("Sleeping 2 seconds...", file=sys.stderr) time.sleep(2) # TODO: Check result for plausibility print("USRP time:", source.get_time_last_pps(0).get_real_secs(), file=sys.stderr) else: # Set a rough time estimate for rx_time tags from the USRP. # This prevents the output from having bogous time stamps if no GPSDO is available. source.set_time_now(uhd.time_spec_t(time.time())) self.source = source 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, max_burst_len=int(self._input_sample_rate * 0.09), threshold=self._threshold, history_size=512, offline=self._offline, debug=self._verbose) self._fft_burst_tagger.set_min_output_buffer(1024 * 64) # 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(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 + '.sigmf-data') 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_channelizer: self._burst_to_pdu_converters = [] self._burst_downmixers = [] sinks = [] for channel in range(self._channels): if not self._use_fft_channelizer: center = channel if channel <= self._channels / 2 else (channel - self._channels) relative_center = center / float(self._channels) relative_span = 1. / self._channels relative_sample_rate = relative_span * self._channelizer_over_sample_ratio # Second and third parameters tell the block where after the PFB it sits. burst_to_pdu_converter = iridium.tagged_burst_to_pdu(self._max_burst_len, relative_center, relative_span, relative_sample_rate, -self._channelizer_delay, self._max_queue_len, not self._offline) self._burst_to_pdu_converters.append(burst_to_pdu_converter) 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) channelizer_debug_sinks = [] #channelizer_debug_sinks = [blocks.file_sink(itemsize=gr.sizeof_gr_complex, filename="/tmp/channel-%d.f32"%i) for i in range(self._channels)] if self._use_fft_channelizer: if not channelizer_debug_sinks and self._offline: # HACK: if there are no stream outputs active GNURadio has issues terminating the # flowgraph on completion. Connect some dummy sinks to them. channelizer_debug_sinks = [blocks.null_sink(gr.sizeof_gr_complex) for i in range(self._channels)] activate_streams = len(channelizer_debug_sinks) > 0 self._channelizer = iridium.fft_channelizer(1024, self._channels - 1, activate_streams, self._n_burst_downmixers, self._max_burst_len, self._max_queue_len * self._n_burst_downmixers, not self._offline) else: self._channelizer = gnuradio.filter.pfb.channelizer_ccf(numchans=self._channels, taps=self._pfb_fir_filter, oversample_rate=self._channelizer_over_sample_ratio) tb.connect(self._fft_burst_tagger, self._channelizer) for i in range(self._channels): if channelizer_debug_sinks: tb.connect((self._channelizer, i), channelizer_debug_sinks[i]) for i in range(self._n_burst_downmixers): if self._burst_to_pdu_converters: tb.connect((self._channelizer, i), self._burst_to_pdu_converters[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')) else: tb.msg_connect((self._channelizer, 'cpdus%d' % i), (self._burst_downmixers[i], 'cpdus')) tb.msg_connect((self._burst_downmixers[i], 'burst_handled'), (self._channelizer, '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'))
def __init__(self): gr.top_block.__init__(self, "Not titled yet") Qt.QWidget.__init__(self) self.setWindowTitle("Not titled yet") qtgui.util.check_set_qss() try: self.setWindowIcon(Qt.QIcon.fromTheme('gnuradio-grc')) except: pass self.top_scroll_layout = Qt.QVBoxLayout() self.setLayout(self.top_scroll_layout) self.top_scroll = Qt.QScrollArea() self.top_scroll.setFrameStyle(Qt.QFrame.NoFrame) self.top_scroll_layout.addWidget(self.top_scroll) self.top_scroll.setWidgetResizable(True) self.top_widget = Qt.QWidget() self.top_scroll.setWidget(self.top_widget) self.top_layout = Qt.QVBoxLayout(self.top_widget) self.top_grid_layout = Qt.QGridLayout() self.top_layout.addLayout(self.top_grid_layout) self.settings = Qt.QSettings("GNU Radio", "fir_siggen") try: if StrictVersion(Qt.qVersion()) < StrictVersion("5.0.0"): self.restoreGeometry( self.settings.value("geometry").toByteArray()) else: self.restoreGeometry(self.settings.value("geometry")) except: pass ################################################## # Variables ################################################## self.tx_taps_hex = tx_taps_hex = [ 226, -36, -211, 246, 40, -527, 379, 850, -1179, -1172, 2637, 1447, -5739, -1633, 19484, 32767, 19484, -1633, -5739, 1447, 2637, -1172, -1179, 850, 379, -527, 40, 246, -211, -36, 226 ] self.tx_taps_exp = tx_taps_exp = [ -0.001129150390625, -0.009796142578125, -0.018341064453125, -0.025115966796875, -0.028533935546875, -0.027191162109375, -0.020172119140625, -0.00714111328125, 0.01141357421875, 0.0343017578125, 0.0596923828125, 0.08526611328125, 0.108551025390625, 0.127197265625, 0.139251708984375, 0.143402099609375, 0.143402099609375, 0.139251708984375, 0.127197265625, 0.108551025390625, 0.08526611328125, 0.0596923828125, 0.0343017578125, 0.01141357421875, -0.00714111328125, -0.020172119140625, -0.027191162109375, -0.028533935546875, -0.025115966796875, -0.018341064453125, -0.009796142578125, -0.001129150390625 ] self.tx_taps = tx_taps = firdes.root_raised_cosine(1, 2, 1, 0.2, 31) self.samp_rate = samp_rate = 32000 self.act_taps = act_taps = [x * 2**-15 for x in tx_taps_hex] ################################################## # Blocks ################################################## self.qtgui_const_sink_x_0 = qtgui.const_sink_c( 1024, #size "", #name 1 #number of inputs ) self.qtgui_const_sink_x_0.set_update_time(0.10) self.qtgui_const_sink_x_0.set_y_axis(-2, 2) self.qtgui_const_sink_x_0.set_x_axis(-2, 2) self.qtgui_const_sink_x_0.set_trigger_mode(qtgui.TRIG_MODE_FREE, qtgui.TRIG_SLOPE_POS, 0.0, 0, "") self.qtgui_const_sink_x_0.enable_autoscale(False) self.qtgui_const_sink_x_0.enable_grid(False) self.qtgui_const_sink_x_0.enable_axis_labels(True) labels = ['', '', '', '', '', '', '', '', '', ''] widths = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1] colors = [ "blue", "red", "red", "red", "red", "red", "red", "red", "red", "red" ] styles = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] markers = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] alphas = [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0] for i in range(1): if len(labels[i]) == 0: self.qtgui_const_sink_x_0.set_line_label( i, "Data {0}".format(i)) else: self.qtgui_const_sink_x_0.set_line_label(i, labels[i]) self.qtgui_const_sink_x_0.set_line_width(i, widths[i]) self.qtgui_const_sink_x_0.set_line_color(i, colors[i]) self.qtgui_const_sink_x_0.set_line_style(i, styles[i]) self.qtgui_const_sink_x_0.set_line_marker(i, markers[i]) self.qtgui_const_sink_x_0.set_line_alpha(i, alphas[i]) self._qtgui_const_sink_x_0_win = sip.wrapinstance( self.qtgui_const_sink_x_0.pyqwidget(), Qt.QWidget) self.top_grid_layout.addWidget(self._qtgui_const_sink_x_0_win) self.interp_fir_filter_xxx_0_0 = filter.interp_fir_filter_ccc( 1, tx_taps_hex) self.interp_fir_filter_xxx_0_0.declare_sample_delay(0) self.blocks_stream_mux_0 = blocks.stream_mux(gr.sizeof_gr_complex * 1, [1, 1]) self.blocks_multiply_const_vxx_2 = blocks.multiply_const_cc(32) self.blocks_multiply_const_vxx_1 = blocks.multiply_const_cc(2**-12) self.blocks_multiply_const_vxx_0 = blocks.multiply_const_cc(2**-8) self.blocks_interleaved_short_to_complex_0 = blocks.interleaved_short_to_complex( False, False) self.blocks_interleaved_char_to_complex_0 = blocks.interleaved_char_to_complex( False) self.blocks_file_source_0 = blocks.file_source( gr.sizeof_short * 1, '/Users/iracigt/Developer/DVB_hat/hdl/iq_bytes.bin', False, 0 * 21690 * 2, ) self.blocks_file_source_0.set_begin_tag(pmt.PMT_NIL) self.blocks_file_sink_0_0 = blocks.file_sink( gr.sizeof_short * 1, '/Users/iracigt/Developer/DVB_hat/hdl/fir_correct_u16.bin', False) self.blocks_file_sink_0_0.set_unbuffered(True) self.blocks_file_sink_0 = blocks.file_sink( gr.sizeof_char * 1, '/Users/iracigt/Developer/DVB_hat/hdl/fir_in_u8.bin', False) self.blocks_file_sink_0.set_unbuffered(True) self.blocks_complex_to_interleaved_short_0 = blocks.complex_to_interleaved_short( False) self.blocks_complex_to_interleaved_char_0 = blocks.complex_to_interleaved_char( False) self.blocks_add_const_vxx_0_0 = blocks.add_const_cc(-4 * (1 + 1j)) self.blocks_add_const_vxx_0 = blocks.add_const_cc(64 + 64j) self.analog_const_source_x_0 = analog.sig_source_c( 0, analog.GR_CONST_WAVE, 0, 0, 0) ################################################## # Connections ################################################## self.connect((self.analog_const_source_x_0, 0), (self.blocks_stream_mux_0, 1)) self.connect((self.blocks_add_const_vxx_0, 0), (self.blocks_complex_to_interleaved_char_0, 0)) self.connect((self.blocks_add_const_vxx_0_0, 0), (self.blocks_stream_mux_0, 0)) self.connect((self.blocks_complex_to_interleaved_char_0, 0), (self.blocks_file_sink_0, 0)) self.connect((self.blocks_complex_to_interleaved_char_0, 0), (self.blocks_interleaved_char_to_complex_0, 0)) self.connect((self.blocks_complex_to_interleaved_short_0, 0), (self.blocks_file_sink_0_0, 0)) self.connect((self.blocks_file_source_0, 0), (self.blocks_interleaved_short_to_complex_0, 0)) self.connect((self.blocks_interleaved_char_to_complex_0, 0), (self.interp_fir_filter_xxx_0_0, 0)) self.connect((self.blocks_interleaved_short_to_complex_0, 0), (self.blocks_multiply_const_vxx_1, 0)) self.connect((self.blocks_multiply_const_vxx_0, 0), (self.blocks_complex_to_interleaved_short_0, 0)) self.connect((self.blocks_multiply_const_vxx_0, 0), (self.qtgui_const_sink_x_0, 0)) self.connect((self.blocks_multiply_const_vxx_1, 0), (self.blocks_add_const_vxx_0_0, 0)) self.connect((self.blocks_multiply_const_vxx_2, 0), (self.blocks_add_const_vxx_0, 0)) self.connect((self.blocks_stream_mux_0, 0), (self.blocks_multiply_const_vxx_2, 0)) self.connect((self.interp_fir_filter_xxx_0_0, 0), (self.blocks_multiply_const_vxx_0, 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, debug_id=None, max_bursts=0, verbose=False, file_info=None): 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 scale = 1 elif sample_format == "hackrf": converter = blocks.interleaved_char_to_complex() itemsize = gr.sizeof_char scale = 1 / 128. elif sample_format == "sc16": converter = blocks.interleaved_short_to_complex() itemsize = gr.sizeof_short scale = 1 / 32768. 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(scale) tb.connect(file_source, converter, multi) source = multi 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(file_info) #self._iridium_qpsk_demod = iridium.iridium_qpsk_demod(250000) 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._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) 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')) 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) 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, 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'))
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'))
def __init__(self, satellite='NOAA-XX', gain=35, freq=1698e6, sync_check=False, side="A:0", rate=2e6, frames_file=os.environ['HOME'] + '/data/noaa/frames/NOAA-XX.hrpt', baseband_file=os.environ['HOME'] + '/data/noaa/baseband/NOAA-XX.dat'): grc_wxgui.top_block_gui.__init__(self, title="Enhanced NOAA HRPT Receiver") _icon_path = "/usr/share/icons/hicolor/32x32/apps/gnuradio-grc.png" self.SetIcon(wx.Icon(_icon_path, wx.BITMAP_TYPE_ANY)) ################################################## # Parameters ################################################## self.satellite = satellite self.gain = gain self.freq = freq self.sync_check = sync_check self.side = side self.rate = rate self.frames_file = frames_file self.baseband_file = baseband_file ################################################## # Variables ################################################## self.sym_rate = sym_rate = 600 * 1109 self.samp_rate = samp_rate = rate self.config_filename = config_filename = os.environ[ 'HOME'] + '/.gnuradio/noaa_hrpt.conf' self.sps = sps = samp_rate / sym_rate self._saved_pll_alpha_config = ConfigParser.ConfigParser() self._saved_pll_alpha_config.read(config_filename) try: saved_pll_alpha = self._saved_pll_alpha_config.getfloat( satellite, 'pll_alpha') except: saved_pll_alpha = 0.005 self.saved_pll_alpha = saved_pll_alpha self._saved_clock_alpha_config = ConfigParser.ConfigParser() self._saved_clock_alpha_config.read(config_filename) try: saved_clock_alpha = self._saved_clock_alpha_config.getfloat( satellite, 'clock_alpha') except: saved_clock_alpha = 0.001 self.saved_clock_alpha = saved_clock_alpha self.sync_check_txt = sync_check_txt = sync_check self.side_text = side_text = side self._saved_gain_config = ConfigParser.ConfigParser() self._saved_gain_config.read(config_filename) try: saved_gain = self._saved_gain_config.getfloat(satellite, 'gain') except: saved_gain = gain self.saved_gain = saved_gain self.satellite_text = satellite_text = satellite self.sample_rate_text = sample_rate_text = samp_rate self.rate_tb = rate_tb = rate self.pll_alpha = pll_alpha = saved_pll_alpha self.max_clock_offset = max_clock_offset = 0.1 self.max_carrier_offset = max_carrier_offset = 2 * math.pi * 100e3 / samp_rate self.hs = hs = int(sps / 2.0) self.gain_slider = gain_slider = gain self.freq_tb = freq_tb = freq self.frames_outfile_text = frames_outfile_text = frames_file self.datetime_text = datetime_text = strftime("%A, %B %d %Y %H:%M:%S", localtime()) self.clock_alpha = clock_alpha = saved_clock_alpha ################################################## # Blocks ################################################## _gain_slider_sizer = wx.BoxSizer(wx.VERTICAL) self._gain_slider_text_box = forms.text_box( parent=self.GetWin(), sizer=_gain_slider_sizer, value=self.gain_slider, callback=self.set_gain_slider, label="Gain", converter=forms.int_converter(), proportion=0, ) self._gain_slider_slider = forms.slider( parent=self.GetWin(), sizer=_gain_slider_sizer, value=self.gain_slider, callback=self.set_gain_slider, minimum=0, maximum=100, num_steps=100, style=wx.SL_HORIZONTAL, cast=int, proportion=1, ) self.GridAdd(_gain_slider_sizer, 2, 0, 1, 1) self.displays = self.displays = wx.Notebook(self.GetWin(), style=wx.NB_TOP) self.displays.AddPage(grc_wxgui.Panel(self.displays), "RX NOAA HRPT") self.displays.AddPage(grc_wxgui.Panel(self.displays), "Information") self.Add(self.displays) _clock_alpha_sizer = wx.BoxSizer(wx.VERTICAL) self._clock_alpha_text_box = forms.text_box( parent=self.GetWin(), sizer=_clock_alpha_sizer, value=self.clock_alpha, callback=self.set_clock_alpha, label="Clock alpha", converter=forms.float_converter(), proportion=0, ) self._clock_alpha_slider = forms.slider( parent=self.GetWin(), sizer=_clock_alpha_sizer, value=self.clock_alpha, callback=self.set_clock_alpha, minimum=0.001, maximum=0.1, num_steps=100, style=wx.SL_HORIZONTAL, cast=float, proportion=1, ) self.GridAdd(_clock_alpha_sizer, 2, 2, 1, 1) self.wxgui_fftsink2_0 = fftsink2.fft_sink_c( self.displays.GetPage(0).GetWin(), baseband_freq=0, y_per_div=5, y_divs=10, ref_level=-70, ref_scale=2.0, sample_rate=samp_rate, fft_size=1024, fft_rate=15, average=True, avg_alpha=0.4, title="NOAA HRPT FFT Spectrum", peak_hold=False, ) self.displays.GetPage(0).Add(self.wxgui_fftsink2_0.win) self.uhd_usrp_source_0 = uhd.usrp_source( device_addr="", stream_args=uhd.stream_args( cpu_format="fc32", channels=range(1), ), ) self.uhd_usrp_source_0.set_subdev_spec(side, 0) self.uhd_usrp_source_0.set_samp_rate(samp_rate) self.uhd_usrp_source_0.set_center_freq(freq, 0) self.uhd_usrp_source_0.set_gain(gain_slider, 0) self._sync_check_txt_static_text = forms.static_text( parent=self.GetWin(), value=self.sync_check_txt, callback=self.set_sync_check_txt, label="Continuous sync check", converter=forms.str_converter(), ) self.GridAdd(self._sync_check_txt_static_text, 0, 2, 1, 1) self._side_text_static_text = forms.static_text( parent=self.GetWin(), value=self.side_text, callback=self.set_side_text, label="USRP Side", converter=forms.str_converter(), ) self.GridAdd(self._side_text_static_text, 0, 0, 1, 1) self._satellite_text_static_text = forms.static_text( parent=self.GetWin(), value=self.satellite_text, callback=self.set_satellite_text, label="Satellite", converter=forms.str_converter(), ) self.GridAdd(self._satellite_text_static_text, 0, 1, 1, 1) self._sample_rate_text_static_text = forms.static_text( parent=self.displays.GetPage(1).GetWin(), value=self.sample_rate_text, callback=self.set_sample_rate_text, label="Sample rate", converter=forms.float_converter(), ) self.displays.GetPage(1).GridAdd(self._sample_rate_text_static_text, 3, 0, 1, 1) self._rate_tb_text_box = forms.text_box( parent=self.GetWin(), value=self.rate_tb, callback=self.set_rate_tb, label="Sample rate", converter=forms.float_converter(), ) self.GridAdd(self._rate_tb_text_box, 1, 0, 1, 1) self.poesweather_noaa_hrpt_deframer_0 = poesweather.noaa_hrpt_deframer( False) _pll_alpha_sizer = wx.BoxSizer(wx.VERTICAL) self._pll_alpha_text_box = forms.text_box( parent=self.GetWin(), sizer=_pll_alpha_sizer, value=self.pll_alpha, callback=self.set_pll_alpha, label="PLL Alpha", converter=forms.float_converter(), proportion=0, ) self._pll_alpha_slider = forms.slider( parent=self.GetWin(), sizer=_pll_alpha_sizer, value=self.pll_alpha, callback=self.set_pll_alpha, minimum=0.005, maximum=0.5, num_steps=100, style=wx.SL_HORIZONTAL, cast=float, proportion=1, ) self.GridAdd(_pll_alpha_sizer, 2, 1, 1, 1) self.pll = noaa.hrpt_pll_cf(pll_alpha, pll_alpha**2 / 4.0, max_carrier_offset) self._freq_tb_text_box = forms.text_box( parent=self.GetWin(), value=self.freq_tb, callback=self.set_freq_tb, label="Frequency", converter=forms.float_converter(), ) self.GridAdd(self._freq_tb_text_box, 1, 1, 1, 1) self._frames_outfile_text_static_text = forms.static_text( parent=self.displays.GetPage(1).GetWin(), value=self.frames_outfile_text, callback=self.set_frames_outfile_text, label="Frames filename", converter=forms.str_converter(), ) self.displays.GetPage(1).GridAdd(self._frames_outfile_text_static_text, 4, 0, 1, 1) self.digital_clock_recovery_mm_xx_0 = digital.clock_recovery_mm_ff( sps / 2.0, clock_alpha**2 / 4.0, 0.5, clock_alpha, max_clock_offset) self.digital_binary_slicer_fb_0 = digital.binary_slicer_fb() self._datetime_text_static_text = forms.static_text( parent=self.displays.GetPage(1).GetWin(), value=self.datetime_text, callback=self.set_datetime_text, label="Acquisition start", converter=forms.str_converter(), ) self.displays.GetPage(1).GridAdd(self._datetime_text_static_text, 2, 0, 1, 1) self.blocks_moving_average_xx_0 = blocks.moving_average_ff( hs, 1.0 / hs, 4000) self.blocks_file_sink_1 = blocks.file_sink(gr.sizeof_short * 1, baseband_file) self.blocks_file_sink_1.set_unbuffered(False) self.blocks_file_sink_0 = blocks.file_sink(gr.sizeof_short * 1, frames_file) self.blocks_file_sink_0.set_unbuffered(False) self.blocks_complex_to_interleaved_short_0 = blocks.complex_to_interleaved_short( ) self.analog_agc_xx_0 = analog.agc_cc(1e-5, 1.0, 1.0 / 32768.0, 1.0) ################################################## # Connections ################################################## self.connect((self.analog_agc_xx_0, 0), (self.pll, 0)) self.connect((self.pll, 0), (self.blocks_moving_average_xx_0, 0)) self.connect((self.uhd_usrp_source_0, 0), (self.wxgui_fftsink2_0, 0)) self.connect((self.uhd_usrp_source_0, 0), (self.analog_agc_xx_0, 0)) self.connect((self.blocks_moving_average_xx_0, 0), (self.digital_clock_recovery_mm_xx_0, 0)) self.connect((self.digital_clock_recovery_mm_xx_0, 0), (self.digital_binary_slicer_fb_0, 0)) self.connect((self.poesweather_noaa_hrpt_deframer_0, 0), (self.blocks_file_sink_0, 0)) self.connect((self.digital_binary_slicer_fb_0, 0), (self.poesweather_noaa_hrpt_deframer_0, 0)) self.connect((self.uhd_usrp_source_0, 0), (self.blocks_complex_to_interleaved_short_0, 0)) self.connect((self.blocks_complex_to_interleaved_short_0, 0), (self.blocks_file_sink_1, 0))
def __init__(self, satellite='NOAA-XX', gain=35, freq=1698e6, sync_check=False, side="A:0", rate=2e6, frames_file=os.environ['HOME'] + '/data/noaa/frames/NOAA-XX.hrpt', baseband_file=os.environ['HOME'] + '/data/noaa/baseband/NOAA-XX.dat'): grc_wxgui.top_block_gui.__init__(self, title="Enhanced NOAA HRPT Receiver") _icon_path = "/usr/share/icons/hicolor/32x32/apps/gnuradio-grc.png" self.SetIcon(wx.Icon(_icon_path, wx.BITMAP_TYPE_ANY)) ################################################## # Parameters ################################################## self.satellite = satellite self.gain = gain self.freq = freq self.sync_check = sync_check self.side = side self.rate = rate self.frames_file = frames_file self.baseband_file = baseband_file ################################################## # Variables ################################################## self.sym_rate = sym_rate = 600*1109 self.samp_rate = samp_rate = rate self.config_filename = config_filename = os.environ['HOME']+'/.gnuradio/noaa_hrpt.conf' self.sps = sps = samp_rate/sym_rate self._saved_pll_alpha_config = ConfigParser.ConfigParser() self._saved_pll_alpha_config.read(config_filename) try: saved_pll_alpha = self._saved_pll_alpha_config.getfloat(satellite, 'pll_alpha') except: saved_pll_alpha = 0.005 self.saved_pll_alpha = saved_pll_alpha self._saved_clock_alpha_config = ConfigParser.ConfigParser() self._saved_clock_alpha_config.read(config_filename) try: saved_clock_alpha = self._saved_clock_alpha_config.getfloat(satellite, 'clock_alpha') except: saved_clock_alpha = 0.001 self.saved_clock_alpha = saved_clock_alpha self.sync_check_txt = sync_check_txt = sync_check self.side_text = side_text = side self._saved_gain_config = ConfigParser.ConfigParser() self._saved_gain_config.read(config_filename) try: saved_gain = self._saved_gain_config.getfloat(satellite, 'gain') except: saved_gain = gain self.saved_gain = saved_gain self.satellite_text = satellite_text = satellite self.sample_rate_text = sample_rate_text = samp_rate self.rate_tb = rate_tb = rate self.pll_alpha = pll_alpha = saved_pll_alpha self.max_clock_offset = max_clock_offset = 0.1 self.max_carrier_offset = max_carrier_offset = 2*math.pi*100e3/samp_rate self.hs = hs = int(sps/2.0) self.gain_slider = gain_slider = gain self.freq_tb = freq_tb = freq self.frames_outfile_text = frames_outfile_text = frames_file self.datetime_text = datetime_text = strftime("%A, %B %d %Y %H:%M:%S", localtime()) self.clock_alpha = clock_alpha = saved_clock_alpha ################################################## # Blocks ################################################## _gain_slider_sizer = wx.BoxSizer(wx.VERTICAL) self._gain_slider_text_box = forms.text_box( parent=self.GetWin(), sizer=_gain_slider_sizer, value=self.gain_slider, callback=self.set_gain_slider, label="Gain", converter=forms.int_converter(), proportion=0, ) self._gain_slider_slider = forms.slider( parent=self.GetWin(), sizer=_gain_slider_sizer, value=self.gain_slider, callback=self.set_gain_slider, minimum=0, maximum=100, num_steps=100, style=wx.SL_HORIZONTAL, cast=int, proportion=1, ) self.GridAdd(_gain_slider_sizer, 2, 0, 1, 1) self.displays = self.displays = wx.Notebook(self.GetWin(), style=wx.NB_TOP) self.displays.AddPage(grc_wxgui.Panel(self.displays), "RX NOAA HRPT") self.displays.AddPage(grc_wxgui.Panel(self.displays), "Information") self.Add(self.displays) _clock_alpha_sizer = wx.BoxSizer(wx.VERTICAL) self._clock_alpha_text_box = forms.text_box( parent=self.GetWin(), sizer=_clock_alpha_sizer, value=self.clock_alpha, callback=self.set_clock_alpha, label="Clock alpha", converter=forms.float_converter(), proportion=0, ) self._clock_alpha_slider = forms.slider( parent=self.GetWin(), sizer=_clock_alpha_sizer, value=self.clock_alpha, callback=self.set_clock_alpha, minimum=0.001, maximum=0.1, num_steps=100, style=wx.SL_HORIZONTAL, cast=float, proportion=1, ) self.GridAdd(_clock_alpha_sizer, 2, 2, 1, 1) self.wxgui_fftsink2_0 = fftsink2.fft_sink_c( self.displays.GetPage(0).GetWin(), baseband_freq=0, y_per_div=5, y_divs=10, ref_level=-70, ref_scale=2.0, sample_rate=samp_rate, fft_size=1024, fft_rate=15, average=True, avg_alpha=0.4, title="NOAA HRPT FFT Spectrum", peak_hold=False, ) self.displays.GetPage(0).Add(self.wxgui_fftsink2_0.win) self.uhd_usrp_source_0 = uhd.usrp_source( device_addr="", stream_args=uhd.stream_args( cpu_format="fc32", channels=range(1), ), ) self.uhd_usrp_source_0.set_subdev_spec(side, 0) self.uhd_usrp_source_0.set_samp_rate(samp_rate) self.uhd_usrp_source_0.set_center_freq(freq, 0) self.uhd_usrp_source_0.set_gain(gain_slider, 0) self._sync_check_txt_static_text = forms.static_text( parent=self.GetWin(), value=self.sync_check_txt, callback=self.set_sync_check_txt, label="Continuous sync check", converter=forms.str_converter(), ) self.GridAdd(self._sync_check_txt_static_text, 0, 2, 1, 1) self._side_text_static_text = forms.static_text( parent=self.GetWin(), value=self.side_text, callback=self.set_side_text, label="USRP Side", converter=forms.str_converter(), ) self.GridAdd(self._side_text_static_text, 0, 0, 1, 1) self._satellite_text_static_text = forms.static_text( parent=self.GetWin(), value=self.satellite_text, callback=self.set_satellite_text, label="Satellite", converter=forms.str_converter(), ) self.GridAdd(self._satellite_text_static_text, 0, 1, 1, 1) self._sample_rate_text_static_text = forms.static_text( parent=self.displays.GetPage(1).GetWin(), value=self.sample_rate_text, callback=self.set_sample_rate_text, label="Sample rate", converter=forms.float_converter(), ) self.displays.GetPage(1).GridAdd(self._sample_rate_text_static_text, 3, 0, 1, 1) self._rate_tb_text_box = forms.text_box( parent=self.GetWin(), value=self.rate_tb, callback=self.set_rate_tb, label="Sample rate", converter=forms.float_converter(), ) self.GridAdd(self._rate_tb_text_box, 1, 0, 1, 1) self.poesweather_noaa_hrpt_deframer_0 = poesweather.noaa_hrpt_deframer(False) _pll_alpha_sizer = wx.BoxSizer(wx.VERTICAL) self._pll_alpha_text_box = forms.text_box( parent=self.GetWin(), sizer=_pll_alpha_sizer, value=self.pll_alpha, callback=self.set_pll_alpha, label="PLL Alpha", converter=forms.float_converter(), proportion=0, ) self._pll_alpha_slider = forms.slider( parent=self.GetWin(), sizer=_pll_alpha_sizer, value=self.pll_alpha, callback=self.set_pll_alpha, minimum=0.005, maximum=0.5, num_steps=100, style=wx.SL_HORIZONTAL, cast=float, proportion=1, ) self.GridAdd(_pll_alpha_sizer, 2, 1, 1, 1) self.pll = noaa.hrpt_pll_cf(pll_alpha, pll_alpha**2/4.0, max_carrier_offset) self._freq_tb_text_box = forms.text_box( parent=self.GetWin(), value=self.freq_tb, callback=self.set_freq_tb, label="Frequency", converter=forms.float_converter(), ) self.GridAdd(self._freq_tb_text_box, 1, 1, 1, 1) self._frames_outfile_text_static_text = forms.static_text( parent=self.displays.GetPage(1).GetWin(), value=self.frames_outfile_text, callback=self.set_frames_outfile_text, label="Frames filename", converter=forms.str_converter(), ) self.displays.GetPage(1).GridAdd(self._frames_outfile_text_static_text, 4, 0, 1, 1) self.digital_clock_recovery_mm_xx_0 = digital.clock_recovery_mm_ff(sps/2.0, clock_alpha**2/4.0, 0.5, clock_alpha, max_clock_offset) self.digital_binary_slicer_fb_0 = digital.binary_slicer_fb() self._datetime_text_static_text = forms.static_text( parent=self.displays.GetPage(1).GetWin(), value=self.datetime_text, callback=self.set_datetime_text, label="Acquisition start", converter=forms.str_converter(), ) self.displays.GetPage(1).GridAdd(self._datetime_text_static_text, 2, 0, 1, 1) self.blocks_moving_average_xx_0 = blocks.moving_average_ff(hs, 1.0/hs, 4000) self.blocks_file_sink_1 = blocks.file_sink(gr.sizeof_short*1, baseband_file) self.blocks_file_sink_1.set_unbuffered(False) self.blocks_file_sink_0 = blocks.file_sink(gr.sizeof_short*1, frames_file) self.blocks_file_sink_0.set_unbuffered(False) self.blocks_complex_to_interleaved_short_0 = blocks.complex_to_interleaved_short() self.analog_agc_xx_0 = analog.agc_cc(1e-5, 1.0, 1.0/32768.0, 1.0) ################################################## # Connections ################################################## self.connect((self.analog_agc_xx_0, 0), (self.pll, 0)) self.connect((self.pll, 0), (self.blocks_moving_average_xx_0, 0)) self.connect((self.uhd_usrp_source_0, 0), (self.wxgui_fftsink2_0, 0)) self.connect((self.uhd_usrp_source_0, 0), (self.analog_agc_xx_0, 0)) self.connect((self.blocks_moving_average_xx_0, 0), (self.digital_clock_recovery_mm_xx_0, 0)) self.connect((self.digital_clock_recovery_mm_xx_0, 0), (self.digital_binary_slicer_fb_0, 0)) self.connect((self.poesweather_noaa_hrpt_deframer_0, 0), (self.blocks_file_sink_0, 0)) self.connect((self.digital_binary_slicer_fb_0, 0), (self.poesweather_noaa_hrpt_deframer_0, 0)) self.connect((self.uhd_usrp_source_0, 0), (self.blocks_complex_to_interleaved_short_0, 0)) self.connect((self.blocks_complex_to_interleaved_short_0, 0), (self.blocks_file_sink_1, 0))
def coreTest(self, rx_gain, tx_amp, centre_freq, sample_rate=20e6): """ |<------------ TX CHAIN ---------->| |<----- RX CHAIN ---->| +------+ +------+ +--------+ +--------+ | | | | +---------+ | sig[0] |--->| c2s[0] |--->|ch0 | | ch0|--->| vsnk[0] | +--------+ +--------+ | | | | +---------+ +--------+ +--------+ | | | | +---------+ | sig[1] |--->| c2s[1] |--->|ch1 | | ch1|--->| vsnk[1] | +--------+ +--------+ | | | | +---------+ +--------+ +--------+ | | | | +---------+ | sig[2] |--->| c2s[2] |--->|ch2 | | ch2|--->| vsnk[2] | +--------+ +--------+ | | | | +---------+ +--------+ +--------+ | | | | +---------+ | sig[3] |--->| c2s[3] |--->|ch3 | | ch3|--->| vsnk[3] | +--------+ +--------+ | csnk | | csrc | +---------+ +------+ +------+ """ # Is above 40 MHz, disable Channels C & D if centre_freq > 40e6: self.channels = range(2) tb = gr.top_block() # Variables. wave_freq = 1e6 sc = uhd.stream_cmd_t(uhd.stream_cmd_t.STREAM_MODE_NUM_SAMPS_AND_DONE) sc.num_samps = 64 # Blocks and Connections (TX CHAIN). sigs = [ analog.sig_source_c(sample_rate, analog.GR_SIN_WAVE, wave_freq, tx_amp, 0.0) for channel in self.channels ] c2ss = [ blocks.complex_to_interleaved_short(True) for channel in self.channels ] csnk = crimson_sink_s(self.channels, sample_rate, centre_freq, 0.0) for channel in self.channels: tb.connect(sigs[channel], c2ss[channel]) tb.connect(c2ss[channel], (csnk, channel)) # Blocks and Connections (RX CHAIN). csrc = crimson_source_c(self.channels, sample_rate, centre_freq, rx_gain) vsnk = [blocks.vector_sink_c() for channel in self.channels] for channel in self.channels: tb.connect((csrc, channel), vsnk[channel]) # Reset TX and RX times to be roughly in sync. csnk.set_time_now(uhd.time_spec_t(0.0)) csrc.set_time_now(uhd.time_spec_t(0.0)) # Issue stream command to start RX chain somewhere in the middle of the test. sc.stream_now = False sc.time_spec = uhd.time_spec_t(self.test_time / 2.0) csrc.issue_stream_cmd(sc) # Run the test. tb.start() time.sleep(self.test_time) tb.stop() tb.wait() # Return a vsnk sample for further processing and verification. # vsnk are to be processed in individual unit tests, eg. def test_xyz_t(self): # Read sigproc.py for further information on signal processing and vsnks. return vsnk, csnk, csrc
def __init__(self, datatype='short', radio_id='F5BE35', sat_name='4F3'): gr.top_block.__init__( self, "4F3_F5BE35_20180903_043626.400121_UTC_80M.short") Qt.QWidget.__init__(self) self.setWindowTitle("4F3_F5BE35_20180903_043626.400121_UTC_80M.short") qtgui.util.check_set_qss() try: self.setWindowIcon(Qt.QIcon.fromTheme('gnuradio-grc')) except: pass self.top_scroll_layout = Qt.QVBoxLayout() self.setLayout(self.top_scroll_layout) self.top_scroll = Qt.QScrollArea() self.top_scroll.setFrameStyle(Qt.QFrame.NoFrame) self.top_scroll_layout.addWidget(self.top_scroll) self.top_scroll.setWidgetResizable(True) self.top_widget = Qt.QWidget() self.top_scroll.setWidget(self.top_widget) self.top_layout = Qt.QVBoxLayout(self.top_widget) self.top_grid_layout = Qt.QGridLayout() self.top_layout.addLayout(self.top_grid_layout) self.settings = Qt.QSettings("GNU Radio", "inmarsat_cband_x310_record") self.restoreGeometry(self.settings.value("geometry").toByteArray()) ################################################## # Parameters ################################################## self.datatype = datatype self.radio_id = radio_id self.sat_name = sat_name ################################################## # Variables ################################################## self.ts_str = ts_str = dt.strftime(dt.utcnow(), "%Y%m%d_%H%M%S.%f") + '_UTC' self.samp_rate = samp_rate = 100e6 self.rf_freq = rf_freq = 3627.5e6 self.resamp_rate = resamp_rate = 0.8 self.hs_lo = hs_lo = 5150e6 self.path = path = "/data1/captures/inmarsat_c/" self.if_freq = if_freq = hs_lo - rf_freq self.fn = fn = "{:s}_{:s}_{:s}_{:s}M.{:s}".format( sat_name, radio_id, ts_str, str(int(samp_rate / 1e6 * resamp_rate)), datatype) self.rx_gain = rx_gain = 10 self.if_freq_lbl = if_freq_lbl = if_freq self.fp = fp = "{:s}{:s}".format(path, fn) ################################################## # Blocks ################################################## self._samp_rate_tool_bar = Qt.QToolBar(self) self._samp_rate_tool_bar.addWidget(Qt.QLabel('SAMP_RATE' + ": ")) self._samp_rate_line_edit = Qt.QLineEdit(str(self.samp_rate)) self._samp_rate_tool_bar.addWidget(self._samp_rate_line_edit) self._samp_rate_line_edit.returnPressed.connect( lambda: self.set_samp_rate( eng_notation.str_to_num( str(self._samp_rate_line_edit.text().toAscii())))) self.top_grid_layout.addWidget(self._samp_rate_tool_bar, 9, 0, 1, 1) for r in range(9, 10): self.top_grid_layout.setRowStretch(r, 1) for c in range(0, 1): self.top_grid_layout.setColumnStretch(c, 1) self._rx_gain_tool_bar = Qt.QToolBar(self) self._rx_gain_tool_bar.addWidget(Qt.QLabel('GAIN' + ": ")) self._rx_gain_line_edit = Qt.QLineEdit(str(self.rx_gain)) self._rx_gain_tool_bar.addWidget(self._rx_gain_line_edit) self._rx_gain_line_edit.returnPressed.connect(lambda: self.set_rx_gain( eng_notation.str_to_num( str(self._rx_gain_line_edit.text().toAscii())))) self.top_grid_layout.addWidget(self._rx_gain_tool_bar, 9, 3, 1, 1) for r in range(9, 10): self.top_grid_layout.setRowStretch(r, 1) for c in range(3, 4): self.top_grid_layout.setColumnStretch(c, 1) self._rf_freq_tool_bar = Qt.QToolBar(self) self._rf_freq_tool_bar.addWidget(Qt.QLabel("rf_freq" + ": ")) self._rf_freq_line_edit = Qt.QLineEdit(str(self.rf_freq)) self._rf_freq_tool_bar.addWidget(self._rf_freq_line_edit) self._rf_freq_line_edit.returnPressed.connect(lambda: self.set_rf_freq( eng_notation.str_to_num( str(self._rf_freq_line_edit.text().toAscii())))) self.top_grid_layout.addWidget(self._rf_freq_tool_bar, 10, 0, 1, 1) for r in range(10, 11): self.top_grid_layout.setRowStretch(r, 1) for c in range(0, 1): self.top_grid_layout.setColumnStretch(c, 1) self.uhd_usrp_source_0 = uhd.usrp_source( ",".join(("addr=192.168.40.2", "")), uhd.stream_args( cpu_format="fc32", channels=range(1), ), ) self.uhd_usrp_source_0.set_clock_source('gpsdo', 0) self.uhd_usrp_source_0.set_time_source('gpsdo', 0) self.uhd_usrp_source_0.set_subdev_spec('A:0', 0) self.uhd_usrp_source_0.set_samp_rate(samp_rate) self.uhd_usrp_source_0.set_time_now(uhd.time_spec(time.time()), uhd.ALL_MBOARDS) self.uhd_usrp_source_0.set_center_freq(if_freq, 0) self.uhd_usrp_source_0.set_gain(rx_gain, 0) self.uhd_usrp_source_0.set_antenna('RX2', 0) self.pfb_arb_resampler_xxx_0 = pfb.arb_resampler_ccf(resamp_rate, taps=None, flt_size=64) self.pfb_arb_resampler_xxx_0.declare_sample_delay(0) self._if_freq_lbl_tool_bar = Qt.QToolBar(self) if None: self._if_freq_lbl_formatter = None else: self._if_freq_lbl_formatter = lambda x: eng_notation.num_to_str(x) self._if_freq_lbl_tool_bar.addWidget(Qt.QLabel('IF [MHz]' + ": ")) self._if_freq_lbl_label = Qt.QLabel( str(self._if_freq_lbl_formatter(self.if_freq_lbl))) self._if_freq_lbl_tool_bar.addWidget(self._if_freq_lbl_label) self.top_grid_layout.addWidget(self._if_freq_lbl_tool_bar, 10, 1, 1, 1) for r in range(10, 11): self.top_grid_layout.setRowStretch(r, 1) for c in range(1, 2): self.top_grid_layout.setColumnStretch(c, 1) self.fosphor_glfw_sink_c_0 = fosphor.glfw_sink_c() self.fosphor_glfw_sink_c_0.set_fft_window(window.WIN_BLACKMAN_hARRIS) self.fosphor_glfw_sink_c_0.set_frequency_range(rf_freq, samp_rate * resamp_rate) self.blocks_multiply_const_vxx_0 = blocks.multiply_const_vff((-1, )) self.blocks_interleaved_short_to_complex_0 = blocks.interleaved_short_to_complex( False, True) self.blocks_float_to_complex_0 = blocks.float_to_complex(1) self.blocks_file_sink_0 = blocks.file_sink(gr.sizeof_short * 1, fp, False) self.blocks_file_sink_0.set_unbuffered(False) self.blocks_complex_to_interleaved_short_0 = blocks.complex_to_interleaved_short( False) self.blocks_complex_to_float_0 = blocks.complex_to_float(1) self.analog_agc2_xx_0 = analog.agc2_cc(1e-1, 1e-2, 1.0, 1.0) self.analog_agc2_xx_0.set_max_gain(65536) ################################################## # Connections ################################################## self.connect((self.analog_agc2_xx_0, 0), (self.blocks_complex_to_float_0, 0)) self.connect((self.blocks_complex_to_float_0, 0), (self.blocks_float_to_complex_0, 0)) self.connect((self.blocks_complex_to_float_0, 1), (self.blocks_multiply_const_vxx_0, 0)) self.connect((self.blocks_complex_to_interleaved_short_0, 0), (self.blocks_file_sink_0, 0)) self.connect((self.blocks_complex_to_interleaved_short_0, 0), (self.blocks_interleaved_short_to_complex_0, 0)) self.connect((self.blocks_float_to_complex_0, 0), (self.blocks_complex_to_interleaved_short_0, 0)) self.connect((self.blocks_interleaved_short_to_complex_0, 0), (self.fosphor_glfw_sink_c_0, 0)) self.connect((self.blocks_multiply_const_vxx_0, 0), (self.blocks_float_to_complex_0, 1)) self.connect((self.pfb_arb_resampler_xxx_0, 0), (self.analog_agc2_xx_0, 0)) self.connect((self.uhd_usrp_source_0, 0), (self.pfb_arb_resampler_xxx_0, 0))