def main(): now = datetime.datetime.now() current_time = now.strftime("%H:%M:%S.%f") print("Current Time =", current_time) #aquire location #start calibration process (needed?) #start syc process sdr = RtlSdr() # rtl-sdr instance # configure device timeToSample = 1 # in sec sampleRate = 2.4e6 # in Mhz sdr.sample_rate = sampleRate sdr.center_freq = 433e6 # in Mhz sdr.gain = 30 # in dB print("gain set to:", sdr.get_gain()) print(now) numberOfSamples = sampleRate * timeToSample fig = figure() ax = fig.add_subplot(111, projection='3d') # used for 3d IQ/time plot samples = sdr.read_samples(numberOfSamples) # aquire samples sdr.close() # I/Q seperation real = samples.real imag = samples.imag samp = np.arange(0, numberOfSamples, 1) # used as an axis #ax.scatter(samp[0:-1:100],real[0:-1:100],imag[0:-1:100],marker='^',s=2)#used for pumba slack simulateRecivers(real, sampleRate) # used to simulation #plt.subplot(3, 1, 2) # xlabel('Real axis')#used for pumba slack # ylabel('img axis')#used for pumba slack ''' pxx,farr=psd(samples, NFFT=1024, Fs=sampleRate / 1e6, Fc=sdr.center_freq / 1e6) plt.subplot(2, 1, 1) plt.plot(samp, imag) plt.subplot(2, 1, 2) plt.plot(farr,pxx) ''' show()
class Input(DataSource.DataSource): def __init__(self, source: str, data_type: str, sample_rate: float, centre_frequency: float, input_bw: float): """ The rtlsdr input source :param source: The device number, normally zero :param data_type: The data type the rtlsdr is providing, we will convert this :param sample_rate: The sample rate we will set the source to, note true sps is set from the device :param centre_frequency: The centre frequency the source will be set to :param input_bw: The filtering of the input, may not be configurable """ # Driver converts to floating point for us, underlying is 8o? self._constant_data_type = "16tle" super().__init__(source, self._constant_data_type, sample_rate, centre_frequency, input_bw) self._connected = False self._sdr = None self._tuner_type = 0 self._device_index = 0 self._gain_modes = ["auto", "manual"] # would ask, but can't super().set_gain_mode(self._gain_modes[0]) super().set_help(help_string) super().set_web_help(web_help_string) def open(self) -> bool: global import_error_msg if import_error_msg != "": msgs = f"No {module_type} support available, ", import_error_msg self._error = msgs logger.error(msgs) raise ValueError(msgs) if self._source == "?": self._error = self.find_devices() return False try: self._device_index = int(self._source) except ValueError as err: msgs = f"port number from {self._source}, {err}" self._error = str(err) logger.error(msgs) raise ValueError(err) try: self._sdr = RtlSdr(device_index=self._device_index) except Exception as err: self._error = f"Failed to connect {str(err)}" logger.error(self._error) raise ValueError(self._error) self._tuner_type = self._sdr.get_tuner_type() logger.debug(f"Connected to {module_type}") try: self.set_sample_rate_sps(self._sample_rate_sps) self.set_centre_frequency_hz(self._centre_frequency_hz) # self._sdr.freq_correction = 0 # ppm self.set_gain_mode('auto') self.set_gain(0) except ValueError: pass except Exception as err: self._error = str(err) raise ValueError(err) # recover the true values from the device self._sample_rate_sps = float(self._sdr.get_sample_rate()) self._centre_frequency_hz = float(self._sdr.get_center_freq()) logger.debug( f"{allowed_tuner_types[self._tuner_type]} {self._centre_frequency_hz / 1e6:.6}MHz @ " f"{self._sample_rate_sps:.3f}sps") self._connected = True return self._connected def close(self) -> None: if self._sdr: self._sdr.close() self._sdr = None self._connected = False @staticmethod def find_devices() -> str: devices = "" # could do with a call that returns the valid device_index's max_device = 10 for device in range(max_device): try: sdr = RtlSdr(device_index=device) type_of_tuner = sdr.get_tuner_type() # index = sdr.get_device_index_by_serial('0000001') # permissions required # addresses = sdr.get_device_serial_addresses() # permissions required sdr.close() devices += f"device {device}, type {type_of_tuner} {allowed_tuner_types[type_of_tuner]}\n" except Exception: pass if devices == "": devices = f"No rtlsdr devices found, scanned 0 to {max_device-1}" print(devices) return devices def get_sample_rate_sps(self) -> float: if self._sdr: self._sample_rate_sps = float(self._sdr.get_sample_rate()) return self._sample_rate_sps def get_centre_frequency_hz(self) -> float: if self._sdr: if self._hw_ppm_compensation: self._centre_frequency_hz = float(self._sdr.get_center_freq()) return self._centre_frequency_hz def set_sample_type(self, data_type: str) -> None: # we can't set a different sample type on this source super().set_sample_type(self._constant_data_type) def set_sample_rate_sps(self, sample_rate: float) -> None: # rtlsdr has limits on allowed sample rates # from librtlsdr.c data_source.get_bytes_per_sample() # /* check if the rate is supported by the resampler */ # if ((samp_rate <= 225000) || (samp_rate > 3200000) || # ((samp_rate > 300000) && (samp_rate <= 900000))) { # fprintf(stderr, "Invalid sample rate: %u Hz\n", samp_rate); # return -EINVAL; # } # logger.info(f"set sr rtlsdr tuner type {self._tuner_type}, {allowed_tuner_types[self._tuner_type]}") if (sample_rate <= 225000) or (sample_rate > 3200000) or ( (sample_rate > 300000) and (sample_rate <= 900000)): err = f"{module_type} invalid sample rate, {sample_rate}sps, 225000-3000000 and not 300000-900000" self._error = err logger.error(err) sample_rate = 1e6 # something safe self._sample_rate_sps = sample_rate if self._sdr: try: self._sdr.sample_rate = sample_rate self._sample_rate_sps = float(self._sdr.get_sample_rate()) except Exception as err: self._error = str(err) logger.debug( f"bad sr {sample_rate} now {self._sample_rate_sps}") logger.info(f"Set sample rate {sample_rate}sps") def set_centre_frequency_hz(self, frequency: float) -> None: # limits depend on tuner type: from https://wiki.radioreference.com/index.php/RTL-SDR # Tuner Frequency Range # ======================================= # Elonics E4000 52 – 1100 MHz / 1250 - 2200 MHz # Rafael Micro R820T(2) 24 – 1766 MHz # Fitipower FC0013 22 – 1100 MHz # Fitipower FC0012 22 - 948.6 MHz # FCI FC2580 146 – 308 MHz / 438 – 924 MHz freq_ok = True ok = True # logger.info(f"set cf rtlsdr tuner type {self._tuner_type}, {allowed_tuner_types[self._tuner_type]}") # what type of tuner do we have ? freq_range = "" if self._tuner_type == 1: # E4000 if (frequency < 52e6) or (frequency > 2200e6): freq_ok = False freq_range = "52 – 1100 MHz and 1250 - 2200 MHz" elif (frequency > 1100e6) and (frequency < 1250e6): freq_ok = False freq_range = "52 – 1100 MHz and 1250 - 2200 MHz" elif self._tuner_type == 2: # FC0012 if (frequency < 22e6) or (frequency > 948.6e6): freq_ok = False freq_range = "22 - 948.6 MHz" elif self._tuner_type == 3: # FC0013 if (frequency < 22e6) or (frequency > 1100e6): freq_ok = False freq_range = "22 – 1100 MHz" elif self._tuner_type == 4: # FC2580 if (frequency < 146e6) or (frequency > 924e6): freq_ok = False freq_range = "146 – 308 MHz and 438 – 924 MHz" elif (frequency > 308e6) and (frequency < 438e6): freq_ok = False freq_range = "146 – 308 MHz and 438 – 924 MHz" elif self._tuner_type == 5 or self._tuner_type == 6: # R820T or R828D if (frequency < 24e6) or (frequency > 1.766e9): freq_ok = False freq_range = "24 – 1766 MHz" else: self._error = f"Unknown tuner type {self._tuner_type}, frequency range checking impossible" logger.error(self._error) ok = False if not freq_ok: self._error = f"{allowed_tuner_types[self._tuner_type]} invalid frequency {frequency}Hz, " \ f"outside range {freq_range}" logger.error(self._error) ok = False if self._sdr and ok: try: if self._hw_ppm_compensation: self._sdr.center_freq = frequency self._centre_frequency_hz = float( self._sdr.get_center_freq()) else: self._centre_frequency_hz = frequency self._sdr.center_freq = self.get_ppm_corrected(frequency) # print(f"freq {frequency} ppm {self._ppm} -> {frequency + (self._ppm * frequency / 1e6)}") logger.info(f"Set frequency {frequency / 1e6:0.6f}MHz") except Exception as err: self._error = str(err) def set_ppm(self, ppm: float) -> None: """ +ve reduces tuned frequency -ve increases the tuned frequency :param ppm: Parts per million error, :return: """ self._ppm = ppm self.set_centre_frequency_hz(self._centre_frequency_hz) def get_gain(self) -> float: if self._sdr: self._gain = self._sdr.get_gain() return self._gain def set_gain(self, gain: float) -> None: self._gain = gain if self._sdr: try: # horrible _sdr.set_gain() - either a number or string if self._gain_mode == 'auto': self._sdr.set_gain('auto') else: self._sdr.set_gain(float(gain)) except Exception as err: self._error = f"failed to set gain of '{gain}', {err}" def set_gain_mode(self, mode: str) -> None: if mode in self._gain_modes: self._gain_mode = mode if self._sdr: # because the 'best' way to set the mode is to set the gain, apparently self.set_gain(self._gain) return def set_bandwidth_hz(self, bw: float) -> None: if self._sdr: try: self._sdr.set_bandwidth(int(bw)) except Exception as err: self._error += str(err) self._bandwidth_hz = self._sdr.get_bandwidth() def get_bandwidth_hz(self) -> float: if self._sdr: self._bandwidth_hz = self._sdr.get_bandwidth() return self._bandwidth_hz def read_cplx_samples(self, number_samples: int) -> Tuple[np.array, float]: """ Get complex float samples from the device Note that we don't use unpack() for this device :return: A tuple of a numpy array of complex samples and time in nsec """ complex_data = None rx_time = 0 if self._sdr and self._connected: try: complex_data = self._sdr.read_samples( number_samples) # will return np.complex128 rx_time = self.get_time_ns() complex_data = np.array( complex_data, dtype=np.complex64 ) # (?) we need all values to be 32bit floats except Exception as err: self._connected = False self._error = str(err) logger.error(self._error) raise ValueError(err) return complex_data, rx_time
class Sdr(): def __init__(self): #Abro el dongle self.dispositivo = RtlSdr() #Lo configuro self.dispositivo.sample_rate = 2.048e6 #Hz self.dispositivo.center_freq = 1e9 #Hz self.dispositivo.freq_correction = 60 #ppm self.dispositivo.gain = 49.6 #dB self.dispositivo.set_agc_mode(False) self.frecuencia_oscilador_local_lnb = 10.5e9 self.limite_inferior_Ku = 11.45e9 self.limite_superior_Ku = 12.25e9 self.nfft = 1024 self.n = 256 self.Vref = 1 #V self.Zo = 2e3 #Ohm self.potencia_calibrada = None def leer_potencia(self): #Leo las muestras iq = self.dispositivo.read_samples(self.n * self.nfft) #Calculo la potencia de la señal potencia = calcular_potencia(iq, self.Vref, self.Zo) - self.dispositivo.gain return potencia def leer_potencia_calibrada(self): if self.esta_calibrado() == True: return self.leer_potencia() - self.potencia_calibrada else: return None def calibrar(self): print('Calibrando...') potencia_aux = 0 for i in range(10): potencia_aux += self.leer_potencia() self.potencia_calibrada = potencia_aux / (i + 1) print('Calibrado a {:2.2f} dBFS'.format(self.potencia_calibrada)) def esta_calibrado(self): return self.potencia_calibrada != None def configurar(self): entrada = 0 while entrada != 6: entrada = interfaz.imprimir_menu_configuracion() if entrada == 1: frecuencia = float( input( 'Ingrese una frecuencia entre {:f} Hz y {:f} (en Hz): ' .format(self.limite_inferior_Ku, self.limite_superior_Ku))) if (frecuencia < self.limite_inferior_Ku) or ( frecuencia > self.limite_superior_Ku): print('Valor fuera de rango') else: self.dispositivo.set_center_freq( frecuencia - self.frecuencia_oscilador_local_lnb) elif entrada == 2: ancho_banda = float( input( 'Ingrese un ancho de banda entre 1 MHz y 3.2 MHz (En Hz): ' )) if (ancho_banda < 1e6) or (ancho_banda > 3.2e6): print('Valor fuera de rango') else: self.dispositivo.set_sample_rate(ancho_banda) elif entrada == 3: lista = np.array(self.dispositivo.valid_gains_db) try: self.dispositivo.set_gain( float( input( 'Ingrese una ganancia entre {:2.1f} dB y {:2.1f} dB: ' .format(np.min(lista), np.max(lista))))) print('Ganancia configurada en {:2.1f} dB'.format( self.dispositivo.get_gain())) except: print('Valor fuera de rango') elif entrada == 4: try: self.n = int( input( 'Ingrese la cantidad de paquetes de 1024 muestras: ' )) except: print('Valor fuera de rango') elif entrada == 5: frecuencia_central = self.get_frecuencia_central() #Hz ancho_banda = self.dispositivo.get_sample_rate() #Hz ganancia = self.dispositivo.get_gain() #dB paquetes = self.n tamanio_paquete = self.nfft print('Configuracion actual: ') print('Frecuencia central: {:f} Hz'.format(frecuencia_central)) print('Ancho de banda: {:f} Hz'.format(ancho_banda)) print('Ganancia: {:2.1f} dB'.format(ganancia)) print('Mediciones compuestas por {} paquetes de {} muestras'. format(paquetes, tamanio_paquete)) print('Tiempo aproximado por medicion: {:f} ms'.format( 1e3 * paquetes * tamanio_paquete / ancho_banda)) elif entrada == 6: print('Configuracion realizada\n') else: interfaz.orden_no_valida() def get_frecuencia_central(self): return self.dispositivo.get_center_freq( ) + self.frecuencia_oscilador_local_lnb def esta_abierto(self): return self.dispositivo.device_opened def cerrar(self): if self.esta_abierto(): self.dispositivo.close()