Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python - Plotting Antenna Radiation Pattern

This is a code I found and slightly modified. How can I scale the color from the origin and set the axes from the origin for the visualization? I've tried to find information but most of it is for 2d plots.

Here I have added two arrays for theta and phi at intervals of 45 degrees and an array of random numbers representing the power of the signal. The plot works but the signals and intervals are not quite correct. My goal here is to just add the axes from the origin and scale the color from the origin.

import pandas as pd
import numpy as np
import scipy as sci
import matplotlib.pyplot as plt
import mpl_toolkits.mplot3d.axes3d as Axes3D
from matplotlib import cm, colors
from array import *
import random

#theta 
vals_theta = array('i',[0,0,0,0,0,0,0,0,0,45,45,45,45,45,45,45,45,45,90,90,90, 
                        90,90,90,90,90,90,135,135,135,135,135,135,135,135,135,
                        180,180,180,180,180,180,180,180,180])
#phi
vals_phi = array('i',[0,45,90,135,180,225,270,315,360,
                      0,45,90,135,180,225,270,315,360,
                      0,45,90,135,180,225,270,315,360,
                      0,45,90,135,180,225,270,315,360,
                      0,45,90,135,180,225,270,315,360])
#random numbers simulating the power data
vals_power = np.random.uniform(low=-7.2E-21, high=7.2E-21, size=(45,))

theta1d = vals_theta
theta1d = np.array(theta1d);
theta2d = theta1d.reshape([5,9])

phi1d = vals_phi
phi1d = np.array(phi1d);
phi2d = phi1d.reshape([5,9])

power1d = vals_power
power1d = np.array(power1d);
power2d = power1d.reshape([5,9])

THETA = np.deg2rad(theta2d)
PHI = np.deg2rad(phi2d)
R = power2d
Rmax = np.max(R)

X = R * np.sin(THETA) * np.cos(PHI)
Y = R * np.sin(THETA) * np.sin(PHI)
Z = R * np.cos(THETA)

fig = plt.figure()

ax = fig.add_subplot(1,1,1, projection='3d')
ax.grid(True)
ax.axis('on')
ax.set_xticks([])
ax.set_yticks([])
ax.set_zticks([])

N = R / Rmax
ax.plot_surface(

    X, Y, Z, rstride=1, cstride=1, cmap=plt.get_cmap('jet'),

    linewidth=0, antialiased=False, alpha=0.5, zorder = 0.5)

ax.set_title('Spherical 3D Plot', fontsize=20)
m = cm.ScalarMappable(cmap=cm.jet)
m.set_array(R)
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
m = cm.ScalarMappable(cmap=cm.jet)
m.set_array(R) 
fig.colorbar(m, shrink=0.8);
ax.view_init(azim=300, elev = 30)

# Add Spherical Grid
phi ,theta = np.linspace(0, 2 * np.pi, 40), np.linspace(0, np.pi, 40)
PHI, THETA  = np.meshgrid(phi,theta)
R = Rmax

X = R * np.sin(THETA) * np.cos(PHI)
Y = R * np.sin(THETA) * np.sin(PHI)
Z = R * np.cos(THETA)

ax.plot_wireframe(X, Y, Z, linewidth=0.5, rstride=3, cstride=3)

print(theta1d)
print(theta2d)
print(power2d)
plt.show()

Trying to get a result approximate to this

like image 489
okrus Avatar asked Oct 23 '25 16:10

okrus


1 Answers

This is building off of Andrea's excellent answer, with a couple of additions that should be helpful for real-world data which may have fairly wide spacing between points. When I first plotted something with 45 degree spacing it looked like this:

Initial Plot

There are two obvious issues:

  1. The faces are very large and only have a single colour, even though they span a wide range of values.
  2. The shape is symmetric about the origin, but the colours of the faces are not symmetric.

Issue 1 can be improved by doing a linear interpolation of the data so that each face is divided into multiple sections that can have different colours.

