def fft_model(inp): if self.INPUT_ORDERING == 'natural': offset = self.LOCAL_FFT_SIZE // 2 twiddles = [W(i, self.LOCAL_FFT_SIZE) for i in range(offset)] packets = np.array(np.reshape(inp, (-1, self.LOCAL_FFT_SIZE))) for pack in packets: for i in range(offset): pack[i], pack[i + offset] = pack[i] + pack[i + offset], \ (pack[i] - pack[i + offset]) * twiddles[i] return packets.flatten() elif self.INPUT_ORDERING == 'bitreversed': input_stride = 2**self.STAGE_NR local_fft_size = self.GLOBAL_FFT_SIZE // input_stride twiddles = toggle_bit_reverse( [W(i, local_fft_size) for i in range(local_fft_size // 2)]) packets = np.array( np.reshape(inp, (len(twiddles), -1)) ) # note: this shape is upside down compared to NORMAL order function offset = input_stride for packet_i, pack in enumerate(packets): for i in range(offset): pack[i], pack[i + offset] = pack[i] + pack[i + offset], \ (pack[i] - pack[i + offset]) * twiddles[packet_i] return packets.flatten()
def test_rev(fft_size): input_signal = np.random.uniform( -1, 1, fft_size) + np.random.uniform(-1, 1, fft_size) * 1j my = pyfft_rev(input_signal, fft_size) input_signal = toggle_bit_reverse(input_signal) ref = np.fft.fft(input_signal, fft_size) np.testing.assert_allclose(ref, my)
def numpy_model(inp, fft_size, input_ordering='natural', inverse=False): """ This basically sums up what is going on in this file """ x = np.array(inp).reshape(-1, fft_size) if input_ordering == 'bitreversed': x = toggle_bit_reverse(x) if inverse: ffts = np.fft.ifft(x, fft_size) ffts *= fft_size else: ffts = np.fft.fft(x, fft_size) ffts /= fft_size if input_ordering == 'natural': ffts = toggle_bit_reverse(ffts) return ffts.flatten()
def test_rand(self, fft_size): input_signal = np.random.uniform( -1, 1, fft_size) + np.random.uniform(-1, 1, fft_size) * 1j rev_input_signal = toggle_bit_reverse(input_signal) stage = 0 inp = rev_input_signal while 2**stage != fft_size: inp = bitrevered_fft_stage(inp, fft_size, stage) stage += 1 ref = np.fft.fft(input_signal, fft_size) np.testing.assert_allclose(ref, inp)
def test_rev8(self): fft_size = 8 input_signal = np.array([ 0.01 + 0.01j, 0.02 + 0.02j, 0.03 + 0.03j, 0.04 + 0.04j, 0.05 + 0.05j, 0.06 + 0.06j, 0.07 + 0.07j, 0.08 + 0.08j ]) bitrev_input_signal = toggle_bit_reverse(input_signal) expect = [ 6.00000000e-02 + 6.00000000e-02j, -4.00000000e-02 - 4.00000000e-02j, 1.00000000e-01 + 1.00000000e-01j, -4.00000000e-02 + 4.00000000e-02j, 8.00000000e-02 + 8.00000000e-02j, -5.65685425e-02 - 3.46944695e-18j, 1.20000000e-01 + 1.20000000e-01j, -6.93889390e-18 + 5.65685425e-02j ] stage0_out = bitrevered_fft_stage(bitrev_input_signal, fft_size, 0) np.testing.assert_allclose(stage0_out, expect) # stage 1 expect = [ 1.60000000e-01 + 1.60000000e-01j, -8.00000000e-02 + 6.93889390e-18j, -4.00000000e-02 - 4.00000000e-02j, 6.93889390e-18 - 8.00000000e-02j, 2.00000000e-01 + 2.00000000e-01j, -5.65685425e-02 + 5.65685425e-02j, -4.00000000e-02 + 4.00000000e-02j, -5.65685425e-02 + 5.65685425e-02j ] stage1_out = bitrevered_fft_stage(stage0_out, fft_size, 1) np.testing.assert_allclose(stage1_out, expect) # stage 2 (last expect = [ 3.60000000e-01 + 3.60000000e-01j, -1.36568542e-01 + 5.65685425e-02j, -8.00000000e-02 - 6.93889390e-18j, -5.65685425e-02 - 2.34314575e-02j, -4.00000000e-02 - 4.00000000e-02j, -2.34314575e-02 - 5.65685425e-02j, -6.93889390e-18 - 8.00000000e-02j, 5.65685425e-02 - 1.36568542e-01j ] stage2_out = bitrevered_fft_stage(stage1_out, fft_size, 2) np.testing.assert_allclose(stage2_out, expect)
def bitrevered_fft_stage(inp, fft_size, stage_nr): """ Calculates one stage of the FFT. Inputs BITREVERSED and outputs NATURAL """ input_stride = 2**stage_nr local_fft_size = fft_size // input_stride twiddles = toggle_bit_reverse( [W(i, local_fft_size) for i in range(local_fft_size // 2)]) packets = np.array( np.reshape(inp, (local_fft_size // 2, -1)) ) # note: this shape is upside down compared to NORMAL order function offset = input_stride for packet_i, pack in enumerate(packets): for i in range(offset): pack[i], pack[i + offset] = pack[i] + pack[i + offset], \ (pack[i] - pack[i + offset]) * twiddles[packet_i] return packets.flatten()
def test_rev4(self): # stage 0 fft_size = 4 input_signal = np.array( [0.01 + 0.01j, 0.02 + 0.02j, 0.03 + 0.03j, 0.04 + 0.04j]) bitrev_input_signal = toggle_bit_reverse(input_signal) expect = [0.04 + 0.04j, -0.02 - 0.02j, 0.06 + 0.06j, -0.02 + 0.02j] stage0_out = bitrevered_fft_stage(bitrev_input_signal, fft_size, 0) np.testing.assert_allclose(stage0_out, expect) # stage 1 (last) expect = [ 1.00000000e-01 + 1.00000000e-01j, -4.00000000e-02 + 3.46944695e-18j, -2.00000000e-02 - 2.00000000e-02j, 3.46944695e-18 - 4.00000000e-02j ] stage1_out = bitrevered_fft_stage(stage0_out, fft_size, 1) np.testing.assert_allclose(stage1_out, expect)
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)