Example #1
0
    def __init__(self, angle=0.0):
        gr.hier_block2.__init__(
            self,
            'SimulatedDevice VOR modulator',
            gr.io_signature(1, 1, gr.sizeof_float * 1),
            gr.io_signature(1, 1, gr.sizeof_gr_complex * 1),
        )

        self.__angle = 0.0  # dummy statically visible value will be overwritten

        # TODO: My signal level parameters are probably wrong because this signal doesn't look like a real VOR signal

        vor_30 = analog.sig_source_f(self.__audio_rate, analog.GR_COS_WAVE,
                                     self.__vor_sig_freq, 1, 0)
        vor_add = blocks.add_cc(1)
        vor_audio = blocks.add_ff(1)
        # Audio/AM signal
        self.connect(
            vor_30,
            blocks.multiply_const_ff(0.3),  # M_n
            (vor_audio, 0))
        self.connect(
            self,
            blocks.multiply_const_ff(audio_modulation_index),  # M_i
            (vor_audio, 1))
        # Carrier component
        self.connect(analog.sig_source_c(0, analog.GR_CONST_WAVE, 0, 0, 1),
                     (vor_add, 0))
        # AM component
        self.__delay = blocks.delay(gr.sizeof_gr_complex,
                                    0)  # configured by set_angle
        self.connect(
            vor_audio,
            make_resampler(self.__audio_rate, self.__rf_rate
                           ),  # TODO make a complex version and do this last
            blocks.float_to_complex(1),
            self.__delay,
            (vor_add, 1))
        # FM component
        vor_fm_mult = blocks.multiply_cc(1)
        self.connect(  # carrier generation
            analog.sig_source_f(self.__rf_rate,
                                analog.GR_COS_WAVE, fm_subcarrier, 1, 0),
            blocks.float_to_complex(1), (vor_fm_mult, 1))
        self.connect(  # modulation
            vor_30,
            make_resampler(self.__audio_rate, self.__rf_rate),
            analog.frequency_modulator_fc(2 * math.pi * fm_deviation /
                                          self.__rf_rate),
            blocks.multiply_const_cc(0.3),  # M_d
            vor_fm_mult,
            (vor_add, 2))
        self.connect(vor_add, self)

        # calculate and initialize delay
        self.set_angle(angle)
Example #2
0
 def __init__(self, angle=0.0):
     gr.hier_block2.__init__(
         self, 'SimulatedDevice VOR modulator',
         gr.io_signature(1, 1, gr.sizeof_float * 1),
         gr.io_signature(1, 1, gr.sizeof_gr_complex * 1),
     )
     
     self.__angle = 0.0  # dummy statically visible value will be overwritten
     
     # TODO: My signal level parameters are probably wrong because this signal doesn't look like a real VOR signal
     
     vor_30 = analog.sig_source_f(self.__audio_rate, analog.GR_COS_WAVE, self.__vor_sig_freq, 1, 0)
     vor_add = blocks.add_cc(1)
     vor_audio = blocks.add_ff(1)
     # Audio/AM signal
     self.connect(
         vor_30,
         blocks.multiply_const_ff(0.3),  # M_n
         (vor_audio, 0))
     self.connect(
         self,
         blocks.multiply_const_ff(audio_modulation_index),  # M_i
         (vor_audio, 1))
     # Carrier component
     self.connect(
         analog.sig_source_c(0, analog.GR_CONST_WAVE, 0, 0, 1),
         (vor_add, 0))
     # AM component
     self.__delay = blocks.delay(gr.sizeof_gr_complex, 0)  # configured by set_angle
     self.connect(
         vor_audio,
         make_resampler(self.__audio_rate, self.__rf_rate),  # TODO make a complex version and do this last
         blocks.float_to_complex(1),
         self.__delay,
         (vor_add, 1))
     # FM component
     vor_fm_mult = blocks.multiply_cc(1)
     self.connect(  # carrier generation
         analog.sig_source_f(self.__rf_rate, analog.GR_COS_WAVE, fm_subcarrier, 1, 0), 
         blocks.float_to_complex(1),
         (vor_fm_mult, 1))
     self.connect(  # modulation
         vor_30,
         make_resampler(self.__audio_rate, self.__rf_rate),
         analog.frequency_modulator_fc(2 * math.pi * fm_deviation / self.__rf_rate),
         blocks.multiply_const_cc(0.3),  # M_d
         vor_fm_mult,
         (vor_add, 2))
     self.connect(
         vor_add,
         self)
     
     # calculate and initialize delay
     self.set_angle(angle)
