diff --git a/8_Utils/Python/RADAR_eq.py b/8_Utils/Python/RADAR_eq.py new file mode 100644 index 0000000..1020b31 --- /dev/null +++ b/8_Utils/Python/RADAR_eq.py @@ -0,0 +1,309 @@ +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( + "", + 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("", lambda e: calculate_btn.config(bg='#45a049')) + calculate_btn.bind("", 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( + "", + 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()