Issue 2 happens because of the way face colours are assigned. Imagine a 3x3 grid of points on a 2D plane, with each point having a value. When you draw surfaces there will only be 2x2 faces, so the last row and column of values get thrown away, and the colour of each face is determined by only one corner of the face. What we really want is the value at the center of each face. We can estimate this by taking the average of the four corner values and using that to assign the colour.

Computationally this ends up being similar to the interpolation for issue 1, so I used the same function "interp_array" for both. I'm not much of a Python programmer so there's probably a more efficient way to do it, but it gets the job done.

Here is the plot with Issue 2 fixed but no interpolation. The symmetry is fixed but only 2 colours are used, because the faces are spaced equally from the origin.

Symmetry Fix

And here is the final plot with 8x interpolation between the points. Now it's closer to the kind of continuous-colour plot that you would see in commercial antenna measurement software.

Interpolation Added

import numpy as np
import matplotlib.pyplot as plt
import mpl_toolkits.mplot3d.axes3d as Axes3D
from matplotlib import cm, colors

def interp_array(N1):  # add interpolated rows and columns to array
    N2 = np.empty([int(N1.shape[0]), int(2*N1.shape[1] - 1)])  # insert interpolated columns
    N2[:, 0] = N1[:, 0]  # original column
    for k in range(N1.shape[1] - 1):  # loop through columns
        N2[:, 2*k+1] = np.mean(N1[:, [k, k + 1]], axis=1)  # interpolated column
        N2[:, 2*k+2] = N1[:, k+1]  # original column
    N3 = np.empty([int(2*N2.shape[0]-1), int(N2.shape[1])])  # insert interpolated columns
    N3[0] = N2[0]  # original row
    for k in range(N2.shape[0] - 1):  # loop through rows
        N3[2*k+1] = np.mean(N2[[k, k + 1]], axis=0)  # interpolated row
        N3[2*k+2] = N2[k+1]  # original row
    return N3


vals_theta = np.arange(0,181,45)
vals_phi = np.arange(0,361,45)

vals_phi, vals_theta = np.meshgrid(vals_phi, vals_theta)

THETA = np.deg2rad(vals_theta)
PHI = np.deg2rad(vals_phi)

# simulate the power data
R = abs(np.cos(PHI)*np.sin(THETA))  # 2 lobes (front and back)

interp_factor = 3  # 0 = no interpolation, 1 = 2x the points, 2 = 4x the points, 3 = 8x, etc

X = R * np.sin(THETA) * np.cos(PHI)
Y = R * np.sin(THETA) * np.sin(PHI)
Z = R * np.cos(THETA)

for counter in range(interp_factor):  # Interpolate between points to increase number of faces
    X = interp_array(X)
    Y = interp_array(Y)
    Z = interp_array(Z)

fig = plt.figure()

ax = fig.add_subplot(1,1,1, projection='3d')
ax.grid(True)
ax.axis('on')
ax.set_xticks([])
ax.set_yticks([])
ax.set_zticks([])

N = np.sqrt(X**2 + Y**2 + Z**2)
Rmax = np.max(N)
N = N/Rmax

axes_length = 1.5
ax.plot([0, axes_length*Rmax], [0, 0], [0, 0], linewidth=2, color='red')
ax.plot([0, 0], [0, axes_length*Rmax], [0, 0], linewidth=2, color='green')
ax.plot([0, 0], [0, 0], [0, axes_length*Rmax], linewidth=2, color='blue')

# Find middle points between values for face colours
N = interp_array(N)[1::2,1::2]

mycol = cm.jet(N)

surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, facecolors=mycol, linewidth=0.5, antialiased=True, shade=False)  # , alpha=0.5, zorder = 0.5)

ax.set_xlim([-axes_length*Rmax, axes_length*Rmax])
ax.set_ylim([-axes_length*Rmax, axes_length*Rmax])
ax.set_zlim([-axes_length*Rmax, axes_length*Rmax])

m = cm.ScalarMappable(cmap=cm.jet)
m.set_array(R)
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')

fig.colorbar(m, shrink=0.8)
ax.view_init(azim=300, elev=30)

plt.show()
like image 144
AMTK Avatar answered Oct 26 '25 05:10

AMTK



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!