From a1dde8d694afe732ab3ecb09f5ab5861ccffb582 Mon Sep 17 00:00:00 2001 From: "Nikolai V. Chr" Date: Sun, 21 May 2023 09:50:30 +0200 Subject: [PATCH] SAM radars now more consistent in when they choose to turn radar off or on. --- MIM-104D/Nasal/fire-control-custom.nas | 5 + MIM-104D/Nasal/fire-control.nas | 206 +++++++++++++++---------- S-75/Nasal/fire-control-custom.nas | 5 + S-75/Nasal/fire-control.nas | 203 ++++++++++++++---------- S-75/S-75-set.xml | 5 +- SA-6/Nasal/fire-control-custom.nas | 5 + SA-6/Nasal/fire-control.nas | 203 ++++++++++++++---------- SA-6/SA-6-set.xml | 1 + 8 files changed, 386 insertions(+), 247 deletions(-) diff --git a/MIM-104D/Nasal/fire-control-custom.nas b/MIM-104D/Nasal/fire-control-custom.nas index 077fba6..f9a5e15 100644 --- a/MIM-104D/Nasal/fire-control-custom.nas +++ b/MIM-104D/Nasal/fire-control-custom.nas @@ -11,6 +11,11 @@ var launcher_align_to_target = 1; var align_speed_dps = 20; var radar_elevation_above_terrain_m = 25; var radar_lowest_pitch = -5;# 0.5 degs = roughly 925 feet at 20 nm, 25 feet at half a nm. # 0.35 = roughly 925 feet at 20 nm, 25 feet at half a nm. +var radar_off_time_min = 30;# When turning off to lure enemies into engagement zone, and to not make the SAM too easy to find, minimum turn off this duration. +var radar_off_time_max = 90;# Bigger missile_max_distance give crew incentive to keep this a bit longer +var radar_on_time = 45;# Minimum the time it would take to scan whole sky +var radar_on_after_detect_time = 180;# Crew is alert after spotting an aircraft, how long should they stay that way? +var can_detect_anti_rad = 0.5;# 0.5 very good, 0 not at all. #reaction tme for s-300p is 28 secs acording to http://www.astronautix.com/s/s-300p.html # sounds a bit high for pmu, as its 4 secs for s-400 diff --git a/MIM-104D/Nasal/fire-control.nas b/MIM-104D/Nasal/fire-control.nas index 82248f4..d856587 100644 --- a/MIM-104D/Nasal/fire-control.nas +++ b/MIM-104D/Nasal/fire-control.nas @@ -19,7 +19,11 @@ var reload_starting = 0; var ACTIVE_MISSILE = 0; var semi_active_track = nil;# with multiple missiles flying in semi-active-radar mode, this can change rather fast. Which means pilots wont get a steady tone, but disrupted tones. (tradeoff) var mutexLock = thread.newlock(); - +var radarStatus = 0;# Current radar status. +var radarOnTimestamp = -500;# Last timestamp of when the radar was online +var radarOffTimestamp = -500;# Last timestamp of when the radar was offline +var radarOffDuration = radar_off_time_min; +var radarTrackTime = 0;# Last time stamp where any enemy aircraft has been spotted. ########################################################################## ######################## Common SAM code ########################### @@ -132,7 +136,7 @@ var scan = func() { return; } - if (systime() - start_time < setupTime ) { + if (isSetup() ) { printf("Seconds till activation: %.1f", setupTime - (systime() - start_time)); setprop("sam/timeleft", setupTime - (systime() - start_time)); setprop("sam/missiles",(NUM_MISSILES+1-ACTIVE_MISSILE)); @@ -154,94 +158,86 @@ var scan = func() { } setprop("sim/multiplay/generic/float[5]", launcher_final_tilt_deg); #### ITERATE THROUGH MP LIST #### - var radarOn = 0; - var my_pos = geo.aircraft_position(); - var radarAlt = my_pos.alt()+radar_elevation_above_terrain_m; - my_pos.set_alt(radarAlt); - var mvec = props.globals.getNode("/ai/models").getChildren("multiplayer"); - var tvec = props.globals.getNode("/ai/models").getChildren("tanker"); - var avec = props.globals.getNode("/ai/models").getChildren("aircraft"); - if (mvec!=nil and tvec != nil) { - mvec = mvec ~ tvec; - } - if (mvec!=nil and avec != nil) { - mvec = mvec ~ avec; - } - var prio_vector = [];#0:node,1:trigger,2:prio value - foreach(var mp; mvec){ - if (mp.getNode("valid") == nil or mp.getNode("valid").getValue() != 1) continue; - var prio = fire_control(mp, my_pos); - append(prio_vector, prio); - if (prio[3] == 1) { - radarOn = 1;#this target is within envelope, we turn on radar. + if (radarStatus == 1) { + var my_pos = geo.aircraft_position(); + var radarAlt = my_pos.alt()+radar_elevation_above_terrain_m; + my_pos.set_alt(radarAlt); + var mvec = props.globals.getNode("/ai/models").getChildren("multiplayer"); + var tvec = props.globals.getNode("/ai/models").getChildren("tanker"); + var avec = props.globals.getNode("/ai/models").getChildren("aircraft"); + if (mvec!=nil and tvec != nil) { + mvec = mvec ~ tvec; } - } - prio_vector = sort(prio_vector, func (a,b) {if (a[2]>b[2]){return -1;} elsif (a[2] fire_minimum_interval ) ) { # - #### ... FOR THE MISSILE #### - #print("callsign " ~ cs ~ " found at " ~ dist_to_target); - missile_delay_time = systime(); - mp[0].getNode("unique",1).setValue(rand()); - armament.contact = radar_logic.Contact.new(mp[0], AIR); - launch_in_progress = 1; - missile_launch(mp[0], systime(), my_pos); - - } elsif ( ciws_installed and trigger == -1 and ROUNDS > 0 and ( systime() - ciws_delay_time > 1.0 ) and lu != nil) { - #### ... FOR THE CIWS #### - var contact = radar_logic.Contact.new(mp[0], AIR); - var cord = contact.get_Coord(); - var dist_nm = my_pos.direct_distance_to(cord)*M2NM; - var probabilityOfBurstKill = 0.50; - if (mp[0].getNode("velocities/true-airspeed-kt").getValue() > 50) { - probabilityOfBurstKill = extrapolate(dist_nm, 0, ciws_domain_nm, ciws_chance, 0); - } - var hits = math.max(0,int(extrapolate(rand(), 0, probabilityOfBurstKill, ciws_burst_rounds, 3)));#3 is a kill approx - var target_bearing = my_pos.course_to(cord); - setprop("sim/multiplay/generic/float[0]", -(target_bearing-getprop("orientation/heading-deg")));#CIWS horiz (inverted) - setprop("sim/multiplay/generic/float[1]", vector.Math.getPitch(my_pos, cord));#CIWS vert - setprop("sim/multiplay/generic/bool[0]",1);settimer(func {setprop("sim/multiplay/generic/bool[0]",0);}, 1);# for sound - if (hits > 0) { - var msg = notifications.ArmamentNotification.new("mhit", 4, -1*(ciws_shell+1)); - msg.RelativeAltitude = 0; - msg.Bearing = 0; - msg.Distance = hits; - msg.RemoteCallsign = mp[0].getNode("callsign").getValue(); - notifications.hitBridgedTransmitter.NotifyAll(msg); - damage.damageLog.push("CIWS fired | rounds remaining: " ~ ROUNDS ~ " | hit on: " ~ mp[0].getNode("callsign").getValue()); - } - print("CIWS fired | bursts remaining: " ~ ROUNDS ~ " | "~hits~" hits on: " ~ mp[0].getNode("callsign").getValue() ~ " @ "~ sprintf("%.1f",dist_nm) ~" nm"); - ciws_delay_time = systime(); - ROUNDS = ROUNDS - 1; - setprop("sam/bursts", ROUNDS); - if (ROUNDS == 0) { - print("Launcher out of shell ammo."); - settimer(autoreload, reload_time); - reloading = 1; - reload_starting = systime(); + if (mvec!=nil and avec != nil) { + mvec = mvec ~ avec; + } + var prio_vector = [];#0:node,1:trigger,2:prio value + foreach(var mp; mvec){ + if (mp.getNode("valid") == nil or mp.getNode("valid").getValue() != 1) continue; + var prio = fire_control(mp, my_pos); + append(prio_vector, prio); + if (prio[3] == 1) { + radarTrackTime = systime();#this target is within envelope, we turn on radar. } } - if (lu != nil and lu.tracking) { - radarOn = 1; - } - } - if (getprop("payload/armament/"~string.lc(missile_name)~"/guidance") == "tvm" and !radarOn) { - foreach(var tgt ; targetsV2.vector) { - if (tgt.in_air > 0) { - radarOn = 1; - break; + prio_vector = sort(prio_vector, func (a,b) {if (a[2]>b[2]){return -1;} elsif (a[2] fire_minimum_interval ) ) { # + #### ... FOR THE MISSILE #### + #print("callsign " ~ cs ~ " found at " ~ dist_to_target); + missile_delay_time = systime(); + mp[0].getNode("unique",1).setValue(rand()); + armament.contact = radar_logic.Contact.new(mp[0], AIR); + launch_in_progress = 1; + missile_launch(mp[0], systime(), my_pos); + + } elsif ( ciws_installed and trigger == -1 and ROUNDS > 0 and ( systime() - ciws_delay_time > 1.0 ) and lu != nil) { + #### ... FOR THE CIWS #### + var contact = radar_logic.Contact.new(mp[0], AIR); + var cord = contact.get_Coord(); + var dist_nm = my_pos.direct_distance_to(cord)*M2NM; + var probabilityOfBurstKill = 0.50; + if (mp[0].getNode("velocities/true-airspeed-kt").getValue() > 50) { + probabilityOfBurstKill = extrapolate(dist_nm, 0, ciws_domain_nm, ciws_chance, 0); + } + var hits = math.max(0,int(extrapolate(rand(), 0, probabilityOfBurstKill, ciws_burst_rounds, 3)));#3 is a kill approx + var target_bearing = my_pos.course_to(cord); + setprop("sim/multiplay/generic/float[0]", -(target_bearing-getprop("orientation/heading-deg")));#CIWS horiz (inverted) + setprop("sim/multiplay/generic/float[1]", vector.Math.getPitch(my_pos, cord));#CIWS vert + setprop("sim/multiplay/generic/bool[0]",1);settimer(func {setprop("sim/multiplay/generic/bool[0]",0);}, 1);# for sound + if (hits > 0) { + var msg = notifications.ArmamentNotification.new("mhit", 4, -1*(ciws_shell+1)); + msg.RelativeAltitude = 0; + msg.Bearing = 0; + msg.Distance = hits; + msg.RemoteCallsign = mp[0].getNode("callsign").getValue(); + notifications.hitBridgedTransmitter.NotifyAll(msg); + damage.damageLog.push("CIWS fired | rounds remaining: " ~ ROUNDS ~ " | hit on: " ~ mp[0].getNode("callsign").getValue()); + } + print("CIWS fired | bursts remaining: " ~ ROUNDS ~ " | "~hits~" hits on: " ~ mp[0].getNode("callsign").getValue() ~ " @ "~ sprintf("%.1f",dist_nm) ~" nm"); + ciws_delay_time = systime(); + ROUNDS = ROUNDS - 1; + setprop("sam/bursts", ROUNDS); + if (ROUNDS == 0) { + print("Launcher out of shell ammo."); + settimer(autoreload, reload_time); + reloading = 1; + reload_starting = systime(); + } + } + if (lu != nil and lu.tracking) { + radarTrackTime = systime(); } } } - setprop("/sim/multiplay/generic/int[2]",!radarOn); - + if (launcher_align_to_target and ACTIVE_MISSILE > NUM_MISSILES and systime()-missile_release_time > 2) { # rotate launcher back to forward: var tgt_dir = 0; @@ -256,7 +252,7 @@ var scan = func() { setprop("sam/info", "Reloading"); setprop("sam/timeleft", reload_time - (systime() - reload_starting)); } else { - setprop("sam/info", ""); + setprop("sam/info", "Search radar is "~(radarStatus==1?"ON":"OFF")); setprop("sam/timeleft", 0); } clearSingleLock(); @@ -276,6 +272,45 @@ var scan = func() { settimer(scan,radar_update_time); } +var isSetup = func { + return systime() - start_time < setupTime; +} + +var acquisitionRadarLoop = func { + if (isSetup() or reloading or getprop("/carrier/sunk") == 1 or getprop("/carrier/disabled") == 1) return; + var reason = ""; + var time = systime(); + if (time - radarTrackTime < radar_on_after_detect_time) { + radarStatus = 1; + reason = sprintf("Crew alert for %d seconds", radar_on_after_detect_time - (time - radarTrackTime)); + } elsif (radarStatus == 0 and time - radarOnTimestamp > radarOffDuration) { + radarStatus = 1; + reason = sprintf("Periodic check for %d seconds", radar_on_time); + } elsif (radarStatus == 1 and time - radarOffTimestamp > radar_on_time) { + radarStatus = 0; + radarOffDuration = rand()*(radar_off_time_max - radar_off_time_min)+radar_off_time_min; + reason = sprintf("Periodic pause for %d seconds", radarOffDuration); + } + + if (getprop("payload/armament/"~string.lc(missile_name)~"/guidance") == "tvm" and radarStatus == 0) { + foreach(var tgt ; targetsV2.vector) { + if (tgt.in_air > 0) { + radarTrackTime = time; + radarStatus = 1; + break; + } + } + } + setprop("/sim/multiplay/generic/int[2]", radarStatus != 1); + if (radarStatus == 1) radarOnTimestamp = time; + else radarOffTimestamp = time; + #print("Search radar is "~(radarStatus==1?"ON":"OFF")); + if (reason != "") print(reason); +} + +var radTimer = maketimer(0.5, acquisitionRadarLoop); +radTimer.start(); + var clearSingleLock = func () { thread.lock(mutexLock); if (semi_active_track == nil) { @@ -556,6 +591,7 @@ var missile_launch = func(mp, launchtime, my_pos) { } if (lu != nil) { lu.tracking = 1; + radarTrackTime = systime(); } } diff --git a/S-75/Nasal/fire-control-custom.nas b/S-75/Nasal/fire-control-custom.nas index b7c1790..55a6d1a 100644 --- a/S-75/Nasal/fire-control-custom.nas +++ b/S-75/Nasal/fire-control-custom.nas @@ -11,6 +11,11 @@ var launcher_align_to_target = 1; var align_speed_dps = 20; var radar_elevation_above_terrain_m = 25; var radar_lowest_pitch = 3.5;# 0.5 degs = roughly 925 feet at 20 nm, 25 feet at half a nm. # 0.35 = roughly 925 feet at 20 nm, 25 feet at half a nm. +var radar_off_time_min = 15;# When turning off to lure enemies into engagement zone, and to not make the SAM too easy to find, minimum turn off this duration. +var radar_off_time_max = 45;# Bigger missile_max_distance give crew incentive to keep this a bit longer +var radar_on_time = 90;# Minimum the time it would take to scan whole sky +var radar_on_after_detect_time = 240;# Crew is alert after spotting an aircraft, how long should they stay that way? +var can_detect_anti_rad = 0.05;# 0.5 very good, 0 not at all. #reaction tme for s-300p is 28 secs acording to http://www.astronautix.com/s/s-300p.html # sounds a bit high for pmu, as its 4 secs for s-400 diff --git a/S-75/Nasal/fire-control.nas b/S-75/Nasal/fire-control.nas index 65598ff..d856587 100644 --- a/S-75/Nasal/fire-control.nas +++ b/S-75/Nasal/fire-control.nas @@ -19,7 +19,11 @@ var reload_starting = 0; var ACTIVE_MISSILE = 0; var semi_active_track = nil;# with multiple missiles flying in semi-active-radar mode, this can change rather fast. Which means pilots wont get a steady tone, but disrupted tones. (tradeoff) var mutexLock = thread.newlock(); - +var radarStatus = 0;# Current radar status. +var radarOnTimestamp = -500;# Last timestamp of when the radar was online +var radarOffTimestamp = -500;# Last timestamp of when the radar was offline +var radarOffDuration = radar_off_time_min; +var radarTrackTime = 0;# Last time stamp where any enemy aircraft has been spotted. ########################################################################## ######################## Common SAM code ########################### @@ -132,7 +136,7 @@ var scan = func() { return; } - if (systime() - start_time < setupTime ) { + if (isSetup() ) { printf("Seconds till activation: %.1f", setupTime - (systime() - start_time)); setprop("sam/timeleft", setupTime - (systime() - start_time)); setprop("sam/missiles",(NUM_MISSILES+1-ACTIVE_MISSILE)); @@ -154,87 +158,86 @@ var scan = func() { } setprop("sim/multiplay/generic/float[5]", launcher_final_tilt_deg); #### ITERATE THROUGH MP LIST #### - var radarOn = 0; - var my_pos = geo.aircraft_position(); - var radarAlt = my_pos.alt()+radar_elevation_above_terrain_m; - my_pos.set_alt(radarAlt); - var mvec = props.globals.getNode("/ai/models").getChildren("multiplayer"); - var tvec = props.globals.getNode("/ai/models").getChildren("tanker"); - var avec = props.globals.getNode("/ai/models").getChildren("aircraft"); - if (mvec!=nil and tvec != nil) { - mvec = mvec ~ tvec; - } - if (mvec!=nil and avec != nil) { - mvec = mvec ~ avec; - } - var prio_vector = [];#0:node,1:trigger,2:prio value - foreach(var mp; mvec){ - if (mp.getNode("valid") == nil or mp.getNode("valid").getValue() != 1) continue; - var prio = fire_control(mp, my_pos); - append(prio_vector, prio); - if (prio[3] == 1) { - radarOn = 1;#this target is within envelope, we turn on radar. + if (radarStatus == 1) { + var my_pos = geo.aircraft_position(); + var radarAlt = my_pos.alt()+radar_elevation_above_terrain_m; + my_pos.set_alt(radarAlt); + var mvec = props.globals.getNode("/ai/models").getChildren("multiplayer"); + var tvec = props.globals.getNode("/ai/models").getChildren("tanker"); + var avec = props.globals.getNode("/ai/models").getChildren("aircraft"); + if (mvec!=nil and tvec != nil) { + mvec = mvec ~ tvec; } - } - prio_vector = sort(prio_vector, func (a,b) {if (a[2]>b[2]){return -1;} elsif (a[2] fire_minimum_interval ) ) { # - #### ... FOR THE MISSILE #### - #print("callsign " ~ cs ~ " found at " ~ dist_to_target); - missile_delay_time = systime(); - mp[0].getNode("unique",1).setValue(rand()); - armament.contact = radar_logic.Contact.new(mp[0], AIR); - launch_in_progress = 1; - missile_launch(mp[0], systime(), my_pos); - - } elsif ( ciws_installed and trigger == -1 and ROUNDS > 0 and ( systime() - ciws_delay_time > 1.0 ) and lu != nil) { - #### ... FOR THE CIWS #### - var contact = radar_logic.Contact.new(mp[0], AIR); - var cord = contact.get_Coord(); - var dist_nm = my_pos.direct_distance_to(cord)*M2NM; - var probabilityOfBurstKill = 0.50; - if (mp[0].getNode("velocities/true-airspeed-kt").getValue() > 50) { - probabilityOfBurstKill = extrapolate(dist_nm, 0, ciws_domain_nm, ciws_chance, 0); - } - var hits = math.max(0,int(extrapolate(rand(), 0, probabilityOfBurstKill, ciws_burst_rounds, 3)));#3 is a kill approx - var target_bearing = my_pos.course_to(cord); - setprop("sim/multiplay/generic/float[0]", -(target_bearing-getprop("orientation/heading-deg")));#CIWS horiz (inverted) - setprop("sim/multiplay/generic/float[1]", vector.Math.getPitch(my_pos, cord));#CIWS vert - setprop("sim/multiplay/generic/bool[0]",1);settimer(func {setprop("sim/multiplay/generic/bool[0]",0);}, 1);# for sound - if (hits > 0) { - var msg = notifications.ArmamentNotification.new("mhit", 4, -1*(ciws_shell+1)); - msg.RelativeAltitude = 0; - msg.Bearing = 0; - msg.Distance = hits; - msg.RemoteCallsign = mp[0].getNode("callsign").getValue(); - notifications.hitBridgedTransmitter.NotifyAll(msg); - damage.damageLog.push("CIWS fired | rounds remaining: " ~ ROUNDS ~ " | hit on: " ~ mp[0].getNode("callsign").getValue()); - } - print("CIWS fired | bursts remaining: " ~ ROUNDS ~ " | "~hits~" hits on: " ~ mp[0].getNode("callsign").getValue() ~ " @ "~ sprintf("%.1f",dist_nm) ~" nm"); - ciws_delay_time = systime(); - ROUNDS = ROUNDS - 1; - setprop("sam/bursts", ROUNDS); - if (ROUNDS == 0) { - print("Launcher out of shell ammo."); - settimer(autoreload, reload_time); - reloading = 1; - reload_starting = systime(); + if (mvec!=nil and avec != nil) { + mvec = mvec ~ avec; + } + var prio_vector = [];#0:node,1:trigger,2:prio value + foreach(var mp; mvec){ + if (mp.getNode("valid") == nil or mp.getNode("valid").getValue() != 1) continue; + var prio = fire_control(mp, my_pos); + append(prio_vector, prio); + if (prio[3] == 1) { + radarTrackTime = systime();#this target is within envelope, we turn on radar. } } - if (lu != nil and lu.tracking) { - radarOn = 1; + prio_vector = sort(prio_vector, func (a,b) {if (a[2]>b[2]){return -1;} elsif (a[2] fire_minimum_interval ) ) { # + #### ... FOR THE MISSILE #### + #print("callsign " ~ cs ~ " found at " ~ dist_to_target); + missile_delay_time = systime(); + mp[0].getNode("unique",1).setValue(rand()); + armament.contact = radar_logic.Contact.new(mp[0], AIR); + launch_in_progress = 1; + missile_launch(mp[0], systime(), my_pos); + + } elsif ( ciws_installed and trigger == -1 and ROUNDS > 0 and ( systime() - ciws_delay_time > 1.0 ) and lu != nil) { + #### ... FOR THE CIWS #### + var contact = radar_logic.Contact.new(mp[0], AIR); + var cord = contact.get_Coord(); + var dist_nm = my_pos.direct_distance_to(cord)*M2NM; + var probabilityOfBurstKill = 0.50; + if (mp[0].getNode("velocities/true-airspeed-kt").getValue() > 50) { + probabilityOfBurstKill = extrapolate(dist_nm, 0, ciws_domain_nm, ciws_chance, 0); + } + var hits = math.max(0,int(extrapolate(rand(), 0, probabilityOfBurstKill, ciws_burst_rounds, 3)));#3 is a kill approx + var target_bearing = my_pos.course_to(cord); + setprop("sim/multiplay/generic/float[0]", -(target_bearing-getprop("orientation/heading-deg")));#CIWS horiz (inverted) + setprop("sim/multiplay/generic/float[1]", vector.Math.getPitch(my_pos, cord));#CIWS vert + setprop("sim/multiplay/generic/bool[0]",1);settimer(func {setprop("sim/multiplay/generic/bool[0]",0);}, 1);# for sound + if (hits > 0) { + var msg = notifications.ArmamentNotification.new("mhit", 4, -1*(ciws_shell+1)); + msg.RelativeAltitude = 0; + msg.Bearing = 0; + msg.Distance = hits; + msg.RemoteCallsign = mp[0].getNode("callsign").getValue(); + notifications.hitBridgedTransmitter.NotifyAll(msg); + damage.damageLog.push("CIWS fired | rounds remaining: " ~ ROUNDS ~ " | hit on: " ~ mp[0].getNode("callsign").getValue()); + } + print("CIWS fired | bursts remaining: " ~ ROUNDS ~ " | "~hits~" hits on: " ~ mp[0].getNode("callsign").getValue() ~ " @ "~ sprintf("%.1f",dist_nm) ~" nm"); + ciws_delay_time = systime(); + ROUNDS = ROUNDS - 1; + setprop("sam/bursts", ROUNDS); + if (ROUNDS == 0) { + print("Launcher out of shell ammo."); + settimer(autoreload, reload_time); + reloading = 1; + reload_starting = systime(); + } + } + if (lu != nil and lu.tracking) { + radarTrackTime = systime(); + } } } - - setprop("/sim/multiplay/generic/int[2]",!radarOn); - + if (launcher_align_to_target and ACTIVE_MISSILE > NUM_MISSILES and systime()-missile_release_time > 2) { # rotate launcher back to forward: var tgt_dir = 0; @@ -249,7 +252,7 @@ var scan = func() { setprop("sam/info", "Reloading"); setprop("sam/timeleft", reload_time - (systime() - reload_starting)); } else { - setprop("sam/info", ""); + setprop("sam/info", "Search radar is "~(radarStatus==1?"ON":"OFF")); setprop("sam/timeleft", 0); } clearSingleLock(); @@ -269,6 +272,45 @@ var scan = func() { settimer(scan,radar_update_time); } +var isSetup = func { + return systime() - start_time < setupTime; +} + +var acquisitionRadarLoop = func { + if (isSetup() or reloading or getprop("/carrier/sunk") == 1 or getprop("/carrier/disabled") == 1) return; + var reason = ""; + var time = systime(); + if (time - radarTrackTime < radar_on_after_detect_time) { + radarStatus = 1; + reason = sprintf("Crew alert for %d seconds", radar_on_after_detect_time - (time - radarTrackTime)); + } elsif (radarStatus == 0 and time - radarOnTimestamp > radarOffDuration) { + radarStatus = 1; + reason = sprintf("Periodic check for %d seconds", radar_on_time); + } elsif (radarStatus == 1 and time - radarOffTimestamp > radar_on_time) { + radarStatus = 0; + radarOffDuration = rand()*(radar_off_time_max - radar_off_time_min)+radar_off_time_min; + reason = sprintf("Periodic pause for %d seconds", radarOffDuration); + } + + if (getprop("payload/armament/"~string.lc(missile_name)~"/guidance") == "tvm" and radarStatus == 0) { + foreach(var tgt ; targetsV2.vector) { + if (tgt.in_air > 0) { + radarTrackTime = time; + radarStatus = 1; + break; + } + } + } + setprop("/sim/multiplay/generic/int[2]", radarStatus != 1); + if (radarStatus == 1) radarOnTimestamp = time; + else radarOffTimestamp = time; + #print("Search radar is "~(radarStatus==1?"ON":"OFF")); + if (reason != "") print(reason); +} + +var radTimer = maketimer(0.5, acquisitionRadarLoop); +radTimer.start(); + var clearSingleLock = func () { thread.lock(mutexLock); if (semi_active_track == nil) { @@ -542,13 +584,14 @@ var missile_launch = func(mp, launchtime, my_pos) { setprop("sam/info", info); if (mp.getNode("callsign") != nil and mp.getNode("callsign").getValue() != nil and mp.getNode("callsign").getValue() != "") { - setprop("sim/multiplay/generic/string[6]", left(md5(mp.getNode("callsign").getValue()), 4)); + if (getprop("payload/armament/"~string.lc(missile_name)~"/guidance") != "tvm") setprop("sim/multiplay/generic/string[6]", left(md5(mp.getNode("callsign").getValue()), 4)); datalink.send_data({"contacts":[{"callsign":mp.getNode("callsign").getValue(),"iff":0}]}); } else { clearSingleLock(); } if (lu != nil) { lu.tracking = 1; + radarTrackTime = systime(); } } diff --git a/S-75/S-75-set.xml b/S-75/S-75-set.xml index 58aef92..bf084e5 100644 --- a/S-75/S-75-set.xml +++ b/S-75/S-75-set.xml @@ -293,7 +293,7 @@ 10.60 4 5340 - 0.20 + 0.10 110000 true false @@ -308,7 +308,8 @@ 15 2750 1 - 0.65 + 0.6 + 0.87 60 0 200 diff --git a/SA-6/Nasal/fire-control-custom.nas b/SA-6/Nasal/fire-control-custom.nas index ef81a26..35701d1 100644 --- a/SA-6/Nasal/fire-control-custom.nas +++ b/SA-6/Nasal/fire-control-custom.nas @@ -12,6 +12,11 @@ var launcher_align_to_target = 1; var align_speed_dps = 20; var radar_elevation_above_terrain_m = 10;#estimate var radar_lowest_pitch = 0.1;# 0.5 degs = roughly 925 feet at 20 nm, 25 feet at half a nm. # 0.35 = roughly 925 feet at 20 nm, 25 feet at half a nm. +var radar_off_time_min = 0;# When turning off to lure enemies into engagement zone, and to not make the SAM too easy to find, minimum turn off this duration. +var radar_off_time_max = 0;# Bigger missile_max_distance give crew incentive to keep this a bit longer +var radar_on_time = 45;# Minimum the time it would take to scan whole sky +var radar_on_after_detect_time = 180;# Crew is alert after spotting an aircraft, how long should they stay that way? +var can_detect_anti_rad = 0.05;# 0.5 very good, 0 not at all. #reaction tme for s-300p is 28 secs acording to http://www.astronautix.com/s/s-300p.html # sounds a bit high for pmu, as its 4 secs for s-400 diff --git a/SA-6/Nasal/fire-control.nas b/SA-6/Nasal/fire-control.nas index 65598ff..d856587 100644 --- a/SA-6/Nasal/fire-control.nas +++ b/SA-6/Nasal/fire-control.nas @@ -19,7 +19,11 @@ var reload_starting = 0; var ACTIVE_MISSILE = 0; var semi_active_track = nil;# with multiple missiles flying in semi-active-radar mode, this can change rather fast. Which means pilots wont get a steady tone, but disrupted tones. (tradeoff) var mutexLock = thread.newlock(); - +var radarStatus = 0;# Current radar status. +var radarOnTimestamp = -500;# Last timestamp of when the radar was online +var radarOffTimestamp = -500;# Last timestamp of when the radar was offline +var radarOffDuration = radar_off_time_min; +var radarTrackTime = 0;# Last time stamp where any enemy aircraft has been spotted. ########################################################################## ######################## Common SAM code ########################### @@ -132,7 +136,7 @@ var scan = func() { return; } - if (systime() - start_time < setupTime ) { + if (isSetup() ) { printf("Seconds till activation: %.1f", setupTime - (systime() - start_time)); setprop("sam/timeleft", setupTime - (systime() - start_time)); setprop("sam/missiles",(NUM_MISSILES+1-ACTIVE_MISSILE)); @@ -154,87 +158,86 @@ var scan = func() { } setprop("sim/multiplay/generic/float[5]", launcher_final_tilt_deg); #### ITERATE THROUGH MP LIST #### - var radarOn = 0; - var my_pos = geo.aircraft_position(); - var radarAlt = my_pos.alt()+radar_elevation_above_terrain_m; - my_pos.set_alt(radarAlt); - var mvec = props.globals.getNode("/ai/models").getChildren("multiplayer"); - var tvec = props.globals.getNode("/ai/models").getChildren("tanker"); - var avec = props.globals.getNode("/ai/models").getChildren("aircraft"); - if (mvec!=nil and tvec != nil) { - mvec = mvec ~ tvec; - } - if (mvec!=nil and avec != nil) { - mvec = mvec ~ avec; - } - var prio_vector = [];#0:node,1:trigger,2:prio value - foreach(var mp; mvec){ - if (mp.getNode("valid") == nil or mp.getNode("valid").getValue() != 1) continue; - var prio = fire_control(mp, my_pos); - append(prio_vector, prio); - if (prio[3] == 1) { - radarOn = 1;#this target is within envelope, we turn on radar. + if (radarStatus == 1) { + var my_pos = geo.aircraft_position(); + var radarAlt = my_pos.alt()+radar_elevation_above_terrain_m; + my_pos.set_alt(radarAlt); + var mvec = props.globals.getNode("/ai/models").getChildren("multiplayer"); + var tvec = props.globals.getNode("/ai/models").getChildren("tanker"); + var avec = props.globals.getNode("/ai/models").getChildren("aircraft"); + if (mvec!=nil and tvec != nil) { + mvec = mvec ~ tvec; } - } - prio_vector = sort(prio_vector, func (a,b) {if (a[2]>b[2]){return -1;} elsif (a[2] fire_minimum_interval ) ) { # - #### ... FOR THE MISSILE #### - #print("callsign " ~ cs ~ " found at " ~ dist_to_target); - missile_delay_time = systime(); - mp[0].getNode("unique",1).setValue(rand()); - armament.contact = radar_logic.Contact.new(mp[0], AIR); - launch_in_progress = 1; - missile_launch(mp[0], systime(), my_pos); - - } elsif ( ciws_installed and trigger == -1 and ROUNDS > 0 and ( systime() - ciws_delay_time > 1.0 ) and lu != nil) { - #### ... FOR THE CIWS #### - var contact = radar_logic.Contact.new(mp[0], AIR); - var cord = contact.get_Coord(); - var dist_nm = my_pos.direct_distance_to(cord)*M2NM; - var probabilityOfBurstKill = 0.50; - if (mp[0].getNode("velocities/true-airspeed-kt").getValue() > 50) { - probabilityOfBurstKill = extrapolate(dist_nm, 0, ciws_domain_nm, ciws_chance, 0); - } - var hits = math.max(0,int(extrapolate(rand(), 0, probabilityOfBurstKill, ciws_burst_rounds, 3)));#3 is a kill approx - var target_bearing = my_pos.course_to(cord); - setprop("sim/multiplay/generic/float[0]", -(target_bearing-getprop("orientation/heading-deg")));#CIWS horiz (inverted) - setprop("sim/multiplay/generic/float[1]", vector.Math.getPitch(my_pos, cord));#CIWS vert - setprop("sim/multiplay/generic/bool[0]",1);settimer(func {setprop("sim/multiplay/generic/bool[0]",0);}, 1);# for sound - if (hits > 0) { - var msg = notifications.ArmamentNotification.new("mhit", 4, -1*(ciws_shell+1)); - msg.RelativeAltitude = 0; - msg.Bearing = 0; - msg.Distance = hits; - msg.RemoteCallsign = mp[0].getNode("callsign").getValue(); - notifications.hitBridgedTransmitter.NotifyAll(msg); - damage.damageLog.push("CIWS fired | rounds remaining: " ~ ROUNDS ~ " | hit on: " ~ mp[0].getNode("callsign").getValue()); - } - print("CIWS fired | bursts remaining: " ~ ROUNDS ~ " | "~hits~" hits on: " ~ mp[0].getNode("callsign").getValue() ~ " @ "~ sprintf("%.1f",dist_nm) ~" nm"); - ciws_delay_time = systime(); - ROUNDS = ROUNDS - 1; - setprop("sam/bursts", ROUNDS); - if (ROUNDS == 0) { - print("Launcher out of shell ammo."); - settimer(autoreload, reload_time); - reloading = 1; - reload_starting = systime(); + if (mvec!=nil and avec != nil) { + mvec = mvec ~ avec; + } + var prio_vector = [];#0:node,1:trigger,2:prio value + foreach(var mp; mvec){ + if (mp.getNode("valid") == nil or mp.getNode("valid").getValue() != 1) continue; + var prio = fire_control(mp, my_pos); + append(prio_vector, prio); + if (prio[3] == 1) { + radarTrackTime = systime();#this target is within envelope, we turn on radar. } } - if (lu != nil and lu.tracking) { - radarOn = 1; + prio_vector = sort(prio_vector, func (a,b) {if (a[2]>b[2]){return -1;} elsif (a[2] fire_minimum_interval ) ) { # + #### ... FOR THE MISSILE #### + #print("callsign " ~ cs ~ " found at " ~ dist_to_target); + missile_delay_time = systime(); + mp[0].getNode("unique",1).setValue(rand()); + armament.contact = radar_logic.Contact.new(mp[0], AIR); + launch_in_progress = 1; + missile_launch(mp[0], systime(), my_pos); + + } elsif ( ciws_installed and trigger == -1 and ROUNDS > 0 and ( systime() - ciws_delay_time > 1.0 ) and lu != nil) { + #### ... FOR THE CIWS #### + var contact = radar_logic.Contact.new(mp[0], AIR); + var cord = contact.get_Coord(); + var dist_nm = my_pos.direct_distance_to(cord)*M2NM; + var probabilityOfBurstKill = 0.50; + if (mp[0].getNode("velocities/true-airspeed-kt").getValue() > 50) { + probabilityOfBurstKill = extrapolate(dist_nm, 0, ciws_domain_nm, ciws_chance, 0); + } + var hits = math.max(0,int(extrapolate(rand(), 0, probabilityOfBurstKill, ciws_burst_rounds, 3)));#3 is a kill approx + var target_bearing = my_pos.course_to(cord); + setprop("sim/multiplay/generic/float[0]", -(target_bearing-getprop("orientation/heading-deg")));#CIWS horiz (inverted) + setprop("sim/multiplay/generic/float[1]", vector.Math.getPitch(my_pos, cord));#CIWS vert + setprop("sim/multiplay/generic/bool[0]",1);settimer(func {setprop("sim/multiplay/generic/bool[0]",0);}, 1);# for sound + if (hits > 0) { + var msg = notifications.ArmamentNotification.new("mhit", 4, -1*(ciws_shell+1)); + msg.RelativeAltitude = 0; + msg.Bearing = 0; + msg.Distance = hits; + msg.RemoteCallsign = mp[0].getNode("callsign").getValue(); + notifications.hitBridgedTransmitter.NotifyAll(msg); + damage.damageLog.push("CIWS fired | rounds remaining: " ~ ROUNDS ~ " | hit on: " ~ mp[0].getNode("callsign").getValue()); + } + print("CIWS fired | bursts remaining: " ~ ROUNDS ~ " | "~hits~" hits on: " ~ mp[0].getNode("callsign").getValue() ~ " @ "~ sprintf("%.1f",dist_nm) ~" nm"); + ciws_delay_time = systime(); + ROUNDS = ROUNDS - 1; + setprop("sam/bursts", ROUNDS); + if (ROUNDS == 0) { + print("Launcher out of shell ammo."); + settimer(autoreload, reload_time); + reloading = 1; + reload_starting = systime(); + } + } + if (lu != nil and lu.tracking) { + radarTrackTime = systime(); + } } } - - setprop("/sim/multiplay/generic/int[2]",!radarOn); - + if (launcher_align_to_target and ACTIVE_MISSILE > NUM_MISSILES and systime()-missile_release_time > 2) { # rotate launcher back to forward: var tgt_dir = 0; @@ -249,7 +252,7 @@ var scan = func() { setprop("sam/info", "Reloading"); setprop("sam/timeleft", reload_time - (systime() - reload_starting)); } else { - setprop("sam/info", ""); + setprop("sam/info", "Search radar is "~(radarStatus==1?"ON":"OFF")); setprop("sam/timeleft", 0); } clearSingleLock(); @@ -269,6 +272,45 @@ var scan = func() { settimer(scan,radar_update_time); } +var isSetup = func { + return systime() - start_time < setupTime; +} + +var acquisitionRadarLoop = func { + if (isSetup() or reloading or getprop("/carrier/sunk") == 1 or getprop("/carrier/disabled") == 1) return; + var reason = ""; + var time = systime(); + if (time - radarTrackTime < radar_on_after_detect_time) { + radarStatus = 1; + reason = sprintf("Crew alert for %d seconds", radar_on_after_detect_time - (time - radarTrackTime)); + } elsif (radarStatus == 0 and time - radarOnTimestamp > radarOffDuration) { + radarStatus = 1; + reason = sprintf("Periodic check for %d seconds", radar_on_time); + } elsif (radarStatus == 1 and time - radarOffTimestamp > radar_on_time) { + radarStatus = 0; + radarOffDuration = rand()*(radar_off_time_max - radar_off_time_min)+radar_off_time_min; + reason = sprintf("Periodic pause for %d seconds", radarOffDuration); + } + + if (getprop("payload/armament/"~string.lc(missile_name)~"/guidance") == "tvm" and radarStatus == 0) { + foreach(var tgt ; targetsV2.vector) { + if (tgt.in_air > 0) { + radarTrackTime = time; + radarStatus = 1; + break; + } + } + } + setprop("/sim/multiplay/generic/int[2]", radarStatus != 1); + if (radarStatus == 1) radarOnTimestamp = time; + else radarOffTimestamp = time; + #print("Search radar is "~(radarStatus==1?"ON":"OFF")); + if (reason != "") print(reason); +} + +var radTimer = maketimer(0.5, acquisitionRadarLoop); +radTimer.start(); + var clearSingleLock = func () { thread.lock(mutexLock); if (semi_active_track == nil) { @@ -542,13 +584,14 @@ var missile_launch = func(mp, launchtime, my_pos) { setprop("sam/info", info); if (mp.getNode("callsign") != nil and mp.getNode("callsign").getValue() != nil and mp.getNode("callsign").getValue() != "") { - setprop("sim/multiplay/generic/string[6]", left(md5(mp.getNode("callsign").getValue()), 4)); + if (getprop("payload/armament/"~string.lc(missile_name)~"/guidance") != "tvm") setprop("sim/multiplay/generic/string[6]", left(md5(mp.getNode("callsign").getValue()), 4)); datalink.send_data({"contacts":[{"callsign":mp.getNode("callsign").getValue(),"iff":0}]}); } else { clearSingleLock(); } if (lu != nil) { lu.tracking = 1; + radarTrackTime = systime(); } } diff --git a/SA-6/SA-6-set.xml b/SA-6/SA-6-set.xml index 229ce52..a1c6d63 100644 --- a/SA-6/SA-6-set.xml +++ b/SA-6/SA-6-set.xml @@ -304,6 +304,7 @@ 2 0.45 75 + 0.89 true 3000 0