Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# RFlect - Antenna Plot Tool <img src="./assets/smith_logo.png" alt="RFlect Logo" width="40">


**Version:** 2.1.0
**Version:** 2.1.1

RFlect is a comprehensive antenna plotting tool, currently designed specifically for visualizing and analyzing antenna measurements from the Howland Company 3100 Antenna Chamber and WTL Test Lab outputs. Additionally, it offers support for .csv VNA files from Copper Mountain RVNA and S2VNA software of S11/VSWR/Group Delay(S21(s)) measurements, making it a versatile choice for a wide range of antenna data processing needs. Through its user-friendly graphical interface, RFlect provides an intuitive way to handle various antenna metrics and visualize results.

Expand Down
3 changes: 3 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# RFlect - Release Notes

## Version 2.1.1 (10/19/2023)
- Added Support For Agilent VNA Group Delay Measurements

## Version 2.1.0 (10/16/2023)
- Added Copper Mountain S2VNA/M5090 .csv export support (S11(dB), S22(dB), S21(dB) or S12(dB), S21(s) or S12(s))
- Fixed Matplotlib backend to display plots properly
Expand Down
20 changes: 20 additions & 0 deletions plot_antenna/file_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -275,4 +275,24 @@ def parse_2port_data(file_path):
# Use only the available columns
organized_data = data[available_columns]

return organized_data

# Function to parse group delay measurements from Agilent VNA
def parse_agilent_data(file_path):
# Read the data, assuming the first row is a header row
# and the file is space or tab-delimited
data = pd.read_csv(file_path, skiprows=2)

# Rename columns for consistency with other data
# Assuming the first column is frequency and the second is group delay
data = data.rename(columns={
data.columns[0]: '! Stimulus(Hz)',
data.columns[1]: 'S21(s)'
})

# Check which columns are available in the data
available_columns = [col for col in ['! Stimulus(Hz)', 'S21(s)', 'S12(s)'] if col in data.columns]
# Use only the available columns
organized_data = data[available_columns]

return organized_data
62 changes: 54 additions & 8 deletions plot_antenna/groupdelay.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from file_utils import parse_2port_data
from file_utils import parse_2port_data, parse_agilent_data
from matplotlib.ticker import ScalarFormatter

import matplotlib
matplotlib.use('TkAgg')
Expand All @@ -14,7 +15,7 @@ def process_groupdelay_files(file_paths, saved_limit1_freq1, saved_limit1_freq2,
data_dict = {}

# Regular expression to match a number followed by 'deg'
pattern = re.compile(r'(\d+)deg')
pattern = re.compile(r'(\d+)(deg|DEG)', re.IGNORECASE)

# Extract data from files
for file_path in file_paths:
Expand All @@ -27,12 +28,22 @@ def process_groupdelay_files(file_paths, saved_limit1_freq1, saved_limit1_freq2,
print(f"Warning: Could not extract theta from filename: {filename}")
continue # Skip this file if no match is found

# Parsing data
data = parse_2port_data(file_path)

# Storing data
data_dict[theta] = data

# Read the first line to determine the source, if Agilent or S2VNA
with open(file_path, 'r', encoding='utf-8-sig') as f:
first_line = f.readline() # Read the first line
if '!' in first_line:
# Process S2VNA 2-Port Measurement
data = parse_2port_data(file_path)
elif '#' in first_line:
# Process Agilent Group Delay Measurement
data = parse_agilent_data(file_path)
else:
print(f"Warning: Could not recognize file structure of: {filename}")
data = None

if data is not None:
# Storing data
data_dict[theta] = data
# Plotting:
# Group Delay vs Frequency for Various Theta, Group Delay Difference vs Theta, & Max. Distance Error vs Theta
plot_group_delay_error(data_dict, min_freq, max_freq)
Expand All @@ -47,6 +58,10 @@ def plot_group_delay_error(data_dict, min_freq=None, max_freq=None):
# Plot Group Delay Vs Frequency
plt.figure(figsize=(10,6))
for theta, data in data_dict.items():
if min_freq is not None and max_freq is not None:
# Convert frequencies to GHz since your min_freq and max_freq are probably in GHz
data = data[(data['! Stimulus(Hz)'] >= min_freq*1e9) & (data['! Stimulus(Hz)'] <= max_freq*1e9)]

if 'S21(s)' or 'S12(s)' in data.columns:
if 'S21(s)' in data.columns:
data_group_delay = data['S21(s)']
Expand All @@ -59,9 +74,32 @@ def plot_group_delay_error(data_dict, min_freq=None, max_freq=None):
# Set frequency range if specified
if min_freq is not None and max_freq is not None:
plt.xlim(min_freq*1e9, max_freq*1e9)
# Initialize an empty list to store the filtered frequency points
filtered_freq_points = []

# If min_freq and max_freq are specified, filter the frequency points
if min_freq is not None and max_freq is not None:
for theta, data in data_dict.items():
data = data[(data['! Stimulus(Hz)'] >= min_freq*1e9) & (data['! Stimulus(Hz)'] <= max_freq*1e9)]
data_dict[theta] = data # Store the filtered data back in the data_dict

if len(filtered_freq_points) == 0: # If the list is empty, populate it with the first set of filtered frequency points
filtered_freq_points = data['! Stimulus(Hz)'].to_numpy()

else: # If no frequency range is specified, use all frequency points from the first dataset
filtered_freq_points = data_dict[next(iter(data_dict))]['! Stimulus(Hz)'].to_numpy()
plt.ylim(0, 5e-9) # This sets the Y-axis limits from 0 to 5 ns
plt.xlabel('Frequency (GHz)')
plt.ylabel('Group Delay (ns)')

# Create a new formatter
formatter = ScalarFormatter(useOffset=False)
formatter.set_scientific(True)
formatter.set_powerlimits((9,9)) # This line forces the scientific notation to 10^9

# Apply the formatter
plt.gca().xaxis.set_major_formatter(formatter)

plt.legend()
plt.title('Group Delay vs Frequency for Various Theta (Azimuthal) Rotation')
plt.grid(True, which='both', linestyle='--', linewidth=0.5)
Expand Down Expand Up @@ -103,6 +141,10 @@ def plot_group_delay_error(data_dict, min_freq=None, max_freq=None):

plt.xlabel('Frequency (GHz)')
plt.ylabel('Peak-to-Peak Group Delay Difference over Theta (ps)')

# Apply the formatter
plt.gca().xaxis.set_major_formatter(formatter)

plt.legend()
plt.title('Max Group Delay Difference over Theta')
plt.grid(True, which='both', linestyle='--', linewidth=0.5)
Expand All @@ -120,6 +162,10 @@ def plot_group_delay_error(data_dict, min_freq=None, max_freq=None):

plt.xlabel('Frequency (GHz)')
plt.ylabel('Max Distance Error (cm)')

# Apply the formatter
plt.gca().xaxis.set_major_formatter(formatter)

plt.legend()
plt.title('Max Distance Error over Theta')
plt.grid(True, which='both', linestyle='--', linewidth=0.5)
Expand Down
2 changes: 1 addition & 1 deletion settings.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"CURRENT_VERSION": "v2.1.0"
"CURRENT_VERSION": "v2.1.1"
}