'Right method for finding 2-D Spatial Spectrum from CSD
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) However, by using improved code I get the figure with higher resolution as Fig. 02 which is different from Fig. 01.
However, by using improved code I get the figure with higher resolution as Fig. 02 which is different from Fig. 01.
 
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.
 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.
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.
Solution 1:[1]
It looks to me like you're zooming in on the central lobe. This would also explain why the scale doesn't go from 0 to 1.
If I change these likes:
kx = np.linspace(-20*k,20*k,nx)  # space vector
ky=  np.linspace(-20*k,20*k,ny)   # space vector
then I get
which looks closer to what you're looking for.
To improve the resolution, I've done a bit of rewriting to get this new picture. See updated code below.
NOTE: I am still not certain this is doing the right thing.
Code I used
# Code from https://stackoverflow.com/questions/70768384/right-method-for-finding-2-d-spatial-spectrum-from-cross-spectral-densities
import numpy as np
from scipy import signal
import matplotlib.pyplot as plt
import cmath
# Set up data
# 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]
if (len(x) != len(y)):
    raise Exception('X and Y lengthd differ')
n = len(x)
dx = np.array(x);  M = len(dx)
dy = np.array(y) ; N = len(dy)
np.random.seed(12345)
raw_data=np.reshape(np.random.rand(10000*n),(10000,n))
f = 10  # frequency in Hz
c = 50  # wave speed 50, 80, 100, 200  m/s
k = 2.0*np.pi*f/c  # wavenumber
kx = np.linspace(-20*k,20*k,n*10)  # space vector
ky=  np.linspace(-20*k,20*k,n*10)   # space vector
# 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)
            #real_csd = np.real(Pxy)
            total_csd.append(Pxy)                     # output as list
            
    return np.array(total_csd)
## Spatial Spectra:- DFT of the csd along two dimension
def DFT2D(data):
    #data = np.asarray(data)
    dft2d = np.zeros((len(kx),len(ky)), 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
# 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, n))
dft = DFT2D(fcsd)  # Data or cross-correlation matrix
spec = np.abs(dft) #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")
Solution 2:[2]
As per above equation, the script for the spatial spectrum is better match as follows. The function of the "DFT2D()" is modified here which satisfy the equation.
    def DFT2D(data):
        P=len(kx)
        Q=len(ky)
        dft2d = np.zeros((P,Q), dtype=complex)
        for k in range(P):
            for l in range(Q):
                sum_matrix = 0.0
                for m in range(M):
                    for n in range(N): #
                        e = cmath.exp(-1j*(float(kx[k]*(dx[m]-dx[n])+ float(ky[l]*(dy[m]-dy[n])))))#* cmath.exp(-1j*w*t[n]))
                        sum_matrix += data[m, n] * e
                        #print('sum matrix would be', sum_matrix)
                #print('sum matrix would be', sum_matrix)
                dft2d[k,l] = sum_matrix
         return dft2d
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source | 
|---|---|
| Solution 1 | |
| Solution 2 | Alan22 | 



