Changed axis definitions and working gain for fix theta

Changed the definitions of theta and phi to follow antenna-theory.net
First working plots
This commit is contained in:
Vincent Bareiss
2024-04-03 11:41:23 +02:00
parent bf58484dfa
commit 83bdce162a
3 changed files with 65394 additions and 203 deletions

View File

@@ -9,23 +9,27 @@ import numpy as np
import pandas as pd
import math
# from spatz.sensors import Sensor
# from spatz.transforms import Transform
# from spatz.dataset import Dataset
# from spatz.logger import Logger
from spatz.sensors import Sensor
from spatz.simulation import Simulation
from spatz.transforms import Transform
from spatz.dataset import Dataset
from spatz.logger import Logger
import time
GAIN_NAME = "Abs(Gain)"
'''
Sensor to simulate TX antenna gain in direction of ground station
Returns the gain in dBi per timestep.
gain_pattern: matrix, groundstation_offset_vector
Class representing a CST gain pattern
This (and the sensor below) follow the convetions laid out by https://www.antenna-theory.com/basics/radpattern.php.
I.e, theta represents the elevation angle and goes from 0 to 180 deg, Phi represents the azimuth angle.
The data is interpolated, you will have to specify the step size for this to work correctly.
'''
class GainPattern():
def __init__(self, filepath: str):
def __init__(self, filepath: str, step_size: int):
self._stepsize = step_size
# This is a cursed parser. If it breaks, though luck.
with open(filepath,"r") as file:
# Read Header
@@ -37,32 +41,62 @@ class GainPattern():
# Parse to DF
lines = file.readlines()
clean_csv = header
for line in lines:
cleaned = re.sub(r'\s+',',',line).removeprefix(',').removesuffix(',').strip()
clean_csv = clean_csv + cleaned + '\n'
clean_csv = [header]
start_time = time.time()
num_lines = len(lines)
for i,line in enumerate(lines):
if(i % step_size == 0 or i == num_lines-1):
cleaned = re.sub(r'\s+',',',line).removeprefix(',').removesuffix(',').strip()
clean_csv.append(cleaned + '\n')
clean_csv = ''.join(clean_csv)
filelike = StringIO(clean_csv)
self._df = pd.read_csv(filelike)
print(self._df.head())
print(f"Processed {num_lines} lines in {(time.time()-start_time):.1f}s.")
print(f"Used {num_lines // step_size} lines due to step size")
self._df.to_csv("gainpattern.csv")
def get_phi_cut(self, phi:float) -> ArrayLike: #Return farfield cut with phi = const (Looking from the side)
assert 0 <= phi < 180
sub_df = self._df.loc[self._df["Phi"] == phi]
angles = sub_df["Theta"]
gain = sub_df[GAIN_NAME]
return angles,gain
def get_theta_cut(self, theta:float) -> ArrayLike: #Return farfield cut with theta = const (looking from the top)
assert 0<= theta < 360
sub_df_left = self._df.loc[self._df["Theta"] == theta]
angles = sub_df_left["Phi"]
gain = sub_df_left[GAIN_NAME]
sub_df_right = self._df.loc[self._df["Theta"] == ((theta + 180) % 360)]
angles = sub_df_right["Phi"]
gain = sub_df_right[GAIN_NAME]
return angles,gain
def __get_gain_internal(self,phi5:float,theta5:float):
assert phi5%5 ==0
assert theta5%5==0
def __get_gain_internal(self,phi_step:float,theta_step:float):
assert phi_step%self._stepsize ==0
assert theta_step%self._stepsize==0
row = self._df.loc[(self._df["Theta"] == theta5) & (self._df["Phi"] == phi5)].iloc[0]
return row["Abs(Dir.)"]
row = self._df.loc[(self._df["Theta"] == theta_step) & (self._df["Phi"] == phi_step)].iloc[0]
return row[GAIN_NAME]
def get_gain(self, phi, theta) -> float:
assert 0 <= phi <= 180
assert 0 <= theta <= 360
assert 0 <= phi < 360
assert 0 <= theta < 180
#Interpolate using binlinear interpolation https://en.wikipedia.org/wiki/Bilinear_interpolation
phi_lower = math.floor(phi/5)*5
phi_upper = phi_lower + 5
theta_lower = math.floor(theta/5)*5
theta_upper = theta_lower + 5
phi_lower = math.floor(phi/self._stepsize)*self._stepsize
phi_upper = phi_lower + self._stepsize
theta_lower = math.floor(theta/self._stepsize)*self._stepsize
theta_upper = theta_lower + self._stepsize
G11 = self.__get_gain_internal(phi_lower,theta_lower)
G12 = self.__get_gain_internal(phi_lower,theta_upper)
@@ -73,37 +107,49 @@ class GainPattern():
v2 = np.array([[theta_upper-theta],[theta-theta_lower]])
A = np.array([[G11,G12],[G21,G22]])
interpolated = 1/25 * v1 @ A @ v2
interpolated = 1/(self._stepsize*self._stepsize) * v1 @ A @ v2
return interpolated[0]
'''
Sensor to simulate TX antenna gain in direction of ground station
# class AntennaTxGain(Sensor):
Returns the gain in dBi per timestep.
# def __init__(self, dataset: Dataset, logger: Logger, transforms: List[Transform] = []):
# super().__init__(dataset, logger, transforms)
'''
class AntennaTxGain(Sensor):
# def _get_data(self) -> ArrayLike | float:
# # Get current position of rocket
# [x,y,z] = self._dataset.fetch_values(['x', 'y', 'z'])
def __init__(self, dataset: Dataset, logger: Logger, transforms: List[Transform] = [], gain_pattern_path = "data/gain_pattern/farfield_2_45_GHz.txt"):
super().__init__(dataset, logger, transforms)
self._pattern = GainPattern(gain_pattern_path,1)
def _get_data(self) -> ArrayLike | float:
# Get current position of rocket in FL Frame (Launcher Frame).
pos_fl = self._dataset.fetch_values(['x', 'y', 'z']) #X,Y,Z is in FL (Launcher frame) -> Z is up, X is east
# Transform X,Y,Z to B Frame (Body Frame)
pos_b = np.array(pos_fl) @ self._dataset.launch_rail_to_body()
# Rocket in body frame is simply [1,0,0]^T by definition
rocket_b = np.array([1,0,0]).T
# Angle between rocket and pos returns elevation angle (Phi). Assume a rotation of 0° for now to get theta
theta = np.rad2deg(np.arccos(np.clip(np.dot(pos_b/np.linalg.norm(pos_b), rocket_b),-1.0,1.0))) #Clip trick from: https://stackoverflow.com/questions/2827393/angles-between-two-n-dimensional-vectors-in-python
#return phi
phi = 0
# # Get current rotation of rocket
# [pitch,roll,yaw] = self._dataset.fetch_values(['pitch','roll','yaw'])
# Fetch gain in this direction
return self._pattern.get_gain(phi,theta)
# # Calculate angle between the vectors
# # Fetch gain in this direction
# return 0
# def _sensor_specific_effects(self, x: ArrayLike) -> ArrayLike:
# return x
def _sensor_specific_effects(self, x: ArrayLike) -> ArrayLike:
return x
# def _get_name(self) -> AnyStr:
# return 'Generic Antenna TX'
def _get_name(self) -> AnyStr:
return 'antenna/tx_gain'
if __name__ == '__main__':