Example #3
0
	def __init__(self, **kwargs):
		demod_rate = 48000
		
		SimpleAudioDemodulator.__init__(self, demod_rate=demod_rate, band_filter=5000, band_filter_transition=5000, **kwargs)
	
		input_rate = self.input_rate
		audio_rate = self.audio_rate
		
		inherent_gain = 0.5  # fudge factor so that our output is similar level to narrow FM
		self.agc_block = analog.feedforward_agc_cc(int(.02 * demod_rate), inherent_gain)
		self.demod_block = blocks.complex_to_mag(1)
		self.resampler_block = make_resampler(demod_rate, audio_rate)
		
		# assuming below 40Hz is not of interest
		dc_blocker = grfilter.dc_blocker_ff(audio_rate // 40, False)
		
		self.connect(
			self,
			self.band_filter_block,
			self.rf_squelch_block,
			self.agc_block,
			self.demod_block,
			dc_blocker,
			self.resampler_block)
		self.connect(self.band_filter_block, self.rf_probe_block)
		self.connect_audio_output(self.resampler_block, self.resampler_block)
Example #4
0
    def __init__(self,
                 mode,
                 input_rate=0,
                 aprs_information=None,
                 context=None):
        assert input_rate > 0
        gr.hier_block2.__init__(
            self,
            str(mode) + ' (FM + Multimon-NG) demodulator',
            gr.io_signature(1, 1, gr.sizeof_gr_complex * 1),
            gr.io_signature(1, 1, gr.sizeof_float * 1),
        )
        self.mode = mode
        self.input_rate = input_rate

        # FM demod
        # TODO: Retry telling the NFMDemodulator to have its output rate be pipe_rate instead of using a resampler. Something went wrong when trying that before.
        self.fm_demod = NFMDemodulator(
            mode='NFM',
            input_rate=input_rate,
            no_audio_filter=True,  # don't remove CTCSS tone
            tau=None)  # no deemphasis
        assert self.fm_demod.get_output_type().get_kind() == 'MONO'
        fm_audio_rate = self.fm_demod.get_output_type().get_sample_rate()

        # Subprocess
        self.mm_demod = APRSDemodulator(aprs_information=aprs_information)
        mm_audio_rate = self.mm_demod.get_input_type().get_sample_rate()

        # Output
        self.connect(self, self.fm_demod,
                     make_resampler(fm_audio_rate, mm_audio_rate),
                     self.mm_demod, self)
Example #5
0
    def __init__(self, mode, input_rate=0, audio_rate=0, context=None):
        assert input_rate > 0
        gr.hier_block2.__init__(
            self,
            str(mode) + " (Multimon-NG) demodulator",
            gr.io_signature(1, 1, gr.sizeof_gr_complex * 1),
            # TODO: Add generic support for demodulators with no audio output
            gr.io_signature(2, 2, gr.sizeof_float * 1),
        )
        self.mode = mode
        self.input_rate = input_rate

        # FM demod
        self.fm_demod = NFMDemodulator(
            mode="NFM", input_rate=input_rate, audio_rate=pipe_rate, tau=None
        )  # no deemphasis

        # Subprocess
        self.process = SubprocessSink(
            args=["multimon-ng", "-t", "raw", "-a", "AFSK1200", "-A", "-v", "10", "-"],
            # args=['python', '../play16bit.py'],
            itemsize=gr.sizeof_short,
        )

        # Output
        converter = blocks.float_to_short(vlen=1, scale=int_scale)
        self.connect(self, self.fm_demod, converter, self.process)
        # Dummy sink for useless stereo output of demod
        self.connect((self.fm_demod, 1), blocks.null_sink(gr.sizeof_float))
        # Audio copy output
        resampler = make_resampler(pipe_rate, audio_rate)
        self.connect(converter, blocks.short_to_float(vlen=1, scale=int_scale), resampler)
        # self.connect(self.fm_demod, resampler)
        self.connect(resampler, (self, 0))
        self.connect(resampler, (self, 1))
Example #6
0
 def __init__(self, modulator, audio_rate, rf_rate, freq):
     modulator = IModulator(modulator)
     
     gr.hier_block2.__init__(
         self, 'SimulatedChannel',
         gr.io_signature(1, 1, gr.sizeof_float * 1),
         gr.io_signature(1, 1, gr.sizeof_gr_complex * 1),
     )
     
     self.__freq = freq
     self.__rf_rate = rf_rate
     
     self.modulator = modulator  # exported
     
     modulator_input_type = modulator.get_input_type()
     if modulator_input_type.get_kind() == 'MONO':
         audio_resampler = make_resampler(audio_rate, modulator_input_type.get_sample_rate())
         self.connect(self, audio_resampler, modulator)
     elif modulator_input_type.get_kind() == 'NONE':
         self.connect(self, blocks.null_sink(gr.sizeof_float))
     else:
         raise Exception('don\'t know how to supply input of type %s' % modulator_input_type)
     
     rf_resampler = rational_resampler.rational_resampler_ccf(
         interpolation=int(rf_rate),
         decimation=int(modulator.get_output_type().get_sample_rate()))
     self.__rotator = blocks.rotator_cc(rotator_inc(rate=rf_rate, shift=freq))
     self.__mult = blocks.multiply_const_cc(dB(-10))
     self.connect(modulator, rf_resampler, self.__rotator, self.__mult, self)
Example #7
0
 def __init__(self, mode, input_rate=0, aprs_information=None, context=None):
     assert input_rate > 0
     gr.hier_block2.__init__(
         self, str(mode) + ' (FM + Multimon-NG) demodulator',
         gr.io_signature(1, 1, gr.sizeof_gr_complex * 1),
         gr.io_signature(1, 1, gr.sizeof_float * 1),
     )
     self.mode = mode
     self.input_rate = input_rate
     
     # FM demod
     # TODO: Retry telling the NFMDemodulator to have its output rate be pipe_rate instead of using a resampler. Something went wrong when trying that before.
     self.fm_demod = NFMDemodulator(
         mode='NFM',
         input_rate=input_rate,
         no_audio_filter=True,  # don't remove CTCSS tone
         tau=None)  # no deemphasis
     assert self.fm_demod.get_output_type().get_kind() == 'MONO'
     fm_audio_rate = self.fm_demod.get_output_type().get_sample_rate()
     
     # Subprocess
     self.mm_demod = APRSDemodulator(
         aprs_information=aprs_information)
     mm_audio_rate = self.mm_demod.get_input_type().get_sample_rate()
     
     # Output
     self.connect(
         self,
         self.fm_demod,
         make_resampler(fm_audio_rate, mm_audio_rate),
         self.mm_demod,
         self)
Example #8
0
	def _make_resampler(self, input_port, input_rate):
		taps = design_lofi_audio_filter(input_rate, self.__no_audio_filter)
		if self.audio_rate == input_rate:
			filt = grfilter.fir_filter_fff(1, taps)
			self.connect(input_port, filt)
			return filt
		elif input_rate % self.audio_rate == 0:
			filt = grfilter.fir_filter_fff(input_rate // self.audio_rate, taps)
			self.connect(input_port, filt)
			return filt
		else:
			# TODO: use combined filter and resampler (need to move filter design)
			filt = grfilter.fir_filter_fff(1, taps)
			resampler = make_resampler(input_rate, self.audio_rate)
			self.connect(input_port, filt, resampler)
			return resampler
Example #9
0
 def _make_resampler(self, input_port, input_rate):
     taps = design_lofi_audio_filter(input_rate, self.__no_audio_filter)
     if self.audio_rate == input_rate:
         filt = grfilter.fir_filter_fff(1, taps)
         self.connect(input_port, filt)
         return filt
     elif input_rate % self.audio_rate == 0:
         filt = grfilter.fir_filter_fff(input_rate // self.audio_rate, taps)
         self.connect(input_port, filt)
         return filt
     else:
         # TODO: use combined filter and resampler (need to move filter design)
         filt = grfilter.fir_filter_fff(1, taps)
         resampler = make_resampler(input_rate, self.audio_rate)
         self.connect(input_port, filt, resampler)
         return resampler
Example #10
0
	def __init__(self, modulator, audio_rate, rf_rate, freq):
		modulator = IModulator(modulator)
		
		gr.hier_block2.__init__(
			self, 'SimulatedChannel',
			gr.io_signature(1, 1, gr.sizeof_float * 1),
			gr.io_signature(1, 1, gr.sizeof_gr_complex * 1),
		)
		
		self.__freq = freq
		self.__rf_rate = rf_rate
		
		self.modulator = modulator  # exported
		
		audio_resampler = make_resampler(audio_rate, modulator.get_input_type().get_sample_rate())
		rf_resampler = rational_resampler.rational_resampler_ccf(
			interpolation=int(rf_rate),
			decimation=int(modulator.get_output_type().get_sample_rate()))
		self.__rotator = blocks.rotator_cc(rotator_inc(rate=rf_rate, shift=freq))
		self.__mult = blocks.multiply_const_cc(10.0 ** -1)
		self.connect(self, audio_resampler, modulator, rf_resampler, self.__rotator, self.__mult, self)
Example #11
0
	def _make_resampler(self):
		return make_resampler(self.post_demod_rate, self.audio_rate)
Example #12
0
	def _do_connect(self):
		"""Do all reconfiguration operations in the proper order."""
		rate_changed = False
		if self.source is not self._sources[self.source_name]:
			print 'Switching source'
			self.__needs_reconnect = True
			
			def tune_hook():
				reactor.callLater(self.source.get_tune_delay(), tune_hook_actual)
			def tune_hook_actual():
				if self.source is not this_source:
					return
				freq = this_source.get_freq()
				self.input_freq = freq
				for key, receiver in self._receivers.iteritems():
					receiver.set_input_center_freq(freq)
					self._update_receiver_validity(key)

			this_source = self._sources[self.source_name]
			this_source.set_tune_hook(tune_hook)
			self.source = this_source
			this_rate = this_source.get_sample_rate()
			rate_changed = self.input_rate != this_rate
			self.input_rate = this_rate
			self.input_freq = this_source.get_freq()
		
		if self.__needs_spectrum or rate_changed:
			print 'Rebuilding spectrum FFT'
			self.__needs_spectrum = False
			self.__needs_reconnect = True
			
			self.spectrum_queue = gr.msg_queue(limit=10)
			self.spectrum_sink = blocks.message_sink(
				self.spectrum_resolution * gr.sizeof_float,
				self.spectrum_queue,
				True) # dont_block
			self.spectrum_fft_block = gnuradio.fft.logpwrfft.logpwrfft_c(
				sample_rate=self.input_rate,
				fft_size=self.spectrum_resolution,
				ref_scale=2,
				frame_rate=self.spectrum_rate,
				avg_alpha=1.0,
				average=False,
			)
			# adjust units so displayed level is independent of resolution (log power per bandwidth rather than per bin)
			# TODO work out and document exactly what units we're using
			self.spectrum_rescale_block = blocks.add_const_vff(
				[10*math.log10(self.spectrum_resolution)] * self.spectrum_resolution)
		
		if rate_changed:
			print 'Changing sample rate'
			for receiver in self._receivers.itervalues():
				receiver.set_input_rate(self.input_rate)

		if self.__needs_reconnect:
			print 'Reconnecting'
			self.__needs_reconnect = False
			
			self._recursive_lock()
			self.disconnect_all()


			# recreated each time because reusing an add_ff w/ different
			# input counts fails; TODO: report/fix bug
			audio_sum_l = blocks.add_ff()
			audio_sum_r = blocks.add_ff()
			
			self.connect(
				self.source,
				self.spectrum_fft_block,
				self.spectrum_rescale_block,
				self.spectrum_sink)
			
			audio_sum_index = 0
			for key, receiver in self._receivers.iteritems():
				self._receiver_valid[key] = receiver.get_is_valid()
				if self._receiver_valid[key]:
					if audio_sum_index >= 6:
						# Sanity-check to avoid burning arbitrary resources
						# TODO: less arbitrary constant; communicate this restriction to client
						print 'Refusing to connect more than 6 receivers'
						break
					self.connect(self.source, receiver)
					self.connect((receiver, 0), (audio_sum_l, audio_sum_index))
					self.connect((receiver, 1), (audio_sum_r, audio_sum_index))
					audio_sum_index += 1
			
			if audio_sum_index > 0:
				# connect audio output only if there is at least one input
				if len(self.audio_queue_sinks) > 0:
					used_resamplers = set()
					for (queue_rate, interleaver, sink) in self.audio_queue_sinks.itervalues():
						if queue_rate == self.audio_rate:
							self.connect(audio_sum_l, (interleaver, 0))
							self.connect(audio_sum_r, (interleaver, 1))
						else:
							if queue_rate not in self.audio_resampler_cache:
								# Moderately expensive due to the internals using optfir
								print 'Constructing resampler for audio rate', queue_rate
								self.audio_resampler_cache[queue_rate] = (
									make_resampler(self.audio_rate, queue_rate),
									make_resampler(self.audio_rate, queue_rate)
								)
							resamplers = self.audio_resampler_cache[queue_rate]
							used_resamplers.add(resamplers)
							self.connect(resamplers[0], (interleaver, 0))
							self.connect(resamplers[1], (interleaver, 1))
						self.connect(interleaver, sink)
					for resamplers in used_resamplers:
						self.connect(audio_sum_l, resamplers[0])
						self.connect(audio_sum_r, resamplers[1])
				else:
					# no stream sinks, gnuradio requires a dummy sink
					self.connect(audio_sum_l, blocks.null_sink(gr.sizeof_float))
					self.connect(audio_sum_r, blocks.null_sink(gr.sizeof_float))
		
			self._recursive_unlock()
			print 'Done reconnecting'
Example #13
0
    def connect(self, inputs, outputs):
        '''
        Make all new connections (graph.disconnect_all() must have been done) between inputs and outputs.
        
        inputs and outputs must be iterables of (sample_rate, block) tuples.
        '''
        inputs = list(inputs)
        outputs = list(outputs)

        # Determine bus rate.
        # The bus obviously does not need to be higher than the rate of any bus input, because that would be extraneous data. It also does not need to be higher than the rate of any bus output, because no output has use for the information.
        if len(inputs) > 0 and len(outputs) > 0:
            max_in_rate = max((rate for rate, _ in inputs))
            max_out_rate = max((rate for rate, _ in outputs))
            new_bus_rate = min(max_out_rate, max_in_rate)
            if new_bus_rate != self.__bus_rate:
                self.__bus_rate = new_bus_rate
                self.__resampler_cache.clear()

        # recreated each time because reusing an add_ff w/ different
        # input counts fails; TODO: report/fix bug
        bus_sums = [blocks.add_ff() for _ in self.__channels]

        in_index = 0
        for in_rate, in_block in inputs:
            if in_rate == self.__bus_rate:
                for ch in self.__channels:
                    self.__graph.connect((in_block, ch),
                                         (bus_sums[ch], in_index))
            else:
                for ch in self.__channels:
                    self.__graph.connect(
                        (in_block, ch),
                        # TODO pool these resamplers
                        make_resampler(in_rate, self.__bus_rate),
                        (bus_sums[ch], in_index))
            in_index += 1

        if in_index > 0:
            # connect output only if there is at least one input
            if len(outputs) > 0:
                used_resamplers = set()
                for out_rate, out_block in outputs:
                    if out_rate == self.__bus_rate:
                        for ch in self.__channels:
                            self.__graph.connect(bus_sums[ch], (out_block, ch))
                    else:
                        if out_rate not in self.__resampler_cache:
                            # Moderately expensive due to the internals using optfir
                            log.msg(
                                'Flow graph: Constructing resampler for audio rate %i'
                                % out_rate)
                            self.__resampler_cache[out_rate] = tuple(
                                make_resampler(self.__bus_rate, out_rate)
                                for _ in self.__channels)
                        resamplers = self.__resampler_cache[out_rate]
                        used_resamplers.add(resamplers)
                        for ch in self.__channels:
                            self.__graph.connect(resamplers[ch],
                                                 (out_block, ch))
                for resamplers in used_resamplers:
                    for ch in self.__channels:
                        self.__graph.connect(bus_sums[ch], resamplers[ch])
            else:
                # gnuradio requires at least one connected output
                for ch in self.__channels:
                    self.__graph.connect(bus_sums[ch],
                                         blocks.null_sink(gr.sizeof_float))
Example #14
0
	def _do_connect(self):
		"""Do all reconfiguration operations in the proper order."""
		rate_changed = False
		if self.source is not self._sources[self.source_name]:
			log.msg('Flow graph: Switching RF source')
			self.__needs_reconnect = True
			
			def tune_hook():
				reactor.callLater(self.source.get_tune_delay(), tune_hook_actual)
			
			def tune_hook_actual():
				if self.source is not this_source:
					return
				freq = this_source.get_freq()
				self.input_freq = freq
				self.monitor.set_input_center_freq(freq)
				for key, receiver in self._receivers.iteritems():
					receiver.set_input_center_freq(freq)
					self._update_receiver_validity(key)
					# TODO: If multiple receivers change validity we'll do redundant reconnects in this loop; avoid that.

			this_source = self._sources[self.source_name]
			this_source.set_tune_hook(tune_hook)
			self.source = this_source
			this_rate = this_source.get_sample_rate()
			rate_changed = self.input_rate != this_rate
			self.input_rate = this_rate
			self.input_freq = this_source.get_freq()
			for key, receiver in self._receivers.iteritems():
				receiver.set_input_center_freq(self.input_freq)
		
		if rate_changed:
			log.msg('Flow graph: Changing sample rates')
			self.monitor.set_sample_rate(self.input_rate)
			for receiver in self._receivers.itervalues():
				receiver.set_input_rate(self.input_rate)

		if self.__needs_reconnect:
			log.msg('Flow graph: Rebuilding connections')
			self.__needs_reconnect = False
			
			self._recursive_lock()
			self.disconnect_all()
			
			self.connect(
				self.source,
				self.monitor)
			
			# recreated each time because reusing an add_ff w/ different
			# input counts fails; TODO: report/fix bug
			audio_sum_l = blocks.add_ff()
			audio_sum_r = blocks.add_ff()
			
			audio_sum_index = 0
			for key, receiver in self._receivers.iteritems():
				self._receiver_valid[key] = receiver.get_is_valid()
				if self._receiver_valid[key]:
					if audio_sum_index >= 6:
						# Sanity-check to avoid burning arbitrary resources
						# TODO: less arbitrary constant; communicate this restriction to client
						log.err('Flow graph: Refusing to connect more than 6 receivers')
						break
					self.connect(self.source, receiver)
					self.connect((receiver, 0), (audio_sum_l, audio_sum_index))
					self.connect((receiver, 1), (audio_sum_r, audio_sum_index))
					audio_sum_index += 1
			
			if audio_sum_index > 0:
				# connect audio output only if there is at least one input
				if len(self.audio_queue_sinks) > 0:
					used_resamplers = set()
					for (queue_rate, interleaver, sink) in self.audio_queue_sinks.itervalues():
						if queue_rate == self.audio_rate:
							self.connect(audio_sum_l, (interleaver, 0))
							self.connect(audio_sum_r, (interleaver, 1))
						else:
							if queue_rate not in self.audio_resampler_cache:
								# Moderately expensive due to the internals using optfir
								log.msg('Flow graph: Constructing resampler for audio rate %i' % queue_rate)
								self.audio_resampler_cache[queue_rate] = (
									make_resampler(self.audio_rate, queue_rate),
									make_resampler(self.audio_rate, queue_rate)
								)
							resamplers = self.audio_resampler_cache[queue_rate]
							used_resamplers.add(resamplers)
							self.connect(resamplers[0], (interleaver, 0))
							self.connect(resamplers[1], (interleaver, 1))
						self.connect(interleaver, sink)
					for resamplers in used_resamplers:
						self.connect(audio_sum_l, resamplers[0])
						self.connect(audio_sum_r, resamplers[1])
				else:
					# no stream sinks, gnuradio requires a dummy sink
					self.connect(audio_sum_l, blocks.null_sink(gr.sizeof_float))
					self.connect(audio_sum_r, blocks.null_sink(gr.sizeof_float))
		
			self._recursive_unlock()
			log.msg('Flow graph: ...done reconnecting.')
Example #15
0
    def __init__(self, mode="VOR", zero_point=59, **kwargs):
        self.channel_rate = channel_rate = 40000
        internal_audio_rate = 20000  # TODO over spec'd
        self.zero_point = zero_point

        transition = 5000
        SimpleAudioDemodulator.__init__(
            self,
            mode=mode,
            demod_rate=channel_rate,
            band_filter=fm_subcarrier * 1.25 + fm_deviation + transition / 2,
            band_filter_transition=transition,
            **kwargs
        )

        self.dir_rate = dir_rate = 10

        if internal_audio_rate % dir_rate != 0:
            raise ValueError(
                "Audio rate %s is not a multiple of direction-finding rate %s" % (internal_audio_rate, dir_rate)
            )
        self.dir_scale = dir_scale = internal_audio_rate // dir_rate
        self.audio_scale = audio_scale = channel_rate // internal_audio_rate

        self.zeroer = blocks.add_const_vff((zero_point * (math.pi / 180),))

        self.dir_vector_filter = grfilter.fir_filter_ccf(
            1, firdes.low_pass(1, dir_rate, 1, 2, firdes.WIN_HAMMING, 6.76)
        )
        self.am_channel_filter_block = grfilter.fir_filter_ccf(
            1, firdes.low_pass(1, channel_rate, 5000, 5000, firdes.WIN_HAMMING, 6.76)
        )
        self.goertzel_fm = fft.goertzel_fc(channel_rate, dir_scale * audio_scale, 30)
        self.goertzel_am = fft.goertzel_fc(internal_audio_rate, dir_scale, 30)
        self.fm_channel_filter_block = grfilter.freq_xlating_fir_filter_ccc(
            1,
            (firdes.low_pass(1.0, channel_rate, fm_subcarrier / 2, fm_subcarrier / 2, firdes.WIN_HAMMING)),
            fm_subcarrier,
            channel_rate,
        )
        self.multiply_conjugate_block = blocks.multiply_conjugate_cc(1)
        self.complex_to_arg_block = blocks.complex_to_arg(1)
        self.am_agc_block = analog.feedforward_agc_cc(1024, 1.0)
        self.am_demod_block = analog.am_demod_cf(
            channel_rate=channel_rate, audio_decim=audio_scale, audio_pass=5000, audio_stop=5500
        )
        self.fm_demod_block = analog.quadrature_demod_cf(1)
        self.phase_agc_fm = analog.agc2_cc(1e-1, 1e-2, 1.0, 1.0)
        self.phase_agc_am = analog.agc2_cc(1e-1, 1e-2, 1.0, 1.0)

        self.probe = blocks.probe_signal_f()

        self.audio_filter_block = make_lofi_audio_filter(internal_audio_rate)
        self.resampler_block = make_resampler(internal_audio_rate, self.audio_rate)

        ##################################################
        # Connections
        ##################################################
        # Input
        self.connect(self, self.band_filter_block)
        # AM chain
        self.connect(self.band_filter_block, self.am_channel_filter_block, self.am_agc_block, self.am_demod_block)
        # AM audio
        self.connect(
            self.am_demod_block,
            blocks.multiply_const_ff(1.0 / audio_modulation_index * 0.5),
            self.audio_filter_block,
            self.resampler_block,
        )
        self.connect_audio_output(self.resampler_block, self.resampler_block)

        # AM phase
        self.connect(self.am_demod_block, self.goertzel_am, self.phase_agc_am, (self.multiply_conjugate_block, 0))
        # FM phase
        self.connect(
            self.band_filter_block,
            self.fm_channel_filter_block,
            self.fm_demod_block,
            self.goertzel_fm,
            self.phase_agc_fm,
            (self.multiply_conjugate_block, 1),
        )
        # Phase comparison and output
        self.connect(
            self.multiply_conjugate_block,
            self.dir_vector_filter,
            self.complex_to_arg_block,
            blocks.multiply_const_ff(-1),  # opposite angle conventions
            self.zeroer,
            self.probe,
        )
Example #16
0
	def _do_connect(self):
		"""Do all reconfiguration operations in the proper order."""
		rate_changed = False
		if self.source is not self._sources[self.source_name]:
			log.msg('Flow graph: Switching RF source')
			self.__needs_reconnect = True

			this_source = self._sources[self.source_name]
			
			def update_input_freqs():
				freq = this_source.get_freq()
				self.input_freq = freq
				self.monitor.set_input_center_freq(freq)
				for receiver in self._receivers.itervalues():
					receiver.set_input_center_freq(freq)
			
			def tune_hook():
				# Note that in addition to the flow graph delay, the callLater is also needed in order to ensure we don't do our reconfiguration in the middle of the source's own workings.
				reactor.callLater(self.__rx_driver.get_tune_delay(), tune_hook_actual)
			
			def tune_hook_actual():
				if self.source is not this_source:
					return
				update_input_freqs()
				for key in self._receivers:
					self._update_receiver_validity(key)
					# TODO: If multiple receivers change validity we'll do redundant reconnects in this loop; avoid that.
			
			if self.__source_tune_subscription is not None:
				self.__source_tune_subscription.unsubscribe()
			self.__source_tune_subscription = this_source.state()['freq'].subscribe(tune_hook)
			
			self.source = this_source
			self.__rx_driver = this_source.get_rx_driver()
			source_signal_type = self.__rx_driver.get_output_type()
			this_rate = source_signal_type.get_sample_rate()
			rate_changed = self.input_rate != this_rate
			self.input_rate = this_rate
			self.monitor.set_signal_type(source_signal_type)
			self.__clip_probe.set_window_and_reconnect(0.5 * this_rate)
			update_input_freqs()
		
		if rate_changed:
			log.msg('Flow graph: Changing sample rates')
			for receiver in self._receivers.itervalues():
				receiver.set_input_rate(self.input_rate)

		if self.__needs_reconnect:
			log.msg('Flow graph: Rebuilding connections')
			self.__needs_reconnect = False
			
			self._recursive_lock()
			self.disconnect_all()
			
			self.connect(
				self.__rx_driver,
				self.monitor)
			self.connect(
				self.__rx_driver,
				self.__clip_probe)
			
			# Determine audio bus rate.
			# The bus obviously does not need to be higher than the rate of any receiver, because that would be extraneous data. It also does not need to be higher than the rate of any queue, because no queue has use for the information.
			if len(self._receivers) > 0 and len(self.audio_queue_sinks) > 0:
				max_out_rate = max((receiver.get_output_type().get_sample_rate() for receiver in self._receivers.itervalues()))
				max_in_rate = max((queue_rate for (queue_rate, sink) in self.audio_queue_sinks.itervalues()))
				new_bus_rate = min(max_out_rate, max_in_rate)
				if new_bus_rate != self.__audio_bus_rate:
					self.__audio_bus_rate = new_bus_rate
					self.audio_resampler_cache.clear()
			
			# recreated each time because reusing an add_ff w/ different
			# input counts fails; TODO: report/fix bug
			audio_sums = [blocks.add_ff() for _ in xrange(self.__audio_channels)]
			
			audio_sum_index = 0
			for key, receiver in self._receivers.iteritems():
				self._receiver_valid[key] = receiver.get_is_valid()
				if self._receiver_valid[key]:
					if audio_sum_index >= 6:
						# Sanity-check to avoid burning arbitrary resources
						# TODO: less arbitrary constant; communicate this restriction to client
						log.err('Flow graph: Refusing to connect more than 6 receivers')
						break
					self.connect(self.__rx_driver, receiver)
					receiver_rate = receiver.get_output_type().get_sample_rate()
					if receiver_rate == self.__audio_bus_rate:
						for ch in xrange(self.__audio_channels):
							self.connect(
								(receiver, ch),
								(audio_sums[ch], audio_sum_index))
					else:
						for ch in xrange(self.__audio_channels):
							self.connect(
								(receiver, ch),
								# TODO pool these resamplers
								make_resampler(receiver_rate, self.__audio_bus_rate),
								(audio_sums[ch], audio_sum_index))
					audio_sum_index += 1
			
			if audio_sum_index > 0:
				# connect audio output only if there is at least one input
				if len(self.audio_queue_sinks) > 0:
					used_resamplers = set()
					for (queue_rate, sink) in self.audio_queue_sinks.itervalues():
						if queue_rate == self.__audio_bus_rate:
							for ch in xrange(self.__audio_channels):
								self.connect(audio_sums[ch], (sink, ch))
						else:
							if queue_rate not in self.audio_resampler_cache:
								# Moderately expensive due to the internals using optfir
								log.msg('Flow graph: Constructing resampler for audio rate %i' % queue_rate)
								self.audio_resampler_cache[queue_rate] = tuple(
									make_resampler(self.__audio_bus_rate, queue_rate)
									for _ in xrange(self.__audio_channels))
							resamplers = self.audio_resampler_cache[queue_rate]
							used_resamplers.add(resamplers)
							for ch in xrange(self.__audio_channels):
								self.connect(resamplers[ch], (sink, ch))
					for resamplers in used_resamplers:
						for ch in xrange(self.__audio_channels):
							self.connect(audio_sums[ch], resamplers[ch])
				else:
					# no stream sinks, gnuradio requires a dummy sink
					for ch in xrange(self.__audio_channels):
						self.connect(audio_sums[ch], blocks.null_sink(gr.sizeof_float))
		
			self._recursive_unlock()
			log.msg('Flow graph: ...done reconnecting.')
Example #17
0
 def connect(self, inputs, outputs):
     '''
     Make all new connections (graph.disconnect_all() must have been done) between inputs and outputs.
     
     inputs and outputs must be iterables of (sample_rate, block) tuples.
     '''
     inputs = list(inputs)
     outputs = list(outputs)
     
     # Determine bus rate.
     # The bus obviously does not need to be higher than the rate of any bus input, because that would be extraneous data. It also does not need to be higher than the rate of any bus output, because no output has use for the information.
     max_in_rate = max((rate for rate, _ in inputs)) if len(inputs) > 0 else 0.0
     max_out_rate = max((rate for rate, _ in outputs)) if len(outputs) > 0 else 0.0
     new_bus_rate = min(max_out_rate, max_in_rate)
     if new_bus_rate == 0.0:
         # There are either no inputs or no outputs. Use the other side's rate so we have a well-defined value.
         new_bus_rate = max(max_out_rate, max_in_rate)
     if new_bus_rate == 0.0:
         # There are both no inputs and no outputs. No point in not keeping the old rate (and its resampler cache).
         new_bus_rate = self.__bus_rate
     elif new_bus_rate != self.__bus_rate:
         self.__bus_rate = new_bus_rate
         self.__resampler_cache.clear()
     
     # recreated each time because reusing an add_ff w/ different
     # input counts fails; TODO: report/fix bug
     bus_sums = [blocks.add_ff() for _ in self.__channels]
     
     in_index = 0
     for in_rate, in_block in inputs:
         if in_rate == self.__bus_rate:
             for ch in self.__channels:
                 self.__graph.connect(
                     (in_block, ch),
                     (bus_sums[ch], in_index))
         else:
             for ch in self.__channels:
                 self.__graph.connect(
                     (in_block, ch),
                     # TODO pool these resamplers
                     make_resampler(in_rate, self.__bus_rate),
                     (bus_sums[ch], in_index))
         in_index += 1
     
     if in_index > 0:
         # connect output only if there is at least one input
         if len(outputs) > 0:
             used_resamplers = set()
             for out_rate, out_block in outputs:
                 if out_rate == self.__bus_rate:
                     for ch in self.__channels:
                         self.__graph.connect(bus_sums[ch], (out_block, ch))
                 else:
                     if out_rate not in self.__resampler_cache:
                         # Moderately expensive due to the internals using optfir
                         log.msg('Flow graph: Constructing resampler for audio rate %i' % out_rate)
                         self.__resampler_cache[out_rate] = tuple(
                             make_resampler(self.__bus_rate, out_rate)
                             for _ in self.__channels)
                     resamplers = self.__resampler_cache[out_rate]
                     used_resamplers.add(resamplers)
                     for ch in self.__channels:
                         self.__graph.connect(resamplers[ch], (out_block, ch))
             for resamplers in used_resamplers:
                 for ch in self.__channels:
                     self.__graph.connect(bus_sums[ch], resamplers[ch])
         else:
             # gnuradio requires at least one connected output
             for ch in self.__channels:
                 self.__graph.connect(bus_sums[ch], blocks.null_sink(gr.sizeof_float))