I am trying to show the gray scott model of diffusion. I keep getting a bunch of runtime warning errors even though I feel like my code is really close to correct. Is there something wrong with my discretization?
import numpy as np
import matplotlib.pyplot as plt
#parameters
N=128
F=.042
k=.062
Du=(2**-5)*(N**2/6.25)
Dv=(1**-5)*(N**2/6.25)
tend=1000
dt=tend/N
t=0
#start arrays
U=np.ones((N,N))
V=np.zeros((N,N))
#Initial Value Boxes (20x20 in middle)
low=int(((N/2)-10))
high=int(((N/2)+10))+1
U[low:high,low:high]=.5
V[low:high,low:high]=.25
#Random Noise
U+=.01*np.random.random((N,N))
V+=.01*np.random.random((N,N))
#Laplace
def Laplace(f):
return np.roll(f,1)+np.roll(f,-1)+np.roll(f,1,axis=False)+np.roll(f,-1,axis=False)-4*f
#Solve
pstep=100
for t in range(tend):
U+=((Du*Laplace(U))-(U*V*V)+(F*(1-U)))
V+=((Dv*Laplace(V))+(U*V*V)-(V*(F+k)))
if t%pstep ==0:
print(t//pstep)
plt.imshow(U, interpolation='bicubic',cmap=plt.cm.jet)
Ok, I got it to work by changing a few things in the calculation, but mostly altering the numeric stability by massively decreasing the diffusion coefficients, and decreasing the timestep. The net result of this, is that the whole simulation changes less between each step, so the value of the change is much smaller.
The errors you were getting were due to overflow of the floats in the calculation of dU and dV, by slowing the whole thing down (more timesteps) you don't need such massive numbers in dU and dV
import numpy as np
import matplotlib.pyplot as plt
# parameters
N = 128
F = .042
k = .062
# Du=(2**-5)*(N**2/6.25) # These were way too high for the
# numeric stability given the timestep
Du = 0.1
# Dv=(1**-5)*(N**2/6.25)
Dv = 0.5
tend = 1000
dt = tend / N
t = 0
dt = 0.1 # Timestep -
# the smaller you go here, the faster you can let the diffusion coefficients be
# start arrays
U = np.ones((N, N))
V = np.zeros((N, N))
# Initial Value Boxes (20x20 in middle)
low = int(((N / 2) - 10))
high = int(((N / 2) + 10)) + 1
U[low:high, low:high] = .5
V[low:high, low:high] = .25
# Random Noise
U += .01 * np.random.random((N, N))
V += .01 * np.random.random((N, N))
# Laplace
def Laplace(f):
return np.roll(f, 1) + np.roll(f, -1) + np.roll(f, 1, axis=False) + np.roll(f,-1, axis=False) - 4 * f
# Solve
pstep = 100
for t in range(tend):
dU = ((Du * Laplace(U)) - (U * V * V) + (F * (1 - U))) * dt
dV = ((Dv * Laplace(V)) + (U * V * V) - (V * (F + k))) * dt
U += dU
V += dV
if t % pstep == 0:
print(t // pstep)
plt.imshow(U, interpolation='bicubic', cmap=plt.cm.jet, vmin=0, vmax=1)
Of course the changes I made alter the physics a bit, and you will need to alter your t and pstep so those values make sense. And also check how you were calculating Du and Dv. If those values are actually supposed to be ~8000, then you need a much much smaller timestep.
For anyone else's reference, the Gray Scott model is explained here
Related
I am solving the dissipation equation using a finite differencing scheme. The initial condition is a half sin wave with Dirchlet boundary conditions on both sides. I insert an extra point on each side of the domain to enforce the Dirchlet boundary condition while maintaining fourth-order-accuracy, then use forwards-euler to evolve it in time
When I switch from the second-order-accurate stencil to the fourth-order-accurate stencil /12.
I do not see an improvement in the rate of convergence when I plot vs an estimate of the error.
I wrote and commented a code that shows my problem. When I use the 5 point strategy, my rate of convergence is the same:
Why is this happening? Why isn't the fourth-order-accurate stencil helping the convergence rate? I combed over this carefully and I think that there must be some issue in my understanding.
# Let's evolve the diffusion equation in time with Dirchlet BCs
# Load modules
import numpy as np
import matplotlib.pyplot as plt
# Domain size
XF = 1
# Viscosity
nu = 0.01
# Spatial Differentiation function, approximates d^u/dx^2
def diffusive_dudt(un, nu, dx, strategy='5c'):
undiff = np.zeros(un.size, dtype=np.float128)
# O(h^2)
if strategy == '3c':
undiff[2:-2] = nu * (un[3:-1] - 2 * un[2:-2] + un[1:-3]) / dx**2
# O(h^4)
elif strategy == '5c':
undiff[2:-2] = nu * (-1 * un[4:] + 16 * un[3:-1] - 30 * un[2:-2] + 16 * un[1:-3] - un[:-4]) / (12 * dx**2 )
else: raise(IOError("Invalid diffusive strategy")) ; quit()
return undiff
def geturec(x, nu=.05, evolution_time=1, u0=None, n_save_t=50, ubl=0., ubr=0., diffstrategy='5c', dt=None, returndt=False):
dx = x[1] - x[0]
# Prescribde cfl=0.1 and ftcs=0.2
if dt is not None: pass
else: dt = min(.1 * dx / 1., .2 / nu * dx ** 2)
if returndt: return dt
nt = int(evolution_time / dt)
divider = int(nt / float(n_save_t))
if divider ==0: raise(IOError("not enough time steps to save %i times"%n_save_t))
# The default initial condition is a half sine wave.
u_initial = ubl + np.sin(x * np.pi)
if u0 is not None: u_initial = u0
u = u_initial
u[0] = ubl
u[-1] = ubr
# insert ghost cells; extra cells on the left and right
# for the edge cases of the finite difference scheme
x = np.insert(x, 0, x[0]-dx)
x = np.insert(x, -1, x[-1]+dx)
u = np.insert(u, 0, ubl)
u = np.insert(u, -1, ubr)
# u_record holds all the snapshots. They are evenly spaced in time,
# except the final and initial states
u_record = np.zeros((x.size, int(nt / divider + 2)))
# Evolve through time
ii = 1
u_record[:, 0] = u
for _ in range(nt):
un = u.copy()
dudt = diffusive_dudt(un, nu, dx, strategy=diffstrategy)
# forward euler time step
u = un + dt * dudt
# Save every xth time step
if _ % divider == 0:
#print "C # ---> ", u * dt / dx
u_record[:, ii] = u.copy()
ii += 1
u_record[:, -1] = u
return u_record[1:-1, :]
# define L-1 Norm
def ul1(u, dx): return np.sum(np.abs(u)) / u.size
# Now let's sweep through dxs to find convergence rate
# Define dxs to sweep
xrang = np.linspace(350, 400, 4)
# this function accepts a differentiation key name and returns a list of dx and L-1 points
def errf(strat):
# Lists to record dx and L-1 points
ypoints = []
dxs= []
# Establish truth value with a more-resolved grid
x = np.linspace(0, XF, 800) ; dx = x[1] - x[0]
# Record truth L-1 and dt associated with finest "truth" grid
trueu = geturec(nu=nu, x=x, diffstrategy=strat, evolution_time=2, n_save_t=20, ubl=0, ubr=0)
truedt = geturec(nu=nu, x=x, diffstrategy=strat, evolution_time=2, n_save_t=20, ubl=0, ubr=0, returndt=True)
trueqoi = ul1(trueu[:, -1], dx)
# Sweep dxs
for nx in xrang:
x = np.linspace(0, XF, nx) ; dx = x[1] - x[0]
dxs.append(dx)
# Run solver, hold dt fixed
u = geturec(nu=nu, x=x, diffstrategy='5c', evolution_time=2, n_save_t=20, ubl=0, ubr=0, dt=truedt)
# record |L-1(dx) - L-1(truth)|
qoi = ul1(u[:, -1], dx)
ypoints.append(np.abs(trueqoi - qoi))
return dxs, ypoints
# Plot results. The fourth order method should have a slope of 4 on the log-log plot.
from scipy.optimize import minimize as mini
strategy = '5c'
dxs, ypoints = errf(strategy)
def fit2(a): return 1000 * np.sum((a * np.array(dxs) ** 2 - ypoints) ** 2)
def fit4(a): return 1000 * np.sum((np.exp(a) * np.array(dxs) ** 4 - ypoints) ** 2)
a = mini(fit2, 500).x
b = mini(fit4, 11).x
plt.plot(dxs, a * np.array(dxs)**2, c='k', label=r"$\nu^2$", ls='--')
plt.plot(dxs, np.exp(b) * np.array(dxs)**4, c='k', label=r"$\nu^4$")
plt.plot(dxs, ypoints, label=r"Convergence", marker='x')
plt.yscale('log')
plt.xscale('log')
plt.xlabel(r"$\Delta X$")
plt.ylabel("$L-L_{true}$")
plt.title(r"$\nu=%f, strategy=%s$"%(nu, strategy))
plt.legend()
plt.savefig('/Users/kilojoules/Downloads/%s.pdf'%strategy, bbox_inches='tight')
The error of the scheme is O(dt,dx²) resp. O(dt, dx⁴). As you keep dt=O(dx^2), the combined error is O(dx²) in both cases. You could try to scale dt=O(dx⁴) in the second case, however the balance of truncation and floating point error of the Euler or any first order method is reached around L*dt=1e-8, where L is a Lipschitz constant for the right side, so higher for more complex right sides. Even in the best case, going beyond dx=0.01 would be futile. Using a higher order method in time direction should help.
You used the wrong error metric. If you compare the fields on a point-by-point basis you'll get the convergence rate you were after.
I attempt to plot bifurcation diagram for following one-dimensional spatially extended system with boundary conditions
x[i,n+1] = (1-eps)*(r*x[i,n]*(1-x[i,n])) + 0.5*eps*( r*x[i-1,n]*(1-x[i-1,n]) + r*x[i+1,n]*(1-x[i+1,n])) + p
I am facing problem in getting desired output figure may be because of number of transients I am using. Can someone help me out by cross-checking my code, what values of nTransients should I choose or how many transients should I ignore ?
My Python code is as follows:
import numpy as np
from numpy import *
from pylab import *
L = 60 # no. of lattice sites
eps = 0.6 # diffusive coupling strength
r = 4.0 # control parameter r
np.random.seed(1010)
ic = np.random.uniform(0.1, 0.9, L) # random initial condition betn. (0,1)
nTransients = 900 # The iterates we'll throw away
nIterates = 1000 # This sets how much the attractor is filled in
nSteps = 400 # This sets how dense the bifurcation diagram will be
pLow = -0.4
pHigh = 0.0
pInc = (pHigh-pLow)/float(nSteps)
def LM(p, x):
x_new = []
for i in range(L):
if i==0:
x_new.append((1-eps)*(r*x[i]*(1-x[i])) + 0.5*eps*(r*x[L-1]*(1-x[L-1]) + r*x[i+1]*(1-x[i+1])) + p)
elif i==L-1:
x_new.append((1-eps)*(r*x[i]*(1-x[i])) + 0.5*eps*(r*x[i-1]*(1-x[i-1]) + r*x[0]*(1-x[0])) + p)
elif i>0 and i<L-1:
x_new.append((1-eps)*(r*x[i]*(1-x[i])) + 0.5*eps*(r*x[i-1]*(1-x[i-1]) + r*x[i+1]*(1-x[i+1])) + p)
return x_new
for p in arange(pLow, pHigh, pInc):
# set initial conditions
state = ic
# throw away the transient iterations
for i in range(nTransients):
state = LM(p, state)
# now stote the next batch of iterates
psweep = [] # store p values
x = [] # store iterates
for i in range(nIterates):
state = LM(p, state)
psweep.append(p)
x.append(state[L/2-1])
plot(psweep, x, 'k,') # Plot the list of (r,x) pairs as pixels
xlabel('Pinning Strength p')
ylabel('X(L/2)')
# Display plot in window
show()
Can someone also tell me figure displayed by pylab in the end has either dots or lines as a marker, if it is lines then how to get plot with dots.
This is my output image for reference, after using pixels:
It still isn't clear exactly what your desired output is, but I'm guessing you're aiming for something that looks like this image from Wikipedia:
Going with that assumption, I gave it my best shot, but I'm guessing your equations (with the boundary conditions and so on) give you something that simply doesn't look quite that pretty. Here's my result:
This plot by itself may not look like the best thing ever, however, if you zoom in, you can really see some beautiful detail (this is right from the center of the plot, where the two arms of the bifurcation come down, meet, and then branch away again):
Note that I have used horizontal lines, with alpha=0.1 (originally you were using solid, vertical lines, which was why the result didn't look good).
The code!
I essentially modified your program a little to make it vectorized: I removed the for loop over p, which made the whole thing run almost instantaneously. This enabled me to use a much denser sampling for p, and allowed me to plot horizontal lines.
from __future__ import print_function, division
import numpy as np
import matplotlib.pyplot as plt
L = 60 # no. of lattice sites
eps = 0.6 # diffusive coupling strength
r = 4.0 # control parameter r
np.random.seed(1010)
ic = np.random.uniform(0.1, 0.9, L) # random initial condition betn. (0,1)
nTransients = 100 # The iterates we'll throw away
nIterates = 100 # This sets how much the attractor is filled in
nSteps = 4000 # This sets how dense the bifurcation diagram will be
pLow = -0.4
pHigh = 0.0
pInc = (pHigh - pLow) / nSteps
def LM(p, x):
x_new = np.empty(x.shape)
for i in range(L):
if i == 0:
x_new[i] = ((1 - eps) * (r * x[i] * (1 - x[i])) + 0.5 * eps * (r * x[L - 1] * (1 - x[L - 1]) + r * x[i + 1] * (1 - x[i + 1])) + p)
elif i == L - 1:
x_new[i] = ((1 - eps) * (r * x[i] * (1 - x[i])) + 0.5 * eps * (r * x[i - 1] * (1 - x[i - 1]) + r * x[0] * (1 - x[0])) + p)
elif i > 0 and i < L - 1:
x_new[i] = ((1 - eps) * (r * x[i] * (1 - x[i])) + 0.5 * eps * (r * x[i - 1] * (1 - x[i - 1]) + r * x[i + 1] * (1 - x[i + 1])) + p)
return x_new
p = np.arange(pLow, pHigh, pInc)
state = np.tile(ic[:, np.newaxis], (1, p.size))
# set initial conditions
# throw away the transient iterations
for i in range(nTransients):
state = LM(p, state)
# now store the next batch of iterates
x = np.empty((p.size, nIterates)) # store iterates
for i in range(nIterates):
state = LM(p, state)
x[:, i] = state[L // 2 - 1]
# Plot the list of (r,x) pairs as pixels
plt.plot(p, x, c=(0, 0, 0, 0.1))
plt.xlabel('Pinning Strength p')
plt.ylabel('X(L/2)')
# Display plot in window
plt.show()
I don't want to try explaining the whole program to you: I've used a few standard numpy tricks, including broadcasting, but otherwise, I have not modified much. I've not modified your LM function at all.
Please don't hesitate to ask me in the comments if you have any questions! I'm happy to explain specifics that you want explained.
A note on transients and iterates: Hopefully, now that the program runs much faster, you can try playing with these elements yourself. To me, the number of transients seemed to decide for how long the plot remained "deterministic-looking". The number of iterates just increases the density of plot lines, so increasing this beyond a point didn't seem to make sense to me.
I tried increasing the number of transients all the way up till 10,000. Here's my result from that experiment, for your reference:
I am trying to implement a finite difference approximation to solve the Heat Equation, u_t = k * u_{xx}, in Python using NumPy.
Here is a copy of the code I am running:
## This program is to implement a Finite Difference method approximation
## to solve the Heat Equation, u_t = k * u_xx,
## in 1D w/out sources & on a finite interval 0 < x < L. The PDE
## is subject to B.C: u(0,t) = u(L,t) = 0,
## and the I.C: u(x,0) = f(x).
import numpy as np
import matplotlib.pyplot as plt
# parameters
L = 1 # legnth of the rod
T = 10 # terminal time
N = 10
M = 100
s = 0.25
# uniform mesh
x_init = 0
x_end = L
dx = float(x_end - x_init) / N
x = np.arange(x_init, x_end, dx)
x[0] = x_init
# time discretization
t_init = 0
t_end = T
dt = float(t_end - t_init) / M
t = np.arange(t_init, t_end, dt)
t[0] = t_init
# Boundary Conditions
for m in xrange(0, M):
t[m] = m * dt
# Initial Conditions
for j in xrange(0, N):
x[j] = j * dx
# definition of solution u(x,t) to u_t = k * u_xx
u = np.zeros((N, M+1)) # array to store values of the solution
# Finite Difference Scheme:
u[:,0] = x**2 #initial condition
for m in xrange(0, M):
for j in xrange(1, N-1):
if j == 1:
u[j-1,m] = 0 # Boundary condition
elif j == N-1:
u[j+1,m] = 0
else:
u[j,m+1] = u[j,m] + s * ( u[j+1,m] -
2 * u[j,m] + u[j-1,m] )
print u, #t, x
plt.plot(u, t)
#plt.show()
I think my code is working properly and it is producing an output. I want to plot the output of the solution u versus t (my time vector). If I can plot the graph then I am able to check if my numerical approximation agrees with the expected phenomena for the Heat Equation. However, I am getting the error that "x and y must have same first dimension". How can I correct this issue?
An additional question: Am I better off attempting to make an animation with matplotlib.animation instead of using matplotlib.plyplot ???
Thanks so much for any and all help! It is very greatly appreciated!
Okay so I had a "brain dump" and tried plotting u vs. t sort of forgetting that u, being the solution to the Heat Equation (u_t = k * u_{xx}), is defined as u(x,t) so it has values for time. I made the following correction to my code:
print u #t, x
plt.plot(u)
plt.show()
And now my programming is finally displaying an image. And here it is:
It is absolutely beautiful, isn't it?
I'm trying to improve time efficiency of part of my script but I don't have any more idea. I ran following script in either Matlab and Python but Matlab implementation is four times quicker than Python 's one. Any idea how to improve ?
Python:
import time
import numpy as np
def ComputeGradient(X, y, theta, alpha):
m = len(y)
factor = alpha / m
h = np.dot(X, theta)
theta = [theta[i] - factor * sum((h-y) * X[:,i]) for i in [0,1]]
#Also tried this but with worse performances
#diff = np.tile((h-y)[:, np.newaxis],2)
#theta = theta - factor * sum(diff * X)
return theta
if __name__ == '__main__':
data = np.loadtxt("data_LinReg.txt", delimiter=',')
theta = [0, 0]
alpha = 0.01
X = data[:,0]
y = data[:,1]
X = np.column_stack((np.ones(len(y)), X))
start_time = time.time()
for i in range(0, 1500, 1):
theta = ComputeGradient(X, y, theta, alpha)
stop_time = time.time()
print("--- %s seconds ---" % (stop_time - start_time))
--> 0.048s
Matlab:
data = load('data_LinReg.txt');
X = data(:, 1); y = data(:, 2);
m = length(y);
X = [ones(m, 1), data(:,1)]; % Add a column of ones to x
theta = zeros(2, 1);
iterations = 1500;
alpha = 0.01;
tic
for i = 1:1500
theta = gradientDescent(X, y, theta, alpha);
end
toc
function theta = gradientDescent(X, y, theta, alpha)
m = length(y); % number of training examples
h = X * theta;
t1 = theta(1) - alpha * sum(X(:,1).*(h-y)) / m;
t2 = theta(2) - alpha * sum(X(:,2).*(h-y)) / m;
theta = [t1; t2];
end
--> 0.01s
[EDIT] : solution avenue
One possible avenue is to use numpy vectorization instead of python root functions. In the proposed code, replacing sum by np.sum improves the time efficiency so that it is closer to Matlab (0.019s instead of 0.048s)
Furthermore, I tested separately the functions on vectors : np.dot, np.sum, * (product) and all these functions seems to be faster (really faster in some case) than the equivalent Matlab. I wonder then why it is still slower in Python....
This solution presents an optimized MATLAB implementation that does -
Funtion-inlining of the gradient-descent implementation.
Pre-computation of certain values that are repeatedly used inside the loop.
Code -
data = load('data_LinReg.txt');
iterations = 1500;
alpha = 0.01;
m = size(data,1);
M = alpha/m; %// scaling factor
%// Pre-compute certain values that are repeatedly used inside the loop
sum_a = M*sum(data(:,1));
sum_p = M*sum(data(:,2));
sum_ap = M*sum(data(:,1).*data(:,2));
sum_sqa = M*sum(data(:,1).^2);
one_minus_alpha = 1 - alpha;
one_minus_sum_sqa = 1 - sum_sqa;
%// Start processing
t1n0 = 0;
t2n0 = 0;
for i = 1:iterations
temp = t1n0*one_minus_alpha - t2n0*sum_a + sum_p;
t2n0 = t2n0*one_minus_sum_sqa - t1n0*sum_a + sum_ap;
t1n0 = temp;
end
theta = [t1n0;t2n0];
Quick tests show that this presents an appreciable speedup over the MATLAB code posted in the question.
Now, I am not too familiar with python, but I would assume that this MATLAB code could be easily ported to python.
I don't know how much of a difference it will make, but you can simplify your function with something like:
s = alpha / size(X,1);
gradientDescent = #(theta)( theta - s * X' * (X*theta - y) );
Since you need theta_{i} in order to find theta_{i+1}, I don't see any way to avoid the loop.
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.7.0). To install, foemphasized textllow instructions here or here.
Here's a use 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
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
# 24: Sample rate in 1/time-units
sPSF = rrcosfilter(N, alpha=0.8, Ts=1, Fs=over_sample)[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.
It would be nice to have the root-raised cosine filter standardized in a common package. Here is my implementation in the meantime based on commpy. It vectorized with numpy, and normalized without consideration of the symbol rate.
def raised_root_cosine(upsample, num_positive_lobes, alpha):
"""
Root raised cosine (RRC) filter (FIR) impulse response.
upsample: number of samples per symbol
num_positive_lobes: number of positive overlaping symbols
length of filter is 2 * num_positive_lobes + 1 samples
alpha: roll-off factor
"""
N = upsample * (num_positive_lobes * 2 + 1)
t = (np.arange(N) - N / 2) / upsample
# result vector
h_rrc = np.zeros(t.size, dtype=np.float)
# index for special cases
sample_i = np.zeros(t.size, dtype=np.bool)
# deal with special cases
subi = t == 0
sample_i = np.bitwise_or(sample_i, subi)
h_rrc[subi] = 1.0 - alpha + (4 * alpha / np.pi)
subi = np.abs(t) == 1 / (4 * alpha)
sample_i = np.bitwise_or(sample_i, subi)
h_rrc[subi] = (alpha / np.sqrt(2)) \
* (((1 + 2 / np.pi) * (np.sin(np.pi / (4 * alpha))))
+ ((1 - 2 / np.pi) * (np.cos(np.pi / (4 * alpha)))))
# base case
sample_i = np.bitwise_not(sample_i)
ti = t[sample_i]
h_rrc[sample_i] = np.sin(np.pi * ti * (1 - alpha)) \
+ 4 * alpha * ti * np.cos(np.pi * ti * (1 + alpha))
h_rrc[sample_i] /= (np.pi * ti * (1 - (4 * alpha * ti) ** 2))
return h_rrc
commpy doesn't seem to be released yet. But here is my nugget of knowledge.
beta = 0.20 # roll off factor
Tsample = 1.0 # sampling period, should at least twice the rate of the symbol
oversampling_rate = 8 # oversampling of the bit stream, this gives samples per symbol
# must be at least 2X the bit rate
Tsymbol = oversampling_rate * Tsample # pulse duration should be at least 2 * Ts
span = 50 # number of symbols to span, must be even
n = span*oversampling_rate # length of the filter = samples per symbol * symbol span
# t_step must be from -span/2 to +span/2 symbols.
# each symbol has 'sps' number of samples per second.
t_step = Tsample * np.linspace(-n/2,n/2,n+1) # n+1 to include 0 time
BW = (1 + beta) / Tsymbol
a = np.zeros_like(t_step)
for item in list(enumerate(t_step)):
i,t = item
# t is n*Ts
if (1-(2.0*beta*t/Tsymbol)**2) == 0:
a[i] = np.pi/4 * np.sinc(t/Tsymbol)
print 'i = %d' % i
elif t == 0:
a[i] = np.cos(beta * np.pi * t / Tsymbol)/ (1-(2.0*beta*t/Tsymbol)**2)
print 't = 0 captured'
print 'i = %d' % i
else:
numerator = np.sinc( np.pi * t/Tsymbol )*np.cos( np.pi*beta*t/Tsymbol )
denominator = (1.0 - (2.0*beta*t/Tsymbol)**2)
a[i] = numerator / denominator
#a = a/sum(a) # normalize total power
plot_filter = 0
if plot_filter == 1:
w,h = signal.freqz(a)
fig = plt.figure()
plt.subplot(2,1,1)
plt.title('Digital filter (raised cosine) frequency response')
ax1 = fig.add_subplot(211)
plt.plot(w/np.pi, 20*np.log10(abs(h)),'b')
#plt.plot(w/np.pi, abs(h),'b')
plt.ylabel('Amplitude (dB)', color = 'b')
plt.xlabel(r'Normalized Frequency ($\pi$ rad/sample)')
ax2 = ax1.twinx()
angles = np.unwrap(np.angle(h))
plt.plot(w/np.pi, angles, 'g')
plt.ylabel('Angle (radians)', color = 'g')
plt.grid()
plt.axis('tight')
plt.show()
plt.subplot(2,1,2)
plt.stem(a)
plt.show()
I think the correct response is to generate the desire impulse response. For a raised cosine filter the function is
h(n) = (sinc(n/T)*cos(pi * alpha* n /T)) / (1-4*(alpha*n/T)**2)
Select the number of points for your filter and generate the weights.
output = scipy.signal.convolve(signal_in, h)
This is basically the same function as in CommPy but much smaller in code:
def rcosfilter(N, beta, Ts, Fs):
t = (np.arange(N) - N / 2) / Fs
return np.where(np.abs(2*t) == Ts / beta,
np.pi / 4 * np.sinc(t/Ts),
np.sinc(t/Ts) * np.cos(np.pi*beta*t/Ts) / (1 - (2*beta*t/Ts) ** 2))
SciPy will support any filter. Just calculate the impulse response and use any of the appropriate scipy.signal filter/convolve functions.