Filter Design for Up- and Downconversion

This article is part of the fundamentals of my real-world tutorial on OFDM using a cheap soundcard as the radio. If this notebook is interesting to you, check out the full tutorial!

In the previous notebook, we have simply chosen some filter for the DA and AD conversion, without further evaluation. For most situations, the chosen filter is fine, though let us still look a bit more detailed at this filter and its effects. In particular we will look at the signal spectrum after upconversion and DA conversion, where we will see interesting effects. The content of this notebook is especially important for the notebook [09 - OFDM-Related and 5G Waveforms.ipynb](09 - OFDM-related and 5G Waveforms) and you can safely omit it on your first exploration.

First, let's import common objects:

In[3]:
# Source code has been redacted in online version
# Omitting 5 lines of source code

Note the PassbandChannel class, which combines Upconversion, actual channel and Downconversion into a single object. We will use this channel often to simplify the simulation code.

Below, we write a function that shows the time domain and spectrum of the upconverted transmit signal of any given signal generator:

In[4]:
# Source code has been redacted in online version
# Omitting 21 lines of source code

Let's try this function out using a white noise transmitter. This transmitter just generates complex white noise. Accordingly, the its spectrum should be flat over the whole bandwidth.

In[5]:
class NoiseTransmitter(TX):
    def __init__(self, environment):
        super().__init__(environment)
    
    def _generateSignal(self):
        return (np.random.randn(1000)+1j*np.random.randn(1000))

First, let's verify this by concatenating some signal blocks and looking at its baseband spectrum:

In[6]:
# Source code has been redacted in online version
# Omitting 7 lines of source code

Yes, in the baseband the transmit spectrum is completely flat. Let's look at the transmit spectrum after DA conversion and upconversion:

In[7]:
showTXSpectrum(NoiseTransmitter)
Using Audio device default
Stream Stopped 1
Stream Stopped 2

Hm... there is not really a flat spectrum. OK, within the red lines (i.e. the baseband bandwidth) the spectrum is almost flat as we saw before. However, there are significant sidelobes in other areas of the spectrum. Where do they come from? Let us recap the implementation of the AD and DA filter:

In[8]:
def UpDownConversionFilter(B, samplerate, N=3, useWindow=False):
    t = np.arange(-N/B, N/B, 1/samplerate)
    rrc_samples = get_filter('rrc', 1/B, rolloff=0.001)(t) * np.sqrt(B / samplerate)
    window = np.hanning(len(rrc_samples))
    window = np.kaiser(len(rrc_samples), beta=5)
    if not useWindow:
        window = 1

    return StatefulFilter(rrc_samples*window, np.array([1]))

In above implementation, we have already added some parameters to tune the filter parameters. Let us write a function to look at the temporal and spectral properties of the DA/AD filter:

In[9]:
# Source code has been redacted in online version
# Omitting 16 lines of source code
In[10]:
showDA_FilterDesign()

Uh, the filter we have designed also shows the sidelobes at exactly the same positions as in the RX spectrum. Looking at the impulse response of the filter, these side lobes become clear: The filter is just abruptly stopping in the time domain, making it virtually windowed by a rectangular window. Let us try to see what happens when we use a different window (in this case it's a Kaiser window):

In[11]:
showDA_FilterDesign(useWindow=True)

Great! The sidelobes significantly decrease. However, as the downside the main lobe becomes much wider, reaching significantly outside of the baseband bandwidth. Let's see, if this behaviour is also occuring in the transmitted signal:

In[12]:
showTXSpectrum(NoiseTransmitter, AD_DA_filterUseWindow=True)
Using Audio device default
Stream Stopped 1
Stream Stopped 2

Yes, the sidelobes have decayed significantly, but the overall signal became much wider than the actual baseband bandwidth. Maybe, this is also not what we want. What can we do to reduce both the sidelobes and keep the signal spectrally confined? We can approximate the underlying infinitely long RRC filter more accurately by using a longer interpolation filter. For example, let's use a filter that has the length of 9 baseband samples and again look at its spectrum:

In[13]:
showDA_FilterDesign(filterLength=9, useWindow=True)

Great! This filter has low sidelobes but also does not widen the mainlobe too much. At the downside, the longer filter requires more calculations to perform the interpolation operation and hence costs more operation power. For completeness, let's use this filter in the RF signal transmission:

In[14]:
showTXSpectrum(NoiseTransmitter, AD_DA_filterLength=9, AD_DA_filterUseWindow=True)
Using Audio device default
Stream Stopped 1
Stream Stopped 2

Yes, again the RF spectrum follows our analysis of the DA filter. Using this filter we have a narrow TX spectrum where at the same time only very low sidelobes occur.

Conclusion

In this notebook we have touched the filter design for our up- and downconversion units. We have seen that using a window and a longer filter response improves the spectral properties, however at the cost of increased calculation complexity. We will need the presented knowledge in the notebook about 5G Waveforms. The next notebook is about fundamentals of OFDM modulation and demodulation.

This article is part of the fundamentals of my real-world tutorial on OFDM using a cheap soundcard as the radio. If this notebook is interesting to you, check out the full tutorial!

Copyright (C) 2025 - dspillustrations.com

In[]:
 

DSPIllustrations.com is a participant in the Amazon Services LLC Associates Program, an affiliate advertising program designed to provide a means for sites to earn advertising fees by advertising and linking to amazon.com, amazon.de, amazon.co.uk, amazon.it.