def main(self, input): """ Args: input (DataValid): -1.0 ... 1.0 range, up to 18 bits Returns: DataValid: 18 bits(-1.0 ... 1.0 range) if transform size is up to 1024 points. Transforms over 1024 points start emphasizing smaller numbers e.g. 2048 would return a result with 18 bits but in -0.5 ... 0.5 range (one extra bit for smaller numbers) etc... """ var = input if self.INVERSE: var = DataValid(Complex(input.data.imag, input.data.real), input.valid) # execute stages for stage in self.stages: var = stage.main(var) if self.INVERSE: var = DataValid(Complex(var.data.imag, var.data.real), var.valid) # this part is active if transform is larger than 10 stages if self.POST_GAIN_CONTROL != 0: self.output.data = scalb(var.data, -self.POST_GAIN_CONTROL) else: self.output.data = var.data self.output.valid = var.valid return self.output
def __init__(self, fft_size, twiddle_bits=9, inverse=False, input_ordering='natural'): self._pyha_simulation_input_callback = NumpyToDataValid( dtype=default_complex) self.INPUT_ORDERING = input_ordering self.INVERSE = inverse self.FFT_SIZE = fft_size self.N_STAGES = int(np.log2(fft_size)) max_gain_control_stages = 10 self.POST_GAIN_CONTROL = max(self.N_STAGES - max_gain_control_stages, 0) self.stages = [ StageR2SDF(self.FFT_SIZE, i, twiddle_bits, inverse, input_ordering, allow_gain_control=i < max_gain_control_stages) for i in range(self.N_STAGES) ] self.output = DataValid( Complex(0, -self.POST_GAIN_CONTROL, -17 - self.POST_GAIN_CONTROL))
def __init__(self, window_len, dtype=Sfix): self._pyha_simulation_input_callback = NumpyToDataValid( dtype=dtype.default()) self.WINDOW_LEN = window_len self.BIT_GROWTH = int(np.log2(window_len)) self.shr = ShiftRegister([dtype()] * self.WINDOW_LEN) self.acc = dtype(0.0, self.BIT_GROWTH, -17) self.output = DataValid( dtype(0, 0, -17, round_style='round')) # negative trend without rounding! self.start_counter = DownCounter(1)
def __init__(self, window_len, dtype=Complex): assert window_len > 2 self._pyha_simulation_input_callback = NumpyToDataValid( dtype=dtype.default()) self.WINDOW_LEN = window_len self.averages = [ MovingAverage(window_len, dtype), MovingAverage(window_len, dtype) ] # input must be delayed by group delay, we can use the SHR from the first averager to get the majority of the delay. self.delayed_input = ShiftRegister([dtype(0.0, 0, -17)] * 3) self.output = DataValid(dtype(0, 0, -17))
def __init__(self, window_length, window='hanning', coefficient_bits=18): self._pyha_simulation_input_callback = NumpyToDataValid( dtype=default_complex) self.WINDOW_LENGTH = window_length self.window_pure = get_window(window, window_length) self.WINDOW = [ Sfix(x, 0, -(coefficient_bits - 1), round_style='round', overflow_style='saturate') for x in self.window_pure ] self.output = DataValid(Complex(0, 0, -17, round_style='round')) self.index_counter = 1 self.coef = self.WINDOW[0]
def main(self, input): """ Args: input (DataValid): type not restricted Returns: DataValid: Lowest 36 bits from the result. Example: Input is 18 bits with format 0:-17, then output is 36 bits 1:-34 """ if not input.valid: return DataValid(self.output.data, valid=False) # (a + bi)(a - bi) = a**2 + b**2 self.output.data = (input.data.real * input.data.real) + ( input.data.imag * input.data.imag) self.output.valid = input.valid return self.output
def main(self, input): """ Args: input (DataValid): -1.0 ... 1.0 range, up to 18 bits Returns: DataValid: Result rounded to 18 bits(-1.0 ... 1.0 range). Overflow impossible. """ if not input.valid: return DataValid(self.output.data, valid=False) self.index_counter = (self.index_counter + 1) % self.WINDOW_LENGTH self.coef = self.WINDOW[self.index_counter] self.output.data = input.data * self.coef self.output.valid = input.valid return self.output
def __init__(self): self._pyha_simulation_input_callback = NumpyToDataValid(dtype=Complex( 0.0, 0, -11, overflow_style='saturate', round_style='round')) # components fft_size = 1024 * 8 avg_freq_axis = 16 avg_time_axis = 8 window_type = 'hamming' fft_twiddle_bits = 8 window_bits = 8 dc_removal_len = 1024 self.spect = Spectrogram(fft_size, avg_freq_axis, avg_time_axis, window_type, fft_twiddle_bits, window_bits, dc_removal_len) # TODO: could be unsigned! self.output = DataValid( Sfix(0.0, upper_bits=32) ) # no need to round because result is positive i.e. truncation = rounding
def main(self, input): """ Args: input (DataValid): -1.0 ... 1.0 range, up to 18 bits Returns: DataValid: Accumulator scaled and rounded to 18 bits(-1.0 ... 1.0 range). Overflow impossible. """ if not input.valid: return DataValid(self.output.data, valid=False) self.shr.push_next(input.data) # add new element to shift register self.acc = self.acc + input.data - self.shr.peek() self.output.data = scalb( self.acc, -self.BIT_GROWTH) # round to standard 18bit format # make sure we don't propagate invalid samples self.start_counter.tick() self.output.valid = self.start_counter.is_over() return self.output
def main(self, input): """ Args: input (DataValid): -1.0 ... 1.0 range, up to 18 bits Returns: DataValid: DC-free output, 18 bits(-1.0 ... 1.0 range). Saturates on overflow. Rounding it down to 12-bits (standard SDR IQ width) wont work, you need ~16 bits to reliably remove the DC-offset. """ avg_out = self.averages[1].main(self.averages[0].main(input)) if not avg_out.valid: return DataValid(self.output.data, valid=False) # delay input -> use averager[0] delay to save alot of RAM self.delayed_input.push_next(self.averages[0].shr.peek()) self.output.data = self.delayed_input.peek() - avg_out.data self.output.valid = True return self.output
def main(self, inp): if not inp.valid: return DataValid(self.out.data, valid=False) # Stage 1: handle the loopback memory; that sets the INPUT_STRIDE; also fetch the twiddle factor for stage 2 if self.IS_NATURAL_ORDER: self.control = (self.control + 1) % (self.LOCAL_FFT_SIZE) self.twiddle = self.TWIDDLES[self.control & self.CONTROL_MASK] else: self.control = (self.control + 1) % (self.GLOBAL_FFT_SIZE) self.twiddle = self.TWIDDLES[self.control >> (self.STAGE_NR + 1)] mode = not (self.control & self.INPUT_STRIDE) self.mode_delay = mode if mode: self.shr.push_next(inp.data) self.stage1_out = self.shr.peek() else: up, down = self.butterfly(self.shr.peek(), inp.data) self.shr.push_next(down) self.stage1_out = up # Stage 2: complex multiply if self.mode_delay and not self.IS_TRIVIAL_MULTIPLIER: self.stage2_out = self.stage1_out * self.twiddle else: self.stage2_out = self.stage1_out # Stage 3: gain control and rounding if self.ALLOW_GAIN_CONTROL and not self.INVERSE: self.out.data = scalb(self.stage2_out, -1) else: self.out.data = self.stage2_out self.start_counter.tick() self.out.valid = self.start_counter.is_over() return self.out
def __init__(self): self._pyha_simulation_input_callback = NumpyToDataValid( dtype=default_complex) self.output = DataValid(Sfix(bits=36))
def __init__(self, global_fft_size, stage_nr, twiddle_bits=18, inverse=False, input_ordering='natural', allow_gain_control=True): self._pyha_simulation_input_callback = NumpyToDataValid( dtype=default_complex) self.ALLOW_GAIN_CONTROL = allow_gain_control self.INVERSE = inverse self.GLOBAL_FFT_SIZE = global_fft_size self.STAGE_NR = stage_nr self.INPUT_ORDERING = input_ordering if input_ordering == 'bitreversed': self.IS_NATURAL_ORDER = False self.INPUT_STRIDE = 2**stage_nr # distance from butterfly input a to b self.LOCAL_FFT_SIZE = global_fft_size // self.INPUT_STRIDE self.CONTROL_MASK = (self.LOCAL_FFT_SIZE - 1) twid = [ W(i, self.LOCAL_FFT_SIZE) for i in range(self.LOCAL_FFT_SIZE // 2) ] twid = toggle_bit_reverse(twid) twid = np.roll(twid, 1, axis=0) self.TWIDDLES = [ Complex(x, 0, -(twiddle_bits - 1), overflow_style='saturate', round_style='round') for x in twid ] elif input_ordering == 'natural': self.IS_NATURAL_ORDER = True self.LOCAL_FFT_SIZE = global_fft_size // 2**stage_nr self.INPUT_STRIDE = self.LOCAL_FFT_SIZE // 2 self.CONTROL_MASK = (self.INPUT_STRIDE - 1) self.TWIDDLES = [ Complex(W(i, self.LOCAL_FFT_SIZE), 0, -(twiddle_bits - 1), overflow_style='saturate', round_style='round') for i in range(self.INPUT_STRIDE) ] self.IS_TRIVIAL_MULTIPLIER = len( self.TWIDDLES) == 1 # mult by 1.0, useless self.shr = ShiftRegister([Complex() for _ in range(self.INPUT_STRIDE)]) self.twiddle = self.TWIDDLES[0] self.stage1_out = Complex(0, 0, -17) self.stage2_out = Complex(0, 0, -17 - (twiddle_bits - 1)) self.output_index = 0 self.mode_delay = False self.control = 0 # replacing this with fixed-point counter saves no resources.. self.out = DataValid(Complex(0, 0, -17, round_style='round'), valid=False) self.start_counter = DownCounter(2 + self.INPUT_STRIDE)
def __init__(self): self._pyha_simulation_input_callback = NumpyToDataValid( dtype=Complex(0.0, 0, -11, overflow_style='saturate', round_style='round')) self.dc_removal = DCRemoval(window_len=2048) self.out = DataValid(Complex(0, 0, -15, round_style='round'))
def __init__(self): self.out = DataValid(Complex(0, 0, -17, overflow_style='saturate'))