Skip to content

ofdm

OFDM signal generation and analysis supporting 16QAM and 256QAM.

rfsoc_rfdc.dsp.ofdm

OFDM implementation with signal generation and analysis capabilities.

Attributes

ofdm = OFDM(modu='256QAM') module-attribute

wave = ofdm.generate() module-attribute

noise = 0.01 * (np.random.randn(wave.shape[0]) + 1j * np.random.randn(wave.shape[0])) module-attribute

wave_with_noise = wave + noise module-attribute

Classes

OFDM(sym_num=14, fft_size=1024, sub_num=768, modu='16QAM', cp_rate=0.0714)

Implements Orthogonal Frequency-Division Multiplexing (OFDM) functionality.

Initializes the OFDM object with given parameters.

Source code in rfsoc_rfdc/dsp/ofdm.py
def __init__(self, sym_num=14, fft_size=1024, sub_num=768, modu='16QAM', cp_rate=0.0714):
    """Initializes the OFDM object with given parameters."""
    np.random.seed(0)  # Fix random seed

    self.sym_num = sym_num
    self.fft_size = fft_size
    self.sub_num = sub_num
    self.modu = modu
    self.cp_rate = cp_rate
    constel_map_head, _ = self._get_constel_map('QPSK')
    self.constel_map_head = constel_map_head
    self.bit_head = np.random.randint(low=0, high=2, size=(sub_num, 2))
    constel_map, speed = self._get_constel_map(modu)
    self.constel_map = constel_map
    self.speed = speed
    self.bit_data = np.random.randint(
        low=0, high=2, size=(sym_num, sub_num, speed))
Attributes
sym_num = sym_num instance-attribute
fft_size = fft_size instance-attribute
sub_num = sub_num instance-attribute
modu = modu instance-attribute
cp_rate = cp_rate instance-attribute
constel_map_head = constel_map_head instance-attribute
bit_head = np.random.randint(low=0, high=2, size=(sub_num, 2)) instance-attribute
constel_map = constel_map instance-attribute
speed = speed instance-attribute
bit_data = np.random.randint(low=0, high=2, size=(sym_num, sub_num, speed)) instance-attribute
Functions
generate(amp=0.5)

Generates OFDM signal.

Source code in rfsoc_rfdc/dsp/ofdm.py
def generate(self, amp=0.5):
    """Generates OFDM signal."""
    cp_len = int(np.round(self.cp_rate * self.fft_size))
    sym_len = self.fft_size + cp_len
    sub_offset = int(np.floor((self.fft_size - self.sub_num) / 2))

    constel_head = np.array([self._bit2constel(self.bit_head[i], self.constel_map_head)
                             for i in range(self.sub_num)]).reshape(1, -1)

    constel_data = np.array([[self._bit2constel(self.bit_data[i, j], self.constel_map)
                              for j in range(self.sub_num)]
                             for i in range(self.sym_num)])

    constel_both = np.vstack((constel_head, constel_data, constel_head))
    wave = np.empty(((self.sym_num + 2) * sym_len), dtype=complex)

    for sym_idx in range(self.sym_num + 2):
        constel_pad = np.zeros(self.fft_size, dtype=complex)
        constel_pad[sub_offset:sub_offset +
                    self.sub_num] = constel_both[sym_idx]
        wave_orig = np.fft.ifft(
            np.roll(constel_pad, shift=self.fft_size // 2))
        wave_cp = wave_orig[:cp_len]
        wave_both = np.concatenate((wave_orig, wave_cp))
        wave[sym_idx * sym_len:(sym_idx + 1) * sym_len] = wave_both

    wave = amp * wave / np.std(wave)
    self.constel_both = constel_both
    return wave
analyze(wave, plot_fname=None, constel_data_fname=None, constel_map_fname=None)

Analyzes received OFDM signal.

Source code in rfsoc_rfdc/dsp/ofdm.py
def analyze(self, wave, plot_fname=None, constel_data_fname=None, constel_map_fname=None):
    """Analyzes received OFDM signal."""
    cp_len = int(np.round(self.cp_rate * self.fft_size))
    sym_len = self.fft_size + cp_len
    sub_offset = int(np.floor((self.fft_size - self.sub_num) / 2))

    constel_both = np.empty((self.sym_num + 2, self.sub_num), dtype=complex)
    for sym_idx in range(self.sym_num + 2):
        wave_both = wave[sym_idx * sym_len:(sym_idx + 1) * sym_len]
        wave_orig = wave_both[cp_len // 2:cp_len // 2 + self.fft_size]
        constel_pad = np.roll(np.fft.fft(wave_orig),
                              shift=-self.fft_size // 2)
        constel_both[sym_idx] = constel_pad[sub_offset:sub_offset + self.sub_num]

    constel_head_gt = np.array([self._bit2constel(self.bit_head[i], self.constel_map_head)
                                for i in range(self.sub_num)])

    csi_1 = constel_both[0] / constel_head_gt
    csi_2 = constel_both[-1] / constel_head_gt
    csi_amp = np.tile((csi_1 + csi_2) / 2, (self.sym_num, 1))
    constel_data = constel_both[1:-1] / csi_amp

    bit_data = np.empty((self.sym_num, self.sub_num, self.speed))
    evm_all = []

    for sym_idx in range(self.sym_num):
        for sub_idx in range(self.sub_num):
            bit, evm = self._constel2bit(constel_data[sym_idx, sub_idx],
                                         self.constel_map, self.speed)
            bit_data[sym_idx, sub_idx] = bit
            evm_all.append(evm)

    evm_all = np.array(evm_all)
    evm = np.sqrt(np.mean(np.abs(evm_all) ** 2))
    ber = np.sum(np.logical_xor(bit_data, self.bit_data)) / \
        (self.sym_num * self.sub_num * self.speed)

    if plot_fname:
        self._plot_constellation(constel_data, evm, ber, plot_fname)

    if plot_fname and constel_data_fname and constel_map_fname:
        self._plot_constellation(
            constel_data, evm, ber, plot_fname, constel_data_fname, constel_map_fname)

    return evm, ber