RADAR_eq.py
1 import tkinter as tk 2 from tkinter import ttk, messagebox 3 import math 4 import numpy as np 5 6 class RadarCalculatorGUI: 7 def __init__(self, root): 8 self.root = root 9 self.root.title("RADAR Parameters Calculator") 10 self.root.geometry("850x750") 11 12 # Configure colors 13 self.bg_color = '#f0f0f0' 14 self.root.configure(bg=self.bg_color) 15 16 # Create main container 17 self.main_frame = ttk.Frame(root, padding="10") 18 self.main_frame.pack(fill=tk.BOTH, expand=True) 19 20 # Title 21 title_label = tk.Label(self.main_frame, text="RADAR PARAMETERS CALCULATOR", 22 font=('Arial', 16, 'bold'), bg=self.bg_color) 23 title_label.pack(pady=10) 24 25 # Create notebook for tabs 26 self.notebook = ttk.Notebook(self.main_frame) 27 self.notebook.pack(fill=tk.BOTH, expand=True, pady=10) 28 29 # Input tab 30 self.input_frame = ttk.Frame(self.notebook) 31 self.notebook.add(self.input_frame, text="Input Parameters") 32 33 # Results tab 34 self.results_frame = ttk.Frame(self.notebook) 35 self.notebook.add(self.results_frame, text="Results") 36 37 # Create input fields 38 self.create_input_fields() 39 40 # Create results display 41 self.create_results_display() 42 43 # Create button frame (outside notebook) 44 self.button_frame = ttk.Frame(self.main_frame) 45 self.button_frame.pack(fill=tk.X, pady=10) 46 47 # Create calculate button 48 self.create_calculate_button() 49 50 # Constants 51 self.c = 3e8 # Speed of light in m/s 52 53 def create_input_fields(self): 54 """Create all input fields with labels and units""" 55 56 # Create a canvas with scrollbar for input fields 57 canvas = tk.Canvas(self.input_frame, borderwidth=0) 58 scrollbar = ttk.Scrollbar(self.input_frame, orient="vertical", command=canvas.yview) 59 scrollable_frame = ttk.Frame(canvas) 60 61 scrollable_frame.bind( 62 "<Configure>", 63 lambda e: canvas.configure(scrollregion=canvas.bbox("all")) 64 ) 65 66 canvas.create_window((0, 0), window=scrollable_frame, anchor="nw") 67 canvas.configure(yscrollcommand=scrollbar.set) 68 69 # Input fields with default values 70 inputs = [ 71 ("Frequency (GHz):", "10.0"), 72 ("Pulse duration (μs):", "1.0"), 73 ("PRF (Hz):", "1000"), 74 ("Emitted power (dBm):", "30"), 75 ("Antenna gain (dBi):", "20"), 76 ("Receiver sensitivity (dBm):", "-90"), 77 ("Radar cross section (m²):", "1.0"), 78 ("System losses (dB):", "3"), 79 ("Noise figure (dB):", "3"), 80 ("Boltzmann constant (k) - optional:", "1.38e-23"), 81 ("Temperature (K):", "290") 82 ] 83 84 self.entries = {} 85 86 for i, (label, default) in enumerate(inputs): 87 # Create a frame for each input row 88 row_frame = ttk.Frame(scrollable_frame) 89 row_frame.pack(fill=tk.X, pady=5) 90 91 # Label 92 ttk.Label(row_frame, text=label, width=30, anchor='w').pack(side=tk.LEFT, padx=5) 93 94 # Entry 95 entry = ttk.Entry(row_frame, width=20, font=('Arial', 10)) 96 entry.pack(side=tk.LEFT, padx=5) 97 entry.insert(0, default) 98 self.entries[label] = entry 99 100 # Additional notes 101 notes_label = tk.Label(scrollable_frame, 102 text="Note: All values must be numeric. Use point (.) for decimals.", 103 font=('Arial', 9, 'italic'), fg='gray') 104 notes_label.pack(pady=20) 105 106 # Pack canvas and scrollbar 107 canvas.pack(side="left", fill="both", expand=True) 108 scrollbar.pack(side="right", fill="y") 109 110 def create_calculate_button(self): 111 """Create the calculate button""" 112 calculate_btn = tk.Button(self.button_frame, 113 text="CALCULATE RADAR PARAMETERS", 114 command=self.calculate_parameters, 115 bg='#4CAF50', fg='white', 116 font=('Arial', 12, 'bold'), 117 padx=30, pady=10, 118 cursor='hand2') 119 calculate_btn.pack() 120 121 # Bind hover effect 122 calculate_btn.bind("<Enter>", lambda e: calculate_btn.config(bg='#45a049')) 123 calculate_btn.bind("<Leave>", lambda e: calculate_btn.config(bg='#4CAF50')) 124 125 def create_results_display(self): 126 """Create the results display area""" 127 128 # Results title 129 title_label = tk.Label(self.results_frame, text="RADAR PERFORMANCE PARAMETERS", 130 font=('Arial', 14, 'bold')) 131 title_label.pack(pady=(20, 20)) 132 133 # Create frame for results with scrollbar 134 canvas = tk.Canvas(self.results_frame, borderwidth=0) 135 scrollbar = ttk.Scrollbar(self.results_frame, orient="vertical", command=canvas.yview) 136 scrollable_frame = ttk.Frame(canvas) 137 138 scrollable_frame.bind( 139 "<Configure>", 140 lambda e: canvas.configure(scrollregion=canvas.bbox("all")) 141 ) 142 143 canvas.create_window((0, 0), window=scrollable_frame, anchor="nw") 144 canvas.configure(yscrollcommand=scrollbar.set) 145 146 # Results fields 147 results = [ 148 ("Maximum detectable range:", "range_result"), 149 ("Range resolution:", "range_res_result"), 150 ("Maximum unambiguous range:", "max_range_result"), 151 ("Maximum detectable speed:", "speed_result"), 152 ("Speed resolution:", "speed_res_result"), 153 ("Doppler frequency resolution:", "doppler_res_result"), 154 ("Pulse width (s):", "pulse_width_result"), 155 ("Bandwidth (Hz):", "bandwidth_result"), 156 ("SNR (dB):", "snr_result") 157 ] 158 159 self.results_labels = {} 160 161 for i, (label, key) in enumerate(results): 162 # Create a frame for each result row 163 row_frame = ttk.Frame(scrollable_frame) 164 row_frame.pack(fill=tk.X, pady=10, padx=20) 165 166 # Label 167 ttk.Label(row_frame, text=label, font=('Arial', 11, 'bold'), 168 width=30, anchor='w').pack(side=tk.LEFT) 169 170 # Value 171 value_label = ttk.Label(row_frame, text="---", font=('Arial', 11), 172 foreground='blue', anchor='w') 173 value_label.pack(side=tk.LEFT, padx=(20, 0)) 174 self.results_labels[key] = value_label 175 176 # Add separator 177 ttk.Separator(scrollable_frame, orient='horizontal').pack(fill=tk.X, pady=20) 178 179 # Add explanatory note 180 note_text = """ 181 NOTES: 182 • Maximum detectable range is calculated using the radar equation 183 • Range resolution = c × τ / 2, where τ is pulse duration 184 • Maximum unambiguous range = c / (2 × PRF) 185 • Maximum detectable speed = λ × PRF / 4 186 • Speed resolution = λ × PRF / (2 × N) where N is number of pulses (assumed 1) 187 • λ (wavelength) = c / f 188 """ 189 190 note_label = tk.Label(scrollable_frame, text=note_text, font=('Arial', 9), 191 justify=tk.LEFT, bg='#f8f9fa', relief='solid', 192 padx=10, pady=10) 193 note_label.pack(fill=tk.X, padx=20, pady=10) 194 195 # Pack canvas and scrollbar 196 canvas.pack(side="left", fill="both", expand=True) 197 scrollbar.pack(side="right", fill="y") 198 199 def get_float_value(self, entry, default=None): 200 """Safely get float value from entry""" 201 try: 202 return float(entry.get()) 203 except ValueError: 204 return default 205 206 def calculate_parameters(self): 207 """Perform all RADAR calculations""" 208 209 try: 210 # Get all input values 211 f_ghz = self.get_float_value(self.entries["Frequency (GHz):"]) 212 pulse_duration_us = self.get_float_value(self.entries["Pulse duration (μs):"]) 213 prf = self.get_float_value(self.entries["PRF (Hz):"]) 214 p_dbm = self.get_float_value(self.entries["Emitted power (dBm):"]) 215 g_dbi = self.get_float_value(self.entries["Antenna gain (dBi):"]) 216 sens_dbm = self.get_float_value(self.entries["Receiver sensitivity (dBm):"]) 217 rcs = self.get_float_value(self.entries["Radar cross section (m²):"]) 218 losses_db = self.get_float_value(self.entries["System losses (dB):"]) 219 nf_db = self.get_float_value(self.entries["Noise figure (dB):"]) 220 k = self.get_float_value(self.entries["Boltzmann constant (k) - optional:"]) 221 temp = self.get_float_value(self.entries["Temperature (K):"]) 222 223 # Validate inputs 224 if None in [f_ghz, pulse_duration_us, prf, p_dbm, g_dbi, sens_dbm, rcs, losses_db, nf_db, temp]: 225 messagebox.showerror("Error", "Please enter valid numeric values for all fields") 226 return 227 228 # Convert units 229 f_hz = f_ghz * 1e9 230 pulse_duration_s = pulse_duration_us * 1e-6 231 wavelength = self.c / f_hz 232 233 # Convert dB values to linear 234 p_linear = 10 ** ((p_dbm - 30) / 10) # Convert dBm to Watts 235 g_linear = 10 ** (g_dbi / 10) 236 sens_linear = 10 ** ((sens_dbm - 30) / 10) 237 losses_linear = 10 ** (losses_db / 10) 238 nf_linear = 10 ** (nf_db / 10) 239 240 # Calculate receiver noise power 241 if k is None: 242 k = 1.38e-23 # Default Boltzmann constant 243 244 # Calculate SNR 245 snr_linear = (p_linear * g_linear**2 * wavelength**2 * rcs) / ( 246 (4 * np.pi)**3 * sens_linear * losses_linear) 247 snr_db = 10 * math.log10(snr_linear) if snr_linear > 0 else float('-inf') 248 249 # Maximum detectable range using radar equation 250 if snr_linear > 0: 251 range_max = ((p_linear * g_linear**2 * wavelength**2 * rcs) / 252 ((4 * np.pi)**3 * sens_linear * losses_linear)) ** (1/4) 253 else: 254 range_max = 0 255 256 # Range resolution 257 range_res = (self.c * pulse_duration_s) / 2 258 259 # Maximum unambiguous range 260 max_unambiguous_range = self.c / (2 * prf) 261 262 # Maximum detectable speed (using half the Nyquist sampling theorem) 263 max_speed = (wavelength * prf) / 4 264 265 # Speed resolution (for a single pulse, approximate) 266 speed_res = max_speed # For single pulse, resolution equals max speed 267 268 # Doppler frequency resolution 269 doppler_res = 1 / pulse_duration_s 270 271 # Bandwidth 272 bandwidth = 1 / pulse_duration_s 273 274 # Update results display with formatted values 275 self.results_labels["range_result"].config( 276 text=f"{range_max:.2f} m ({range_max/1000:.2f} km)") 277 self.results_labels["range_res_result"].config( 278 text=f"{range_res:.2f} m") 279 self.results_labels["max_range_result"].config( 280 text=f"{max_unambiguous_range:.2f} m ({max_unambiguous_range/1000:.2f} km)") 281 self.results_labels["speed_result"].config( 282 text=f"{max_speed:.2f} m/s ({max_speed*3.6:.2f} km/h)") 283 self.results_labels["speed_res_result"].config( 284 text=f"{speed_res:.2f} m/s ({speed_res*3.6:.2f} km/h)") 285 self.results_labels["doppler_res_result"].config( 286 text=f"{doppler_res:.2f} Hz") 287 self.results_labels["pulse_width_result"].config( 288 text=f"{pulse_duration_s:.2e} s") 289 self.results_labels["bandwidth_result"].config( 290 text=f"{bandwidth:.2e} Hz") 291 self.results_labels["snr_result"].config( 292 text=f"{snr_db:.2f} dB") 293 294 # Switch to results tab 295 self.notebook.select(1) 296 297 # Show success message 298 messagebox.showinfo("Success", "Calculation completed successfully!") 299 300 except Exception as e: 301 messagebox.showerror("Calculation Error", f"An error occurred during calculation:\n{str(e)}") 302 303 def main(): 304 root = tk.Tk() 305 app = RadarCalculatorGUI(root) 306 root.mainloop() 307 308 if __name__ == "__main__": 309 main()