Add files via upload
|
After Width: | Height: | Size: 122 KiB |
|
After Width: | Height: | Size: 263 KiB |
|
After Width: | Height: | Size: 89 KiB |
|
After Width: | Height: | Size: 100 KiB |
|
After Width: | Height: | Size: 980 KiB |
|
After Width: | Height: | Size: 60 KiB |
@@ -0,0 +1,188 @@
|
|||||||
|
import numpy as np
|
||||||
|
import pandas as pd
|
||||||
|
import math
|
||||||
|
|
||||||
|
def generate_radar_csv(filename="pulse_compression_output.csv"):
|
||||||
|
"""
|
||||||
|
Generate realistic radar CSV data for testing the Python GUI
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Radar parameters matching your testbench
|
||||||
|
num_long_chirps = 16
|
||||||
|
num_short_chirps = 16
|
||||||
|
samples_per_chirp = 512 # Reduced for manageable file size
|
||||||
|
fs_adc = 400e6 # 400 MHz ADC
|
||||||
|
timestamp_ns = 0
|
||||||
|
|
||||||
|
# Target parameters
|
||||||
|
targets = [
|
||||||
|
{'range': 3000, 'velocity': 25, 'snr': 30, 'azimuth': 10, 'elevation': 5}, # Fast moving target
|
||||||
|
{'range': 5000, 'velocity': -15, 'snr': 25, 'azimuth': 20, 'elevation': 2}, # Approaching target
|
||||||
|
{'range': 8000, 'velocity': 5, 'snr': 20, 'azimuth': 30, 'elevation': 8}, # Slow moving target
|
||||||
|
{'range': 12000, 'velocity': -8, 'snr': 18, 'azimuth': 45, 'elevation': 3}, # Distant target
|
||||||
|
]
|
||||||
|
|
||||||
|
# Noise parameters
|
||||||
|
noise_std = 5
|
||||||
|
clutter_std = 10
|
||||||
|
|
||||||
|
data = []
|
||||||
|
chirp_number = 0
|
||||||
|
|
||||||
|
# Generate Long Chirps (30µs duration equivalent)
|
||||||
|
print("Generating Long Chirps...")
|
||||||
|
for chirp in range(num_long_chirps):
|
||||||
|
for sample in range(samples_per_chirp):
|
||||||
|
# Base noise
|
||||||
|
i_val = np.random.normal(0, noise_std)
|
||||||
|
q_val = np.random.normal(0, noise_std)
|
||||||
|
|
||||||
|
# Add clutter (stationary targets)
|
||||||
|
clutter_range = 2000 # Fixed clutter at 2km
|
||||||
|
if sample < 100: # Simulate clutter in first 100 samples
|
||||||
|
i_val += np.random.normal(0, clutter_std)
|
||||||
|
q_val += np.random.normal(0, clutter_std)
|
||||||
|
|
||||||
|
# Add moving targets with Doppler shift
|
||||||
|
for target in targets:
|
||||||
|
# Calculate range bin (simplified)
|
||||||
|
range_bin = int(target['range'] / 20) # ~20m per bin
|
||||||
|
doppler_phase = 2 * math.pi * target['velocity'] * chirp / 100 # Doppler phase shift
|
||||||
|
|
||||||
|
# Target appears around its range bin with some spread
|
||||||
|
if abs(sample - range_bin) < 10:
|
||||||
|
# Signal amplitude decreases with range
|
||||||
|
amplitude = target['snr'] * (10000 / target['range'])
|
||||||
|
phase = 2 * math.pi * sample / 50 + doppler_phase
|
||||||
|
|
||||||
|
i_val += amplitude * math.cos(phase)
|
||||||
|
q_val += amplitude * math.sin(phase)
|
||||||
|
|
||||||
|
# Calculate derived values
|
||||||
|
magnitude_squared = i_val**2 + q_val**2
|
||||||
|
|
||||||
|
data.append({
|
||||||
|
'timestamp_ns': timestamp_ns,
|
||||||
|
'chirp_number': chirp_number,
|
||||||
|
'chirp_type': 'LONG',
|
||||||
|
'sample_index': sample,
|
||||||
|
'I_value': int(i_val),
|
||||||
|
'Q_value': int(q_val),
|
||||||
|
'magnitude_squared': int(magnitude_squared)
|
||||||
|
})
|
||||||
|
|
||||||
|
timestamp_ns += int(1e9 / fs_adc) # 2.5ns per sample at 400MHz
|
||||||
|
|
||||||
|
chirp_number += 1
|
||||||
|
timestamp_ns += 137000 # Add listening period (137µs)
|
||||||
|
|
||||||
|
# Guard time between long and short chirps
|
||||||
|
timestamp_ns += 175400 # 175.4µs guard time
|
||||||
|
|
||||||
|
# Generate Short Chirps (0.5µs duration equivalent)
|
||||||
|
print("Generating Short Chirps...")
|
||||||
|
for chirp in range(num_short_chirps):
|
||||||
|
for sample in range(samples_per_chirp):
|
||||||
|
# Base noise
|
||||||
|
i_val = np.random.normal(0, noise_std)
|
||||||
|
q_val = np.random.normal(0, noise_std)
|
||||||
|
|
||||||
|
# Add clutter (different characteristics for short chirps)
|
||||||
|
if sample < 50: # Less clutter for short chirps
|
||||||
|
i_val += np.random.normal(0, clutter_std/2)
|
||||||
|
q_val += np.random.normal(0, clutter_std/2)
|
||||||
|
|
||||||
|
# Add moving targets with different Doppler for short chirps
|
||||||
|
for target in targets:
|
||||||
|
# Range bin calculation (different for short chirps)
|
||||||
|
range_bin = int(target['range'] / 40) # Different range resolution
|
||||||
|
doppler_phase = 2 * math.pi * target['velocity'] * (chirp + 5) / 80 # Different Doppler
|
||||||
|
|
||||||
|
# Target appears around its range bin
|
||||||
|
if abs(sample - range_bin) < 8:
|
||||||
|
# Different amplitude characteristics for short chirps
|
||||||
|
amplitude = target['snr'] * 0.7 * (8000 / target['range']) # 70% amplitude
|
||||||
|
phase = 2 * math.pi * sample / 30 + doppler_phase
|
||||||
|
|
||||||
|
i_val += amplitude * math.cos(phase)
|
||||||
|
q_val += amplitude * math.sin(phase)
|
||||||
|
|
||||||
|
# Calculate derived values
|
||||||
|
magnitude_squared = i_val**2 + q_val**2
|
||||||
|
|
||||||
|
data.append({
|
||||||
|
'timestamp_ns': timestamp_ns,
|
||||||
|
'chirp_number': chirp_number,
|
||||||
|
'chirp_type': 'SHORT',
|
||||||
|
'sample_index': sample,
|
||||||
|
'I_value': int(i_val),
|
||||||
|
'Q_value': int(q_val),
|
||||||
|
'magnitude_squared': int(magnitude_squared)
|
||||||
|
})
|
||||||
|
|
||||||
|
timestamp_ns += int(1e9 / fs_adc) # 2.5ns per sample
|
||||||
|
|
||||||
|
chirp_number += 1
|
||||||
|
timestamp_ns += 174500 # Add listening period (174.5µs)
|
||||||
|
|
||||||
|
# Create DataFrame
|
||||||
|
df = pd.DataFrame(data)
|
||||||
|
|
||||||
|
# Save to CSV
|
||||||
|
df.to_csv(filename, index=False)
|
||||||
|
print(f"Generated CSV file: {filename}")
|
||||||
|
print(f"Total samples: {len(df)}")
|
||||||
|
print(f"Long chirps: {num_long_chirps}, Short chirps: {num_short_chirps}")
|
||||||
|
print(f"Samples per chirp: {samples_per_chirp}")
|
||||||
|
print(f"File size: {len(df) // 1000}K samples")
|
||||||
|
|
||||||
|
return df
|
||||||
|
|
||||||
|
def analyze_generated_data(df):
|
||||||
|
"""
|
||||||
|
Analyze the generated data to verify target detection
|
||||||
|
"""
|
||||||
|
print("\n=== Data Analysis ===")
|
||||||
|
|
||||||
|
# Basic statistics
|
||||||
|
long_chirps = df[df['chirp_type'] == 'LONG']
|
||||||
|
short_chirps = df[df['chirp_type'] == 'SHORT']
|
||||||
|
|
||||||
|
print(f"Long chirp samples: {len(long_chirps)}")
|
||||||
|
print(f"Short chirp samples: {len(short_chirps)}")
|
||||||
|
print(f"Unique chirp numbers: {df['chirp_number'].nunique()}")
|
||||||
|
|
||||||
|
# Calculate actual magnitude and phase for analysis
|
||||||
|
df['magnitude'] = np.sqrt(df['I_value']**2 + df['Q_value']**2)
|
||||||
|
df['phase_rad'] = np.arctan2(df['Q_value'], df['I_value'])
|
||||||
|
|
||||||
|
# Find high-magnitude samples (potential targets)
|
||||||
|
high_mag_threshold = df['magnitude'].quantile(0.95) # Top 5%
|
||||||
|
targets_detected = df[df['magnitude'] > high_mag_threshold]
|
||||||
|
|
||||||
|
print(f"\nTarget detection threshold: {high_mag_threshold:.2f}")
|
||||||
|
print(f"High magnitude samples: {len(targets_detected)}")
|
||||||
|
|
||||||
|
# Group by chirp type
|
||||||
|
long_targets = targets_detected[targets_detected['chirp_type'] == 'LONG']
|
||||||
|
short_targets = targets_detected[targets_detected['chirp_type'] == 'SHORT']
|
||||||
|
|
||||||
|
print(f"Targets in long chirps: {len(long_targets)}")
|
||||||
|
print(f"Targets in short chirps: {len(short_targets)}")
|
||||||
|
|
||||||
|
return df
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
# Generate the CSV file
|
||||||
|
df = generate_radar_csv("test_radar_data.csv")
|
||||||
|
|
||||||
|
# Analyze the generated data
|
||||||
|
analyze_generated_data(df)
|
||||||
|
|
||||||
|
print("\n=== CSV File Ready ===")
|
||||||
|
print("You can now test the Python GUI with this CSV file!")
|
||||||
|
print("The file contains:")
|
||||||
|
print("- 16 Long chirps + 16 Short chirps")
|
||||||
|
print("- 4 simulated targets at different ranges and velocities")
|
||||||
|
print("- Realistic noise and clutter")
|
||||||
|
print("- Proper I/Q data for Doppler processing")
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
import numpy as np
|
||||||
|
import pandas as pd
|
||||||
|
import math
|
||||||
|
|
||||||
|
def generate_small_radar_csv(filename="small_test_radar_data.csv"):
|
||||||
|
"""
|
||||||
|
Generate a smaller, faster-to-process radar CSV
|
||||||
|
"""
|
||||||
|
# Reduced parameters for faster processing
|
||||||
|
num_long_chirps = 8 # Reduced from 16
|
||||||
|
num_short_chirps = 8 # Reduced from 16
|
||||||
|
samples_per_chirp = 128 # Reduced from 512
|
||||||
|
fs_adc = 400e6
|
||||||
|
|
||||||
|
targets = [
|
||||||
|
{'range': 3000, 'velocity': 25, 'snr': 40},
|
||||||
|
{'range': 5000, 'velocity': -15, 'snr': 35},
|
||||||
|
]
|
||||||
|
|
||||||
|
data = []
|
||||||
|
chirp_number = 0
|
||||||
|
timestamp_ns = 0
|
||||||
|
|
||||||
|
# Generate Long Chirps
|
||||||
|
for chirp in range(num_long_chirps):
|
||||||
|
for sample in range(samples_per_chirp):
|
||||||
|
i_val = np.random.normal(0, 3)
|
||||||
|
q_val = np.random.normal(0, 3)
|
||||||
|
|
||||||
|
# Add targets
|
||||||
|
for target in targets:
|
||||||
|
range_bin = int(target['range'] / 40)
|
||||||
|
doppler_phase = 2 * math.pi * target['velocity'] * chirp / 50
|
||||||
|
|
||||||
|
if abs(sample - range_bin) < 5:
|
||||||
|
amplitude = target['snr'] * (8000 / target['range'])
|
||||||
|
phase = 2 * math.pi * sample / 30 + doppler_phase
|
||||||
|
i_val += amplitude * math.cos(phase)
|
||||||
|
q_val += amplitude * math.sin(phase)
|
||||||
|
|
||||||
|
magnitude_squared = i_val**2 + q_val**2
|
||||||
|
|
||||||
|
data.append({
|
||||||
|
'timestamp_ns': timestamp_ns,
|
||||||
|
'chirp_number': chirp_number,
|
||||||
|
'chirp_type': 'LONG',
|
||||||
|
'sample_index': sample,
|
||||||
|
'I_value': int(i_val),
|
||||||
|
'Q_value': int(q_val),
|
||||||
|
'magnitude_squared': int(magnitude_squared)
|
||||||
|
})
|
||||||
|
|
||||||
|
timestamp_ns += int(1e9 / fs_adc)
|
||||||
|
|
||||||
|
chirp_number += 1
|
||||||
|
timestamp_ns += 137000
|
||||||
|
|
||||||
|
# Generate Short Chirps
|
||||||
|
for chirp in range(num_short_chirps):
|
||||||
|
for sample in range(samples_per_chirp):
|
||||||
|
i_val = np.random.normal(0, 3)
|
||||||
|
q_val = np.random.normal(0, 3)
|
||||||
|
|
||||||
|
for target in targets:
|
||||||
|
range_bin = int(target['range'] / 60)
|
||||||
|
doppler_phase = 2 * math.pi * target['velocity'] * (chirp + 2) / 40
|
||||||
|
|
||||||
|
if abs(sample - range_bin) < 4:
|
||||||
|
amplitude = target['snr'] * 0.6 * (6000 / target['range'])
|
||||||
|
phase = 2 * math.pi * sample / 25 + doppler_phase
|
||||||
|
i_val += amplitude * math.cos(phase)
|
||||||
|
q_val += amplitude * math.sin(phase)
|
||||||
|
|
||||||
|
magnitude_squared = i_val**2 + q_val**2
|
||||||
|
|
||||||
|
data.append({
|
||||||
|
'timestamp_ns': timestamp_ns,
|
||||||
|
'chirp_number': chirp_number,
|
||||||
|
'chirp_type': 'SHORT',
|
||||||
|
'sample_index': sample,
|
||||||
|
'I_value': int(i_val),
|
||||||
|
'Q_value': int(q_val),
|
||||||
|
'magnitude_squared': int(magnitude_squared)
|
||||||
|
})
|
||||||
|
|
||||||
|
timestamp_ns += int(1e9 / fs_adc)
|
||||||
|
|
||||||
|
chirp_number += 1
|
||||||
|
timestamp_ns += 174500
|
||||||
|
|
||||||
|
df = pd.DataFrame(data)
|
||||||
|
df.to_csv(filename, index=False)
|
||||||
|
print(f"Generated small CSV: {filename}")
|
||||||
|
print(f"Total samples: {len(df)}")
|
||||||
|
return df
|
||||||
|
|
||||||
|
generate_small_radar_csv()
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import numpy as np
|
||||||
|
from numpy.fft import fft, ifft
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
|
||||||
|
n = np.arange(0, 61, 1)
|
||||||
|
Fs=125e6
|
||||||
|
Ts = 1.0 / Fs
|
||||||
|
Tb=1e-6
|
||||||
|
y = 1 + np.sin(2*np.pi*(29e6*(n**2)*(Ts**2)/(2.0*Tb)+1e6*n*Ts))
|
||||||
|
|
||||||
|
t = Ts*np.arange(0,61,1)
|
||||||
|
Y = fft(y)
|
||||||
|
M =len(Y)
|
||||||
|
m = np.arange(M)
|
||||||
|
T = M*Ts
|
||||||
|
freq = m/T
|
||||||
|
|
||||||
|
|
||||||
|
plt.figure(figsize = (12, 6))
|
||||||
|
plt.subplot(121)
|
||||||
|
plt.stem(freq, np.abs(Y), 'b', \
|
||||||
|
markerfmt=" ", basefmt="-b")
|
||||||
|
plt.xlabel('Freq (Hz)')
|
||||||
|
plt.ylabel('FFT Amplitude |Y(freq)|')
|
||||||
|
plt.xlim(0, 40*pow(10,6))
|
||||||
|
plt.ylim(0, 20)
|
||||||
|
|
||||||
|
plt.subplot(122)
|
||||||
|
plt.plot(t, ifft(Y), 'r')
|
||||||
|
plt.xlabel('Time (s)')
|
||||||
|
plt.ylabel('Amplitude')
|
||||||
|
plt.tight_layout()
|
||||||
|
|
||||||
|
plt.show()
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
import numpy as np
|
||||||
|
from numpy.fft import fft, ifft
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
|
||||||
|
|
||||||
|
fs=125*pow(10,6) #sampling frequency
|
||||||
|
Ts=1/fs # sampling time
|
||||||
|
Tb=0.25*pow(10,-6) # burst time
|
||||||
|
Tau=1.5*pow(10,-6) # pulse repetition time
|
||||||
|
fmax=32*pow(10,6) # maximum frequency on ramp
|
||||||
|
fmin=1*pow(10,6) # minimum frequency on ramp
|
||||||
|
n=int(Tb/Ts) # number of samples per ramp
|
||||||
|
N = np.arange(0, n, 1)
|
||||||
|
theta_n= 2*np.pi*(pow(N,2)*pow(Ts,2)*(fmax-fmin)/(2*Tb)+fmin*N*Ts) # instantaneous phase
|
||||||
|
y = 1 + np.sin(theta_n) # ramp signal in time domain
|
||||||
|
|
||||||
|
M = np.arange(n, 2*n, 1)
|
||||||
|
theta_m= 2*np.pi*(pow(M,2)*pow(Ts,2)*(-fmax+fmin)/(2*Tb)+(-fmin+2*fmax)*M*Ts)-2*np.pi*((fmin-fmax)*Tb/2+(2*fmax-fmin)*Tb) # instantaneous phase
|
||||||
|
z = 1 + np.sin(theta_m) # ramp signal in time domain
|
||||||
|
|
||||||
|
x = np.concatenate((y, z))
|
||||||
|
|
||||||
|
t = Ts*np.arange(0,2*n,1)
|
||||||
|
X = fft(x)
|
||||||
|
L =len(X)
|
||||||
|
l = np.arange(L)
|
||||||
|
T = L*Ts
|
||||||
|
freq = l/T
|
||||||
|
|
||||||
|
|
||||||
|
plt.figure(figsize = (12, 6))
|
||||||
|
plt.subplot(121)
|
||||||
|
plt.stem(freq, np.abs(X), 'b', \
|
||||||
|
markerfmt=" ", basefmt="-b")
|
||||||
|
plt.xlabel('Freq (Hz)')
|
||||||
|
plt.ylabel('FFT Amplitude |Y(freq)|')
|
||||||
|
plt.xlim(0, (fmax+fmax/10))
|
||||||
|
plt.ylim(0, 20)
|
||||||
|
|
||||||
|
plt.subplot(122)
|
||||||
|
plt.plot(2*n, y)
|
||||||
|
plt.xlabel('Time (s)')
|
||||||
|
plt.ylabel('Amplitude')
|
||||||
|
plt.tight_layout()
|
||||||
|
|
||||||
|
plt.show()
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import numpy as np
|
||||||
|
from numpy.fft import fft, ifft
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
|
||||||
|
|
||||||
|
fs=125*pow(10,6) #sampling frequency
|
||||||
|
Ts=1/fs # sampling time
|
||||||
|
Tb=0.5*pow(10,-6) # burst time
|
||||||
|
Tau=900*pow(10,-6) # pulse repetition time
|
||||||
|
fmax=30*pow(10,6) # maximum frequency on ramp
|
||||||
|
fmin=10*pow(10,6) # minimum frequency on ramp
|
||||||
|
n=int(Tb/Ts) # number of samples per ramp
|
||||||
|
N = np.arange(0, n, 1)
|
||||||
|
theta_n= 2*np.pi*(pow(N,2)*pow(Ts,2)*(fmax-fmin)/(2*Tb)+fmin*N*Ts) # instantaneous phase
|
||||||
|
|
||||||
|
y = 1 + np.sin(theta_n) # ramp signal in time domain
|
||||||
|
|
||||||
|
t = Ts*np.arange(0,n,1)
|
||||||
|
Y = fft(y)
|
||||||
|
M =len(Y)
|
||||||
|
m = np.arange(M)
|
||||||
|
T = M*Ts
|
||||||
|
freq = m/T
|
||||||
|
|
||||||
|
|
||||||
|
plt.figure(figsize = (12, 6))
|
||||||
|
plt.subplot(121)
|
||||||
|
plt.stem(freq, np.abs(Y), 'b', \
|
||||||
|
markerfmt=" ", basefmt="-b")
|
||||||
|
plt.xlabel('Freq (Hz)')
|
||||||
|
plt.ylabel('FFT Amplitude |Y(freq)|')
|
||||||
|
plt.xlim(0, (1.3*fmax))
|
||||||
|
plt.ylim(0, 60)
|
||||||
|
|
||||||
|
plt.subplot(122)
|
||||||
|
plt.plot(t, ifft(Y), 'r')
|
||||||
|
plt.xlabel('Time (s)')
|
||||||
|
plt.ylabel('Amplitude')
|
||||||
|
plt.tight_layout()
|
||||||
|
|
||||||
|
plt.show()
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
import numpy as np
|
||||||
|
from numpy.fft import fft, ifft
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
|
||||||
|
|
||||||
|
fs=125*pow(10,6) #sampling frequency
|
||||||
|
Ts=1/fs # sampling time
|
||||||
|
Tb=0.25*pow(10,-6) # burst time
|
||||||
|
Tau=1.5*pow(10,-6) # pulse repetition time
|
||||||
|
fmax=32*pow(10,6) # maximum frequency on ramp
|
||||||
|
fmin=1*pow(10,6) # minimum frequency on ramp
|
||||||
|
n=int(Tb/Ts) # number of samples per ramp
|
||||||
|
N = np.arange(0, n, 1)
|
||||||
|
theta_n= 2*np.pi*(pow(N,2)*pow(Ts,2)*(fmax-fmin)/(2*Tb)+fmin*N*Ts) # instantaneous phase
|
||||||
|
y = 1 + np.sin(theta_n) # ramp signal in time domain
|
||||||
|
|
||||||
|
M = np.arange(n, 2*n, 1)
|
||||||
|
theta_m= 2*np.pi*(pow(M,2)*pow(Ts,2)*(-fmax+fmin)/(2*Tb)+(-fmin+2*fmax)*M*Ts)-2*np.pi*((fmin-fmax)*Tb/2+(2*fmax-fmin)*Tb) # instantaneous phase
|
||||||
|
z = 1 + np.sin(theta_m) # ramp signal in time domain
|
||||||
|
|
||||||
|
x = np.concatenate((y, z))
|
||||||
|
|
||||||
|
t = Ts*np.arange(0,2*n,1)
|
||||||
|
plt.plot(t, x)
|
||||||
|
X = fft(x)
|
||||||
|
L =len(X)
|
||||||
|
l = np.arange(L)
|
||||||
|
T = L*Ts
|
||||||
|
freq = l/T
|
||||||
|
|
||||||
|
print("The Array is: ", x) #printing the array
|
||||||
|
|
||||||
|
plt.figure(figsize = (12, 6))
|
||||||
|
plt.subplot(121)
|
||||||
|
plt.stem(freq, np.abs(X), 'b', \
|
||||||
|
markerfmt=" ", basefmt="-b")
|
||||||
|
plt.xlabel('Freq (Hz)')
|
||||||
|
plt.ylabel('FFT Amplitude |Y(freq)|')
|
||||||
|
plt.xlim(0, (fmax+fmax/10))
|
||||||
|
plt.ylim(0, 20)
|
||||||
|
|
||||||
|
plt.subplot(122)
|
||||||
|
plt.plot(t, ifft(X), 'r')
|
||||||
|
plt.xlabel('Time (s)')
|
||||||
|
plt.ylabel('Amplitude')
|
||||||
|
plt.tight_layout()
|
||||||
|
|
||||||
|
plt.show()
|
||||||
|
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import numpy as np
|
||||||
|
|
||||||
|
# Define parameters
|
||||||
|
fs = 120e6 # Sampling frequency
|
||||||
|
Ts = 1 / fs # Sampling time
|
||||||
|
Tb = 1e-6 # Burst time
|
||||||
|
Tau = 30e-6 # Pulse repetition time
|
||||||
|
fmax = 15e6 # Maximum frequency on ramp
|
||||||
|
fmin = 1e6 # Minimum frequency on ramp
|
||||||
|
|
||||||
|
# Compute number of samples per ramp
|
||||||
|
n = int(Tb / Ts)
|
||||||
|
N = np.arange(0, n, 1)
|
||||||
|
|
||||||
|
# Compute instantaneous phase
|
||||||
|
theta_n = 2 * np.pi * ((N**2 * Ts**2 * (fmax - fmin) / (2 * Tb)) + fmin * N * Ts)
|
||||||
|
|
||||||
|
# Generate waveform and scale it to 8-bit unsigned values (0 to 255)
|
||||||
|
y = 1 + np.sin(theta_n) # Normalize from 0 to 2
|
||||||
|
y_scaled = np.round(y * 127.5).astype(int) # Scale to 8-bit range (0-255)
|
||||||
|
|
||||||
|
# Print values in Verilog-friendly format
|
||||||
|
for i in range(n):
|
||||||
|
print(f"waveform_LUT[{i}] = 8'h{y_scaled[i]:02X};")
|
||||||
@@ -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(
|
||||||
|
"<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()
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import numpy as np
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
n = np.arange(0, 61, 1)
|
||||||
|
Ts=8*pow(10,-9)
|
||||||
|
y = 1 + np.sin(2*np.pi*(-16*pow(10,12)*pow(n,2)*pow(Ts,2)+16*pow(10,6)*n*Ts))
|
||||||
|
plt.plot(n, y)
|
||||||
|
plt.show()
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
import numpy as np
|
||||||
|
|
||||||
|
def calculate_patch_antenna_parameters(frequency, epsilon_r, h_sub, h_cu, array):
|
||||||
|
# Constants
|
||||||
|
c = 3e8 # Speed of light in m/s
|
||||||
|
|
||||||
|
# Convert height from mm to meters
|
||||||
|
h_sub_m = h_sub * 1e-3
|
||||||
|
h_cu_m = h_cu * 1e-3
|
||||||
|
|
||||||
|
# Calculate Lambda
|
||||||
|
lamb = c /(frequency * 1e9)
|
||||||
|
|
||||||
|
# Calculate the effective dielectric constant
|
||||||
|
epsilon_eff = (epsilon_r + 1) / 2 + (epsilon_r - 1) / 2 * (1 + 12 * h_sub_m / (array[1] * h_cu_m)) ** (-0.5)
|
||||||
|
|
||||||
|
# Calculate the width of the patch
|
||||||
|
W = c / (2 * frequency * 1e9) * np.sqrt(2 / (epsilon_r + 1))
|
||||||
|
|
||||||
|
# Calculate the effective length
|
||||||
|
delta_L = 0.412 * h_sub_m * (epsilon_eff + 0.3) * (W / h_sub_m + 0.264) / ((epsilon_eff - 0.258) * (W / h_sub_m + 0.8))
|
||||||
|
|
||||||
|
# Calculate the length of the patch
|
||||||
|
L = c / (2 * frequency * 1e9 * np.sqrt(epsilon_eff)) - 2 * delta_L
|
||||||
|
|
||||||
|
# Calculate the separation distance in the horizontal axis (dx)
|
||||||
|
dx = lamb/2 # Typically 1.5 times the width of the patch
|
||||||
|
|
||||||
|
# Calculate the separation distance in the vertical axis (dy)
|
||||||
|
dy = lamb/2 # Typically 1.5 times the length of the patch
|
||||||
|
|
||||||
|
# Calculate the feeding line width (W_feed)
|
||||||
|
Z0 = 50 # Characteristic impedance of the feeding line (typically 50 ohms)
|
||||||
|
A = Z0 / 60 * np.sqrt((epsilon_r + 1) / 2) + (epsilon_r - 1) / (epsilon_r + 1) * (0.23 + 0.11 / epsilon_r)
|
||||||
|
W_feed = 8 * h_sub_m / np.exp(A) - 2 * h_cu_m
|
||||||
|
|
||||||
|
# Convert results back to mm
|
||||||
|
W_mm = W * 1e3
|
||||||
|
L_mm = L * 1e3
|
||||||
|
dx_mm = dx * 1e3
|
||||||
|
dy_mm = dy * 1e3
|
||||||
|
W_feed_mm = W_feed * 1e3
|
||||||
|
|
||||||
|
return W_mm, L_mm, dx_mm, dy_mm, W_feed_mm
|
||||||
|
|
||||||
|
# Example usage
|
||||||
|
frequency = 10.5 # Frequency in GHz
|
||||||
|
epsilon_r = 3.48 # Relative permittivity of the substrate
|
||||||
|
h_sub = 0.102 # Height of substrate in mm
|
||||||
|
h_cu = 0.07 # Height of copper in mm
|
||||||
|
array = [2, 2] # 2x2 array
|
||||||
|
|
||||||
|
W_mm, L_mm, dx_mm, dy_mm, W_feed_mm = calculate_patch_antenna_parameters(frequency, epsilon_r, h_sub, h_cu, array)
|
||||||
|
|
||||||
|
print(f"Width of the patch: {W_mm:.4f} mm")
|
||||||
|
print(f"Length of the patch: {L_mm:.4f} mm")
|
||||||
|
print(f"Separation distance in horizontal axis: {dx_mm:.4f} mm")
|
||||||
|
print(f"Separation distance in vertical axis: {dy_mm:.4f} mm")
|
||||||
|
print(f"Feeding line width: {W_feed_mm:.2f} mm")
|
||||||
|
After Width: | Height: | Size: 149 KiB |