Files
uarep-ctl/scripts/interfer.gd
2025-09-18 23:24:42 +03:00

556 lines
23 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

class_name Iterfer extends Node
## Представление помехи.
## Режимы помехи.
const ITERFER_MODE = {
INTERFER_OFF: [INTERFER_OFF, {}, 0],
'srni1': ['ПСП + ФМ-2', SRNI1_PARAMS, 1],
'srnm2': ['Линейная ЧМ', SRNM2_PARAMS, 2],
'srngps': [INTERFER_IQFILE, SRNGPS_PARAMS, 3],
'lchm1': ['Линейная ЧМ', LCHM1_PARAMS, 10],
'lchm2': ['Линейная ЧМ', LCHM2_PARAMS, 16],
'mchm1': [INTERFER_IQFILE, MCHM1_PARAMS, 11],
'pdm2': [INTERFER_IQFILE, PDM2_PARAMS, 12],
'pdm4': [INTERFER_IQFILE, PDM4_PARAMS, 13],
'pdm4s': [INTERFER_IQFILE, PDM4S_PARAMS, 14],
'pdchm': [INTERFER_IQFILE, PDCHM_PARAMS, 15],
'svi1': [INTERFER_IQFILE, SVI1_PARAMS, 20],
'svm2': [INTERFER_IQFILE, SVM2_PARAMS, 21],
'k1': [INTERFER_IQFILE, K1_PARAMS, 50],
'ozp1': [INTERFER_IQFILE, OZP1_PARAMS, 51] }
const SRNI1_PARAMS = {'par_names' : ['freq', 'width']}
const SRNM2_PARAMS = {'par_names' : ['freq', 'width', 'period']}
const SRNGPS_PARAMS = {'par_names' : ['freq', 'filename'],
'files': 'SRNGPS.pcm',
'call_proc': 'call_all_diap'}
const LCHM1_PARAMS = {'par_names' : ['freq', 'width', 'period']}
const LCHM2_PARAMS = {'par_names' : ['freq', 'width', 'period']}
const MCHM1_PARAMS = {'par_names' : ['freq', 'filename'],
'files': 'MCHM1.pcm',
'call_proc': 'call_all_diap'}
const PDM2_PARAMS = {'par_names' : ['freq', 'filename'],
'files': 'PDM2_20.pcm',
'call_proc': 'call_all_diap'}
const PDM4_PARAMS = {'par_names' : ['freq', 'filename'],
'files': 'PDM4_20.pcm',
'call_proc': 'call_all_diap'}
const PDM4S_PARAMS = {'par_names' : ['freq', 'filename'],
'files': 'PDM4S_5.pcm',
'call_proc': 'call_all_diap'}
const K1_PARAMS = {'files' : 'K1.pcm',
'call_proc' : 'call_all_diap',
'par_names' : ['freq', 'filename']}
const OZP1_PARAMS = {'files': 'OZP1.pcm',
'call_proc': 'call_all_diap',
'par_names' : ['freq', 'filename']}
# Файл : Диапазон: ['имя файла', частота]
const SVI1_PARAMS = {'files': {1: ['SVI1_LIT2.pcm', 1017],
2: ['SVI1_LIT3.pcm', 1156]},
'call_proc': 'call_diap',
'par_names' : ['freq', 'filename']}
const SVM2_PARAMS = {'files': {1: ['SVM2_LIT2.pcm', 1017],
2: ['SVM2_LIT3.pcm', 1156]},
'call_proc': 'call_diap',
'par_names' : ['freq', 'filename']}
# Файл : Ключ (ширина): ['имя файла']
const PDCHM_PARAMS = {'files': {
5 : 'pdchm_5.pcm',
10: 'pdchm__0.pcm',
20: 'pdchm_20.pcm',
40: 'pdchm_40.pcm' },
'call_proc': 'call_pdchm',
'par_names' : ['freq', 'filename'],}
const SOURCE_INTERFER = ['th','fs','cu']
const EXCLUDE_INTERFER = ['srni1', 'srnm2', 'srngps', 'svi1', 'svm2']
const ID_INTERFERS_LEN = 63 ## Размер пула номеров целей.
const INTERFER_OFF = 'off'
const INTERFER_IQFILE = 'IQ-файл'
const POW_DEFAULT: float = 100.0
var msg6_num: int = 0 ## Счётчик сообщений "тип 6"
var msg8_num: int = 0 ## Счётчик сообщений "тип 8"
var id_array: Array ## Массив id воздействий.
var thrs: Dictionary ## Словарь воздействия для целей, ключ - id воздействия.
var fs: Dictionary ## Словарь воздействий для секторов, ключ - id воздействия.
var cu: Dictionary ## Словарь воздействий для 5П-28, ключ - id воздействия.
var thrs_dict: Dictionary ## Ссылка на словарь целей
var auto_enabled: bool = false ## Режим автомата
var emit_enable: bool = true ## Разрешить излучение
## Сеанс помехи.
class Interfer:
var ispp: int = 0 ## Индекс сеанса.
var svk: = 0 ## Состояние сеанса. Ответ КАПСРПБ. Возможные значения в [param ITERFER_STATE].
var krp: String = '' ## Тип помехи. Возможные значения в [param INTERFER_MODE].
var powp: float = 100.0 ## Мощность излучения выраженная в процентах.
var kni: = 0.0 ## Курсовой угол направления излучения.
var params: = {} ## Параметры помехи.
var get_params: Array[String] ## Запрос массива параметров (список запрошенных параметров).
var retss: int ## Значение счётчика сообщения в котором был произведён запрос массива параметров.
var tick_tx: int ## Тик время обновления цели, мс.
var source_interfer: String = '' ## Тип объекта по которой поставлена помеха.
var modulation: bool ## Модуляция для включения усилителей
var um: float ## Угол места для помехи.
var nmfs: int = 0 ## Номер модуля ФС в нумерации СТЦ
func _to_string() -> String:
return 'номер: %d название: %s частота: %0.1f МГц пеленг: %0.1f гр.' % [ispp, ITERFER_MODE[krp][0], params['freq'], kni]
func get_msg6_data(msg_num: int) -> PackedByteArray:
var data_to_json : Dictionary = {}
data_to_json['ts'] = 6
data_to_json['ss'] = msg_num
data_to_json['ispp'] = ispp
data_to_json['krp'] = ITERFER_MODE[krp][0]
data_to_json['nmfs'] = nmfs
if krp != INTERFER_OFF:
data_to_json['kni'] = kni
data_to_json['powp'] = powp
data_to_json['params'] = {}
for key in ITERFER_MODE[krp][1]['par_names']:
if params.has(key):
data_to_json['params'][key] = params[key]
data_to_json['get'] = get_params
var pac = JSON.stringify(data_to_json).to_utf8_buffer()
return pac
func set_from_dic(dic: Dictionary):
var names: = tools.get_members_names(self)
names.any(func(vn): if dic.has(vn): set(vn, dic[vn]); return false)
func get_value(value_key):
var param = self.get(value_key)
if param == null:
if params.has(value_key): param = params[value_key]
return param
func set_value(value, value_key):
var param = self.get(value_key)
if param == null:
params[value_key] = value
else:
self.set(value_key, value)
signal interfer_preparing(ecm: Interfer) ## Вызывается в момент запуска помехи.
signal interfer_run(ecm: Interfer) ## Вызывается в момент начала работы помехи.
signal interfer_error(ecm: Interfer, msg: String) ## Вызывается при ошибке.
signal interfer_update(ecm: Interfer) ## Вызывается при обновлении помехи.
signal interfer_resized(ecms: Dictionary) ## Вызывается при изменении количества сеансов помех.
## Отправляет словарь помех в юнит, для запаковки и отправки в 5П28.
func on_get_interfers(unit, ecms) -> void: unit.pack_interfers(cu, ecms)
## Автомат выключен
func on_manual_control() -> void: auto_enabled = false
## Автомат отключен
func on_full_auto() -> void: auto_enabled = true
func _ready() -> void:
var ecms: Dictionary = {} ## Словарь помех.
for i in ID_INTERFERS_LEN: # Заполняет массив ID целей
id_array.append(i + 1)
var unit_caps = network.get_unit_instance('уарэп-капсрпб')
var unit_5p28 = network.get_unit_instance('уарэп-5п28')
var timer_check_state: = Timer.new()
add_child(timer_check_state)
var resend_timeout = ProjectSettings.get_setting('application/interfer/resend_timeout', 1000.0)
timer_check_state.connect('timeout', Callable(self, 'on_timer_check_state').bind(unit_caps, ecms, resend_timeout))
timer_check_state.start(resend_timeout / 1000.0)
unit_caps.connect('data_received', Callable(self, 'on_data_capsrpb_received').bind(ecms))
unit_5p28.connect('get_interfers', Callable(self, 'on_get_interfers').bind(unit_5p28, ecms))
signaller.connect('interfer_create', Callable(self, 'on_interfer_create').bind(unit_caps, ecms))
signaller.connect('interfer_rcv', Callable(self, 'on_interfer_rcv').bind(unit_5p28, ecms))
signaller.connect('interfer_new', Callable(self, 'send_ecm').bind(unit_caps))
signaller.connect('interfer_off_all', Callable(self, 'on_interfer_off_all').bind(unit_caps, ecms))
signaller.connect('th_aoa_update', Callable(self, 'on_th_aoa_update').bind(ecms, unit_caps))
signaller.connect('interfer_off', Callable(self, 'on_interfer_off'))
signaller.connect('manual_control', Callable(self, 'on_manual_control'))
signaller.connect('full_auto', Callable(self, 'on_full_auto'))
signaller.connect('interfer_prinuditelno', Callable(self, 'on_interfer_prinuditelno'))
signaller.connect('emit_changed', Callable(self, 'on_emit_changed').bind(ecms))
threats.connect('threat_lost', Callable(self, 'on_threats_lost').bind(ecms, unit_caps))
var unit_nav = network.get_unit_instance('навигация')
unit_nav.connect('data_received', on_nav_data_received.bind(unit_caps))
threats.connect('threats_resized', Callable(self, 'on_threats_resized'))
threats.connect('threat_update', Callable(self, 'on_threats_update').bind(ecms, unit_caps))
func on_nav_data_received(coords: Dictionary, unit_caps):
var ship_time = Time.get_datetime_dict_from_system()
var data_to_json = {
'ts': 8,
'ss': msg8_num,
'roll': coords.get('bk', 0),
'trim': coords.get('kk', 0),
'long': coords.get('longitude', 0),
'lat': coords.get('latitude', 0),
'course': coords.get('k', 0),
'speed': coords.get('speed', 0),
'year': ship_time['year'],
'month': ship_time['month'],
'day': ship_time['day'],
'hour': ship_time['hour'],
'minute': ship_time['minute'],
'second': ship_time['second'],}
var json_data = JSON.stringify(data_to_json).to_utf8_buffer()
unit_caps.tx_stack.append(json_data)
msg8_num += 1
msg8_num %= 0xffffffff
## Проверяет состояние отправки пакета и если не доставлен, повторяет отправку.
func on_timer_check_state(unit_instance, ecms, resend_timeout) -> void:
var tick = Time.get_ticks_msec()
for ecm in ecms.values():
if (tick - ecm.tick_tx) >= resend_timeout:
send_ecm(ecm, unit_instance)
ecm.tick_tx = tick
func on_th_aoa_update(threats: Dictionary, ecms: Dictionary, unit_instance) -> void:
for thr in threats.values():
var th_id = thr.id
for ecm_i in thrs.keys():
if thrs[ecm_i] == th_id:
var ecm = ecms[ecm_i]
ecm.params['freq'] = thr.freq
ecm.kni = thr.aoa
send_ecm(ecm, unit_instance)
signaller.emit_signal('fs_update_color', ecms)
prd.update_fs_state(ecms)
if not auto_enabled:
return
if auto_enabled:
for thr in threats.values():
thr.auto_selected = true
signaller.emit_signal('interfer_init', 'lchm1', [], interfer.POW_DEFAULT)
thr.auto_selected = false
## Производит приём состояния помех. [param unit] -> [param ecms].
func on_data_capsrpb_received(unit_instance: capsrpb.CapsRpb, ecms: Dictionary) -> void:
if not unit_instance.json_dic.has('ts'): return
if not (unit_instance.json_dic['ts'] is float): return
if unit_instance.json_dic['ts'] != 5: return
map_msvk_to_ecms(unit_instance, ecms)
map_mpchf_to_view(unit_instance)
## Извлекает массив состояний выполнения команды
func map_msvk_to_ecms(unit_instance: unit.Unit, ecms: Dictionary):
var json_dic = unit_instance.json_dic
if not json_dic.has('msvk'): return
var msvk = unit_instance.json_dic['msvk']
if msvk is not Array: return
for dic_msvk in msvk:
if not (dic_msvk is Dictionary): continue
if not dic_msvk.has('ispp'): continue
var ispp = int(dic_msvk['ispp'])
var errmsg = dic_msvk.get('errmsg', '')
if errmsg:
log.error('сеанс %d: \"%s\"' % [ispp, errmsg])
if ecms.has(ispp):
var ecm = ecms[ispp]
ecm.set_from_dic(dic_msvk)
call_deferred('emit_signal', 'interfer_update', ecm)
else:
var ecm = Interfer.new()
ecm.set_from_dic(dic_msvk)
ecm.krp = INTERFER_OFF
send_ecm(ecm, unit_instance)
## Извлекает массив пространственно-частотных фильтров
func map_mpchf_to_view(unit_instance: unit.Unit):
var json_dic = unit_instance.json_dic
if not json_dic.has('mpchf'): return
var mpchf = json_dic['mpchf']
if mpchf is not Array: return
## Вызыватся, когда пользователь выбрал помеху и выбраны цели.
func on_interfer_create(interfer_name, sel_threats, fs_arr, power, u, ecms) -> void:
for item in sel_threats:
var param: Dictionary = {}
var kni: float = item.aoa
var id = item.id
param['freq'] = item.freq
param['width'] = item.width
if (param['width'] < settings.WIDTH_MIN):
param['width'] = settings.WIDTH_MIN
elif (param['width'] > settings.WIDTH_MAX):
param['width'] = settings.WIDTH_MAX
interfer_create(u, interfer_name, ecms, param, kni, id, thrs, SOURCE_INTERFER[0], power)
for item in fs_arr:
var param: Dictionary = {}
var kni: float = item[0]
var id = item[3]
var th = null
if not interfer_name in EXCLUDE_INTERFER:
th = find_threat(item[1], kni)
if th != null:
param['freq'] = th.freq
param['width'] = th.width
else:
param['freq'] = item[1]
param['width'] = item[2]
if (param['width'] < settings.WIDTH_MIN):
param['width'] = settings.WIDTH_MIN
elif (param['width'] > settings.WIDTH_MAX):
param['width'] = settings.WIDTH_MAX
if th == null:
interfer_create(u, interfer_name, ecms, param, kni, id, fs, SOURCE_INTERFER[1], power)
else:
id = th.id
interfer_create(u, interfer_name, ecms, param, kni, id, thrs, SOURCE_INTERFER[0], power)
func on_interfer_rcv(interfer_params: Array, unit0, ecms) -> void:
var err: bool = false
var mode = interfer_params[0]
var id = interfer_params[1]
var aoa = interfer_params[2]
var um = interfer_params[3]
if (aoa < 0.0) and (aoa > 360.0):
err = true
var param: Dictionary = {}
if (um < -settings.UM_MIN) and (um > settings.UM_MAX):
err = true
param['um'] = um
param['freq'] = interfer_params[4]
if (param['freq'] < settings.FREQ_MIN) and (param['freq'] > settings.FREQ_MAX):
err = true
param['width'] = interfer_params[5]
if (param['width'] < settings.WIDTH_MIN) and (param['width'] > settings.WIDTH_MAX):
err = true
var interfer_name: = ''
for key in ITERFER_MODE:
if ITERFER_MODE[key][2] == mode:
interfer_name = key
break
if interfer_name == '':
err = true
if not err:
interfer_create(unit0, interfer_name, ecms, param, aoa, id, cu, SOURCE_INTERFER[2], POW_DEFAULT)
unit0.on_interfer_accept(err)
func interfer_create(u, interfer_name, ecms, param, kni, id, dict, source_interfer, power) -> void:
var sz = ecms.size()
var ispp: int = 0
param['dur'] = 1.0 # Необходимо добавить параметры управления
param['period'] = 1.0 # Необходимо добавить параметры управления
var key = dict.find_key(id)
if source_interfer != SOURCE_INTERFER[2]:
if key != null:
ispp = key
else:
if interfer_name == 'off':
return
ispp = pop_id()
if ispp == 0:
return
else:
ispp = id
dict[ispp] = id
var ecm: Interfer
if ecms.has(ispp):
ecm = ecms[ispp]
else:
ecm = Interfer.new()
ecm.ispp = ispp
ecms[ispp] = ecm
ecm.kni = kni
ecm.krp = interfer_name
ecm.params = param
ecm.powp = power
ecm.source_interfer = source_interfer
ecm.modulation = emit_enable
ecm.um = param.get('um', 15.0) # Значение по умолчанию
var ecm_params = ITERFER_MODE[interfer_name][1]
if ecm_params.has('call_proc'):
var call_set_params = Callable(self, ecm_params['call_proc']).bind(ecm, ecm_params)
call_set_params.call()
var set_nmfs_ok = set_nmfs(ecm)
if set_nmfs_ok != Error.OK:
log.error('Ошибка выбора модуля ФС для помехи: %s' %[ecm.ispp])
signaller.emit_signal('interfer_new', ecm)
if interfer_name == 'off':
signaller.emit_signal('interfer_off', ecm, ecms, thrs)
thrs.erase(ispp)
ecm.tick_tx = Time.get_ticks_msec()
send_ecm(ecm, u)
if sz != ecms.size():
call_deferred('emit_signal', 'interfer_resized', ecms)
func send_ecm(ecm, unit0) -> void:
if ecm.krp != INTERFER_OFF:
if not emit_enable:
return
elif not ecm.modulation:
return
msg6_num += 1
var json_data = ecm.get_msg6_data(msg6_num)
unit0.tx_stack.append(json_data)
func on_interfer_off_all(unit0, ecms: Dictionary) -> void:
for ecm in ecms.values():
ecm.krp = INTERFER_OFF
send_ecm(ecm, unit0)
signaller.emit_signal('interfer_off', ecm, ecms, thrs)
func on_threats_lost(th, ecms, unit0) -> void:
for key in interfer.thrs:
if interfer.thrs[key] == th.id:
var ecm_id = key
var ecm = ecms[ecm_id]
ecm.krp = INTERFER_OFF
send_ecm(ecm, unit0)
signaller.emit_signal('interfer_off', ecm, ecms, thrs)
func pop_id() -> int:
var id = 0
if len(id_array):
id = id_array[0]
id_array.remove_at(0)
return id
func push_id(id: int) -> void:
id_array.append(id)
id_array.sort()
func on_emit_changed(state, _ecms) -> void:
emit_enable = state
func call_all_diap(ecm, ecm_params: Dictionary):
ecm.params['filename'] = ecm_params['files']
func call_diap(ecm, ecm_params: Dictionary):
var fs_index_arr: Array
prd.get_fs_index(ecm, fs_index_arr)
if len(fs_index_arr):
var band = fs_index_arr[0] / 4
if ecm_params['files'].has(band):
ecm.params['filename'] = ecm_params['files'][band][0]
ecm.params['freq'] = ecm_params['files'][band][1]
func call_pdchm(ecm, ecm_params: Dictionary):
var temp_key: float = settings.WIDTH_MIN
for key in ecm_params['files'].keys():
if key > temp_key: temp_key = key
for key in ecm_params['files'].keys():
if key < ecm.params['width']: continue
if key < temp_key:
temp_key = key
ecm.params['filename'] = ecm_params['files'][int(temp_key)]
func set_nmfs(ecm):
var fs_index_arr: Array
prd.get_fs_index(ecm, fs_index_arr)
if len(fs_index_arr):
ecm.nmfs = prd.FS_PRD[fs_index_arr[0]][1]
return Error.OK
else:
return Error.FAILED
func on_threats_resized(th_dic):
thrs_dict = th_dic
func on_threats_update(threat, ecms, unit_caps):
var th_id = threat.id
var ecm = null
for ecm_i in thrs.keys():
if thrs[ecm_i] == th_id:
ecm = ecms[ecm_i]
ecm.params['freq'] = threat.freq
ecm.kni = threat.aoa
set_nmfs(ecm)
if threat.tmod != 'fm':
if ecm != null:
send_ecm(ecm, unit_caps)
return
var th_fm_dict: Dictionary
var fs_index_arr: Array
prd.find_fs_index(threat.aoa, threat.freq, fs_index_arr)
if fs_index_arr.size() == 0:
if ecm != null:
send_ecm(ecm, unit_caps)
return
var fs_index = fs_index_arr[0]
var nmfs = prd.FS_PRD[fs_index][1]
if ecm == null:
for ecm_i in ecms.values():
if nmfs != ecm_i.nmfs: continue
if ecm_i.svk != 1: continue
ecm = ecm_i
if ecm == null: return
for th_index in thrs_dict:
if thrs_dict[th_index].tmod != 'fm': continue
fs_index_arr = []
prd.find_fs_index(thrs_dict[th_index].aoa, thrs_dict[th_index].freq, fs_index_arr)
if fs_index_arr.size() == 0: continue
if fs_index_arr[0] != fs_index: continue
th_fm_dict[th_index] = thrs_dict[th_index]
if len(th_fm_dict) > 1:
pass
send_ecm(ecm, unit_caps)
func get_freq_range(freq_range_dict: Dictionary, th_dict: Dictionary):
for key_i in th_dict:
var min_range: int = 6000
var key_min = null
for key_j in th_dict:
if key_i == key_j: continue
var width = (th_dict[key_i].width / 2) + (th_dict[key_j].width / 2)
var freq_range = abs(th_dict[key_i].freq - th_dict[key_j].freq) + width
if freq_range < min_range:
key_min = key_j
min_range = freq_range
var max_width = 0
for key in PDCHM_PARAMS['files'].keys():
if max_width < key: max_width = key
if min_range > max_width:
th_dict.erase(key_i)
continue
else:
if key_min == null: continue
freq_range_dict[key_i] = [th_dict[key_i], th_dict[key_min], min_range]
func find_threat(freq: int, aoa: float):
var fs_index_arr = []
prd.find_fs_index(aoa, freq, fs_index_arr)
if not fs_index_arr.size(): return null
var fs_index = fs_index_arr[0]
for th in thrs_dict.values():
fs_index_arr = []
prd.find_fs_index(th.aoa, th.freq, fs_index_arr)
if not fs_index_arr.size(): continue
if fs_index_arr[0] == fs_index:
return th
return null