SciPy/Numpy seems to support many filters, but not the root-raised cosine filter. Is there a trick to easily create one rather than calculating the transfer function? An approximation would be fine as well.
The commpy
package has several filters included with it. The order of return variables was switched in an earlier version (as of this edit, current version is 0.8.0). To install, follow instructions here or here.
Here's an example for 1024 symbols of QAM16:
import numpy as np
from commpy.modulation import QAMModem
from commpy.filters import rrcosfilter
N = 1024 # Number of symbols. Also, the filter length in samples.
os = 8 # Over-sampling factor
# Create modulation. QAM16 makes 4 bits/symbol
mod1 = QAMModem(16)
# Generate the bit stream for N symbols
sB = np.random.randint(0, 2, N*mod1.num_bits_symbol)
# Generate N complex-integer valued symbols
sQ = mod1.modulate(sB)
sQ_upsampled = np.zeros(os*(len(sQ)-1)+1,dtype = np.complex64)
sQ_upsampled[::os] = sQ
# Create a filter with limited bandwidth. Parameters:
# N: Filter length in samples
# 0.8: Roll off factor alpha
# 1: Symbol period in time-units
# os: Sample rate in 1/time-units
sPSF = rrcosfilter(N, alpha=0.8, Ts=1, Fs=os)[1]
# Analog signal has N/2 leading and trailing near-zero samples
qW = np.convolve(sPSF, sQ_upsampled)
Here's some explanation of the parameters. N
is the number of baud samples. You need 4 times as many bits (in the case of QAM) as samples. I made the sPSF
array return with N
elements so we can see the signal with leading and trailing samples. See the Wikipedia Root-raised-cosine filter page for explanation of parameter alpha
. Ts
is the symbol period in seconds and Fs
is the number of filter samples per Ts
. I like to pretend Ts=1
to keep things simple (unit symbol rate). Then Fs
is the number of complex waveform samples per baud point.
If you use return element 0 from rrcosfilter
to get the sample time indexes, you need to insert the correct symbol period and filter sample rate in Ts
and Fs
for the index values to be correctly scaled.