310 lines
13 KiB
Python
310 lines
13 KiB
Python
import tkinter as tk
|
||
from tkinter import ttk, messagebox
|
||
import math
|
||
import numpy as np
|
||
|
||
class RadarCalculatorGUI:
|
||
def __init__(self, root):
|
||
self.root = root
|
||
self.root.title("RADAR Parameters Calculator")
|
||
self.root.geometry("850x750")
|
||
|
||
# Configure colors
|
||
self.bg_color = '#f0f0f0'
|
||
self.root.configure(bg=self.bg_color)
|
||
|
||
# Create main container
|
||
self.main_frame = ttk.Frame(root, padding="10")
|
||
self.main_frame.pack(fill=tk.BOTH, expand=True)
|
||
|
||
# Title
|
||
title_label = tk.Label(self.main_frame, text="RADAR PARAMETERS CALCULATOR",
|
||
font=('Arial', 16, 'bold'), bg=self.bg_color)
|
||
title_label.pack(pady=10)
|
||
|
||
# Create notebook for tabs
|
||
self.notebook = ttk.Notebook(self.main_frame)
|
||
self.notebook.pack(fill=tk.BOTH, expand=True, pady=10)
|
||
|
||
# Input tab
|
||
self.input_frame = ttk.Frame(self.notebook)
|
||
self.notebook.add(self.input_frame, text="Input Parameters")
|
||
|
||
# Results tab
|
||
self.results_frame = ttk.Frame(self.notebook)
|
||
self.notebook.add(self.results_frame, text="Results")
|
||
|
||
# Create input fields
|
||
self.create_input_fields()
|
||
|
||
# Create results display
|
||
self.create_results_display()
|
||
|
||
# Create button frame (outside notebook)
|
||
self.button_frame = ttk.Frame(self.main_frame)
|
||
self.button_frame.pack(fill=tk.X, pady=10)
|
||
|
||
# Create calculate button
|
||
self.create_calculate_button()
|
||
|
||
# Constants
|
||
self.c = 3e8 # Speed of light in m/s
|
||
|
||
def create_input_fields(self):
|
||
"""Create all input fields with labels and units"""
|
||
|
||
# Create a canvas with scrollbar for input fields
|
||
canvas = tk.Canvas(self.input_frame, borderwidth=0)
|
||
scrollbar = ttk.Scrollbar(self.input_frame, orient="vertical", command=canvas.yview)
|
||
scrollable_frame = ttk.Frame(canvas)
|
||
|
||
scrollable_frame.bind(
|
||
"<Configure>",
|
||
lambda e: canvas.configure(scrollregion=canvas.bbox("all"))
|
||
)
|
||
|
||
canvas.create_window((0, 0), window=scrollable_frame, anchor="nw")
|
||
canvas.configure(yscrollcommand=scrollbar.set)
|
||
|
||
# Input fields with default values
|
||
inputs = [
|
||
("Frequency (GHz):", "10.0"),
|
||
("Pulse duration (μs):", "1.0"),
|
||
("PRF (Hz):", "1000"),
|
||
("Emitted power (dBm):", "30"),
|
||
("Antenna gain (dBi):", "20"),
|
||
("Receiver sensitivity (dBm):", "-90"),
|
||
("Radar cross section (m²):", "1.0"),
|
||
("System losses (dB):", "3"),
|
||
("Noise figure (dB):", "3"),
|
||
("Boltzmann constant (k) - optional:", "1.38e-23"),
|
||
("Temperature (K):", "290")
|
||
]
|
||
|
||
self.entries = {}
|
||
|
||
for i, (label, default) in enumerate(inputs):
|
||
# Create a frame for each input row
|
||
row_frame = ttk.Frame(scrollable_frame)
|
||
row_frame.pack(fill=tk.X, pady=5)
|
||
|
||
# Label
|
||
ttk.Label(row_frame, text=label, width=30, anchor='w').pack(side=tk.LEFT, padx=5)
|
||
|
||
# Entry
|
||
entry = ttk.Entry(row_frame, width=20, font=('Arial', 10))
|
||
entry.pack(side=tk.LEFT, padx=5)
|
||
entry.insert(0, default)
|
||
self.entries[label] = entry
|
||
|
||
# Additional notes
|
||
notes_label = tk.Label(scrollable_frame,
|
||
text="Note: All values must be numeric. Use point (.) for decimals.",
|
||
font=('Arial', 9, 'italic'), fg='gray')
|
||
notes_label.pack(pady=20)
|
||
|
||
# Pack canvas and scrollbar
|
||
canvas.pack(side="left", fill="both", expand=True)
|
||
scrollbar.pack(side="right", fill="y")
|
||
|
||
def create_calculate_button(self):
|
||
"""Create the calculate button"""
|
||
calculate_btn = tk.Button(self.button_frame,
|
||
text="CALCULATE RADAR PARAMETERS",
|
||
command=self.calculate_parameters,
|
||
bg='#4CAF50', fg='white',
|
||
font=('Arial', 12, 'bold'),
|
||
padx=30, pady=10,
|
||
cursor='hand2')
|
||
calculate_btn.pack()
|
||
|
||
# Bind hover effect
|
||
calculate_btn.bind("<Enter>", lambda e: calculate_btn.config(bg='#45a049'))
|
||
calculate_btn.bind("<Leave>", lambda e: calculate_btn.config(bg='#4CAF50'))
|
||
|
||
def create_results_display(self):
|
||
"""Create the results display area"""
|
||
|
||
# Results title
|
||
title_label = tk.Label(self.results_frame, text="RADAR PERFORMANCE PARAMETERS",
|
||
font=('Arial', 14, 'bold'))
|
||
title_label.pack(pady=(20, 20))
|
||
|
||
# Create frame for results with scrollbar
|
||
canvas = tk.Canvas(self.results_frame, borderwidth=0)
|
||
scrollbar = ttk.Scrollbar(self.results_frame, orient="vertical", command=canvas.yview)
|
||
scrollable_frame = ttk.Frame(canvas)
|
||
|
||
scrollable_frame.bind(
|
||
"<Configure>",
|
||
lambda e: canvas.configure(scrollregion=canvas.bbox("all"))
|
||
)
|
||
|
||
canvas.create_window((0, 0), window=scrollable_frame, anchor="nw")
|
||
canvas.configure(yscrollcommand=scrollbar.set)
|
||
|
||
# Results fields
|
||
results = [
|
||
("Maximum detectable range:", "range_result"),
|
||
("Range resolution:", "range_res_result"),
|
||
("Maximum unambiguous range:", "max_range_result"),
|
||
("Maximum detectable speed:", "speed_result"),
|
||
("Speed resolution:", "speed_res_result"),
|
||
("Doppler frequency resolution:", "doppler_res_result"),
|
||
("Pulse width (s):", "pulse_width_result"),
|
||
("Bandwidth (Hz):", "bandwidth_result"),
|
||
("SNR (dB):", "snr_result")
|
||
]
|
||
|
||
self.results_labels = {}
|
||
|
||
for i, (label, key) in enumerate(results):
|
||
# Create a frame for each result row
|
||
row_frame = ttk.Frame(scrollable_frame)
|
||
row_frame.pack(fill=tk.X, pady=10, padx=20)
|
||
|
||
# Label
|
||
ttk.Label(row_frame, text=label, font=('Arial', 11, 'bold'),
|
||
width=30, anchor='w').pack(side=tk.LEFT)
|
||
|
||
# Value
|
||
value_label = ttk.Label(row_frame, text="---", font=('Arial', 11),
|
||
foreground='blue', anchor='w')
|
||
value_label.pack(side=tk.LEFT, padx=(20, 0))
|
||
self.results_labels[key] = value_label
|
||
|
||
# Add separator
|
||
ttk.Separator(scrollable_frame, orient='horizontal').pack(fill=tk.X, pady=20)
|
||
|
||
# Add explanatory note
|
||
note_text = """
|
||
NOTES:
|
||
• Maximum detectable range is calculated using the radar equation
|
||
• Range resolution = c × τ / 2, where τ is pulse duration
|
||
• Maximum unambiguous range = c / (2 × PRF)
|
||
• Maximum detectable speed = λ × PRF / 4
|
||
• Speed resolution = λ × PRF / (2 × N) where N is number of pulses (assumed 1)
|
||
• λ (wavelength) = c / f
|
||
"""
|
||
|
||
note_label = tk.Label(scrollable_frame, text=note_text, font=('Arial', 9),
|
||
justify=tk.LEFT, bg='#f8f9fa', relief='solid',
|
||
padx=10, pady=10)
|
||
note_label.pack(fill=tk.X, padx=20, pady=10)
|
||
|
||
# Pack canvas and scrollbar
|
||
canvas.pack(side="left", fill="both", expand=True)
|
||
scrollbar.pack(side="right", fill="y")
|
||
|
||
def get_float_value(self, entry, default=None):
|
||
"""Safely get float value from entry"""
|
||
try:
|
||
return float(entry.get())
|
||
except ValueError:
|
||
return default
|
||
|
||
def calculate_parameters(self):
|
||
"""Perform all RADAR calculations"""
|
||
|
||
try:
|
||
# Get all input values
|
||
f_ghz = self.get_float_value(self.entries["Frequency (GHz):"])
|
||
pulse_duration_us = self.get_float_value(self.entries["Pulse duration (μs):"])
|
||
prf = self.get_float_value(self.entries["PRF (Hz):"])
|
||
p_dbm = self.get_float_value(self.entries["Emitted power (dBm):"])
|
||
g_dbi = self.get_float_value(self.entries["Antenna gain (dBi):"])
|
||
sens_dbm = self.get_float_value(self.entries["Receiver sensitivity (dBm):"])
|
||
rcs = self.get_float_value(self.entries["Radar cross section (m²):"])
|
||
losses_db = self.get_float_value(self.entries["System losses (dB):"])
|
||
nf_db = self.get_float_value(self.entries["Noise figure (dB):"])
|
||
k = self.get_float_value(self.entries["Boltzmann constant (k) - optional:"])
|
||
temp = self.get_float_value(self.entries["Temperature (K):"])
|
||
|
||
# Validate inputs
|
||
if None in [f_ghz, pulse_duration_us, prf, p_dbm, g_dbi, sens_dbm, rcs, losses_db, nf_db, temp]:
|
||
messagebox.showerror("Error", "Please enter valid numeric values for all fields")
|
||
return
|
||
|
||
# Convert units
|
||
f_hz = f_ghz * 1e9
|
||
pulse_duration_s = pulse_duration_us * 1e-6
|
||
wavelength = self.c / f_hz
|
||
|
||
# Convert dB values to linear
|
||
p_linear = 10 ** ((p_dbm - 30) / 10) # Convert dBm to Watts
|
||
g_linear = 10 ** (g_dbi / 10)
|
||
sens_linear = 10 ** ((sens_dbm - 30) / 10)
|
||
losses_linear = 10 ** (losses_db / 10)
|
||
nf_linear = 10 ** (nf_db / 10)
|
||
|
||
# Calculate receiver noise power
|
||
if k is None:
|
||
k = 1.38e-23 # Default Boltzmann constant
|
||
|
||
# Calculate SNR
|
||
snr_linear = (p_linear * g_linear**2 * wavelength**2 * rcs) / (
|
||
(4 * np.pi)**3 * sens_linear * losses_linear)
|
||
snr_db = 10 * math.log10(snr_linear) if snr_linear > 0 else float('-inf')
|
||
|
||
# Maximum detectable range using radar equation
|
||
if snr_linear > 0:
|
||
range_max = ((p_linear * g_linear**2 * wavelength**2 * rcs) /
|
||
((4 * np.pi)**3 * sens_linear * losses_linear)) ** (1/4)
|
||
else:
|
||
range_max = 0
|
||
|
||
# Range resolution
|
||
range_res = (self.c * pulse_duration_s) / 2
|
||
|
||
# Maximum unambiguous range
|
||
max_unambiguous_range = self.c / (2 * prf)
|
||
|
||
# Maximum detectable speed (using half the Nyquist sampling theorem)
|
||
max_speed = (wavelength * prf) / 4
|
||
|
||
# Speed resolution (for a single pulse, approximate)
|
||
speed_res = max_speed # For single pulse, resolution equals max speed
|
||
|
||
# Doppler frequency resolution
|
||
doppler_res = 1 / pulse_duration_s
|
||
|
||
# Bandwidth
|
||
bandwidth = 1 / pulse_duration_s
|
||
|
||
# Update results display with formatted values
|
||
self.results_labels["range_result"].config(
|
||
text=f"{range_max:.2f} m ({range_max/1000:.2f} km)")
|
||
self.results_labels["range_res_result"].config(
|
||
text=f"{range_res:.2f} m")
|
||
self.results_labels["max_range_result"].config(
|
||
text=f"{max_unambiguous_range:.2f} m ({max_unambiguous_range/1000:.2f} km)")
|
||
self.results_labels["speed_result"].config(
|
||
text=f"{max_speed:.2f} m/s ({max_speed*3.6:.2f} km/h)")
|
||
self.results_labels["speed_res_result"].config(
|
||
text=f"{speed_res:.2f} m/s ({speed_res*3.6:.2f} km/h)")
|
||
self.results_labels["doppler_res_result"].config(
|
||
text=f"{doppler_res:.2f} Hz")
|
||
self.results_labels["pulse_width_result"].config(
|
||
text=f"{pulse_duration_s:.2e} s")
|
||
self.results_labels["bandwidth_result"].config(
|
||
text=f"{bandwidth:.2e} Hz")
|
||
self.results_labels["snr_result"].config(
|
||
text=f"{snr_db:.2f} dB")
|
||
|
||
# Switch to results tab
|
||
self.notebook.select(1)
|
||
|
||
# Show success message
|
||
messagebox.showinfo("Success", "Calculation completed successfully!")
|
||
|
||
except Exception as e:
|
||
messagebox.showerror("Calculation Error", f"An error occurred during calculation:\n{str(e)}")
|
||
|
||
def main():
|
||
root = tk.Tk()
|
||
app = RadarCalculatorGUI(root)
|
||
root.mainloop()
|
||
|
||
if __name__ == "__main__":
|
||
main()
|