Exemple #1
0
	def __init__(self,
			osmo_device,
			name=None,
			profile=OsmoSDRProfile(),
			sample_rate=None,
			external_freq_shift=0.0,
			correction_ppm=0.0,
			**kwargs):
		'''
		osmo_device: gr-osmosdr device string
		name: block name (usually not specified)
		profile: an OsmoSDRProfile (see docs)
		sample_rate: desired sample rate, or None == guess a good rate
		external_freq_shift: external (down|up)converter frequency (Hz)
		correction_ppm: oscillator frequency calibration (parts-per-million)
		'''
		# The existence of the external_freq_shift and correction_ppm parameters (but not all of the others) is a workaround for the current inability to dynamically change an exported field's type (the frequency range), allowing them to be initialized early enough, in the configuration, to take effect.
		
		if name is None:
			name = 'OsmoSDR %s' % osmo_device
		Source.__init__(self, name=name, **kwargs)
		
		self.__osmo_device = osmo_device
		self.__profile = profile
		
		self.osmosdr_source_block = source = osmosdr.source('numchan=1 ' + osmo_device)
		if source.get_num_channels() < 1:
			# osmosdr.source doesn't throw an exception, allegedly because gnuradio can't handle it in a hier_block2 initializer. But we want to fail understandably, so recover by detecting it (sample rate = 0, which is otherwise nonsense)
			raise LookupError('OsmoSDR device not found (device string = %r)' % osmo_device)
		elif source.get_num_channels() > 1:
			raise LookupError('Too many devices/channels; need exactly one (device string = %r)' % osmo_device)
		
		if sample_rate is None:
			# If sample_rate is unspecified, we pick the closest available rate to a reasonable value. (Reasonable in that it's within the data handling capabilities of this software and of USB 2.0 connections.) Previously, we chose the maximum sample rate, but that may be too high for the connection the RF hardware, or too high for the CPU to FFT/demodulate.
			source.set_sample_rate(convert_osmosdr_range(source.get_sample_rates())(2.4e6))
		else:
			source.set_sample_rate(sample_rate)
		
		self.connect(self.osmosdr_source_block, self)
		
		# Misc state
		self.external_freq_shift = external_freq_shift
		self.correction_ppm = correction_ppm
		self.dc_state = 0
		self.iq_state = 0
		source.set_dc_offset_mode(self.dc_state, ch)  # no getter, set to known state
		source.set_iq_balance_mode(self.iq_state, ch)  # no getter, set to known state

		hw_initial_freq = source.get_center_freq()
		if hw_initial_freq == 0.0:
			# If the hardware/driver isn't providing a reasonable default (RTLs don't), do it ourselves; go to the middle of the FM broadcast band (rounded up or down to what the hardware reports it supports).
			self.set_freq(100e6)
		else:
			# Note: _invert_frequency won't actually do anything useful currently because external_freq_shift and correction_ppm aren't initialized at this point; it's just the most-correct expression. And if we add ctor args for the frequency modifiers, it'll do the right thing.
			self.freq = self._invert_frequency(hw_initial_freq)
Exemple #2
0
	def __init__(self, name='Simulated Source', freq=0):
		Source.__init__(self, name=name)
		
		audio_rate = 1e4
		rf_rate = self.__sample_rate = 200e3
		interp = int(rf_rate / audio_rate)
		
		self.__freq = freq
		self.noise_level = -2
		
		interp_taps = firdes.low_pass(
			1,  # gain
			rf_rate,
			audio_rate / 2,
			audio_rate * 0.2,
			firdes.WIN_HAMMING)
		
		def make_interpolator():
			return filter.interp_fir_filter_ccf(interp, interp_taps)
		
		def make_channel(freq):
			osc = analog.sig_source_c(rf_rate, analog.GR_COS_WAVE, freq, 1, 0)
			mult = blocks.multiply_cc(1)
			self.connect(osc, (mult, 1))
			return mult
		
		self.bus = blocks.add_vcc(1)
		self.channel_model = channels.channel_model(
			noise_voltage=10 ** self.noise_level,
			frequency_offset=0,
			epsilon=1.01,  # TODO: expose this parameter
			#taps=...,  # TODO: apply something here?
			)
		self.throttle = blocks.throttle(gr.sizeof_gr_complex, rf_rate)
		self.connect(
			self.bus,
			self.channel_model,
			self.throttle,
			self)
		signals = []
		
		# Audio input signal
		pitch = analog.sig_source_f(audio_rate, analog.GR_SAW_WAVE, -1, 2000, 1000)
		audio_signal = vco = blocks.vco_f(audio_rate, 1, 1)
		self.connect(pitch, vco)
		
		# Baseband / DSB channel
		baseband_interp = make_interpolator()
		self.connect(
			audio_signal,
			blocks.float_to_complex(1),
			baseband_interp)
		signals.append(baseband_interp)
		
		# AM channel
		am_channel = make_channel(10e3)
		self.connect(
			audio_signal,
			blocks.float_to_complex(1),
			blocks.add_const_cc(1),
			make_interpolator(),
			am_channel)
		signals.append(am_channel)
		
		# NFM channel
		nfm_channel = make_channel(30e3)
		self.connect(
			audio_signal,
			analog.nbfm_tx(
				audio_rate=audio_rate,
				quad_rate=rf_rate,
				tau=75e-6,
				max_dev=5e3),
			nfm_channel)
		signals.append(nfm_channel)
		
		# VOR channels
		# TODO: My signal level parameters are probably wrong because this signal doesn't look like a real VOR signal
		def add_vor(freq, angle):
			compensation = math.pi / 180 * -6.5  # empirical, calibrated against VOR receiver (and therefore probably wrong)
			angle = angle + compensation
			angle = angle % (2 * math.pi)
			vor_sig_freq = 30
			phase_shift = int(rf_rate / vor_sig_freq * (angle / (2 * math.pi)))
			vor_dev = 480
			vor_channel = make_channel(freq)
			vor_30 = analog.sig_source_f(audio_rate, analog.GR_COS_WAVE, 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(audio_signal,
				blocks.multiply_const_ff(0.07),  # 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.connect(
				vor_audio,
				blocks.float_to_complex(1),
				make_interpolator(),
				blocks.delay(gr.sizeof_gr_complex, phase_shift),
				(vor_add, 1))
			# FM component
			vor_fm_mult = blocks.multiply_cc(1)
			self.connect(  # carrier generation
				analog.sig_source_f(rf_rate, analog.GR_COS_WAVE, 9960, 1, 0), 
				blocks.float_to_complex(1),
				(vor_fm_mult, 1))
			self.connect(  # modulation
				vor_30,
				filter.interp_fir_filter_fff(interp, interp_taps),  # float not complex
				analog.frequency_modulator_fc(2 * math.pi * vor_dev / rf_rate),
				blocks.multiply_const_cc(0.3),  # M_d
				vor_fm_mult,
				(vor_add, 2))
			self.connect(
				vor_add,
				vor_channel)
			signals.append(vor_channel)
		add_vor(-30e3, 0)
		add_vor(-60e3, math.pi / 2)
		
		bus_input = 0
		for signal in signals:
			self.connect(signal, (self.bus, bus_input))
			bus_input = bus_input + 1