AERO4200-Ass3/main.py

188 lines
7.2 KiB
Python
Raw Normal View History

2023-10-22 14:40:10 +10:00
# Aero4200-Ass3
2023-10-22 17:25:20 +10:00
# Cal Wing, Sem 2 2023
2023-10-22 14:40:10 +10:00
2023-10-22 17:25:20 +10:00
# Import System / Common Libs
2023-10-22 19:05:34 +10:00
import os, time, pickle
2023-10-22 14:40:10 +10:00
import numpy as np
2023-10-22 17:25:20 +10:00
import pandas as pd
2023-10-22 14:40:10 +10:00
from tqdm import tqdm
2023-10-22 18:05:38 +10:00
from numpy import pi, sin, cos, tan
2023-10-22 17:25:20 +10:00
#Import MakeGraph, a custom graphing wrapper I developed, refer to it for documentation
2023-10-22 14:40:10 +10:00
from makeGraph import makeGraph, pltKeyClose, UQ_COLOURS as UQC # Custom Graphing Lib
# Override Sin & Cos to use & return degrees
#def sin(angle): return np.sin(np.deg2rad(angle))
#def cos(angle): return np.cos(np.deg2rad(angle))
# Make sure the relevant folders folder exists
2023-10-22 19:05:34 +10:00
folders = ["./images", "./tmp"]
2023-10-22 14:40:10 +10:00
for folder in folders:
if not os.path.isdir(folder): os.mkdir(folder)
2023-10-22 19:05:34 +10:00
# This is a cacheing function, it checks if a cache file exists and loads it or it calcs & saves it.
2023-10-22 19:36:56 +10:00
def cacheData(dataFilePath: str, calcFunction: callable = lambda x: x, args: tuple = (), kargs: dict = {}, defArg=None):
if len(args) == 0 and defArg is not None:
args = (defArg, )
2023-10-22 19:05:34 +10:00
data = None
dataFileExt = dataFilePath.rsplit(".")[-1]
# Check if file exists
if os.path.isfile(dataFilePath):
2023-10-22 19:36:56 +10:00
print(f"Found data file \"{dataFilePath}\", loading data.")
2023-10-22 19:05:34 +10:00
# Check if file is a compressed numpy file
if dataFileExt == "npz":
2023-10-22 19:36:56 +10:00
data = np.load(dataFilePath, allow_pickle=True)["arr_0"]
2023-10-22 19:05:34 +10:00
elif dataFileExt == "pkl":
#If its not then just pickle it normally
with open(dataFilePath, 'rb') as handle:
data = pickle.load(handle)
2023-10-22 19:36:56 +10:00
elif dataFileExt == "dfz":
data = pd.read_pickle(dataFilePath, compression="gzip")
2023-10-22 19:05:34 +10:00
else:
raise TypeError(f"Cannot determine file type of: {dataFilePath}")
else:
2023-10-22 19:36:56 +10:00
print(f"Could not find data file \"{dataFilePath}\", generating & saving data.")
2023-10-22 19:05:34 +10:00
# Calculate Value
data = calcFunction(*args, **kargs)
# Check if file is a compressed numpy file
if dataFileExt == "npz":
2023-10-22 19:36:56 +10:00
np.savez_compressed(dataFilePath, data, allow_pickle=True)
elif dataFileExt == "dfz":
data.to_pickle(dataFilePath, compression="gzip")
2023-10-22 19:05:34 +10:00
elif dataFileExt == "pkl":
#If its not then just pickle it normally
with open(dataFilePath, 'wb') as handle:
pickle.dump(data, handle)
else:
raise TypeError(f"Cannot determine file type of: {dataFilePath}")
if data is None: raise ValueError("Could not import or generate data requested")
return data
2023-10-22 17:25:20 +10:00
# IMU Data Loading
# I map in to a heading to add units / make things make more sense
## The gyroscopic body angular rates from the IMU are given:
# - WBE_1 (in rad/s) - the roll rate about the body-fixed x-axis
# - WBE_2 (in rad/s) - the pitch rate about the body-fixed y-axis
# - WBE_3 (in rad/s) - the yaw rate about the body-fixed z-axis
## Specific forces:
2023-10-22 19:05:34 +10:00
# - FSP_X (in m/s^2) - the specific force in the body-fixed x-direction
# - FSP_Y (in m/s^2) - the specific force in the body-fixed y-direction
# - FSP_Z (in m/s^2) - the specific force in the body-fixed z-direction
2023-10-22 18:05:38 +10:00
IMU_TIME_HEADER = ["Time [s]"]
IMU_WBE_HEADERS = ["WBE_1 [rad/s]", "WBE_2 [rad/s]", "WBE_3 [rad/s]"]
IMU_FSP_HEADERS = ["FSP_X [m/s^2]", "FSP_Y [m/s^2]", "FSP_Z [m/s^2]"]
IMU_DATA_HEADER = IMU_TIME_HEADER + IMU_WBE_HEADERS + IMU_FSP_HEADERS
2023-10-22 17:25:20 +10:00
def importIMUData(mission, imu):
# If IMU is not a string, then convert based on bool eval where "H" == True
if type(imu) != str: imu = "H" if imu else "L"
2023-10-22 17:25:43 +10:00
data = pd.read_csv(
2023-10-22 17:25:20 +10:00
f"./data/IMU_M{str(mission) + str(imu)}.txt",
header=None, skiprows=1,
names=IMU_DATA_HEADER,
)
2023-10-22 17:25:43 +10:00
return data
2023-10-22 19:05:34 +10:00
# Load the Mission Data
2023-10-22 17:25:20 +10:00
m1_IMUData = importIMUData(1, 0), importIMUData(1, 1) #(L, H) Data
m2_IMUData = importIMUData(2, 0), importIMUData(2, 1)
2023-10-22 19:05:34 +10:00
# NED Translation & Force Functions
2023-10-22 18:05:38 +10:00
INIT_EULER_ANGLES = (0, 0, 0)
def translate2NED(angles, euler_angles):
phi, theta, psi = euler_angles
p, q, r = angles
transMat = np.array([ [1, sin(phi)*tan(theta), cos(phi)*tan(theta) ],
[0, cos(phi), -sin(phi) ],
[0, sin(phi)/cos(theta), cos(phi)/cos(theta) ]
])
angleMat = np.array([ [p],
[q],
[r]
])
2023-10-22 17:25:20 +10:00
2023-10-22 18:05:38 +10:00
return np.matmul(transMat, angleMat)
def getNEDForces(NEDPos):
phi, theta, psi = NEDPos
forceMat = np.array([ [cos(psi)*cos(theta), cos(psi)*sin(theta)*sin(phi)-sin(psi)*cos(phi), cos(psi)*sin(theta)*cos(phi)+sin(psi)*sin(phi)],
[sin(psi)*cos(theta), sin(psi)*sin(theta)*sin(phi)+cos(psi)*cos(phi), sin(psi)*sin(theta)*cos(phi)-cos(psi)*sin(phi)],
[-sin(theta), cos(theta)*sin(phi), cos(theta)*cos(phi) ]
])
return forceMat
2023-10-22 14:46:20 +10:00
2023-10-22 19:36:56 +10:00
# Calculate Mission Translation & Force Data
def calculateTranslatedData(missionWBEData) -> pd.DataFrame:
2023-10-22 19:05:34 +10:00
print("Translating Motion & Calculating Resulting Forces")
translatedData = pd.DataFrame(columns=IMU_TIME_HEADER + IMU_WBE_HEADERS + ["Forces"])
for i in tqdm(range(len(missionWBEData))):
dataPoint = missionWBEData[IMU_WBE_HEADERS].iloc[i]
trans = translate2NED(dataPoint.values, INIT_EULER_ANGLES).flatten()
forces = getNEDForces(trans)
dataRow = {
IMU_TIME_HEADER[0]: missionWBEData[IMU_TIME_HEADER].iloc[i],
IMU_WBE_HEADERS[0]: trans[0],
IMU_WBE_HEADERS[1]: trans[1],
IMU_WBE_HEADERS[2]: trans[2],
"Forces": forces
}
translatedData.loc[i] = dataRow
return translatedData
2023-10-22 14:40:10 +10:00
if __name__ == '__main__':
2023-10-22 19:05:34 +10:00
missionWBEData = m1_IMUData[0][IMU_TIME_HEADER + IMU_WBE_HEADERS]
2023-10-22 19:36:56 +10:00
boo = "" #calculateTranslatedData(missionWBEData)
translatedData = cacheData("./tmp/m1_transData.dfz", defArg=boo)
2023-10-22 18:05:38 +10:00
2023-10-22 19:05:34 +10:00
#print("Raw Data")
#print(dataPoint)
2023-10-22 17:25:20 +10:00
2023-10-22 19:05:34 +10:00
#print("\nTranslated Point")
#print(trans)
2023-10-22 18:05:38 +10:00
2023-10-22 19:05:34 +10:00
#print("\nForces")
#print(forces)
2023-10-22 18:05:38 +10:00
2023-10-22 17:25:20 +10:00
input("Damn")
2023-10-22 14:46:20 +10:00
if __name__ == '__main__1':
2023-10-22 14:40:10 +10:00
#This is an example of drawing 4 plots by generating them
graphData = {
"figTitle": "Simple Plot",
"figTitleFontSize": 16,
"figSize": (8,8), #Yay America, this is in inches :/ # Note: cm = 1/2.54
"xLabel": "x label",
"yLabel": "y label",
"plotDim": (2,2),
"subPlots":[]
}
#Create 4 identical plots with different names
for i in range(4):
newPlot = {
"title": f"Graph {i+1}",
"plots": [
{"x":[0,1,2,3,4], "y":[0,1,2,3,4], "label":"Linear"},
{"x":[0,1,2,3,4], "y":[5,5,5,5,5]},
{"x":[4,3,2,1,0], "y":[4,3,2,1,0], "label":"Linear2"},
{"x":0, "type":"axvLine", "label":"Red Vertical Line", "color":"red"},
{"y":6, "type":"axhLine", "label":"Dashed Horizontal Line", "args":{"linestyle":"--"}},
{"type":"scatter", "x":4, "y":4, "label":"A Random Point", "colour":"purple", "args":{"zorder":2}}
]
}
graphData["subPlots"].append(newPlot)
makeGraph(graphData, figSavePath="./images/example.png")