def design_sawtooth_filter( ntaps=40, decreasing=False, window_type=window.WIN_HAMMING, beta=0): """ This filter has a response which increases or decreases linearly with frequency, cut at f_s/2. Its gain is 1 at frequency 0 and thus also 1 averaged over all frequencies. """ window_values = window.build(window_type, ntaps, beta) # Formula provided by Olli Niemitalo in <http://dsp.stackexchange.com/a/28035/4655>. taps = [] for i in xrange(0, ntaps): k = i - ntaps // 2 # k = 0 at middle if k == 0: # substitute limit for division by zero ideal_response = complex(pi, 0) else: # The real part is pi * sinc(k), but that is always zero when k != 0. ideal_response = complex(0, sin(pi * k) / (pi * k * k) - cos(pi * k) / k) taps.append(window_values[i] * ideal_response) # Compute gain at frequency 0, and divide by it so as to set the wanted gain. gain_factor = 1.0 / abs(sum(taps)) for i in xrange(0, ntaps): taps[i] *= gain_factor # Reverse if appropriate. if decreasing: taps = taps[::-1] return taps
def design_sawtooth_filter(ntaps=40, decreasing=False, window_type=window.WIN_HAMMING, beta=0): """ This filter has a response which increases or decreases linearly with frequency, cut at f_s/2. Its gain is 1 at frequency 0 and thus also 1 averaged over all frequencies. """ window_values = window.build(window_type, ntaps, beta) # Formula provided by Olli Niemitalo in <http://dsp.stackexchange.com/a/28035/4655>. taps = [] for i in six.moves.range(0, ntaps): k = i - ntaps // 2 # k = 0 at middle if k == 0: # substitute limit for division by zero ideal_response = complex(pi, 0) else: # The real part is pi * sinc(k), but that is always zero when k != 0. ideal_response = complex( 0, sin(pi * k) / (pi * k * k) - cos(pi * k) / k) taps.append(window_values[i] * ideal_response) # Compute gain at frequency 0, and divide by it so as to set the wanted gain. gain_factor = 1.0 / abs(sum(taps)) for i in six.moves.range(0, ntaps): taps[i] *= gain_factor # Reverse if appropriate. if decreasing: taps = taps[::-1] return taps
def __do_connect(self): itemsize = self.__itemsize if self.__signal_type.is_analytic(): input_length = self.__freq_resolution output_length = self.__freq_resolution self.__after_fft = None else: # use vector_to_streams to cut the output in half and discard the redundant part input_length = self.__freq_resolution * 2 output_length = self.__freq_resolution self.__after_fft = blocks.vector_to_streams( itemsize=output_length * gr.sizeof_float, nstreams=2) sample_rate = self.__signal_type.get_sample_rate() overlap_factor = int( math.ceil(_maximum_fft_rate * input_length / sample_rate)) # sanity limit -- OverlapGimmick is not free overlap_factor = min(16, overlap_factor) self.__frame_rate_to_decimation_conversion = sample_rate * overlap_factor / input_length self.__gate = blocks.copy(itemsize) self.__gate.set_enabled(not self.__paused) overlapper = _OverlappedStreamToVector(size=input_length, factor=overlap_factor, itemsize=itemsize) self.__frame_dec = blocks.keep_one_in_n( itemsize=itemsize * input_length, n=max( 1, int( round(self.__frame_rate_to_decimation_conversion / self.__frame_rate)))) # the actual FFT logic, which is similar to GR's logpwrfft_c window = windows.build(self.__window_type, input_length, 6.76) window_power = sum(x * x for x in window) # TODO: use fft_vfc when applicable fft_block = (fft_vcc if itemsize == gr.sizeof_gr_complex else fft_vfc)( fft_size=input_length, forward=True, window=window) mag_squared = blocks.complex_to_mag_squared(input_length) logarithmizer = blocks.nlog10_ff( n=10, # the "deci" in "decibel" vlen=input_length, k=( -to_dB(window_power) + # compensate for window -to_dB(sample_rate) + # convert from power-per-sample to power-per-Hz self.__power_offset # offset for packing into bytes )) # It would make slightly more sense to use unsigned chars, but blocks.float_to_uchar does not support vlen. self.__fft_converter = blocks.float_to_char( vlen=self.__freq_resolution, scale=1.0) fft_sink = self.__fft_cell.create_sink_internal( numpy.dtype((numpy.int8, output_length))) scope_sink = self.__scope_cell.create_sink_internal( numpy.dtype(('c8', self.__time_length))) scope_chunker = blocks.stream_to_vector_decimator( item_size=gr.sizeof_gr_complex, sample_rate=sample_rate, vec_rate=self.__frame_rate, # TODO doesn't need to be coupled vec_len=self.__time_length) # connect everything self.__context.lock() try: self.disconnect_all() self.connect(self, self.__gate, overlapper, self.__frame_dec, fft_block, mag_squared, logarithmizer) if self.__after_fft is not None: self.connect(logarithmizer, self.__after_fft) self.connect(self.__after_fft, self.__fft_converter, fft_sink) self.connect( (self.__after_fft, 1), blocks.null_sink(gr.sizeof_float * self.__freq_resolution)) else: self.connect(logarithmizer, self.__fft_converter, fft_sink) if self.__enable_scope: self.connect(self.__gate, scope_chunker, scope_sink) finally: self.__context.unlock()
def __do_connect(self): itemsize = self.__itemsize if self.__signal_type.is_analytic(): input_length = self.__freq_resolution output_length = self.__freq_resolution self.__after_fft = None else: # use vector_to_streams to cut the output in half and discard the redundant part input_length = self.__freq_resolution * 2 output_length = self.__freq_resolution self.__after_fft = blocks.vector_to_streams(itemsize=output_length * gr.sizeof_float, nstreams=2) sample_rate = self.__signal_type.get_sample_rate() overlap_factor = int(math.ceil(_maximum_fft_rate * input_length / sample_rate)) # sanity limit -- OverlapGimmick is not free overlap_factor = min(16, overlap_factor) self.__frame_rate_to_decimation_conversion = sample_rate * overlap_factor / input_length self.__gate = blocks.copy(itemsize) self.__gate.set_enabled(not self.__paused) overlapper = _OverlappedStreamToVector( size=input_length, factor=overlap_factor, itemsize=itemsize) self.__frame_dec = blocks.keep_one_in_n( itemsize=itemsize * input_length, n=max(1, int(round(self.__frame_rate_to_decimation_conversion / self.__frame_rate)))) # the actual FFT logic, which is similar to GR's logpwrfft_c window = windows.build(self.__window_type, input_length, 6.76) window_power = sum(x * x for x in window) # TODO: use fft_vfc when applicable fft_block = (fft_vcc if itemsize == gr.sizeof_gr_complex else fft_vfc)( fft_size=input_length, forward=True, window=window) mag_squared = blocks.complex_to_mag_squared(input_length) logarithmizer = blocks.nlog10_ff( n=10, # the "deci" in "decibel" vlen=input_length, k=( -to_dB(window_power) + # compensate for window -to_dB(sample_rate) + # convert from power-per-sample to power-per-Hz self.__power_offset # offset for packing into bytes )) # It would make slightly more sense to use unsigned chars, but blocks.float_to_uchar does not support vlen. self.__fft_converter = blocks.float_to_char(vlen=self.__freq_resolution, scale=1.0) fft_sink = self.__fft_cell.create_sink_internal(numpy.dtype((numpy.int8, output_length))) scope_sink = self.__scope_cell.create_sink_internal(numpy.dtype(('c8', self.__time_length))) scope_chunker = blocks.stream_to_vector_decimator( item_size=gr.sizeof_gr_complex, sample_rate=sample_rate, vec_rate=self.__frame_rate, # TODO doesn't need to be coupled vec_len=self.__time_length) # connect everything self.__context.lock() try: self.disconnect_all() self.connect( self, self.__gate, overlapper, self.__frame_dec, fft_block, mag_squared, logarithmizer) if self.__after_fft is not None: self.connect(logarithmizer, self.__after_fft) self.connect(self.__after_fft, self.__fft_converter, fft_sink) self.connect((self.__after_fft, 1), blocks.null_sink(gr.sizeof_float * self.__freq_resolution)) else: self.connect(logarithmizer, self.__fft_converter, fft_sink) if self.__enable_scope: self.connect( self.__gate, scope_chunker, scope_sink) finally: self.__context.unlock()