radar_image_quality / src /app_backend.py
sergiev's picture
Upload folder using huggingface_hub
52ce6eb verified
"""
Модуль для расчета параметров РЛС по модели бистатической РЛС
Автор кода: Владислав Калинников
"""
import math as mt
from typing import List
# скорость света в вакууме, м|с
C = 299792458
# постоянная Больцмана, Дж/К
BOLTZMANN = 1.38 * 10 ** (-23)
# температура предатчика/приемника, К
RADAR_TEMPERATURE = 290
# ширина антенны, м
# ANTENNA_WIDTH = 0.08
# длина антенны, м
# ANTENNA_LENGTH = 0.25
# коэффициент усиления антенны, дБ
# ANTENNA_GAIN = 14
# длина волны, м
# WAVELENGTH = 0.032
# пиковая мощность, ВТ
# PEAK_POWER = 10
# полоса пропускания, МГц
# BANDWIDTH = 1000
# шум-фактор, дБ
# NOISE_FACTOR = 3
# коэффициент заполнения
# q_fill = 0.08
# угол поляризации, град. 0 - горизонтальная поляризация, 90 - вертикальная поляризация
# POLATIZATION_TILT_ANGLE = 0
# функция определяет координаты углов области пересечения проекций на земную поверхность главных лепестков
# point_name - имя точки (A,B,C,D), по умолчанию равно A
# h - высота полета в м, uav_interval - размер базы между РЛС, м
# psi_t - угол курса оси главного лепестка ДНА на излучение (по часовой стрелке>0, против часовой <0), град
# psi_r - угол курса оси главного лепестка ДНА на прием (по часовой стрелке>0, против часовой <0), град
def footprint_corner_crd(
point_name: str,
h: float,
uav_interval: float,
psi_t: float,
psi_r: float,
wavelength: float,
antenna_length: float,
):
# угловой размер главного главного лепестка по азимуту
theta = wavelength / antenna_length
# угол курса главного лепестка ДНА на излучение в радианах
psi_t = psi_t * mt.pi / 180
# угол курса главного лепестка ДНА на прием в радианах
psi_r = psi_r * mt.pi / 180
if psi_r - psi_t > theta:
# начальные значения поправок угла курса точки относительно РЛС1 и РЛС2
dpsi_1 = 0.1
dpsi_2 = 0.1
# начальные обновленные значения поправок угла курса точки относительно РЛС1 и РЛС2
dpsi_1upd = 0
dpsi_2upd = 0
# коэффициенты для расчета углов места и углов курса точки относительно РЛС1 и РЛС2
k1 = 1
k2 = 1
if point_name == "B":
k1 = 1
k2 = -1
elif point_name == "C":
k1 = -1
k2 = -1
elif point_name == "D":
k1 = -1
k2 = 1
# определение углов места и углов курса точки относительно РЛС1 и РЛС2 методом последовательных приближений
j = 0
while abs(dpsi_1upd - dpsi_1) > 0.01 or abs(dpsi_2upd - dpsi_2) > 0.01:
dpsi_1 = dpsi_1upd
dpsi_2 = dpsi_2upd
psi_1 = psi_t + dpsi_1
psi_2 = psi_r + dpsi_2
phi_1 = mt.atan(uav_interval / h * mt.cos(psi_2) / mt.sin(psi_2 - psi_1))
phi_2 = mt.atan(uav_interval / h * mt.cos(psi_1) / mt.sin(psi_2 - psi_1))
if (
abs(mt.sin(theta / 2) / mt.sin(phi_1)) > 1
or abs(mt.sin(theta / 2) / mt.sin(phi_2)) > 1
or j > 1e3
):
return "none"
j += 1
dpsi_1upd = k1 * mt.asin(mt.sin(theta / 2) / mt.sin(phi_1))
dpsi_2upd = k2 * mt.asin(mt.sin(theta / 2) / mt.sin(phi_2))
# итоговые значения углов места и углов курса точки относительно РЛС1 и РЛС2
dpsi_1 = dpsi_1upd
dpsi_2 = dpsi_2upd
psi_1 = psi_t + dpsi_1
psi_2 = psi_r + dpsi_2
phi_1 = mt.atan(uav_interval / h * mt.cos(psi_2) / mt.sin(psi_2 - psi_1))
phi_2 = mt.atan(uav_interval / h * mt.cos(psi_1) / mt.sin(psi_2 - psi_1))
# координаты точки
x = h * mt.tan(phi_2) * mt.cos(psi_2)
y = h * mt.tan(phi_2) * mt.sin(psi_2)
return [x, y]
else:
return "none"
# функция определяет водность облаков, г|м3
# cloud_thickness - толщина облаков
def cloud_liquid_water_content(cloud_thickness: float) -> float:
if cloud_thickness != 0:
W = 0.132574 * (cloud_thickness / 1000) ** 2.30215
return W / cloud_thickness * 1000
else:
return 0
# функция определяет ординтаты точек пересечения выпуклого многоугольника (polygon) с лучом, имеющем ординату x
def polygon_cross_points(polygon: List[float], x: float) -> List[float]:
y1 = "none"
y2 = "none"
for i in range(0, len(polygon[0])):
if i < len(polygon[0]) - 1:
k = i + 1
else:
k = 0
if x >= min(polygon[0][i], polygon[0][k]) and x <= max(
polygon[0][i], polygon[0][k]
):
if polygon[0][i] != polygon[0][k]:
if y1 == "none":
y1 = (polygon[1][k] - polygon[1][i]) / (
polygon[0][k] - polygon[0][i]
) * (x - polygon[0][i]) + polygon[1][i]
else:
y2 = (polygon[1][k] - polygon[1][i]) / (
polygon[0][k] - polygon[0][i]
) * (x - polygon[0][i]) + polygon[1][i]
else:
y1 = polygon[1][i]
y2 = polygon[1][k]
return [min(y1, y2), max(y1, y2)]
# функция определяет координаты углов РЛИ
# h - высота полета в м, uav_interval - размер базы между РЛС, м
# psi_t - угол курса оси главного лепестка ДНА на излучение (по часовой стрелке>0, против часовой <0), град
# psi_r - угол курса оси главного лепестка ДНА на прием (по часовой стрелке>0, против часовой <0), град
def frame_corner_crd(
h: float,
uav_interval: float,
psi_t: float,
psi_r: float,
wavelength: float,
antenna_length: float,
):
# координаты области пересечения проекций на земную поверхность главных лепестков
crd_A = footprint_corner_crd(
point_name="A",
h=h,
uav_interval=uav_interval,
psi_t=psi_t,
psi_r=psi_r,
wavelength=wavelength,
antenna_length=antenna_length,
)
crd_B = footprint_corner_crd(
point_name="B",
h=h,
uav_interval=uav_interval,
psi_t=psi_t,
psi_r=psi_r,
wavelength=wavelength,
antenna_length=antenna_length,
)
crd_C = footprint_corner_crd(
point_name="C",
h=h,
uav_interval=uav_interval,
psi_t=psi_t,
psi_r=psi_r,
wavelength=wavelength,
antenna_length=antenna_length,
)
crd_D = footprint_corner_crd(
point_name="D",
h=h,
uav_interval=uav_interval,
psi_t=psi_t,
psi_r=psi_r,
wavelength=wavelength,
antenna_length=antenna_length,
)
if crd_A != "none" and crd_B != "none" and crd_C != "none" and crd_D != "none":
polygon = [
[crd_A[0], crd_B[0], crd_C[0], crd_D[0]],
[crd_A[1], crd_B[1], crd_C[1], crd_D[1]],
]
# максимальное и минимальное значение абсциисы области пересечения проекций на земную поверхность главных лепестков
x_min = min(polygon[0])
x_max = max(polygon[0])
# точность оценики координат углов РЛИ
accuracy = 20
# число итераций для поиска максимальной площади РЛИ
N = 1 + int((x_max - x_min) / accuracy)
# шаг по оси абсцисс при поиске максимальной плоащади РЛИ
dx = (x_max - x_min) / N
# начальные значения коориднат углов и площади РЛИ
s_upd = 0
# dq_upd = 100
x1_upd = "none"
x2_upd = "none"
y1_upd = "none"
y2_upd = "none"
# поиск макисальной площади и углов РЛИ
for i in range(0, N - 1):
x1 = x_min + i * dx
for j in range(i + 1, N):
x2 = x_min + j * dx
y11 = polygon_cross_points(polygon, x1)[0]
y12 = polygon_cross_points(polygon, x1)[1]
y21 = polygon_cross_points(polygon, x2)[0]
y22 = polygon_cross_points(polygon, x2)[1]
if y11 >= y21 and y12 <= y22:
y1 = y11
y2 = y12
elif y11 <= y21 and y12 >= y21 and y12 <= y22:
y1 = y21
y2 = y12
elif y11 <= y22 and y11 >= y21 and y12 >= y22:
y1 = y11
y2 = y22
elif y11 <= y21 and y12 >= y22:
y1 = y21
y2 = y22
else:
y1 = "none"
y2 = "none"
if y1 != "none" and y2 != "none":
s = (x2 - x1) * (y2 - y1)
# dq = abs((y2-y1)/(x2-x1)-q)
if s > s_upd: # dq < dq_upd:
s_upd = s
# dq_upd = dq
x1_upd = x1
x2_upd = x2
y1_upd = y1
y2_upd = y2
# возврат массива координат углов РЛК (точек T1,T2,T3,T4)
return [[x1_upd, x1_upd, x2_upd, x2_upd], [y1_upd, y2_upd, y2_upd, y1_upd]]
else:
return "none"
# параметры a1, a2, a3 модели Кулемина для расчета УЭПР разных типов подстилающих поверхностей, дБ
kulemin_parameters = {
"лес летом": [-20, 10, 6],
"лес зимой": [-40, 10, 6],
"луг высокотравный": [-21, 10, 6],
"луг низкотравный": [-28, 10, 6],
"пашня": [-37, 18, 15],
"снег": [-34, 25, 15],
}
# функция определеяет УЭПР поверхности в дБ согласно модели Кулемина
# surface_type - тип подстилающей поверхности (пашня, снег и т.д.), phi - угол падения, град
# если задан неизвестный тип подстилающей поверхности, функция вернет значение -20 дБ
def kulemin_specific_rcs(surface_type, phi, wavelength):
# частота зондирования в ГГц
f = C / wavelength * 10 ** (-9)
# угол скольжения в град
slip = 90 - phi
if kulemin_parameters.get(surface_type) != None:
a1, a2, a3 = kulemin_parameters[surface_type]
return a1 + a2 * mt.log(slip / 20, 10) + a3 * mt.log(f / 10, 10)
else:
return -20
# функция определяет коэффициент погонного ослабления в облаке, дБ/км/(г/м3)
def itu_cloud_attenuation(wavelength):
# частота зондирования в ГГц
f = C / wavelength * 10 ** (-9)
theta = 300 / 273.15
eps0 = 77.66 + 103.3 * (theta - 1)
eps1 = 0.0671 * eps0
eps2 = 3.52
fp = 20.20 - 146 * (theta - 1) + 316 * (theta - 1) ** 2
fs = 39.8 * fp
eps_prime = f * (eps0 - eps1) / fp / (1 + (f / fp) ** 2) + f * (
eps1 - eps2
) / fs / (1 + (f / fs) ** 2)
eps_double_prime = (
(eps0 - eps1) / (1 + (f / fp) ** 2) + (eps1 - eps2) / (1 + (f / fs) ** 2) + eps2
)
nabla = (2 + eps_prime) / eps_double_prime
return 0.819 * f / eps_double_prime / (1 + nabla**2)
# параметры kh, ah, kv, av модели погонного ослабления в дожде МСЭ-R P.838-3 для частот 7 - 12 ГГц
itu_rain_parameters = {
7: [0.001915, 1.4810, 0.001425, 1.4745],
8: [0.004115, 1.3905, 0.003450, 1.3797],
9: [0.007535, 1.3155, 0.006691, 1.2895],
10: [0.012170, 1.2571, 0.011290, 1.2156],
11: [0.017720, 1.2140, 0.017310, 1.1617],
12: [0.023860, 1.1825, 0.024550, 1.1216],
}
# классификация погодных условий по интенсивности осадков, мм/ч
rain_rate_classes = {
"ясно": 0,
"слабый дождь": 5,
"умеренный дождь": 12,
"сильный дождь": 30,
"ливень": 40,
}
# функция определяет коэффициент погонного ослабления в дожде в дБ/км для диапазона X согласно модели МСЭ-R P.838-3
# rain_rate - интенсивность дождя в мм/ч, phi- угол падения, град
# если длина волны зондирования лежит не в диапазоне X, функция вернет 0
def itu_rain_attenuation(
rain_rate: float, phi: float, wavelength: float, polarization_tilt_angle: float
) -> float:
# частота зондирования в ГГц
f = C / wavelength * 10 ** (-9)
# угол скольжения в радианах
slip = (90 - phi) * mt.pi / 180
# угол поляризации в радианах
tilt = polarization_tilt_angle * mt.pi / 180
# целочисленные значения частот вокруг частоты зондирования
f1 = int(f)
f2 = int(f) + 1
if (itu_rain_parameters.get(f1) != None) and (itu_rain_parameters.get(f2) != None):
# параметры модели для частоты f1
kh1 = itu_rain_parameters[f1][0]
ah1 = itu_rain_parameters[f1][1]
kv1 = itu_rain_parameters[f1][2]
av1 = itu_rain_parameters[f1][3]
# параметры модели для частоты f2
kh2 = itu_rain_parameters[f2][0]
ah2 = itu_rain_parameters[f2][1]
kv2 = itu_rain_parameters[f2][2]
av2 = itu_rain_parameters[f2][3]
# интерполяция параметров модели на частоту зондирования f
kh = kh1 + (kh2 - kh1) / (f2 - f1) * (f - f1)
ah = ah1 + (ah2 - ah1) / (f2 - f1) * (f - f1)
kv = kv1 + (kv2 - kv1) / (f2 - f1) * (f - f1)
av = av1 + (av2 - av1) / (f2 - f1) * (f - f1)
# параметры модели для заданной поляризации
k = (kh + kv + (kh - kv) * (mt.cos(slip)) ** 2 * mt.cos(2 * tilt)) / 2
a = (
(
kh * ah
+ kv * av
+ (kh * ah - kv * av) * (mt.cos(slip)) ** 2 * mt.cos(2 * tilt)
)
/ 2
/ k
)
return k * (rain_rate) ** a
else:
return 0
# входные параметры модели:
# v - скорость полета, м/с
# h - высота полета, м
# uav_interval - размер базы между РЛС, м
# psi_t - угол курса оси главного лепестка ДНА на излучение (по часовой стрелке>0, против часовой <0), град
# psi_r - угол курса оси главного лепестка ДНА на прием (по часовой стрелке>0, против часовой <0), град
# srcs - УЭПР фона, дБ или тип поверхности по модели Кулемина
# cloud_base - высота нижней границы облаков, м
# cloud_thickness - толщина облаков, м
# rain_rate - интенсивность дождя, мм/ч
# выходные параметры модели:
# dx - предельное разрешение по горизонтальной дальности в центре РЛК, м
# dy - предельное разрешение по азимуту в центре РЛК, м
# snr - отношение сигнал/шум фона в центре РЛК, дБ
# tau - длина импульса, с
# tau_echo - длина эхо-сигнала, с
# t_repeat - период повторения импульсов, с
# t_synthesis_max - максимальное время синтеза апертуры, с
def bistatic_radar_model(
v: float,
h: float,
uav_interval: float,
psi_t: float,
psi_r: float,
srcs: str | float,
cloud_base: float,
cloud_thickness: float,
rain_rate: float,
q_fill: float,
bandwidth: float,
wavelength: float,
antenna_gain: float,
antenna_length: float,
noise_factor: float,
peak_power: float,
polarization_tilt_angle: float,
):
# координаты РЛС1
x1 = 0
y1 = uav_interval
# координаты РЛС2
x2 = 0
y2 = 0
# коодинаты точки T1
frame_crd = frame_corner_crd(
h=h,
uav_interval=uav_interval,
psi_t=psi_t,
psi_r=psi_r,
wavelength=wavelength,
antenna_length=antenna_length,
)
if frame_crd != "none":
x_t1 = frame_crd[0][0]
y_t1 = frame_crd[1][0]
# коодинаты точки T2
x_t2 = frame_crd[0][1]
y_t2 = frame_crd[1][1]
# коодинаты точки T3
x_t3 = frame_crd[0][2]
y_t3 = frame_crd[1][2]
# коодинаты точки T4
x_t4 = frame_crd[0][3]
y_t4 = frame_crd[1][3]
# коодинаты центра РЛК
xo = (x_t2 + x_t3) / 2
yo = (y_t1 + y_t2) / 2
# наклонные дальности от РЛС1 до углов и центра РЛК
r1_t1 = mt.sqrt((x_t1 - x1) ** 2 + (y_t1 - y1) ** 2 + h**2)
r1_t2 = mt.sqrt((x_t2 - x1) ** 2 + (y_t1 - y1) ** 2 + h**2)
r1_t3 = mt.sqrt((x_t3 - x1) ** 2 + (y_t1 - y1) ** 2 + h**2)
r1_t4 = mt.sqrt((x_t4 - x1) ** 2 + (y_t1 - y1) ** 2 + h**2)
r1_o = mt.sqrt((xo - x1) ** 2 + (yo - y1) ** 2 + h**2)
# наклонные дальности от РЛС2 до углов и центра РЛК
r2_t1 = mt.sqrt((x_t1 - x2) ** 2 + (y_t1 - y2) ** 2 + h**2)
r2_t2 = mt.sqrt((x_t2 - x2) ** 2 + (y_t1 - y2) ** 2 + h**2)
r2_t3 = mt.sqrt((x_t3 - x2) ** 2 + (y_t1 - y2) ** 2 + h**2)
r2_t4 = mt.sqrt((x_t4 - x2) ** 2 + (y_t1 - y2) ** 2 + h**2)
r2_o = mt.sqrt((xo - x2) ** 2 + (yo - y2) ** 2 + h**2)
# угол падения импульса от РЛС1 в центр РЛК
phi1_o = mt.atan(h / r1_o) * 180 / mt.pi
# угол отражения импульса от центра РЛК к РЛС2
phi2_o = mt.atan(h / r2_o) * 180 / mt.pi
# модуль градиента от суммы наклонных дальностей от РЛС1 и РЛС2 до центра РЛК
grad_r = mt.sqrt((xo / r1_o + xo / r2_o) ** 2 + (yo / r1_o + yo / r2_o) ** 2)
# модуль градиента от суммы доплеровских частот на трассах от РЛС1 до центра РЛК и от центра РЛК до РЛС2
grad_f = (
v
/ wavelength
* mt.sqrt(
(1 / r1_o + 1 / r2_o - xo**2 / r1_o**3 - xo**2 / r2_o**3) ** 2
+ (xo * yo / r1_o**3 + xo * yo / r2_o**3) ** 2
)
)
# длина импульса
tau = min(r1_t1 + r2_t1, r1_t2 + r2_t2) / C
# длина эхо-сигнала
tau_echo = (
tau
+ max(r1_t3 + r2_t3, r1_t4 + r2_t4) / C
- min(r1_t1 + r2_t1, r1_t2 + r2_t2) / C
)
# период повторения импульсов
t_repeat = tau / q_fill
# максимальный коэффициент сжатия
k_compression_max = bandwidth * 1000000 * tau
# максимальное время синтеза апертуры
t_synthesis_max = 2 * (x_t3 - x_t2) / v
# максимальное число когерентных импульсов
n_coh_max = 1 + int(t_synthesis_max / t_repeat)
# разрешение по горизонтальной дальности
dx = C * tau / k_compression_max / grad_r
# разрешение по азимуту
dy = 1 / t_synthesis_max / grad_f
# УЭПР фона
if type(srcs) == str:
srcs = kulemin_specific_rcs(surface_type=srcs, phi=(phi1_o + phi2_o) / 2, wavelength=wavelength)
# ЭПР точечного отражателя фона
sigma = 10 ** (srcs / 10) * dx * dy
# эффективная площадь антенны
antenna_area = 10 ** (antenna_gain / 10) * wavelength**2 / 4 / mt.pi
# мощность эхо-сигнала от центра РЛК
p_echo = (
peak_power
* 10 ** (antenna_gain / 10)
* antenna_area
/ (4 * mt.pi * r1_o * r2_o) ** 2
* sigma
* k_compression_max
* n_coh_max
)
# мощность шума
p_noise = (
BOLTZMANN
* 10 ** (noise_factor / 10)
* RADAR_TEMPERATURE
* bandwidth
* 1000000
)
# отношение сигнал/шум без учета погоды
snr = 10 * mt.log(p_echo / p_noise, 10)
# протяженность наклонных дальностей в облаках и дожде, км
if cloud_thickness != 0:
cloud_path1 = (
r1_o
/ 1000
* (min(h, cloud_base + cloud_thickness) - min(h, cloud_base))
/ h
)
cloud_path2 = (
r2_o
/ 1000
* (min(h, cloud_base + cloud_thickness) - min(h, cloud_base))
/ h
)
rain_path1 = r1_o / 1000 * min(h, cloud_base) / h
rain_path2 = r2_o / 1000 * min(h, cloud_base) / h
else:
cloud_path1 = 0
cloud_path2 = 0
rain_path1 = 0
rain_path2 = 0
# водность облаков
w = cloud_liquid_water_content(cloud_thickness=cloud_thickness)
# ослабление в облаках по модели МСЭ-R P.840-7
cloud_att1 = itu_cloud_attenuation(wavelength=wavelength) * w * cloud_path1
cloud_att2 = itu_cloud_attenuation(wavelength=wavelength) * w * cloud_path2
# ослабление в дожде по модели МСЭ-R P.838-3
rain_att1 = (
itu_rain_attenuation(
rain_rate=rain_rate,
phi=phi1_o,
wavelength=wavelength,
polarization_tilt_angle=polarization_tilt_angle,
)
* rain_path1
)
rain_att2 = (
itu_rain_attenuation(
rain_rate=rain_rate,
phi=phi2_o,
wavelength=wavelength,
polarization_tilt_angle=polarization_tilt_angle,
)
* rain_path2
)
# отношение сигнал/шум с учетом погодных условий
snr = snr - cloud_att1 - cloud_att2 - rain_att1 - rain_att2
return {
"dx": dx,
"dy": dy,
"snr": snr,
"tau": tau,
"tau_echo": tau_echo,
"t_repeat": t_repeat,
"t_synthesis_max": t_synthesis_max,
}
else:
return "none"