I try to implement the spatial spectrum from the above equation (attached)
Where kX, kY are the grid points in k space, C(w,r) - cross spectral densities between the i'th and j'th sensor(here it is a matrix of size ns * ns >no. of sensors). x, y are distances between the sensors. (nk - grid density for kx, ky)
I look for suitable python implementation of the above equation. I've 34 sensors which generates data of size [row*column]=[n*34]
. At first, I've found the cross spectral densities (CSD) of among the data of each sensor. Then 2D DFT is performed of the CSD values to get the spatial spectrum.
*) I'm not sure whether the procedure is correct or not. **) Does the python implementation procedure correct? ***) Also, if someone provides some relevant tutorial/link, it will also be helpful for me.
import numpy as np
from scipy import signal
import matplotlib.pyplot as plt
import cmath
# Finding cross spectral density (CSD)
fs=500
def csdMat(data):
rows, cols = data.shape
total_csd = []
for i in range(cols):
for j in range(cols):
f, Pxy = signal.csd(data[:,i], data[:,j], fs, nperseg=512)
abs_csd = np.abs(Pxy)
total_csd.append(abs_csd) # output as list
csd_mat = np.array(total_csd)
return csd_mat
## Spatial Spectra:- DFT of the csd along two dimension
def DFT2D(data):
#data = np.asarray(data)
dft2d = np.zeros((M,N), dtype=complex)
for k in range(len(kx)):
for l in range(len(ky)):
sum_matrix = 0.0
for m in range(M):
for n in range(N):
e = cmath.exp(- 1j * ((kx[k] * dx[m]) / len(dx) + (ky[l] * dy[n]) / len(dy)))
sum_matrix += data[m,n] * e
dft2d[k,l] = sum_matrix
return dft2d
raw_data=np.reshape(np.random.rand(10000*34),(10000,34))
# Call the seismic array
#** Open .NPY files as an array
#with open('res_array_1000f_131310.npy', 'rb') as f:
# arr= np.load(f)
#raw_data = arr[0:10000, :]
#CSD of the seismic data
csd = csdMat(raw_data)
print('Shape of CSD data', csd.shape)
# CSD data of a specific frequency
csd_dat=csd[:, 11]
fcsd = np.reshape(csd_dat, (-1, 34))
fcsd.shape
n = 34
f = 10 # frequency in Hz
c = 50 # wave speed 50, 80, 100, 200 m/s
k = 2.0*np.pi*f/c # wavenumber
nx = n # grid density
ny = n
kx = np.linspace(-k,k,nx) # space vector
ky= np.linspace(-k,k,ny) # space vector
# Distance[Meter] between sensors
x = [2.1,2.1,-0.7,-2.1,-2.1,-0.7,-0.7,0.6,-5.7,-8.5,-11.4,-7.7,-6.3,-3.5,-2.1,-3.4,5.4,-5.2,-8.9,-10,-10,5.4,5.4,-0.8,-3.6,-6.2,-6.8,-12.2,-17.1,-19,-18.6,-13.5,14.8,14.8]
y = [6.65,4.15,3.65,5.05,7.25,8.95,11.85,8.95,-2,-0.6,-0.9,1.25,2.9,0.9,-0.1,-1.4,9.2,5.2,4.8,6.1,8.9,13.3,17.1,17.9,13.8,-9.3,-5.2,-3.6,-3.6,-0.9,3.7,3.7,-1.8,5.7]
dx = np.array(x); M = len(dx)
dy = np.array(y) ; N = len(dy)
X,Y = np.meshgrid(kx, ky)
dft = DFT2D(fcsd) # Data or cross-correlation matrix
spec = dft.real # Spectrum or 2D_DFT of data[real part]
spec = spec/spec.max()
plt.figure()
c = plt.imshow(spec, cmap ='seismic', vmin = spec.min(), vmax = spec.max(),
extent =[kx.min(), kx.max(), ky.min(), ky.max()],
interpolation ='nearest', origin ='lower')
plt.colorbar(c)
plt.rcParams.update({'font.size': 18})
plt.xlabel("Wavenumber, $K_x$ [rad/m]", fontsize=18)
plt.ylabel("Wavenumber,$K_y$ [rad/m]", fontsize=18)
plt.title(f'Spatial Spectrum @10Hz', weight="bold")
#c = Wave Speed; 50, 80,100,200
cc = 2*np.pi*f /c *np.cos(np.linspace(0, 2*np.pi, 34))
cs = 2*np.pi*f /c *np.sin(np.linspace(0, 2*np.pi, 34))
plt.plot(cc,cs)
I've added another two figures to compare with the Fig. 01. When consider the range [-k, k], the plot looks like Fig. 03 which is analogous [w.r.t. XY-axis] to Fig. 01, I think this figure is OK except some K-space missed. I hope here exist an issue that need to be fixed.
In Fig. 04, we consider the k-space range [-20k, 20k], which looks good but don't have similar axis as of Fig. 01.
I've put the update Figure as follows: Can anyone help me to generate the figure 01 or similar type? I'm confused about the Figure 02. Can anybody help to make me understand? Thanks in advance.
spec = dft.real # Spectrum or 2D_DFT of data[real part]
? The spectrum is more likenp.abs(dft)
than just the real part of the DFT. – Unereal_csd = np.real(Pxy)
The cross-spectral density will be complex. Just taking the real part drops information. However, that doesn't fix the problem. – Unespec = np.abs(dft)
? I f I considerspec/(sampling frequency (fs)*len(kx)*len(ky)*M*N)
is that correct? – Janiknp.abs
to get the absolute value. DFTs are generally defined in pairs (forward and inverse). Some people put the scaling on the forward, but most put it on the inverse. Some put the scaling (square-rooted) on both. – Unespec/(Signal length(L) *len(kx)*len(ky)*M*N)
. Also, what will be the unit of the amplitude? Again, how to find the unit of amplitude is(ms)^2/Hertz
? – Janik