diff --git a/BUK-M2/Nasal/damage.nas b/BUK-M2/Nasal/damage.nas index 133e770..7b8973a 100644 --- a/BUK-M2/Nasal/damage.nas +++ b/BUK-M2/Nasal/damage.nas @@ -28,16 +28,6 @@ var warhead_lbs = { "AGM-84": 488.00, "AGM-88": 146.00, "AGM65": 200.00, - "aim-120": 44.00, - "AIM-120": 44.00, - "AIM-54": 135.00, - "aim-7": 88.00, - "AIM-7": 88.00, - "aim-9": 20.80, - "AIM-9": 20.80, - "AIM120": 44.00, - "AIM132": 22.05, - "AIM9": 20.80, "ALARM": 450.00, "AM39-Exocet": 364.00, "AS-37-Martel": 330.00, @@ -47,12 +37,13 @@ var warhead_lbs = { "FAB-250": 202.85, "FAB-500": 564.38, "GBU-12": 190.00, + "GBU-24": 945.00, "GBU-31": 945.00, "GBU12": 190.00, "GBU16": 450.00, "HVAR": 7.50,#P51 "KAB-500": 564.38, - "KH-25MP": 197.53, + "Kh-25MP": 197.53, "Kh-66": 244.71, "KN-06": 315.00, "LAU-68": 10.00, @@ -60,40 +51,14 @@ var warhead_lbs = { "M71": 200.00, "M71R": 200.00, "M90": 500.00, - "Magic-2": 27.00, - "Matra MICA": 30.00, - "Matra R550 Magic 2": 27.00, - "MATRA-R530": 55.00, - "MatraMica": 30.00, - "MatraMicaIR": 30.00, - "MatraR550Magic2": 27.00, - "Meteor": 55.00, - "MICA-EM": 30.00, - "MICA-IR": 30.00, "MK-82": 192.00, "MK-83": 445.00, "MK-84": 945.00, "OFAB-100": 92.59, - "R-13M": 16.31, - "R-27R1": 85.98, - "R-27T1": 85.98, - "R-3R": 16.31, - "R-3S": 16.31, - "R-55": 20.06, - "R-60": 6.60, - "R-60M": 7.70, - "R-73E": 16.31, - "R-77": 49.60, - "R74": 16.00, "RB-04E": 661.00, "RB-05A": 353.00, "RB-15F": 440.92, - "RB-24": 20.80, - "RB-24J": 20.80, - "RB-71": 88.00, - "RB-74": 20.80, "RB-75": 126.00, - "RB-99": 44.00, "RN-14T": 800.00, #fictional, thermobaeric replacement for the RN-24 nuclear bomb "RN-18T": 1200.00, #fictional, thermobaeric replacement for the RN-28 nuclear bomb "RS-2US": 28.66, diff --git a/BUK-M2/Nasal/fire-control.nas b/BUK-M2/Nasal/fire-control.nas deleted file mode 100644 index c5641e5..0000000 --- a/BUK-M2/Nasal/fire-control.nas +++ /dev/null @@ -1,245 +0,0 @@ -################### GLOBALS - -var false = 0; -var true = 1; - -var radar_update_time = 2; -var launch_update_time = 0.3; - -var missile_delay_time = 0; -var ciws_delay_time = 0; - -var AIR = 0; -var MARINE = 1; -var SURFACE = 2; -var ORDNANCE = 3; - -var ACTIVE_MISSILE = 0; -var NUM_MISSILES = 3; # total carried minus 1 -var ROUNDS = 30; -var RELOAD_TIME = 600; - -################### MISSILE INFO - -var missile_name = "M317"; -var missile_brevity = "Grizzly"; -var missile_max_distance = 31; #max distance in nm -var missile_min_distance = 2; #minimum distance in nm -var lockon_time = 12; #time in seconds it takes to lock on to a target -var launch_in_progress = 0; - -################### IFF - -var target = { - new: func (callsign) { - var m = { parents: [target] }; - m.callsign = callsign; - m.fired = false; - return m; - }, -}; - -var targets = { - "pinto": target.new("pinto"), - "Leto": target.new("Leto"), - "YV-187": target.new("YV-187"), - "swamp": target.new("swamp"), - "S": target.new("S"), - "G-UNTER": target.new("G-UNTER"), - "J-Mav16": target.new("J-Mav16"), - "Raider1": target.new("Raider1"), - "AF-MA13": target.new("AF-MA13"), - "KOL24M": target.new("KOL24M"), - "SNOWY1": target.new("SNOWY1"), - "Evading-target": target.new("Evading-target"),# for testing with AI targets - "Slow-evading-target": target.new("Slow-evading-target"), -}; - -################### MAIN LOOP - -var scan = func() { - if ( getprop("/carrier/sunk") == 1 ) { - return; - } - - #### ITERATE THROUGH MP LIST #### - var my_pos = geo.aircraft_position(); - foreach(var mp; props.globals.getNode("/ai/models").getChildren("multiplayer")){#change to "aircraft" to shoot at scenario AI planes. - #### DO WE HAVE FIRING SOLUTION... #### - # is plane in range, do we still have missiles, and is a missile already inbound, and has it been 4 seconds since the last missile launch? - var trigger = fire_control(mp, my_pos); - #print("dist to target = " ~ dist_to_target); - #### ... FOR THE MISSILE #### - if ( launch_in_progress == 0 and trigger == true and targets[mp.getNode("callsign").getValue()].fired == false and ACTIVE_MISSILE <= NUM_MISSILES and ( systime() - missile_delay_time > 7 ) ) { # - #print("callsign " ~ cs ~ " found at " ~ dist_to_target); - missile_delay_time = systime(); - targets[mp.getNode("callsign").getValue()].fired = true; - mp.getNode("unique",1).setValue(rand()); - armament.contact = radar_logic.Contact.new(mp, AIR); - missile_launch(mp, systime(), my_pos); - launch_in_progress = 1; - #### ... FOR THE CIWS - # disabled - } - } - settimer(scan,radar_update_time); -} - -################## FIRE CONTROL -################## ALL AI LOGIC RELATED TO FIGURING OUT IF A TARGET SHOULD BE SHOT AT SHOULD GO HERE. - -var fire_control = func(mp, my_pos) { - #gather some data about the target - var ufo_pos = geo.Coord.new().set_latlon(mp.getNode("position/latitude-deg").getValue(),mp.getNode("position/longitude-deg").getValue(),(mp.getNode("position/altitude-ft").getValue() * 0.3048)); - var target_distance = my_pos.direct_distance_to(ufo_pos) * .000539957; #in nautical miles - var target_heading = mp.getNode("orientation/true-heading-deg").getValue(); - var target_bearing = my_pos.course_to(ufo_pos); - var relative_bearing = math.abs(ufo_pos.course_to(my_pos) - target_heading); - var target_altitude = mp.getNode("position/altitude-ft").getValue(); - var target_airspeed = mp.getNode("velocities/true-airspeed-kt").getValue(); - - # can the radar see it? - if ( mp.getNode("valid").getValue() == false ) { return false; } - if ( radar_logic.isNotBehindTerrain(mp) == false ) { return false; } - if ( target_airspeed < 40 ) { return false; } - if ( target_altitude - props.globals.getNode("/position/altitude-ft").getValue() < 150 ) { return false; } - if ( target_altitude > 70000 ) { return false; } - if ( target_distance > missile_max_distance ) { return false; } - # is this plane a friend or foe? - if ( targets[mp.getNode("callsign").getValue()] == nil ) { return false; } - - #should we shoot? using linear interpolation, with minimum probability of 0.01 to 0.10 and maximum probability of 1 - var distance_probability = 1 + (0.01 - 1) * (( target_distance - missile_min_distance ) / ( missile_max_distance - missile_min_distance )); - var bearing_probability = 1 + (0.05 - 1) * (( relative_bearing ) / ( 180 )); - var altitude_probability = 1 + (0.05 - 1) * (( target_altitude - 150 ) / ( 70000 - 150 )); - var speed_probability = distance_probability / 2.5 + (1 - distance_probability / 2.5) * (( target_airspeed - 1000 ) / ( 40 - 1000 )); - - var distance_weight = 0.4; - var bearing_weight = 0.3; - var altitude_weight = 0.2; - var speed_weight = 0.1; - - var fire_probability = (distance_probability * distance_weight) + (bearing_probability * bearing_weight) + (altitude_probability * altitude_weight) + (speed_probability * speed_weight); - var the_dice = rand(); - - print("probability for " ~ mp.getNode("callsign").getValue() ~ ": "~fire_probability); - - if ( fire_probability > 0.60 and fire_probability > the_dice ) { - return true; - } else { - return false; - } -} - -################### MISSILE CONTROL - -### missile reload - -var reload = func() { - #figure out how many to add - for ( var i = 0; i <= NUM_MISSILES; i = i + 1 ) { - if (armament.AIM.new(i,missile_name,missile_brevity) != -1) { - #if statement just in case reload was called before all missiles were fired. Cause avoid calling search() on same missile twice. - armament.AIM.active[i].status = 0; - armament.AIM.active[i].search(); - } - } - ACTIVE_MISSILE = 0; - print("SAM reloaded with "~(NUM_MISSILES+1)~" missiles."); -} - -### missile launch - -var missile_launch = func(mp, launchtime, my_pos) { - var ufo_pos = geo.Coord.new().set_latlon(mp.getNode("position/latitude-deg").getValue(),mp.getNode("position/longitude-deg").getValue(),(mp.getNode("position/altitude-ft").getValue() * 0.3048)); - var target_bearing = my_pos.course_to(ufo_pos); - setprop("/orientation/heading-deg",target_bearing); - if ( armament.AIM.active[ACTIVE_MISSILE].status == 1 and systime() - launchtime > lockon_time and radar_logic.isNotBehindTerrain(mp) == true ) { - var brevity = armament.AIM.active[ACTIVE_MISSILE].brevity; - armament.AIM.active[ACTIVE_MISSILE].release(); - defeatSpamFilter(brevity ~ " at: " ~ mp.getNode("callsign").getValue()); - ACTIVE_MISSILE = ACTIVE_MISSILE + 1; - print("Fired "~missile_name~" #" ~ ACTIVE_MISSILE ~ " at: " ~ mp.getNode("callsign").getValue()); - launch_in_progress = 0; - return; - } elsif ((systime() - launchtime) > (lockon_time*3) or radar_logic.isNotBehindTerrain(mp) == false) { - # launch cancelled so it dont forever goes in this loop and dont allow for other firings. - launch_in_progress = 0; - return; - } - settimer( func { missile_launch(mp, launchtime, my_pos); },launch_update_time); -} - -### missile set launched to false for target - -var incoming_listener = func { - var history = getprop("/sim/multiplay/chat-history"); - var hist_vector = split("\n", history); - if (size(hist_vector) > 0) { - var last = hist_vector[size(hist_vector)-1]; - var last_vector = split(":", last); - var author = last_vector[0]; - var callsign = getprop("sim/multiplay/callsign"); - if (size(last_vector) > 1 and author == callsign) { - var last1 = split(" ", last_vector[1]); - print(last1); - if(size(last1) > 2 and (last1[size(last1)-1] == "exploded" or last1[size(last1)-1] == "disarmed") ) { - print("missile hit"); - if (size(last_vector) > 3) { - #print("that someone is me!"); - var type = last1[1]; - #callsign = target, type = missile - last_vector[3] = right(last_vector[3],size(last_vector[3]) - 1); - if ( targets[last_vector[3]] != nil and type == missile_name ) { - targets[last_vector[3]].fired = false; - } - } - } elsif(size(last1) > 2 and last1[size(last1)-2] == "missed" ) { - print("missile missed"); - var target = last1[size(last1)-1]; - var type = last1[1]; - #callsign = target, type = missile - last_vector[3] = right(last_vector[3],size(last_vector[3]) - 1); - if (targets[target] != nil and type == missile_name) { - targets[target].fired = false; - } - } - } - } -} - - -################### MISC - -var spams = 0; -var spamList = []; - -var defeatSpamFilter = func (str) { - spams += 1; - if (spams == 15) { - spams = 1; - } - str = str~":"; - for (var i = 1; i <= spams; i+=1) { - str = str~"."; - } - var newList = [str]; - for (var i = 0; i < size(spamList); i += 1) { - append(newList, spamList[i]); - } - spamList = newList; -} - -var spamLoop = func { - var spam = pop(spamList); - if (spam != nil) { - setprop("/sim/multiplay/chat", spam); - } - settimer(spamLoop, 1.20); -} - -spamLoop(); - -reload(); -scan(); -setlistener("/sim/multiplay/chat-history", incoming_listener, 0, 0); \ No newline at end of file diff --git a/BUK-M2/Nasal/guided-missiles.nas b/BUK-M2/Nasal/guided-missiles.nas deleted file mode 100644 index 7a6c528..0000000 --- a/BUK-M2/Nasal/guided-missiles.nas +++ /dev/null @@ -1,2222 +0,0 @@ -################################################################################# -####### -####### Guided/Cruise missiles, rockets and dumb/glide bombs code for Flightgear. -####### -####### License: GPL 2 -####### -####### Authors: -####### Alexis Bory, Fabien Barbier, Justin Nicholson, Nikolai V. Chr. -####### -####### In addition, some code is derived from work by: -####### David Culp, Vivian Meazza, M. Franz -####### -################################################################################## - -# Some notes about making weapons: -# -# Firstly make sure you read the comments (line 190+) below for the properties. -# For laser/gps guided gravity bombs make sure to set the max G very low, like 0.5G, to simulate them slowly adjusting to hit the target. -# Remember for air to air missiles the speed quoted in literature is normally the speed above the launch platform. I usually fly at the typical max usage -# regime for that missile, so for example for AIM-7 it would be at 40000 ft, -# there I make sure it can reach approx the max relative speed. For older missiles the max speed quoted is sometimes absolute speed though, so beware. -# If it quotes aerodynamic speed then its the absolute speed. Speeds quoted in in unofficial sources can be any of them, -# but if its around mach 5 for A/A its a good bet its absolute, only very few A/A missiles are likely hypersonic. (probably due to heat or fuel limitations) -# If you cannot find fuel weight in literature, you probably wont go far off with a value that is 1/4 to 1/3 of total launch weight for a A/A missile. -# Stage durations is allowed to be 0, so can thrust values. If there is no second stage, instead of just setting stage 2 thrust to 0, -# set stage 2 duration to 0 also. For unpowered munitions, set all thrusts to 0. -# For very low sea skimming missiles, be sure to set terrain following to false, you cannot have it both ways. -# Since if it goes very low (below 100ft), it cannot navigate terrain reliable. -# The property terrain following only goes into effect, if a cruise altitude is set below 10000ft and not set to 0. -# Cruise missiles against ground targets will always terrain follow, no matter that property. -# If literature quotes a max distance for a weapon, its a good bet it is under the condition that the target -# is approaching the launch platform with high speed and does not evade, and also if the launch platform is an aircraft, -# that it also is approaching the target with high speed. In other words, high closing rate. For example the AIM-7, which can hit bombers out at 32 NM, -# will often have to be within 3 NM of an escaping target to hit it (source). Missiles typically have significantly less range against an evading -# or escaping target than what is commonly believed. I typically fly at 40000 ft at mach 2, approach a target flying head-on with same speed and altitude, -# to test max range. -# When you test missiles against aircraft, be sure to do it with a framerate of 25+, else they will not hit very good, especially high speed missiles like -# Amraam or Phoenix. Also notice they generally not hit so close against Scenario/AI objects compared to MP aircraft due to the way these are updated. -# Laser and semi-radar guided munitions need the target to be painted to keep lock. Notice gps guided munition that are all aspect will never lose lock, -# whether they can 'see' the target or not. -# Remotely controlled navigation is not implemented, but the way it flies can be simulated by setting direct navigation with semi-radar or laser guidance. -# -# -# Usage: -# -# To create a weapon call AIM.new(pylon, type, description). The pylon is an integer from 0 or higher. When its launched it will read the pylon position in -# controls/armament/station[pylon+1]/offsets, where the position properties must be x-m, y-m and z-m. The type is just a string, the description is a string -# that is exposed in its radar properties under AI/models during flight. -# The model that is loaded and shown is located in the aircraft folder at the value of property payload/armament/models in a subfolder with same name as type. -# Inside the subfolder the xml file is called [lowercase type]-[pylon].xml -# To start making the missile try to get a lock, set its status to MISSILE_SEARCH and call search(), the missile will then keep trying to get a lock on 'contact'. -# 'contact' can be set to nil at any time or changed. To stop the search, just set its status to MISSILE_STANDBY. To resume the search you again have to set -# the status and call search(). -# To release the munition at a target call release(), do this only after the missile has set its own status to MISSILE_LOCK. -# When using weapons without target, call releaseAtNothing() instead of release(), search() does not need to have been called beforehand. -# To then find out where it hit the ground check the impact report in AI/models. The impact report will contain warhead weight, but that will be zero if -# the weapon did not have time to arm before hitting ground. -# To drop the munition, without arming it nor igniting its engine, call eject(). -# -# -# Limitations: -# -# The weapons use a simplified flight model that does not have AoA or sideslip. Mass balance, rotational inertia, wind is also not implemented. They also do not roll. -# If you fire a weapon and have HoT enabled in flightgear, they likely will not hit very precise. -# The weapons are highly dependant on framerate, so low frame rate will make them hit imprecise. -# APN does not take target sideslip and AoA into account when considering the targets acceleration. It assumes the target flies in the direction its pointed. -# The drag curves are tailored for sizable munitions, so it does not work well will bullet or cannon sized munition, submodels are better suited for that. -# -# -# Future features: -# -# Make ground hitting weapons hit all nearby targets, not just what its locked on. -# Chaff interaction for radar guided weapons. -# ECM disturbance of getting radar lock. -# Lock on jam. (advanced feature) -# After FG gets HLA: stop using MP chat for hit messages. -# Allow firing only if certain conditions are met. Like not being inverted when firing dropped weapons. -# Remote controlled guidance (advanced feature and probably not very practical in FG..yet) -# Ground launched rails/tubes that rotate towards target before firing. -# Make weapon unreliable by design, to simulate weapons which were unreliable, like Phoenix. -# Sub munitions that have their own guidance/FDM. (advanced) -# GPS guided munitions could have waypoints added. -# Specify terminal manouvres and preferred impact aspect. -# Limit guiding if needed so that the missile don't lose sight of target. -# Change flare to use helicopter property double. -# Make check for seeker FOV round instead of square. -# Consider to average the closing speed in proportional navigation. So get it between second last positions and current, instead of last to current. -# Drag coeff reduction due to exhaust plume. -# Proportional navigation should use vector math instead decomposition horizontal/vertical navigation. -# If closing speed is negative, consider to switch to pure pursuit from proportional navigation, the target might turn back into missile. -# -# -# Please report bugs and features to Nikolai V. Chr. | ForumUser: Necolatis | Callsign: Leto - -var AcModel = props.globals.getNode("payload"); -var OurHdg = props.globals.getNode("orientation/heading-deg"); -var OurRoll = props.globals.getNode("orientation/roll-deg"); -var OurPitch = props.globals.getNode("orientation/pitch-deg"); -var OurAlpha = props.globals.getNode("orientation/alpha-deg"); -var OurBeta = props.globals.getNode("orientation/side-slip-deg"); -var deltaSec = props.globals.getNode("sim/time/delta-sec"); -var speedUp = props.globals.getNode("sim/speed-up"); -var noseAir = props.globals.getNode("velocities/uBody-fps"); -var belowAir = props.globals.getNode("velocities/wBody-fps"); -var HudReticleDev = props.globals.getNode("payload/armament/hud/reticle-total-deviation", 1);#polar coords -var HudReticleDeg = props.globals.getNode("payload/armament/hud/reticle-total-angle", 1); -var update_loop_time = 0.000; - -var SIM_TIME = 0; -var REAL_TIME = 1; - -var TRUE = 1; -var FALSE = 0; - -var use_fg_default_hud = FALSE; - -var MISSILE_STANDBY = -1; -var MISSILE_SEARCH = 0; -var MISSILE_LOCK = 1; -var MISSILE_FLYING = 2; - -var AIR = 0; -var MARINE = 1; -var SURFACE = 2; -var ORDNANCE = 3; - -var g_fps = 9.80665 * M2FT; -var slugs_to_lbm = 32.1740485564; -var const_e = 2.71828183; - -var first_in_air = FALSE;# first missile is in the air, other missiles should not write to blade[x]. - -# -# The radar will make sure to keep this variable updated. -# Whatever is targeted and ready to be fired upon, should be set here. -# -var contact = nil; -# -# Contact should implement the following interface: -# -# get_type() - (AIR, MARINE, SURFACE or ORDNANCE) -# getUnique() - Used when comparing 2 targets to each other and determining if they are the same target. -# isValid() - If this target is valid -# getElevation() -# get_bearing() -# get_Callsign() -# get_range() -# get_Coord() -# get_Latitude() -# get_Longitude() -# get_altitude() -# get_Pitch() -# get_heading() -# getFlareNode() - Used for flares. -# isPainted() - Tells if this target is still being tracked by the launch platform, only used in semi-radar and laser guided missiles. - -var AIM = { - #done - new : func (p, type = "AIM-9", sign = "Sidewinder") { - if(AIM.active[p] != nil) { - #do not make new missile logic if one exist for this pylon. - return -1; - } - var m = { parents : [AIM]}; - # Args: p = Pylon. - - m.type_lc = string.lc(type); - m.type = type; - - m.deleted = FALSE; - - m.status = MISSILE_STANDBY; # -1 = stand-by, 0 = searching, 1 = locked, 2 = fired. - m.free = 0; # 0 = status fired with lock, 1 = status fired but having lost lock. - m.trackWeak = 1; - - m.prop = AcModel.getNode("armament/"~m.type_lc~"/").getChild("msl", 0, 1); - m.SwSoundOnOff = AcModel.getNode("armament/"~m.type_lc~"/sound-on-off"); - m.SwSoundVol = AcModel.getNode("armament/"~m.type_lc~"/sound-volume"); - m.PylonIndex = m.prop.getNode("pylon-index", 1).setValue(p); - m.ID = p; - m.pylon_prop = props.globals.getNode("controls/armament").getChild("station", p+1); - m.Tgt = nil; - m.callsign = "Unknown"; - m.update_track_time = 0; - m.direct_dist_m = nil; - m.speed_m = 0; - - # AIM specs: - m.fcs_fov = getprop("payload/armament/"~m.type_lc~"/FCS-field-deg") / 2; # fire control system total field of view - m.max_detect_rng = getprop("payload/armament/"~m.type_lc~"/max-fire-range-nm"); # max range that the FCS allows firing - m.max_seeker_dev = getprop("payload/armament/"~m.type_lc~"/seeker-field-deg") / 2; # missiles own seekers total FOV - m.force_lbf_1 = getprop("payload/armament/"~m.type_lc~"/thrust-lbf-stage-1"); # stage 1 thrust, set both stages to zero to simulate gravity bomb, set them to 1 to simulate glide bomb - m.force_lbf_2 = getprop("payload/armament/"~m.type_lc~"/thrust-lbf-stage-2"); # stage 2 thrust - m.stage_1_duration = getprop("payload/armament/"~m.type_lc~"/stage-1-duration-sec"); # stage 1 duration - m.stage_2_duration = getprop("payload/armament/"~m.type_lc~"/stage-2-duration-sec"); # stage 2 duration - m.weight_launch_lbm = getprop("payload/armament/"~m.type_lc~"/weight-launch-lbs"); # total weight of armament, including fuel and warhead. - m.weight_whead_lbm = getprop("payload/armament/"~m.type_lc~"/weight-warhead-lbs"); # warhead weight - m.weight_fuel_lbm = getprop("payload/armament/"~m.type_lc~"/weight-fuel-lbm"); # fuel weight [optional]. If this property is not present, it won't lose weight as the fuel is used. - m.Cd_base = getprop("payload/armament/"~m.type_lc~"/drag-coeff"); # drag coefficient - m.eda = getprop("payload/armament/"~m.type_lc~"/drag-area"); # normally is crosssection area of munition (without fins) - m.max_g = getprop("payload/armament/"~m.type_lc~"/max-g"); # max G-force the missile can pull at sealevel - m.arming_time = getprop("payload/armament/"~m.type_lc~"/arming-time-sec"); # time for weapon to arm - m.min_speed_for_guiding = getprop("payload/armament/"~m.type_lc~"/min-speed-for-guiding-mach"); # minimum speed before the missile steers, before it reaches this speed it will fly straight - m.selfdestruct_time = getprop("payload/armament/"~m.type_lc~"/self-destruct-time-sec"); # time before selfdestruct - m.guidance = getprop("payload/armament/"~m.type_lc~"/guidance"); # heat/radar/semi-radar/laser/gps/vision/unguided - m.navigation = getprop("payload/armament/"~m.type_lc~"/navigation"); # direct/PN/APN (use direct for bombs, use PN for very old missiles, use APN for modern missiles) - m.all_aspect = getprop("payload/armament/"~m.type_lc~"/all-aspect"); # set to false if missile only locks on reliably to rear of target aircraft - m.vol_search = getprop("payload/armament/"~m.type_lc~"/vol-search"); # sound volume when searcing - m.vol_track = getprop("payload/armament/"~m.type_lc~"/vol-track"); # sound volume when having lock - m.vol_track_weak = getprop("payload/armament/"~m.type_lc~"/vol-track-weak"); # sound volume before getting solid lock - m.angular_speed = getprop("payload/armament/"~m.type_lc~"/seeker-angular-speed-dps"); # only for heat/vision seeking missiles. Max angular speed that the target can move as seen from seeker, before seeker loses lock. - m.sun_lock = getprop("payload/armament/"~m.type_lc~"/lock-on-sun-deg"); # only for heat seeking missiles. If it looks at sun within this angle, it will lose lock on target. - m.loft_alt = getprop("payload/armament/"~m.type_lc~"/loft-altitude"); # if 0 then no snap up. Below 10000 then cruise altitude above ground. Above 10000 max altitude it will snap up to. - m.follow = getprop("payload/armament/"~m.type_lc~"/terrain-follow"); # used for anti-ship missiles that should be able to terrain follow instead of purely sea skimming. - m.min_dist = getprop("payload/armament/"~m.type_lc~"/min-fire-range-nm"); # it wont get solid lock before the target has this range - m.rail = getprop("payload/armament/"~m.type_lc~"/rail"); # if the weapon is rail or tube fired set to true. If dropped 7ft before ignited set to false. - m.rail_dist_m = getprop("payload/armament/"~m.type_lc~"/rail-length-m"); # length of tube/rail - m.rail_forward = getprop("payload/armament/"~m.type_lc~"/rail-point-forward"); # true for rail, false for rail/tube with a pitch - m.rail_pitch_deg = getprop("payload/armament/"~m.type_lc~"/rail-pitch-deg"); # Only used when rail is not forward. 90 for vertical tube. - m.class = getprop("payload/armament/"~m.type_lc~"/class"); # put in letters here that represent the types the missile can fire at. A=air, M=marine, G=ground - m.brevity = getprop("payload/armament/"~m.type_lc~"/fire-msg"); # what the pilot will call out over the comm when he fires this weapon - m.reportDist = getprop("payload/armament/"~m.type_lc~"/max-report-distance"); # max distance from target the missile will report that it has exploded, instead of just passed. - - - m.weapon_model = getprop("payload/armament/models")~type~"/"~m.type_lc~"-"; - m.elapsed_last = 0; - - m.target_air = find("A", m.class)==-1?FALSE:TRUE; - m.target_sea = find("M", m.class)==-1?FALSE:TRUE;#use M for marine, since S can be confused with surface. - m.target_gnd = find("G", m.class)==-1?FALSE:TRUE; - - if (m.navigation == nil) { - m.navigation = "APN"; - } - - # Find the next index for "models/model" and create property node. - # Find the next index for "ai/models/aim-9" and create property node. - # (M. Franz, see Nasal/tanker.nas) - var n = props.globals.getNode("models", 1); - var i = 0; - for (i = 0; 1==1; i += 1) { - if (n.getChild("model", i, 0) == nil) { - break; - } - } - m.model = n.getChild("model", i, 1); - - n = props.globals.getNode("ai/models", 1); - for (i = 0; 1==1; i += 1) { - if (n.getChild(m.type_lc, i, 0) == nil) { - break; - } - } - m.ai = n.getChild(m.type_lc, i, 1); - - m.ai.getNode("valid", 1).setBoolValue(1); - m.ai.getNode("name", 1).setValue(type); - m.ai.getNode("sign", 1).setValue(sign); - #m.model.getNode("collision", 1).setBoolValue(0); - #m.model.getNode("impact", 1).setBoolValue(0); - var id_model = m.weapon_model ~ m.ID ~ ".xml"; - m.model.getNode("path", 1).setValue(id_model); - m.life_time = 0; - - # Create the AI position and orientation properties. - m.latN = m.ai.getNode("position/latitude-deg", 1); - m.lonN = m.ai.getNode("position/longitude-deg", 1); - m.altN = m.ai.getNode("position/altitude-ft", 1); - m.hdgN = m.ai.getNode("orientation/true-heading-deg", 1); - m.pitchN = m.ai.getNode("orientation/pitch-deg", 1); - m.rollN = m.ai.getNode("orientation/roll-deg", 1); - - m.ac = nil; - - m.coord = geo.Coord.new().set_latlon(0, 0, 0); - m.last_coord = nil; - m.before_last_coord = nil; - m.t_coord = geo.Coord.new().set_latlon(0, 0, 0); - m.last_t_coord = m.t_coord; - m.before_last_t_coord = nil; - - m.speed_down_fps = nil; - m.speed_east_fps = nil; - m.speed_north_fps = nil; - m.alt_ft = nil; - m.pitch = nil; - m.hdg = nil; - - # Nikolai V. Chr. - # The more variables here instead of declared locally, the better for performance. - # Due to garbage collector. - # - - - m.density_alt_diff = 0; - m.max_g_current = m.max_g; - m.old_speed_horz_fps = nil; - m.paused = 0; - m.old_speed_fps = 0; - m.dt = 0; - m.g = 0; - - # navigation and guidance - m.last_deviation_e = nil; - m.last_deviation_h = nil; - m.last_track_e = 0; - m.last_track_h = 0; - m.guiding = TRUE; - m.t_alt = 0; - m.dist_curr = 0; - m.dist_curr_direct = 0; - m.t_elev_deg = 0; - m.t_course = 0; - m.dist_last = nil; - m.dist_direct_last = nil; - m.last_t_course = nil; - m.last_t_elev_deg = nil; - m.last_cruise_or_loft = FALSE; - m.last_t_norm_speed = nil; - m.last_t_elev_norm_speed = nil; - m.last_dt = 0; - m.dive_token = FALSE; - m.raw_steer_signal_elev = 0; - m.raw_steer_signal_head = 0; - m.cruise_or_loft = FALSE; - m.curr_deviation_e = 0; - m.curr_deviation_h = 0; - m.track_signal_e = 0; - m.track_signal_h = 0; - - # cruise-missiles - m.nextGroundElevation = 0; # next Ground Elevation - m.nextGroundElevationMem = [-10000, -1]; - - #rail - m.drop_time = 0; - m.rail_passed = FALSE; - m.x = 0; - m.y = 0; - m.z = 0; - m.rail_pos = 0; - m.rail_speed_into_wind = 0; - - # stats - m.maxMach = 0; - m.energyBleedKt = 0; - - m.lastFlare = 0; - m.fooled = FALSE; - m.explodeSound = TRUE; - m.first = FALSE; - - # these 3 is used for limiting spam to console: - m.heatLostLock = FALSE; - m.semiLostLock = FALSE; - m.tooLowSpeed = FALSE; - - m.SwSoundOnOff.setBoolValue(FALSE); - m.SwSoundVol.setDoubleValue(m.vol_search); - #me.trackWeak = 1; - - return AIM.active[m.ID] = m; - }, - - del: func {#GCD (garbage collection optimization done) - #print("deleted"); - if (me.first == TRUE) { - me.resetFirst(); - } - me.model.remove(); - me.ai.remove(); - if (me.status == MISSILE_FLYING) { - delete(AIM.flying, me.flyID); - } else { - delete(AIM.active, me.ID); - } - me.SwSoundVol.setDoubleValue(0); - me.deleted = TRUE; - }, - - getGPS: func(x, y, z, pitch) {#GCD - # - # get Coord from body position. x,y,z must be in meters. - # derived from Vivian's code in AIModel/submodel.cxx. - # - me.ac = geo.aircraft_position(); - - if(x == 0 and y==0 and z==0) { - return geo.Coord.new(me.ac); - } - - me.ac_roll = OurRoll.getValue(); - me.ac_pitch = pitch; - me.ac_hdg = OurHdg.getValue(); - - me.in = [0,0,0]; - me.trans = [[0,0,0],[0,0,0],[0,0,0]]; - me.out = [0,0,0]; - - me.in[0] = -x * M2FT; - me.in[1] = y * M2FT; - me.in[2] = z * M2FT; - # Pre-process trig functions: - me.cosRx = math.cos(-me.ac_roll * D2R); - me.sinRx = math.sin(-me.ac_roll * D2R); - me.cosRy = math.cos(-me.ac_pitch * D2R); - me.sinRy = math.sin(-me.ac_pitch * D2R); - me.cosRz = math.cos(me.ac_hdg * D2R); - me.sinRz = math.sin(me.ac_hdg * D2R); - # Set up the transform matrix: - me.trans[0][0] = me.cosRy * me.cosRz; - me.trans[0][1] = -1 * me.cosRx * me.sinRz + me.sinRx * me.sinRy * me.cosRz ; - me.trans[0][2] = me.sinRx * me.sinRz + me.cosRx * me.sinRy * me.cosRz; - me.trans[1][0] = me.cosRy * me.sinRz; - me.trans[1][1] = me.cosRx * me.cosRz + me.sinRx * me.sinRy * me.sinRz; - me.trans[1][2] = -1 * me.sinRx * me.cosRx + me.cosRx * me.sinRy * me.sinRz; - me.trans[2][0] = -1 * me.sinRy; - me.trans[2][1] = me.sinRx * me.cosRy; - me.trans[2][2] = me.cosRx * me.cosRy; - # Multiply the input and transform matrices: - me.out[0] = me.in[0] * me.trans[0][0] + me.in[1] * me.trans[0][1] + me.in[2] * me.trans[0][2]; - me.out[1] = me.in[0] * me.trans[1][0] + me.in[1] * me.trans[1][1] + me.in[2] * me.trans[1][2]; - me.out[2] = me.in[0] * me.trans[2][0] + me.in[1] * me.trans[2][1] + me.in[2] * me.trans[2][2]; - # Convert ft to degrees of latitude: - me.out[0] = me.out[0] / (366468.96 - 3717.12 * math.cos(me.ac.lat() * D2R)); - # Convert ft to degrees of longitude: - me.out[1] = me.out[1] / (365228.16 * math.cos(me.ac.lat() * D2R)); - # Set submodel initial position: - me.mlat = me.ac.lat() + me.out[0]; - me.mlon = me.ac.lon() + me.out[1]; - me.malt = (me.ac.alt() * M2FT) + me.out[2]; - - me.c = geo.Coord.new(); - me.c.set_latlon(me.mlat, me.mlon, me.malt * FT2M); - - return me.c; - }, - - eject: func () {#GCD - me.stage_1_duration = 0; - me.force_lbf_1 = 0; - me.stage_2_duration = 0; - me.force_lbf_2 = 0; - me.arming_time = 5000; - me.rail = FALSE; - me.releaseAtNothing(); - }, - - releaseAtNothing: func() {#GCD - me.Tgt = nil; - me.release(); - }, - - release: func() {#GCn - # Release missile/bomb from its pylon/rail/tube and send it away. - # - me.status = MISSILE_FLYING; - me.flyID = rand(); - AIM.flying[me.flyID] = me; - delete(AIM.active, me.ID); - me.animation_flags_props(); - - # Get the A/C position and orientation values. - me.ac = geo.aircraft_position(); - me.ac_init = geo.Coord.new(me.ac); - var ac_roll = OurRoll.getValue();# positive is banking right - var ac_pitch = OurPitch.getValue(); - var ac_hdg = OurHdg.getValue(); - - if (me.rail == TRUE) { - if (me.rail_forward == FALSE) { - ac_pitch = ac_pitch + me.rail_pitch_deg; - } - } - - # Compute missile initial position relative to A/C center - me.x = me.pylon_prop.getNode("offsets/x-m").getValue(); - me.y = me.pylon_prop.getNode("offsets/y-m").getValue(); - me.z = me.pylon_prop.getNode("offsets/z-m").getValue(); - var init_coord = me.getGPS(me.x, me.y, me.z, ac_pitch); - - # Set submodel initial position: - var mlat = init_coord.lat(); - var mlon = init_coord.lon(); - var malt = init_coord.alt() * M2FT; - me.latN.setDoubleValue(mlat); - me.lonN.setDoubleValue(mlon); - me.altN.setDoubleValue(malt); - me.hdgN.setDoubleValue(ac_hdg); - - if (me.rail == FALSE) { - # drop distance in time - me.drop_time = math.sqrt(2*7/g_fps);# time to fall 7 ft to clear aircraft - } - - me.pitchN.setDoubleValue(ac_pitch); - me.rollN.setDoubleValue(0); - - me.coord = geo.Coord.new(init_coord); - - me.model.getNode("latitude-deg-prop", 1).setValue(me.latN.getPath()); - me.model.getNode("longitude-deg-prop", 1).setValue(me.lonN.getPath()); - me.model.getNode("elevation-ft-prop", 1).setValue(me.altN.getPath()); - me.model.getNode("heading-deg-prop", 1).setValue(me.hdgN.getPath()); - me.model.getNode("pitch-deg-prop", 1).setValue(me.pitchN.getPath()); - me.model.getNode("roll-deg-prop", 1).setValue(me.rollN.getPath()); - var loadNode = me.model.getNode("load", 1); - loadNode.setBoolValue(1); - - # Get initial velocity vector (aircraft): - me.speed_down_fps = getprop("velocities/speed-down-fps"); - me.speed_east_fps = getprop("velocities/speed-east-fps"); - me.speed_north_fps = getprop("velocities/speed-north-fps"); - if (me.rail == TRUE) { - if (me.rail_forward == FALSE) { - if (me.rail_pitch_deg == 90) { - # rail is actually a tube pointing upward - me.rail_speed_into_wind = -getprop("velocities/wBody-fps");# wind from below - } else { - #does not account for incoming airstream, yet. - me.rail_speed_into_wind = 0; - } - } else { - # rail is pointing forward - me.rail_speed_into_wind = getprop("velocities/uBody-fps");# wind from nose - } - } - - me.alt_ft = malt; - me.pitch = ac_pitch; - me.hdg = ac_hdg; - - if (getprop("sim/flight-model") == "jsb") { - # currently not supported in Yasim - me.density_alt_diff = getprop("fdm/jsbsim/atmosphere/density-altitude") - me.ac.alt()*M2FT; - } - - # setup lofting and cruising - me.snapUp = me.loft_alt > 10000; - me.rotate_token = FALSE; - #if (me.Tgt != nil and me.snapUp == TRUE) { - #var dst = me.coord.distance_to(me.Tgt.get_Coord()) * M2NM; - # - #f(x) = y1 + ((x - x1) / (x2 - x1)) * (y2 - y1) -# me.loft_alt = me.loft_alt - ((me.max_detect_rng - 10) - (dst - 10))*500; original code -# me.loft_alt = 0+((dst-38)/(me.max_detect_rng-38))*(me.loft_alt-36000); originally for phoenix missile -# me.loft_alt = 0+((dst-10)/(me.max_detect_rng-10))*(me.loft_alt-0); also doesn't really work -# me.loft_alt = me.clamp(me.loft_alt, 0, 200000); - #printf("Loft to max %5d ft.", me.loft_alt); - #} - - - me.SwSoundVol.setDoubleValue(0); - me.trackWeak = 1; - #settimer(func { HudReticleDeg.setValue(0) }, 2); - #interpolate(HudReticleDev, 0, 2); - - me.startMach = getprop("velocities/mach"); - me.startAlt = getprop("position/altitude-ft"); - me.startDist = 0; - me.maxAlt = me.startAlt; - if (me.Tgt != nil) { - me.startDist = me.ac_init.direct_distance_to(me.Tgt.get_Coord()); - } - printf("Launch %s at %s.", me.type, me.callsign); - - me.weight_current = me.weight_launch_lbm; - me.mass = me.weight_launch_lbm / slugs_to_lbm; - - # find the fuel consumption - lbm/sec - if (me.weight_fuel_lbm == nil) { - me.weight_fuel_lbm = 0; - } - var energy1 = me.force_lbf_1 * me.stage_1_duration; - var energy2 = me.force_lbf_2 * me.stage_2_duration; - var energyT = energy1 + energy2; - var fuel_per_energy = me.weight_fuel_lbm / energyT; - me.fuel_per_sec_1 = (fuel_per_energy * energy1) / me.stage_1_duration; - me.fuel_per_sec_2 = (fuel_per_energy * energy2) / me.stage_2_duration; - - # find the sun: - if(me.guidance == "heat") { - var sun_x = getprop("ephemeris/sun/local/x"); - var sun_y = getprop("ephemeris/sun/local/x"); - var sun_z = getprop("ephemeris/sun/local/x"); - me.sun_power = getprop("/rendering/scene/diffuse/red"); - me.sun = geo.Coord.new(me.ac_init); - me.sun.set_xyz(me.sun.x()+sun_x*200000, me.sun.y()+sun_y*200000, me.sun.z()+sun_z*200000);#heat seeking missiles don't fly far, so setting it 200Km away is fine. - } - me.lock_on_sun = FALSE; - - me.flight(); - loadNode.remove(); - }, - - drag: func (mach) {#GCD - # Nikolai V. Chr.: Made the drag calc more in line with big missiles as opposed to small bullets. - # - # The old equations were based on curves for a conventional shell/bullet (no boat-tail), - # and derived from Davic Culps code in AIBallistic. - me.Cd = 0; - if (mach < 0.7) { - me.Cd = (0.0125 * mach + 0.20) * 5 * me.Cd_base; - } elsif (mach < 1.2 ) { - me.Cd = (0.3742 * math.pow(mach, 2) - 0.252 * mach + 0.0021 + 0.2 ) * 5 * me.Cd_base; - } else { - me.Cd = (0.2965 * math.pow(mach, -1.1506) + 0.2) * 5 * me.Cd_base; - } - - return me.Cd; - }, - - maxG: func (rho, max_g_sealevel) {#GCD - # Nikolai V. Chr.: A function to determine max G-force depending on air density. - # - # density for 0ft and 50kft: - #print("0:"~rho_sndspeed(0)[0]); = 0.0023769 - #print("50k:"~rho_sndspeed(50000)[0]); = 0.00036159 - # - # Fact: An aim-9j can do 22G at sealevel, 13G at 50Kft - # 13G = 22G * 0.5909 - # - # extra/inter-polation: - # f(x) = y1 + ((x - x1) / (x2 - x1)) * (y2 - y1) - # calculate its performance at current air density: - return max_g_sealevel+((rho-0.0023769)/(0.00036159-0.0023769))*(max_g_sealevel*0.5909-max_g_sealevel); - }, - - thrust: func () {#GCD - # Determine the thrust at this moment. - # - # If dropped, then ignited after fall time of what is the equivalent of 7ft. - # If the rocket is 2 stage, then ignite the second stage when 1st has burned out. - # - me.thrust_lbf = 0;# pounds force (lbf) - if (me.life_time > me.drop_time) { - me.thrust_lbf = me.force_lbf_1; - } - if (me.life_time > me.stage_1_duration + me.drop_time) { - me.thrust_lbf = me.force_lbf_2; - } - if (me.life_time > (me.drop_time + me.stage_1_duration + me.stage_2_duration)) { - me.thrust_lbf = 0; - } - if (me.thrust_lbf < 1) { - me.smoke_prop.setBoolValue(0); - } else { - me.smoke_prop.setBoolValue(1); - } - return me.thrust_lbf; - }, - - speedChange: func (thrust_lbf, rho, Cd) {#GCD - # Calculate speed change from last update. - # - # Acceleration = thrust/mass - drag/mass; - - me.acc = thrust_lbf / me.mass; - me.q = 0.5 * rho * me.old_speed_fps * me.old_speed_fps;# dynamic pressure - me.drag_acc = (me.Cd * me.q * me.eda) / me.mass; - - # get total new speed change (minus gravity) - return me.acc*me.dt - me.drag_acc*me.dt; - }, - - energyBleed: func (gForce, altitude) {#GCD - # Bleed of energy from pulling Gs. - # This is very inaccurate, but better than nothing. - # - # First we get the speedloss due to normal drag: - me.b300 = me.bleed32800at0g(); - me.b000 = me.bleed0at0g(); - # - # We then subtract the normal drag from the loss due to G and normal drag. - me.b325 = me.bleed32800at25g()-me.b300; - me.b025 = me.bleed0at25g()-me.b000; - me.b300 = 0; - me.b000 = 0; - # - # We now find what the speedloss will be at sealevel and 32800 ft. - me.speedLoss32800 = me.b300 + ((gForce-0)/(25-0))*(me.b325 - me.b300); - me.speedLoss0 = me.b000 + ((gForce-0)/(25-0))*(me.b025 - me.b000); - # - # We then inter/extra-polate that to the currect density-altitude. - me.speedLoss = me.speedLoss0 + ((altitude-0)/(32800-0))*(me.speedLoss32800-me.speedLoss0); - # - # For good measure the result is clamped to below zero. - me.speedLoss = me.clamp(me.speedLoss, -100000, 0); - me.energyBleedKt += me.speedLoss * FPS2KT; - return me.speedLoss; - }, - - bleed32800at0g: func () {#GCD - me.loss_fps = 0 + ((me.last_dt - 0)/(15 - 0))*(-330 - 0); - return me.loss_fps*M2FT; - }, - - bleed32800at25g: func () {#GCD - me.loss_fps = 0 + ((me.last_dt - 0)/(3.5 - 0))*(-240 - 0); - return me.loss_fps*M2FT; - }, - - bleed0at0g: func () {#GCD - me.loss_fps = 0 + ((me.last_dt - 0)/(22 - 0))*(-950 - 0); - return me.loss_fps*M2FT; - }, - - bleed0at25g: func () {#GCD - me.loss_fps = 0 + ((me.last_dt - 0)/(7 - 0))*(-750 - 0); - return me.loss_fps*M2FT; - }, - - flight: func {#GCD - if (me.Tgt != nil and me.Tgt.isValid() == FALSE) { - print(me.type~": Target went away, deleting missile."); - me.del(); - return; - } - me.dt = deltaSec.getValue();#TODO: time since last time nasal timers were called - if (me.dt == 0) { - #FG is likely paused - me.paused = 1; - settimer(func me.flight(), 0.00); - return; - } - #if just called from release() then dt is almost 0 (cannot be zero as we use it to divide with) - # It can also not be too small, then the missile will lag behind aircraft and seem to be fired from behind the aircraft. - #dt = dt/2; - me.elapsed = systime(); - if (me.paused == 1) { - # sim has been unpaused lets make sure dt becomes very small to let elapsed time catch up. - me.paused = 0; - me.elapsed_last = me.elapsed-0.02; - } - me.init_launch = 0; - if (me.elapsed_last != 0) { - #if (getprop("sim/speed-up") == 1) { - me.dt = (me.elapsed - me.elapsed_last)*speedUp.getValue(); - #} else { - # dt = getprop("sim/time/delta-sec")*getprop("sim/speed-up"); - #} - me.init_launch = 1; - if(me.dt <= 0) { - # to prevent pow floating point error in line:cdm = 0.2965 * math.pow(me.speed_m, -1.1506) + me.cd; - # could happen if the OS adjusts the clock backwards - me.dt = 0.00001; - } - } - me.elapsed_last = me.elapsed; - - - me.life_time += me.dt; - - - me.thrust_lbf = me.thrust();# pounds force (lbf) - - - # Get total old speed, thats what we will use in next loop. - me.old_speed_horz_fps = math.sqrt((me.speed_east_fps*me.speed_east_fps)+(me.speed_north_fps*me.speed_north_fps)); - me.old_speed_fps = math.sqrt((me.old_speed_horz_fps*me.old_speed_horz_fps)+(me.speed_down_fps*me.speed_down_fps)); - - me.setRadarProperties(me.old_speed_fps); - - - - # Get air density and speed of sound (fps): - me.rs = me.rho_sndspeed(me.altN.getValue() + me.density_alt_diff); - me.rho = me.rs[0]; - me.sound_fps = me.rs[1]; - - me.max_g_current = me.maxG(me.rho, me.max_g); - - me.speed_m = me.old_speed_fps / me.sound_fps; - - if (me.speed_m > me.maxMach) { - me.maxMach = me.speed_m; - } - - me.Cd = me.drag(me.speed_m); - - me.speed_change_fps = me.speedChange(me.thrust_lbf, me.rho, me.Cd); - - - if (me.last_dt != 0) { - me.speed_change_fps = me.speed_change_fps + me.energyBleed(me.g, me.altN.getValue() + me.density_alt_diff); - } - - # Get target position. - if (me.Tgt != nil) { - me.t_coord = me.Tgt.get_Coord(); - } - - ################### - #### Guidance.##### - ################### - if (me.Tgt != nil and me.free == FALSE and me.guidance != "unguided" - and (me.rail == FALSE or me.rail_passed == TRUE)) { - # - # Here we figure out how to guide, navigate and steer. - # - me.guide(); - me.limitG(); - - me.pitch += me.track_signal_e; - me.hdg += me.track_signal_h; - #printf("%.1f deg elevation command done, new pitch: %.1f deg", me.track_signal_e, pitch_deg); - #printf("%.1f deg bearing command done, new heading: %.1f", me.last_track_h, hdg_deg); - } else { - me.track_signal_e = 0; - me.track_signal_h = 0; - } - me.last_track_e = me.track_signal_e; - me.last_track_h = me.track_signal_h; - - me.new_speed_fps = me.speed_change_fps + me.old_speed_fps; - if (me.new_speed_fps < 0) { - # drag and bleed can theoretically make the speed less than 0, this will prevent that from happening. - me.new_speed_fps = 0.001; - } - - # Break speed change down total speed to North, East and Down components. - me.speed_down_fps = -math.sin(me.pitch * D2R) * me.new_speed_fps; - me.speed_horizontal_fps = math.cos(me.pitch * D2R) * me.new_speed_fps; - me.speed_north_fps = math.cos(me.hdg * D2R) * me.speed_horizontal_fps; - me.speed_east_fps = math.sin(me.hdg * D2R) * me.speed_horizontal_fps; - me.speed_down_fps += g_fps * me.dt; - - if (me.rail == TRUE and me.rail_passed == FALSE) { - # missile still on rail, lets calculate its speed relative to the wind coming in from the aircraft nose. - me.rail_speed_into_wind = me.rail_speed_into_wind + me.speed_change_fps; - } else { - # gravity acc makes the weapon pitch down - me.pitch = math.atan2(-me.speed_down_fps, me.speed_horizontal_fps ) * R2D; - } - - - - #printf("down_s=%.1f grav=%.1f", me.speed_down_fps*me.dt, g_fps * me.dt * !grav_bomb * me.dt); - - if (me.rail == TRUE and me.rail_passed == FALSE) { - me.u = noseAir.getValue();# airstream from nose - #var v = getprop("velocities/vBody-fps");# airstream from side - me.w = belowAir.getValue();# airstream from below - - if (me.rail_forward == TRUE) { - me.pitch = OurPitch.getValue(); - me.opposing_wind = me.u; - me.hdg = OurHdg.getValue(); - } else { - me.pitch = OurPitch.getValue() + me.rail_pitch_deg; - if (me.rail_pitch_deg == 90) { - me.opposing_wind = -me.w; - } else { - # no incoming airstream if not vertical tube - me.opposing_wind = 0; - } - me.hdg = me.Tgt.get_bearing(); - } - - me.speed_on_rail = me.clamp(me.rail_speed_into_wind - me.opposing_wind, 0, 1000000); - me.movement_on_rail = me.speed_on_rail * me.dt; - - me.rail_pos = me.rail_pos + me.movement_on_rail; - if (me.rail_forward == TRUE) { - me.x = me.x - (me.movement_on_rail * FT2M);# negative cause positive is rear in body coordinates - } elsif (me.rail_pitch_deg == 90) { - me.z = me.z + (me.movement_on_rail * FT2M);# positive cause positive is up in body coordinates - } else { - me.x = me.x - (me.movement_on_rail * FT2M); - } - } - - if (me.rail == FALSE or me.rail_passed == TRUE) { - # misssile not on rail, lets move it to next waypoint - me.alt_ft = me.alt_ft - (me.speed_down_fps * me.dt); - me.dist_h_m = me.speed_horizontal_fps * me.dt * FT2M; - me.coord.apply_course_distance(me.hdg, me.dist_h_m); - me.coord.set_alt(me.alt_ft * FT2M); - } else { - # missile on rail, lets move it on the rail - if (me.rail_pitch_deg == 90 or me.rail_forward == TRUE) { - me.coord = me.getGPS(me.x, me.y, me.z, OurPitch.getValue()); - } else { - # kind of a hack, but work - me.coord = me.getGPS(me.x, me.y, me.z, OurPitch.getValue()+me.rail_pitch_deg); - } - me.alt_ft = me.coord.alt() * M2FT; - # find its speed, for used in calc old speed - me.speed_down_fps = -math.sin(me.pitch * D2R) * me.rail_speed_into_wind; - me.speed_horizontal_fps = math.cos(me.pitch * D2R) * me.rail_speed_into_wind; - me.speed_north_fps = math.cos(me.hdg * D2R) * me.speed_horizontal_fps; - me.speed_east_fps = math.sin(me.hdg * D2R) * me.speed_horizontal_fps; - } - if (me.alt_ft > me.maxAlt) { - me.maxAlt = me.alt_ft; - } - - # performance logging: - # - #var q = 0.5 * rho * me.old_speed_fps * me.old_speed_fps; - #setprop("logging/missile/dist-nm", me.ac_init.distance_to(me.coord)*M2NM); - #setprop("logging/missile/alt-m", me.alt_ft * FT2M); - #setprop("logging/missile/speed-m", me.speed_m*1000); - #setprop("logging/missile/drag-lbf", Cd * q * me.eda); - #setprop("logging/missile/thrust-lbf", thrust_lbf); - - me.setFirst(); - - me.latN.setDoubleValue(me.coord.lat()); - me.lonN.setDoubleValue(me.coord.lon()); - me.altN.setDoubleValue(me.alt_ft); - me.pitchN.setDoubleValue(me.pitch); - me.hdgN.setDoubleValue(me.hdg); - - # log missiles to unicsv for visualizing flightpath in Google Earth - # - #setprop("/logging/missile/latitude-deg", me.coord.lat()); - #setprop("/logging/missile/longitude-deg", me.coord.lon()); - #setprop("/logging/missile/altitude-ft", alt_ft); - #setprop("/logging/missile/t-latitude-deg", me.t_coord.lat()); - #setprop("/logging/missile/t-longitude-deg", me.t_coord.lon()); - #setprop("/logging/missile/t-altitude-ft", me.t_coord.alt()*M2FT); - - ############################## - #### Proximity detection.##### - ############################## - if (me.rail == FALSE or me.rail_passed == TRUE) { - if ( me.free == FALSE ) { - # check if the missile overloaded with G force. - me.g = me.steering_speed_G(me.track_signal_e, me.track_signal_h, me.old_speed_fps, me.dt); - - if ( me.g > me.max_g_current and me.init_launch != 0) { - me.free = TRUE; - printf("%s: Missile attempted to pull too many G, it broke.", me.type); - } - } else { - me.g = 0; - } - - me.exploded = me.proximity_detection(); - -# -# Uncomment the following lines to check stats while flying: -# -#printf("Mach %02.2f , time %03.1f s , thrust %03.1f lbf , G-force %02.2f", me.speed_m, me.life_time, me.thrust_lbf, me.g); -#printf("Alt %05.1f ft , distance to target %02.1f NM", me.alt_ft, me.direct_dist_m*M2NM); - - if (me.exploded == TRUE) { - printf("%s max absolute %.2f Mach. Max relative %.2f Mach. Max alt %6d ft.", me.type, me.maxMach, me.maxMach-me.startMach, me.maxAlt); - printf(" Fired at %s from %.1f Mach, %5d ft at %3d NM distance. Flew %0.1f NM.", me.callsign, me.startMach, me.startAlt, me.startDist * M2NM, me.ac_init.direct_distance_to(me.coord)*M2NM); - # We exploded, and start the sound propagation towards the plane - me.sndSpeed = me.sound_fps; - me.sndDistance = 0; - me.elapsed_last = systime(); - if (me.explodeSound == TRUE) { - me.sndPropagate(); - } else { - settimer( func me.del(), 10); - } - return; - } - } else { - me.g = 0; - } - # record coords so we can give the latest nearest position for impact. - me.before_last_coord = geo.Coord.new(me.last_coord); - me.last_coord = geo.Coord.new(me.coord); - if (me.Tgt != nil) { - me.before_last_t_coord = geo.Coord.new(me.last_t_coord); - me.last_t_coord = geo.Coord.new(me.t_coord); - } - - if (me.rail_passed == FALSE and (me.rail == FALSE or me.rail_pos > me.rail_dist_m * M2FT)) { - me.rail_passed = TRUE; - #print("rail passed"); - } - - # consume fuel - if (me.life_time > (me.drop_time + me.stage_1_duration + me.stage_2_duration)) { - me.weight_current = me.weight_launch_lbm - me.weight_fuel_lbm; - } elsif (me.life_time > (me.drop_time + me.stage_1_duration)) { - me.weight_current = me.weight_current - me.fuel_per_sec_2 * me.dt; - } elsif (me.life_time > me.drop_time) { - me.weight_current = me.weight_current - me.fuel_per_sec_1 * me.dt; - } - #printf("weight %0.1f", me.weight_current); - me.mass = me.weight_current / slugs_to_lbm; - - me.last_dt = me.dt; - settimer(func me.flight(), update_loop_time, SIM_TIME); - }, - - setFirst: func() {#GCD - if (me.smoke_prop.getValue() == TRUE) { - if (me.first == TRUE or first_in_air == FALSE) { - # report position over MP for MP animation of smoke trail. - me.first = TRUE; - first_in_air = TRUE; - # using helicopter properties for reporting over MP. To mount this code on a helicopter, you best change that. - setprop("rotors/main/blade[0]/flap-deg", me.coord.lat()); - setprop("rotors/main/blade[1]/flap-deg", me.coord.lon()); - setprop("rotors/main/blade[2]/flap-deg", me.coord.alt()); - } - } elsif (me.first == TRUE and me.life_time > me.drop_time + me.stage_1_duration + me.stage_2_duration) { - # this weapon was reporting its position over MP, but now its fuel has used up. So allow for another to do that. - me.resetFirst(); - } - }, - - resetFirst: func() {#GCD - first_in_air = FALSE; - me.first = FALSE; - setprop("rotors/main/blade[0]/flap-deg", 0); - setprop("rotors/main/blade[1]/flap-deg", 0); - setprop("rotors/main/blade[2]/flap-deg", 0); - }, - - limitG: func () {#GCD - # - # Here will be set the max angle of pitch and the max angle of heading to avoid G overload - # - me.myG = me.steering_speed_G(me.track_signal_e, me.track_signal_h, me.old_speed_fps, me.dt); - if(me.max_g_current < me.myG) - { - me.MyCoef = me.max_G_Rotation(me.track_signal_e, me.track_signal_h, me.old_speed_fps, me.dt, me.max_g_current); - me.track_signal_e = me.track_signal_e * me.MyCoef; - me.track_signal_h = me.track_signal_h * me.MyCoef; - #print(sprintf("G1 %.2f", myG)); - me.myG = me.steering_speed_G(me.track_signal_e, me.track_signal_h, me.old_speed_fps, me.dt); - #print(sprintf("G2 %.2f", myG)~sprintf(" - Coeff %.2f", MyCoef)); - if (me.limitGs == FALSE) { - printf("%s: Missile pulling almost max G: %.1f G", me.type, me.myG); - } - } - if (me.limitGs == TRUE and me.myG > me.max_g_current/2) { - # Save the high performance manouving for later - me.track_signal_e = me.track_signal_e /2; - } - }, - - setRadarProperties: func (new_speed_fps) {#GCD - # - # Set missile radar properties for use in selection view, radar and HUD. - # - me.self = geo.aircraft_position(); - me.ai.getNode("radar/bearing-deg", 1).setDoubleValue(me.self.course_to(me.coord)); - me.ai.getNode("radar/elevation-deg", 1).setDoubleValue(me.getPitch(me.self, me.coord)); - me.ai.getNode("velocities/true-airspeed-kt",1).setDoubleValue(new_speed_fps * FPS2KT); - }, - - rear_aspect: func () {#GCD - # - # If is heat-seeking rear-aspect-only missile, check if it has good view on engine(s) and can keep lock. - # - me.offset = me.aspect(); - - if (me.offset < 45) { - # clear view of engine heat, keep the lock - me.rearAspect = 1; - } else { - # the greater angle away from clear engine view the greater chance of losing lock. - me.offset_away = me.offset - 45; - me.probability = me.offset_away/135; - me.probability = me.probability*2.5;# The higher the factor, the less chance to keep lock. - me.rearAspect = rand() > me.probability; - } - - #print ("RB-24J deviation from full rear-aspect: "~sprintf("%01.1f", offset)~" deg, keep IR lock on engine: "~rearAspect); - - return me.rearAspect;# 1: keep lock, 0: lose lock - }, - - aspect: func () {#GCD - me.rearAspect = 0; - - #var t_dist_m = me.coord.distance_to(me.t_coord); - #var alt_delta_m = me.coord.alt() - me.t_coord.alt(); - me.elev_deg = me.getPitch(me.t_coord, me.coord);#math.atan2( alt_delta_m, t_dist_m ) * R2D; elevation to missile from target aircraft - me.elevation_offset = me.elev_deg - me.Tgt.get_Pitch(); - - me.courseA = me.t_coord.course_to(me.coord); - me.heading_offset = me.courseA - me.Tgt.get_heading(); - - # - while (me.heading_offset < -180) { - me.heading_offset += 360; - } - while (me.heading_offset > 180) { - me.heading_offset -= 360; - } - while (me.elevation_offset < -180) { - me.elevation_offset += 360; - } - while (me.elevation_offset > 180) { - me.elevation_offset -= 360; - } - me.elevation_offset = math.abs(me.elevation_offset); - me.heading_offset = 180 - math.abs(me.heading_offset); - - me.offset = math.max(me.elevation_offset, me.heading_offset); - - return me.offset; - }, - - guide: func() {#GCD - # - # navigation and guidance - # - me.raw_steer_signal_elev = 0; - me.raw_steer_signal_head = 0; - - me.guiding = TRUE; - - # Calculate current target elevation and azimut deviation. - me.t_alt = me.t_coord.alt()*M2FT; - #var t_alt_delta_m = (me.t_alt - me.alt_ft) * FT2M; - me.dist_curr = me.coord.distance_to(me.t_coord); - me.dist_curr_direct = me.coord.direct_distance_to(me.t_coord); - me.t_elev_deg = me.getPitch(me.coord, me.t_coord);#math.atan2( t_alt_delta_m, me.dist_curr ) * R2D; - me.t_course = me.coord.course_to(me.t_coord); - me.curr_deviation_e = me.t_elev_deg - me.pitch; - me.curr_deviation_h = me.t_course - me.hdg; - - #var (t_course, me.dist_curr) = courseAndDistance(me.coord, me.t_coord); - #me.dist_curr = me.dist_curr * NM2M; - - #printf("Altitude above launch platform = %.1f ft", M2FT * (me.coord.alt()-me.ac.alt())); - - while(me.curr_deviation_h < -180) { - me.curr_deviation_h += 360; - } - while(me.curr_deviation_h > 180) { - me.curr_deviation_h -= 360; - } - - me.checkForFlare(); - - me.checkForSun(); - - me.checkForGuidance(); - - me.canSeekerKeepUp(); - - me.cruiseAndLoft(); - - me.APN();# Proportional navigation - - me.track_signal_e = me.raw_steer_signal_elev * !me.free * me.guiding; - me.track_signal_h = me.raw_steer_signal_head * !me.free * me.guiding; - - #printf("%.1f deg elevate command desired", me.track_signal_e); - #printf("%.1f deg heading command desired", me.track_signal_h); - - # record some variables for next loop: - me.dist_last = me.dist_curr; - me.dist_direct_last = me.dist_curr_direct; - me.last_t_course = me.t_course; - me.last_t_elev_deg = me.t_elev_deg; - me.last_cruise_or_loft = me.cruise_or_loft; - }, - - checkForFlare: func () {#GCD - # - # Check for being fooled by flare. - # - if (me.guidance == "heat") { - # - # TODO: Use Richards Emissary for this. - # - me.flareNode = me.Tgt.getFlareNode(); - if (me.flareNode != nil) { - me.flareNumber = me.flareNode.getValue(); - if (me.flareNumber != nil and me.flareNumber != 0) { - if (me.flareNumber != me.lastFlare) { - # target has released a new flare, lets check if it fools us - me.lastFlare = me.flareNumber; - me.aspectDeg = me.aspect() / 180; - me.fooled = rand() < (0.15 + 0.15 * me.aspectDeg); - # 15% chance to be fooled, extra up till 15% chance added if front aspect - if (me.fooled == TRUE) { - # fooled by the flare - print(me.type~": Missile fooled by flare"); - me.free = TRUE; - } else { - print(me.type~": Missile ignored flare"); - } - } - } - } - } - }, - - checkForSun: func () { - if (me.guidance == "heat" and me.sun_power > 0.6) { - # test for heat seeker locked on to sun - me.sun_dev_e = me.getPitch(me.coord, me.sun) - me.pitch; - me.sun_dev_h = me.coord.course_to(me.sun) - me.hdg; - while(me.sun_dev_h < -180) { - me.sun_dev_h += 360; - } - while(me.sun_dev_h > 180) { - me.sun_dev_h -= 360; - } - # now we check if the sun is behind the target, which is the direction the gyro seeker is pointed at: - me.sun_dev = math.sqrt((me.sun_dev_e-me.curr_deviation_e)*(me.sun_dev_e-me.curr_deviation_e)+(me.sun_dev_h-me.curr_deviation_h)*(me.sun_dev_h-me.curr_deviation_h)); - if (me.sun_dev < me.sun_lock) { - print(me.type~": Locked onto sun, lost target. "); - me.lock_on_sun = TRUE; - me.free = TRUE; - } - } - }, - - checkForGuidance: func () {#GCD - if(me.speed_m < me.min_speed_for_guiding) { - # it doesn't guide at lower speeds - me.guiding = FALSE; - if (me.tooLowSpeed == FALSE) { - print(me.type~": Not guiding (too low speed)"); - } - me.tooLowSpeed = TRUE; - } elsif ((me.guidance == "semi-radar" or me.guidance =="laser") and me.is_painted(me.Tgt) == FALSE) { - # if its semi-radar guided and the target is no longer painted - me.guiding = FALSE; - if (me.semiLostLock == FALSE) { - print(me.type~": Not guiding (lost radar reflection, trying to reaquire)"); - } - me.semiLostLock = TRUE; - } elsif ((math.abs(me.curr_deviation_e) > me.max_seeker_dev or math.abs(me.curr_deviation_h) > me.max_seeker_dev) and me.guidance != "gps") { - # target is not in missile seeker view anymore - if (me.curr_deviation_e > me.max_seeker_dev) { - me.viewLost = "Target is above seeker view."; - } elsif (me.curr_deviation_e < (-1 * me.max_seeker_dev)) { - me.viewLost = "Target is below seeker view. "~(me.dist_curr*M2NM)~" NM and "~((me.coord.alt()-me.t_coord.alt())*M2FT)~" ft diff."; - } elsif (me.curr_deviation_h > me.max_seeker_dev) { - me.viewLost = "Target is right of seeker view."; - } else { - me.viewLost = "Target is left of seeker view."; - } - print(me.type~": Target is not in missile seeker view anymore. "~me.viewLost); - me.free = TRUE; - } elsif (me.all_aspect == FALSE and me.rear_aspect() == FALSE) { - me.guiding = FALSE; - if (me.heatLostLock == FALSE) { - print(me.type~": Missile lost heat lock, attempting to reaquire.."); - } - me.heatLostLock = TRUE; - } elsif (me.life_time < me.drop_time) { - me.guiding = FALSE; - } elsif (me.semiLostLock == TRUE) { - print(me.type~": Reaquired radar reflection."); - me.semiLostLock = FALSE; - } elsif (me.heatLostLock == TRUE) { - print(me.type~": Regained heat lock."); - me.heatLostLock = FALSE; - } elsif (me.tooLowSpeed == TRUE) { - print(me.type~": Gained speed and started guiding."); - me.tooLowSpeed = FALSE; - } - }, - - canSeekerKeepUp: func () {#GCD - if (me.last_deviation_e != nil and (me.guidance == "heat" or me.guidance == "vision")) { - # calculate if the seeker can keep up with the angular change of the target - # - # missile own movement is subtracted from this change due to seeker being on gyroscope - # - me.dve_dist = me.curr_deviation_e - me.last_deviation_e + me.last_track_e; - me.dvh_dist = me.curr_deviation_h - me.last_deviation_h + me.last_track_h; - me.deviation_per_sec = math.sqrt(me.dve_dist*me.dve_dist+me.dvh_dist*me.dvh_dist)/me.dt; - - if (me.deviation_per_sec > me.angular_speed) { - #print(sprintf("last-elev=%.1f", me.last_deviation_e)~sprintf(" last-elev-adj=%.1f", me.last_track_e)); - #print(sprintf("last-head=%.1f", me.last_deviation_h)~sprintf(" last-head-adj=%.1f", me.last_track_h)); - # lost lock due to angular speed limit - printf("%s: %.1f deg/s too fast angular change for seeker head.", me.type, me.deviation_per_sec); - me.free = TRUE; - } - } - - me.last_deviation_e = me.curr_deviation_e; - me.last_deviation_h = me.curr_deviation_h; - }, - - cruiseAndLoft: func () {#GCD - # - # cruise, loft, cruise-missile - # - me.loft_angle = 15;# notice Shinobi used 26.5651 degs, but Raider1 found a source saying 10-20 degs. - me.cruise_or_loft = FALSE; - me.time_before_snap_up = me.drop_time * 3; - me.limitGs = FALSE; - - if(me.loft_alt != 0 and me.snapUp == FALSE) { - # this is for Air to ground/sea cruise-missile (SCALP, Sea-Eagle, Taurus, Tomahawk, RB-15...) - - # detect terrain for use in terrain following - me.nextGroundElevationMem[1] -= 1; - me.geoPlus2 = me.nextGeoloc(me.coord.lat(), me.coord.lon(), me.hdg, me.old_speed_fps, me.dt*5); - me.geoPlus3 = me.nextGeoloc(me.coord.lat(), me.coord.lon(), me.hdg, me.old_speed_fps, me.dt*10); - me.geoPlus4 = me.nextGeoloc(me.coord.lat(), me.coord.lon(), me.hdg, me.old_speed_fps, me.dt*20); - me.e1 = geo.elevation(me.coord.lat(), me.coord.lon());# This is done, to make sure is does not decline before it has passed obstacle. - me.e2 = geo.elevation(me.geoPlus2.lat(), me.geoPlus2.lon());# This is the main one. - me.e3 = geo.elevation(me.geoPlus3.lat(), me.geoPlus3.lon());# This is an extra, just in case there is an high cliff it needs longer time to climb. - me.e4 = geo.elevation(me.geoPlus4.lat(), me.geoPlus4.lon()); - if (me.e1 != nil) { - me.nextGroundElevation = me.e1; - } else { - print(me.type~": nil terrain, blame terrasync! Cruise-missile keeping altitude."); - } - if (me.e2 != nil and me.e2 > me.nextGroundElevation) { - me.nextGroundElevation = me.e2; - if (me.e2 > me.nextGroundElevationMem[0] or me.nextGroundElevationMem[1] < 0) { - me.nextGroundElevationMem[0] = me.e2; - me.nextGroundElevationMem[1] = 5; - } - } - if (me.nextGroundElevationMem[0] > me.nextGroundElevation) { - me.nextGroundElevation = me.nextGroundElevationMem[0]; - } - if (me.e3 != nil and me.e3 > me.nextGroundElevation) { - me.nextGroundElevation = me.e3; - } - if (me.e4 != nil and me.e4 > me.nextGroundElevation) { - me.nextGroundElevation = me.e4; - } - - me.Daground = 0;# zero for sealevel in case target is ship. Don't shoot A/S missiles over terrain. :) - if(me.Tgt.get_type() == SURFACE or me.follow == TRUE) { - me.Daground = me.nextGroundElevation * M2FT; - } - me.loft_alt_curr = me.loft_alt; - if (me.dist_curr < me.old_speed_fps * 4 * FT2M and me.dist_curr > me.old_speed_fps * 2.5 * FT2M) { - # the missile lofts a bit at the end to avoid APN to slam it into ground before target is reached. - # end here is between 2.5-4 seconds - me.loft_alt_curr = me.loft_alt*2; - } - if (me.dist_curr > me.old_speed_fps * 2.5 * FT2M) {# need to give the missile time to do final navigation - # it's 1 or 2 seconds for this kinds of missiles... - me.t_alt_delta_ft = (me.loft_alt_curr + me.Daground - me.alt_ft); - #print("var t_alt_delta_m : "~t_alt_delta_m); - if(me.loft_alt_curr + me.Daground > me.alt_ft) { - # 200 is for a very short reaction to terrain - #print("Moving up"); - me.raw_steer_signal_elev = -me.pitch + math.atan2(me.t_alt_delta_ft, me.old_speed_fps * me.dt * 5) * R2D; - } else { - # that means a dive angle of 22.5° (a bit less - # coz me.alt is in feet) (I let this alt in feet on purpose (more this figure is low, more the future pitch is high) - #print("Moving down"); - me.slope = me.clamp(me.t_alt_delta_ft / 300, -5, 0);# the lower the desired alt is, the steeper the slope. - me.raw_steer_signal_elev = -me.pitch + me.clamp(math.atan2(me.t_alt_delta_ft, me.old_speed_fps * me.dt * 5) * R2D, me.slope, 0); - } - me.cruise_or_loft = TRUE; - } elsif (me.dist_curr > 500) { - # we put 9 feets up the target to avoid ground at the - # last minute... - #print("less than 1000 m to target"); - #me.raw_steer_signal_elev = -me.pitch + math.atan2(t_alt_delta_m + 100, me.dist_curr) * R2D; - #me.cruise_or_loft = 1; - } else { - #print("less than 500 m to target"); - } - if (me.cruise_or_loft == TRUE) { - #print(" pitch "~me.pitch~" + me.raw_steer_signal_elev "~me.raw_steer_signal_elev); - } - } elsif (me.rail == TRUE and me.rail_forward == FALSE and me.rotate_token == FALSE) { - # tube launched missile turns towards target - - me.raw_steer_signal_elev = -me.pitch + me.t_elev_deg; - #print("Turning, desire "~me.t_elev_deg~" degs pitch."); - me.cruise_or_loft = TRUE; - me.limitGs = TRUE; - if (math.abs(me.curr_deviation_e) < 7.5) { - me.rotate_token = TRUE; - #print("Is last turn, snap-up/PN takes it from here..") - } - } elsif (me.snapUp == TRUE and me.t_elev_deg > -25 and me.dist_curr * M2NM > 10 - and me.t_elev_deg < me.loft_angle #and me.t_elev_deg > -7.5 - and me.dive_token == FALSE) { - # lofting: due to target is more than 10 miles out and we havent reached - # our desired cruising alt, and the elevation to target is less than lofting angle. - # The -7.5 limit, is so the seeker don't lose track of target when lofting. - if (me.life_time < me.time_before_snap_up and me.coord.alt() * M2FT < me.loft_alt) { - #print("preparing for lofting"); - me.cruise_or_loft = TRUE; - } elsif (me.coord.alt() * M2FT < me.loft_alt) { - me.raw_steer_signal_elev = -me.pitch + me.loft_angle; - me.limitGs = TRUE; - #print(sprintf("Lofting %.1f degs, dev is %.1f", me.loft_angle, me.raw_steer_signal_elev)); - } else { - me.dive_token = TRUE; - #print("Stopped lofting"); - } - me.cruise_or_loft = TRUE; - } elsif (me.snapUp == TRUE and me.coord.alt() > me.t_coord.alt() and me.last_cruise_or_loft == TRUE - and me.t_elev_deg > -25 and me.dist_curr * M2NM > 10) { - # cruising: keeping altitude since target is below and more than -45 degs down - - me.ratio = (g_fps * me.dt)/me.old_speed_fps; - me.attitude = 0; - - if (me.ratio < 1 and me.ratio > -1) { - me.attitude = math.asin(me.ratio)*R2D; - } - - me.raw_steer_signal_elev = -me.pitch + me.attitude; - #print("Cruising, desire "~me.attitude~" degs pitch."); - me.cruise_or_loft = TRUE; - me.limitGs = TRUE; - me.dive_token = TRUE; - } elsif (me.last_cruise_or_loft == TRUE and math.abs(me.curr_deviation_e) > 7.5 and me.life_time > me.time_before_snap_up) { - # after cruising, point the missile in the general direction of the target, before APN starts guiding. - #print("Rotating toward target"); - me.raw_steer_signal_elev = me.curr_deviation_e; - me.cruise_or_loft = TRUE; - #me.limitGs = TRUE; - } - }, - - APN: func () {#GCD - # - # augmented proportional navigation - # - if (me.guiding == TRUE and me.free == FALSE and me.dist_last != nil and me.last_dt != 0) { - # augmented proportional navigation for heading # - ################################################# - - if (me.navigation == "direct") { - me.raw_steer_signal_head = me.curr_deviation_h; - if (me.cruise_or_loft == FALSE) { - me.raw_steer_signal_elev = me.curr_deviation_e; - } - return; - } elsif (me.navigation == "PN") { - me.apn = 0; - } else { - me.apn = 1; - } - - me.horz_closing_rate_fps = me.clamp(((me.dist_last - me.dist_curr)*M2FT)/me.dt, 0.1, 1000000);#clamped due to cruise missiles that can fly slower than target. - #printf("Horz closing rate: %5d ft/sec", me.horz_closing_rate_fps); - me.proportionality_constant = 3; - - me.c_dv = me.t_course-me.last_t_course; - while(me.c_dv < -180) { - me.c_dv += 360; - } - while(me.c_dv > 180) { - me.c_dv -= 360; - } - - me.line_of_sight_rate_rps = (D2R*me.c_dv)/me.dt; - - #printf("LOS rate: %.4f rad/s", line_of_sight_rate_rps); - - #if (me.before_last_t_coord != nil) { - # var t_heading = me.before_last_t_coord.course_to(me.t_coord); - # var t_dist = me.before_last_t_coord.distance_to(me.t_coord); - # var t_dist_dir = me.before_last_t_coord.direct_distance_to(me.t_coord); - # var t_climb = me.t_coord.alt() - me.before_last_t_coord.alt(); - # var t_horz_speed = (t_dist*M2FT)/(me.dt+me.last_dt); - # var t_speed = (t_dist_dir*M2FT)/(me.dt+me.last_dt); - #} else { - # var t_heading = me.last_t_coord.course_to(me.t_coord); - # var t_dist = me.last_t_coord.distance_to(me.t_coord); - # var t_dist_dir = me.last_t_coord.direct_distance_to(me.t_coord); - # var t_climb = me.t_coord.alt() - me.last_t_coord.alt(); - # var t_horz_speed = (t_dist*M2FT)/me.dt; - # var t_speed = (t_dist_dir*M2FT)/me.dt; - #} - - #var t_pitch = math.atan2(t_climb,t_dist)*R2D; - - - # calculate target acc as normal to LOS line: - me.t_heading = me.Tgt.get_heading(); - me.t_pitch = me.Tgt.get_Pitch(); - me.t_speed = me.Tgt.get_Speed()*KT2FPS;#true airspeed - - #if (me.last_t_coord.direct_distance_to(me.t_coord) != 0) { - # # taking sideslip and AoA into consideration: - # me.t_heading = me.last_t_coord.course_to(me.t_coord); - # me.t_climb = me.t_coord.alt() - me.last_t_coord.alt(); - # me.t_dist = me.last_t_coord.distance_to(me.t_coord); - # me.t_pitch = math.atan2(me.t_climb, me.t_dist) * R2D; - #} elsif (me.Tgt.get_Speed() > 25) { - # target position was not updated since last loop. - # to avoid confusing the navigation, we just fly - # straight. - #print("not updated"); - # return; - #} - - - - me.t_horz_speed = math.abs(math.cos(me.t_pitch*D2R)*me.t_speed); - me.t_LOS_norm_head = me.t_course + 90; - me.t_LOS_norm_speed = math.cos((me.t_LOS_norm_head - me.t_heading)*D2R)*me.t_horz_speed; - - if (me.last_t_norm_speed == nil) { - me.last_t_norm_speed = me.t_LOS_norm_speed; - } - - me.t_LOS_norm_acc = (me.t_LOS_norm_speed - me.last_t_norm_speed)/me.dt; - - me.last_t_norm_speed = me.t_LOS_norm_speed; - - # acceleration perpendicular to instantaneous line of sight in feet/sec^2 - me.acc_sideways_ftps2 = me.proportionality_constant*me.line_of_sight_rate_rps*me.horz_closing_rate_fps+me.apn*me.proportionality_constant*me.t_LOS_norm_acc/2; - #printf("horz acc = %.1f + %.1f", proportionality_constant*line_of_sight_rate_rps*horz_closing_rate_fps, proportionality_constant*t_LOS_norm_acc/2); - - # now translate that sideways acc to an angle: - me.velocity_vector_length_fps = me.old_speed_horz_fps; - me.commanded_sideways_vector_length_fps = me.acc_sideways_ftps2*me.dt; - me.raw_steer_signal_head = math.atan2(me.commanded_sideways_vector_length_fps, me.velocity_vector_length_fps)*R2D; - - #printf("Proportional lead: %0.1f deg horz", -(me.curr_deviation_h-me.raw_steer_signal_head)); - - #print(sprintf("LOS-rate=%.2f rad/s - closing-rate=%.1f ft/s",line_of_sight_rate_rps,horz_closing_rate_fps)); - #print(sprintf("commanded-perpendicular-acceleration=%.1f ft/s^2", acc_sideways_ftps2)); - #print(sprintf("horz leading by %.1f deg, commanding %.1f deg", me.curr_deviation_h, me.raw_steer_signal_head)); - - if (me.cruise_or_loft == FALSE) {# and me.last_cruise_or_loft == FALSE - # augmented proportional navigation for elevation # - ################################################### - #print(me.navigation~" in fully control"); - me.vert_closing_rate_fps = me.clamp(((me.dist_direct_last - me.dist_curr_direct)*M2FT)/me.dt, 0.1, 1000000); - #printf("Vert closing rate: %5d ft/sec", me.vert_closing_rate_fps); - me.line_of_sight_rate_up_rps = (D2R*(me.t_elev_deg-me.last_t_elev_deg))/me.dt; - - # calculate target acc as normal to LOS line: (up acc is positive) - me.t_approach_bearing = me.t_course + 180; - - - # used to do this with trigonometry, but vector math is simpler to understand: (they give same result though) - me.t_LOS_elev_norm_speed = me.scalarProj(me.t_heading,me.t_pitch,me.t_speed,me.t_approach_bearing,me.t_elev_deg*-1 +90); - - if (me.last_t_elev_norm_speed == nil) { - me.last_t_elev_norm_speed = me.t_LOS_elev_norm_speed; - } - - me.t_LOS_elev_norm_acc = (me.t_LOS_elev_norm_speed - me.last_t_elev_norm_speed)/me.dt; - me.last_t_elev_norm_speed = me.t_LOS_elev_norm_speed; - - me.acc_upwards_ftps2 = me.proportionality_constant*me.line_of_sight_rate_up_rps*me.vert_closing_rate_fps+me.apn*me.proportionality_constant*me.t_LOS_elev_norm_acc/2; - me.velocity_vector_length_fps = me.old_speed_fps; - me.commanded_upwards_vector_length_fps = me.acc_upwards_ftps2*me.dt; - - me.raw_steer_signal_elev = math.atan2(me.commanded_upwards_vector_length_fps, me.velocity_vector_length_fps)*R2D; - - # now compensate for the predicted gravity drop of attitude: - me.attitudePN = math.atan2(-(me.speed_down_fps+g_fps * me.dt), me.speed_horizontal_fps ) * R2D; - me.gravComp = me.pitch - me.attitudePN; - #printf("Gravity compensation %0.2f degs", me.gravComp); - me.raw_steer_signal_elev += me.gravComp; - - #printf("Proportional lead: %0.1f deg elev", -(me.curr_deviation_e-me.raw_steer_signal_elev)); - } - } - }, - - scalarProj: func (head, pitch, magn, projHead, projPitch) {#GCD - head = head*D2R; - pitch = pitch*D2R; - projHead = projHead*D2R; - projPitch = projPitch*D2R; - - # Convert the 2 polar vectors to cartesian - me.ax = magn * math.cos(pitch) * math.cos(-head); - me.ay = magn * math.cos(pitch) * math.sin(-head); - me.az = magn * math.sin(pitch); - - me.bx = 1 * math.cos(projPitch) * math.cos(-projHead); - me.by = 1 * math.cos(projPitch) * math.sin(-projHead); - me.bz = 1 * math.sin(projPitch); - - # the dot product is the scalar projection. - me.result = (me.ax * me.bx + me.ay*me.by+me.az*me.bz)/1; - return me.result; - }, - - map: func (value, leftMin, leftMax, rightMin, rightMax) { - # Figure out how 'wide' each range is - var leftSpan = leftMax - leftMin; - var rightSpan = rightMax - rightMin; - - # Convert the left range into a 0-1 range (float) - var valueScaled = (value - leftMin) / leftSpan; - - # Convert the 0-1 range into a value in the right range. - return rightMin + (valueScaled * rightSpan); - }, - - proximity_detection: func {#GCD - - ####Ground interaction - me.ground = geo.elevation(me.coord.lat(), me.coord.lon()); - if(me.ground != nil) - { - if(me.ground > me.coord.alt()) { - me.event = "exploded"; - if(me.life_time < me.arming_time) { - me.event = "landed disarmed"; - } - if ((me.Tgt != nil and me.direct_dist_m != nil) or me.Tgt == nil) { - me.explode("Hit terrain.", me.event); - return TRUE; - } - } - } - - if (me.Tgt != nil) { - me.cur_dir_dist_m = me.coord.direct_distance_to(me.t_coord); - # Get current direct distance. - if ( me.direct_dist_m != nil and me.life_time > me.arming_time) { - #print("distance to target_m = "~cur_dir_dist_m~" prev_distance to target_m = "~me.direct_dist_m); - if ( me.cur_dir_dist_m > me.direct_dist_m and me.cur_dir_dist_m < 250) { - #print("passed target"); - # Distance to target increase, trigger explosion. - me.explode("Passed target."); - return TRUE; - } - if (me.life_time > me.selfdestruct_time) { - me.explode("Selfdestructed."); - return TRUE; - } - } - me.direct_dist_m = me.cur_dir_dist_m; - } - return FALSE; - }, - - explode: func (reason, event = "exploded") { - # Get missile relative position to the target at last frame. - #var t_bearing_deg = me.last_t_coord.course_to(me.last_coord); - #var t_delta_alt_m = me.last_coord.alt() - me.last_t_coord.alt(); - #var new_t_alt_m = me.t_coord.alt() + t_delta_alt_m; - #var t_dist_m = me.direct_dist_m; - - if (me.lock_on_sun == TRUE) { - reason = "Locked onto sun."; - } elsif (me.fooled == TRUE) { - reason = "Fooled by flare."; - } - - var explosion_coord = me.last_coord; - if (me.Tgt != nil) { - var min_distance = me.direct_dist_m; - - #print("min1 "~min_distance); - #print("last_t to t : "~me.last_t_coord.direct_distance_to(me.t_coord)); - #print("last to current: "~me.last_coord.direct_distance_to(me.coord)); - for (var i = 0.00; i <= 1; i += 0.025) { - var t_coord = me.interpolate(me.last_t_coord, me.t_coord, i); - var coord = me.interpolate(me.last_coord, me.coord, i); - var dist = coord.direct_distance_to(t_coord); - if (dist < min_distance) { - min_distance = dist; - explosion_coord = coord; - } - } - #print("min2 "~min_distance); - if (me.before_last_coord != nil and me.before_last_t_coord != nil) { - for (var i = 0.00; i <= 1; i += 0.025) { - var t_coord = me.interpolate(me.before_last_t_coord, me.last_t_coord, i); - var coord = me.interpolate(me.before_last_coord, me.last_coord, i); - var dist = coord.direct_distance_to(t_coord); - if (dist < min_distance) { - min_distance = dist; - explosion_coord = coord; - } - } - } - } - me.coord = explosion_coord; - #print("min3 "~min_distance); - - # Create impact coords from this previous relative position applied to target current coord. - #me.t_coord.apply_course_distance(t_bearing_deg, t_dist_m); - #me.t_coord.set_alt(new_t_alt_m); - var wh_mass = event == "exploded"?me.weight_whead_lbm:0;#will report 0 mass if did not have time to arm - #print("FOX2: me.direct_dist_m = ", me.direct_dist_m, " time ",getprop("sim/time/elapsed-sec")); - impact_report(me.coord, wh_mass, "munition", me.type); # pos, alt, mass_slug,(speed_mps) - - if (me.Tgt != nil) { - var phrase = sprintf( me.type~" "~event~": %01.1f", min_distance) ~ " meters from: " ~ me.callsign; - print(phrase~" Reason: "~reason~sprintf(" time %.1f", me.life_time)); - if (min_distance < me.reportDist) { - me.sendMessage(phrase); - } else { - me.sendMessage(me.type~" missed "~me.callsign~": "~reason); - } - } - - me.ai.getNode("valid", 1).setBoolValue(0); - if (event == "exploded") { - me.animate_explosion(); - me.explodeSound = TRUE; - } else { - me.animate_dud(); - me.explodeSound = FALSE; - } - me.Tgt = nil; - }, - - sendMessage: func (str) {#GCD - if (getprop("payload/armament/msg")) { - defeatSpamFilter(str); - } else { - setprop("/sim/messages/atc", str); - } - }, - - interpolate: func (start, end, fraction) {#GCD - me.xx = (start.x()*(1-fraction)+end.x()*fraction); - me.yy = (start.y()*(1-fraction)+end.y()*fraction); - me.zz = (start.z()*(1-fraction)+end.z()*fraction); - - me.cc = geo.Coord.new(); - me.cc.set_xyz(me.xx,me.yy,me.zz); - - return me.cc; - }, - - getPitch: func (coord1, coord2) {#GCD - #pitch from c1 to c2 - me.coord3 = geo.Coord.new(coord1); - me.coord3.set_alt(coord2.alt()); - me.d12 = coord1.direct_distance_to(coord2); - me.d32 = me.coord3.direct_distance_to(coord2); - if (me.d12 > 0.01) { - me.altDi = coord1.alt()-me.coord3.alt(); - me.yyy = R2D * math.acos((math.pow(me.d12, 2)+math.pow(me.altDi,2)-math.pow(me.d32, 2))/(2 * me.d12 * me.altDi)); - me.pitchC = -1* (90 - me.yyy); - return me.pitchC; - } else{ - # arccos wont like if the coord are the same - return 0; - } - - }, - - # aircraft searching for lock - search: func {#GCD - if ( me.status == MISSILE_FLYING ) { - me.SwSoundVol.setDoubleValue(0); - me.SwSoundOnOff.setBoolValue(FALSE); - return; - } elsif ( me.status == MISSILE_STANDBY ) { - # Stand by. - me.SwSoundVol.setDoubleValue(0); - me.SwSoundOnOff.setBoolValue(FALSE); - me.trackWeak = 1; - return; - } elsif ( me.status > MISSILE_SEARCH) { - # Locked or fired. - return; - } elsif (me.deleted == TRUE) { - return; - } - #print("search"); - # search. - if (1==1 or contact != me.Tgt) { - #print("search2"); - if (contact != nil and contact.isValid() == TRUE and - ( (contact.get_type() == SURFACE and me.target_gnd == TRUE) - or (contact.get_type() == AIR and me.target_air == TRUE) - or (contact.get_type() == MARINE and me.target_sea == TRUE))) { - #print("search3"); - me.tagt = contact; # In the radar range and horizontal field. - me.rng = me.tagt.get_range(); - me.total_elev = deviation_normdeg(OurPitch.getValue(), me.tagt.getElevation()); # deg. - me.total_horiz = deviation_normdeg(OurHdg.getValue(), me.tagt.get_bearing()); # deg. - # Check if in range and in the (square shaped here) seeker FOV. - me.abs_total_elev = math.abs(me.total_elev); - me.abs_dev_deg = math.abs(me.total_horiz); - if (((me.guidance != "semi-radar" and me.guidance != "laser") or me.is_painted(me.tagt) == TRUE) - and me.rng < me.max_detect_rng and me.abs_total_elev < me.fcs_fov and me.abs_dev_deg < me.fcs_fov ) { - #print("search4"); - me.status = MISSILE_LOCK; - me.SwSoundOnOff.setBoolValue(TRUE); - me.SwSoundVol.setDoubleValue(me.vol_track_weak); - me.trackWeak = 1; - me.Tgt = me.tagt; - - me.callsign = me.Tgt.get_Callsign(); - - me.time = props.globals.getNode("/sim/time/elapsed-sec", 1).getValue(); - me.update_track_time = me.time; - - settimer(func me.update_lock(), 0.1); - return; - } else { - me.Tgt = nil; - } - } else { - me.Tgt = nil; - } - } - me.SwSoundVol.setDoubleValue(me.vol_search); - me.SwSoundOnOff.setBoolValue(TRUE); - me.trackWeak = 1; - settimer(func me.search(), 0.1); - }, - - update_lock: func() {#GCD - # - # Missile locked on target - # - if ( me.Tgt == nil or me.status == MISSILE_FLYING) { - return TRUE; - } - if (me.status == MISSILE_SEARCH) { - # Status = searching. - #print("search commanded"); - me.return_to_search(); - return TRUE; - } elsif ( me.status == MISSILE_STANDBY ) { - # Status = stand-by. - me.reset_seeker(); - me.SwSoundOnOff.setBoolValue(FALSE); - me.SwSoundVol.setDoubleValue(0); - me.trackWeak = 1; - return TRUE; - } elsif (!me.Tgt.isValid()) { - # Lost of lock due to target disapearing: - # return to search mode. - #print("invalid"); - me.return_to_search(); - return TRUE; - } elsif (me.deleted == TRUE) { - return; - } - #print("lock"); - # Time interval since lock time or last track loop. - - if (me.status == MISSILE_LOCK) { - # Status = locked. Get target position relative to our aircraft. - me.curr_deviation_e = - deviation_normdeg(OurPitch.getValue(), me.Tgt.getElevation()); - me.curr_deviation_h = - deviation_normdeg(OurHdg.getValue(), me.Tgt.get_bearing()); - } - - me.time = props.globals.getNode("/sim/time/elapsed-sec", 1).getValue(); - - # Compute HUD reticle position. - if ( use_fg_default_hud == TRUE and me.status == MISSILE_LOCK ) { - var h_rad = (90 - me.curr_deviation_h) * D2R; - var e_rad = (90 - me.curr_deviation_e) * D2R; - var devs = develev_to_devroll(h_rad, e_rad); - var combined_dev_deg = devs[0]; - var combined_dev_length = devs[1]; - var clamped = devs[2]; - if ( clamped ) { SW_reticle_Blinker.blink();} - else { SW_reticle_Blinker.cont();} - HudReticleDeg.setDoubleValue(combined_dev_deg); - HudReticleDev.setDoubleValue(combined_dev_length); - } - if (me.status != MISSILE_STANDBY ) { - me.in_view = me.check_t_in_fov(); - if (me.in_view == FALSE) { - #print("out of view"); - me.return_to_search(); - return TRUE; - } - # We are not launched yet: update_track() loops by itself at 10 Hz. - me.dist = geo.aircraft_position().direct_distance_to(me.Tgt.get_Coord()); - if (me.time - me.update_track_time > 1 and me.dist != nil and me.dist > (me.min_dist * NM2M)) { - # after 1 second we get solid track if target is further than minimum distance. - me.SwSoundOnOff.setBoolValue(TRUE); - me.SwSoundVol.setDoubleValue(me.vol_track); - me.trackWeak = 0; - } else { - me.SwSoundOnOff.setBoolValue(TRUE); - me.SwSoundVol.setDoubleValue(me.vol_track_weak); - me.trackWeak = 1; - } - if (contact == nil or (contact.getUnique() != nil and me.Tgt.getUnique() != nil and contact.getUnique() != me.Tgt.getUnique())) { - #print("oops "); - me.return_to_search(); - return TRUE; - } - settimer(func me.update_lock(), 0.1); - } - return TRUE; - }, - - return_to_search: func {#GCD - me.status = MISSILE_SEARCH; - me.Tgt = nil; - me.SwSoundOnOff.setBoolValue(TRUE); - me.SwSoundVol.setDoubleValue(me.vol_search); - me.trackWeak = 1; - me.reset_seeker(); - settimer(func me.search(), 0.1); - }, - - check_t_in_fov: func {#GCD - me.total_elev = deviation_normdeg(OurPitch.getValue(), me.Tgt.getElevation()); # deg. - me.total_horiz = deviation_normdeg(OurHdg.getValue(), me.Tgt.get_bearing()); # deg. - # Check if in range and in the (square shaped here) seeker FOV. - me.abs_total_elev = math.abs(me.total_elev); - me.abs_dev_deg = math.abs(me.total_horiz); - if (me.abs_total_elev < me.fcs_fov and me.abs_dev_deg < me.fcs_fov and me.Tgt.get_range() < me.max_detect_rng) { - return TRUE; - } - # Target out of FOV or range while still not launched, return to search loop. - return FALSE; - }, - - is_painted: func (target) {#GCD - if(target != nil and target.isPainted() != nil and target.isPainted() == TRUE) { - return TRUE; - } - return FALSE; - }, - - reset_seeker: func {#GCD - settimer(func { HudReticleDeg.setDoubleValue(0) }, 2); - interpolate(HudReticleDev, 0, 2); - }, - - clamp_min_max: func (v, mm) { - if ( v < -mm ) { - v = -mm; - } elsif ( v > mm ) { - v = mm; - } - return(v); - }, - - clamp: func(v, min, max) { v < min ? min : v > max ? max : v }, - - animation_flags_props: func { - # Create animation flags properties. - var path_base = "payload/armament/"~me.type_lc~"/flags/"; - - var msl_path = path_base~"msl-id-" ~ me.ID; - me.msl_prop = props.globals.initNode( msl_path, TRUE, "BOOL", TRUE); - me.msl_prop.setBoolValue(TRUE);# this is cause it might already exist, and so need to force value - - var smoke_path = path_base~"smoke-id-" ~ me.ID; - me.smoke_prop = props.globals.initNode( smoke_path, FALSE, "BOOL", TRUE); - - var explode_path = path_base~"explode-id-" ~ me.ID; - me.explode_prop = props.globals.initNode( explode_path, FALSE, "BOOL", TRUE); - - var explode_smoke_path = path_base~"explode-smoke-id-" ~ me.ID; - me.explode_smoke_prop = props.globals.initNode( explode_smoke_path, FALSE, "BOOL", TRUE); - - var explode_sound_path = "payload/armament/flags/explode-sound-on-" ~ me.ID;; - me.explode_sound_prop = props.globals.initNode( explode_sound_path, FALSE, "BOOL", TRUE); - - var explode_sound_vol_path = "payload/armament/flags/explode-sound-vol-" ~ me.ID;; - me.explode_sound_vol_prop = props.globals.initNode( explode_sound_vol_path, 0, "DOUBLE", TRUE); - }, - - animate_explosion: func {#GCD - # - # a last position update to where the explosion happened: - # - me.latN.setDoubleValue(me.coord.lat()); - me.lonN.setDoubleValue(me.coord.lon()); - me.altN.setDoubleValue(me.coord.alt()*M2FT); - me.pitchN.setDoubleValue(0);# this will make explosions from cluster bombs (like M90) align to ground 'sorta'. - me.msl_prop.setBoolValue(FALSE); - me.smoke_prop.setBoolValue(FALSE); - me.explode_prop.setBoolValue(TRUE); - settimer( func me.explode_prop.setBoolValue(FALSE), 0.5 ); - settimer( func me.explode_smoke_prop.setBoolValue(TRUE), 0.5 ); - settimer( func me.explode_smoke_prop.setBoolValue(FALSE), 3 ); - }, - - animate_dud: func {#GCD - # - # a last position update to where the impact happened: - # - me.latN.setDoubleValue(me.coord.lat()); - me.lonN.setDoubleValue(me.coord.lon()); - me.altN.setDoubleValue(me.coord.alt()*M2FT); - #me.pitchN.setDoubleValue(0); uncomment this to let it lie flat on ground, instead of sticking its nose in it. - me.smoke_prop.setBoolValue(FALSE); - }, - - sndPropagate: func { - var dt = deltaSec.getValue(); - if (dt == 0) { - #FG is likely paused - settimer(func me.sndPropagate(), 0.01); - return; - } - #dt = update_loop_time; - var elapsed = systime(); - if (me.elapsed_last != 0) { - dt = (elapsed - me.elapsed_last) * speedUp.getValue(); - } - me.elapsed_last = elapsed; - - me.ac = geo.aircraft_position(); - var distance = me.coord.direct_distance_to(me.ac); - - me.sndDistance = me.sndDistance + (me.sndSpeed * dt) * FT2M; - if(me.sndDistance > distance) { - var volume = math.pow(2.71828,(-.00025*(distance-1000))); - #print("explosion heard "~distance~"m vol:"~volume); - me.explode_sound_vol_prop.setDoubleValue(volume); - me.explode_sound_prop.setBoolValue(1); - settimer( func me.explode_sound_prop.setBoolValue(0), 3); - settimer( func me.del(), 4); - return; - } elsif (me.sndDistance > 5000) { - settimer(func { me.del(); }, 4 ); - } else { - settimer(func me.sndPropagate(), 0.05); - return; - } - }, - - steering_speed_G: func(steering_e_deg, steering_h_deg, s_fps, dt) {#GCD - # Get G number from steering (e, h) in deg, speed in ft/s. - me.steer_deg = math.sqrt((steering_e_deg*steering_e_deg) + (steering_h_deg*steering_h_deg)); - - # next speed vector - me.vector_next_x = math.cos(me.steer_deg*D2R)*s_fps; - me.vector_next_y = math.sin(me.steer_deg*D2R)*s_fps; - - # present speed vector - me.vector_now_x = s_fps; - me.vector_now_y = 0; - - # subtract the vectors from each other - me.dv = math.sqrt((me.vector_now_x - me.vector_next_x)*(me.vector_now_x - me.vector_next_x)+(me.vector_now_y - me.vector_next_y)*(me.vector_now_y - me.vector_next_y)); - - # calculate g-force - # dv/dt=a - me.g = (me.dv/dt) / g_fps; - - return me.g; - }, - - max_G_Rotation: func(steering_e_deg, steering_h_deg, s_fps, dt, gMax) {#GCD - me.guess = 1; - me.coef = 1; - me.lastgoodguess = 1; - - for(var i=1;i<25;i+=1){ - me.coef = me.coef/2; - me.new_g = me.steering_speed_G(steering_e_deg*me.guess, steering_h_deg*me.guess, s_fps, dt); - if (me.new_g < gMax) { - me.lastgoodguess = me.guess; - me.guess = me.guess + me.coef; - } else { - me.guess = me.guess - me.coef; - } - } - return me.lastgoodguess; - }, - - nextGeoloc: func(lat, lon, heading, speed, dt, alt=100) { - # lng & lat & heading, in degree, speed in fps - # this function should send back the futures lng lat - me.distanceN = speed * dt * FT2M; # should be a distance in meters - #print("distance ", distance); - # much simpler than trigo - me.NextGeo = geo.Coord.new().set_latlon(lat, lon, alt); - me.NextGeo.apply_course_distance(heading, me.distanceN); - return me.NextGeo; - }, - - rho_sndspeed: func(altitude) { - # Calculate density of air: rho - # at altitude (ft), using standard atmosphere, - # standard temperature T and pressure p. - - me.T = 0; - me.p = 0; - if (altitude < 36152) { - # curve fits for the troposphere - me.T = 59 - 0.00356 * altitude; - me.p = 2116 * math.pow( ((me.T + 459.7) / 518.6) , 5.256); - } elsif ( 36152 < altitude and altitude < 82345 ) { - # lower stratosphere - me.T = -70; - me.p = 473.1 * math.pow( const_e , 1.73 - (0.000048 * altitude) ); - } else { - # upper stratosphere - me.T = -205.05 + (0.00164 * altitude); - me.p = 51.97 * math.pow( ((me.T + 459.7) / 389.98) , -11.388); - } - - me.rho = me.p / (1718 * (me.T + 459.7)); - - # calculate the speed of sound at altitude - # a = sqrt ( g * R * (T + 459.7)) - # where: - # snd_speed in feet/s, - # g = specific heat ratio, which is usually equal to 1.4 - # R = specific gas constant, which equals 1716 ft-lb/slug/R - - me.snd_speed = math.sqrt( 1.4 * 1716 * (me.T + 459.7)); - return [me.rho, me.snd_speed]; - - }, - - active: {}, - flying: {}, -}; - - -# Create impact report. - -#altitde-agl-ft DOUBLE -#impact -# elevation-m DOUBLE -# heading-deg DOUBLE -# latitude-deg DOUBLE -# longitude-deg DOUBLE -# pitch-deg DOUBLE -# roll-deg DOUBLE -# speed-mps DOUBLE -# type STRING -# valid "true" BOOL -var impact_report = func(pos, mass, string, name) { - # Find the next index for "ai/models/model-impact" and create property node. - var n = props.globals.getNode("ai/models", 1); - for (var i = 0; 1; i += 1) - if (n.getChild(string, i, 0) == nil) - break; - var impact = n.getChild(string, i, 1); - - impact.getNode("impact/elevation-m", 1).setDoubleValue(pos.alt()); - impact.getNode("impact/latitude-deg", 1).setDoubleValue(pos.lat()); - impact.getNode("impact/longitude-deg", 1).setDoubleValue(pos.lon()); - impact.getNode("warhead-lbm", 1).setDoubleValue(mass); - #impact.getNode("speed-mps", 1).setValue(speed_mps); - impact.getNode("valid", 1).setBoolValue(1); - impact.getNode("impact/type", 1).setValue("terrain"); - impact.getNode("name", 1).setValue(name); - - var impact_str = "/ai/models/" ~ string ~ "[" ~ i ~ "]"; - setprop("ai/models/model-impact", impact_str); -} - -# HUD clamped target blinker -SW_reticle_Blinker = aircraft.light.new("payload/armament/hud/hud-sw-reticle-switch", [0.1, 0.1]); -setprop("payload/armament/hud/hud-sw-reticle-switch/enabled", 1); - - - - - -var eye_hud_m = 0.6;#pilot: -3.30 hud: -3.9 -var hud_radius_m = 0.100; - -#was in hud -var develev_to_devroll = func(dev_rad, elev_rad) { - var clamped = 0; - # Deviation length on the HUD (at level flight), - # 0.6686m = distance eye <-> virtual HUD screen. - var h_dev = eye_hud_m / ( math.sin(dev_rad) / math.cos(dev_rad) ); - var v_dev = eye_hud_m / ( math.sin(elev_rad) / math.cos(elev_rad) ); - # Angle between HUD center/top <-> HUD center/symbol position. - # -90° left, 0° up, 90° right, +/- 180° down. - var dev_deg = math.atan2( h_dev, v_dev ) * R2D; - # Correction with own a/c roll. - var combined_dev_deg = dev_deg - OurRoll.getValue(); - # Lenght HUD center <-> symbol pos on the HUD: - var combined_dev_length = math.sqrt((h_dev*h_dev)+(v_dev*v_dev)); - # clamp and squeeze the top of the display area so the symbol follow the egg shaped HUD limits. - var abs_combined_dev_deg = math.abs( combined_dev_deg ); - var clamp = hud_radius_m; - if ( abs_combined_dev_deg >= 0 and abs_combined_dev_deg < 90 ) { - var coef = ( 90 - abs_combined_dev_deg ) * 0.00075; - if ( coef > 0.050 ) { coef = 0.050 } - clamp -= coef; - } - if ( combined_dev_length > clamp ) { - combined_dev_length = clamp; - clamped = 1; - } - var v = [combined_dev_deg, combined_dev_length, clamped]; - return(v); -} - -#was in radar -var deviation_normdeg = func(our_heading, target_bearing) { - var dev_norm = our_heading - target_bearing; - while (dev_norm < -180) dev_norm += 360; - while (dev_norm > 180) dev_norm -= 360; - return(dev_norm); -} - -# -# this code make sure messages don't trigger the MP spam filter: - -var spams = 0; -var spamList = []; - -var defeatSpamFilter = func (str) { - spams += 1; - if (spams == 15) { - spams = 1; - } - str = str~":"; - for (var i = 1; i <= spams; i+=1) { - str = str~"."; - } - var newList = [str]; - for (var i = 0; i < size(spamList); i += 1) { - append(newList, spamList[i]); - } - spamList = newList; -} - -var spamLoop = func { - var spam = pop(spamList); - if (spam != nil) { - setprop("/sim/multiplay/chat", spam); - } - settimer(spamLoop, 1.20); -} - -spamLoop(); \ No newline at end of file diff --git a/Frigate/Nasal/damage.nas b/Frigate/Nasal/damage.nas index 11410b4..7e95537 100644 --- a/Frigate/Nasal/damage.nas +++ b/Frigate/Nasal/damage.nas @@ -28,16 +28,6 @@ var warhead_lbs = { "AGM-84": 488.00, "AGM-88": 146.00, "AGM65": 200.00, - "aim-120": 44.00, - "AIM-120": 44.00, - "AIM-54": 135.00, - "aim-7": 88.00, - "AIM-7": 88.00, - "aim-9": 20.80, - "AIM-9": 20.80, - "AIM120": 44.00, - "AIM132": 22.05, - "AIM9": 20.80, "ALARM": 450.00, "AM39-Exocet": 364.00, "AS-37-Martel": 330.00, @@ -47,12 +37,13 @@ var warhead_lbs = { "FAB-250": 202.85, "FAB-500": 564.38, "GBU-12": 190.00, + "GBU-24": 945.00, "GBU-31": 945.00, "GBU12": 190.00, "GBU16": 450.00, "HVAR": 7.50,#P51 "KAB-500": 564.38, - "KH-25MP": 197.53, + "Kh-25MP": 197.53, "Kh-66": 244.71, "KN-06": 315.00, "LAU-68": 10.00, @@ -60,40 +51,14 @@ var warhead_lbs = { "M71": 200.00, "M71R": 200.00, "M90": 500.00, - "Magic-2": 27.00, - "Matra MICA": 30.00, - "Matra R550 Magic 2": 27.00, - "MATRA-R530": 55.00, - "MatraMica": 30.00, - "MatraMicaIR": 30.00, - "MatraR550Magic2": 27.00, - "Meteor": 55.00, - "MICA-EM": 30.00, - "MICA-IR": 30.00, "MK-82": 192.00, "MK-83": 445.00, "MK-84": 945.00, "OFAB-100": 92.59, - "R-13M": 16.31, - "R-27R1": 85.98, - "R-27T1": 85.98, - "R-3R": 16.31, - "R-3S": 16.31, - "R-55": 20.06, - "R-60": 6.60, - "R-60M": 7.70, - "R-73E": 16.31, - "R-77": 49.60, - "R74": 16.00, "RB-04E": 661.00, "RB-05A": 353.00, "RB-15F": 440.92, - "RB-24": 20.80, - "RB-24J": 20.80, - "RB-71": 88.00, - "RB-74": 20.80, "RB-75": 126.00, - "RB-99": 44.00, "RN-14T": 800.00, #fictional, thermobaeric replacement for the RN-24 nuclear bomb "RN-18T": 1200.00, #fictional, thermobaeric replacement for the RN-28 nuclear bomb "RS-2US": 28.66, diff --git a/Frigate/Nasal/fire-control.nas b/Frigate/Nasal/fire-control.nas deleted file mode 100644 index 158251f..0000000 --- a/Frigate/Nasal/fire-control.nas +++ /dev/null @@ -1,209 +0,0 @@ -################### GLOBALS - -var false = 0; -var true = 1; - -var radar_update_time = 2; -var launch_update_time = 0.3; - -var missile_delay_time = 0; -var ciws_delay_time = 0; - -var AIR = 0; -var MARINE = 1; -var SURFACE = 2; -var ORDNANCE = 3; - -var ACTIVE_MISSILE = 0; -var NUM_MISSILES = 7; -var ROUNDS = 30; -var RELOAD_TIME = 600; - -################### MISSILE INFO - -var missile_name = "KN-06"; -var missile_brevity = "Grumble"; -var missile_max_distance = 85; #max distance in nm -var missile_min_distance = 2; #minimum distance in nm -var lockon_time = 6; #time in seconds it takes to lock on to a target - -################### IFF - -var target = { - new: func (callsign) { - var m = { parents: [target] }; - m.callsign = callsign; - m.fired = false; - return m; - }, -}; - -var targets = { - "pinto": target.new("pinto"), - "Leto": target.new("Leto"), - "YV-187": target.new("YV-187"), - "swamp": target.new("swamp"), - "Raider1": target.new("Raider1"), - "AF-MA13": target.new("AF-MA13"), - "KOL24M": target.new("KOL24M"), - "SNOWY1": target.new("SNOWY1"), -}; - -################### MAIN LOOP - -var scan = func() { - if ( getprop("/carrier/sunk") == 1 ) { - return; - } - - #### ITERATE THROUGH MP LIST #### - var my_pos = geo.aircraft_position(); - foreach(var mp; props.globals.getNode("/ai/models").getChildren("multiplayer")){ - #### DO WE HAVE FIRING SOLUTION... #### - # is plane in range, do we still have missiles, and is a missile already inbound, and has it been 4 seconds since the last missile launch? - var trigger = fire_control(mp, my_pos); - #print("dist to target = " ~ dist_to_target); - #### ... FOR THE MISSILE #### - if ( trigger == true and targets[mp.getNode("callsign").getValue()].fired == false and ACTIVE_MISSILE <= NUM_MISSILES and ( systime() - missile_delay_time > 7 ) ) { # - #print("callsign " ~ cs ~ " found at " ~ dist_to_target); - missile_delay_time = systime(); - targets[mp.getNode("callsign").getValue()].fired = true; - mp.getNode("unique",1).setValue(rand()); - armament.contact = radar_logic.Contact.new(mp, AIR); - missile_launch(mp, systime()); - #### ... FOR THE CIWS #### - } elsif ( trigger == 2 and ROUNDS > 0 and ( systime() - ciws_delay_time > 1.0 ) ) { - hit_msg = defeatSpamFilter("Gun Splash On : " ~ mp.getNode("callsign").getValue()); - print("CIWS fired | rounds remaining: " ~ ROUNDS ~ " | hit on: " ~ mp.getNode("callsign").getValue()); - ciws_delay_time = systime(); - ROUNDS = ROUNDS - 1; - setprop("/sim/multiplay/chat",hit_msg); - } - } - settimer(scan,radar_update_time); -} - -################## FIRE CONTROL -################## ALL AI LOGIC RELATED TO FIGURING OUT IF A TARGET SHOULD BE SHOT AT SHOULD GO HERE. - -var fire_control = func(mp, my_pos) { - #gather some data about the target - var ufo_pos = geo.Coord.new().set_latlon(mp.getNode("position/latitude-deg").getValue(),mp.getNode("position/longitude-deg").getValue(),(mp.getNode("position/altitude-ft").getValue() * 0.3048)); - var target_distance = my_pos.direct_distance_to(ufo_pos) * .000539957; #in nautical miles - var target_heading = mp.getNode("orientation/true-heading-deg").getValue(); - var target_bearing = my_pos.course_to(ufo_pos); - var relative_bearing = math.abs(ufo_pos.course_to(my_pos) - target_heading); - var target_altitude = mp.getNode("position/altitude-ft").getValue(); - var target_airspeed = mp.getNode("velocities/true-airspeed-kt").getValue(); - - # can the radar see it? - if ( mp.getNode("valid").getValue() == false ) { return false; } - if ( radar_logic.isNotBehindTerrain(mp) == false ) { return false; } - if ( target_airspeed < 40 ) { return false; } - if ( target_altitude - props.globals.getNode("/position/altitude-ft").getValue() < 150 ) { return false; } - if ( target_altitude > 70000 ) { return false; } - if ( target_distance > missile_max_distance ) { return false; } - # is this plane a friend or foe? - if ( targets[mp.getNode("callsign").getValue()] == nil ) { return false; } - - #special CIWS handling - if ( target_distance < 0.5 ) { return 2; } - - #should we shoot? using linear interpolation, with minimum probability of 0.01 to 0.10 and maximum probability of 1 - var distance_probability = 1 + (0.01 - 1) * (( target_distance - missile_min_distance ) / ( missile_max_distance - missile_min_distance )); - var bearing_probability = 1 + (0.05 - 1) * (( relative_bearing ) / ( 180 )); - var altitude_probability = 1 + (0.05 - 1) * (( target_altitude - 150 ) / ( 70000 - 150 )); - var speed_probability = distance_probability / 2.5 + (1 - distance_probability / 2.5) * (( target_airspeed - 1000 ) / ( 40 - 1000 )); - - var distance_weight = 0.4; - var bearing_weight = 0.3; - var altitude_weight = 0.2; - var speed_weight = 0.1; - - var fire_probability = (distance_probability * distance_weight) + (bearing_probability * bearing_weight) + (altitude_probability * altitude_weight) + (speed_probability * speed_weight); - var the_dice = rand(); - - print("probability for " ~ mp.getNode("callsign").getValue() ~ ": "~fire_probability); - - if ( fire_probability > 0.60 and fire_probability > the_dice ) { - return true; - } else { - return false; - } -} - -################### MISSILE CONTROL - -### missile reload - -var reload = func() { - #figure out how many to add - for ( var i = 0; i <= NUM_MISSILES; i = i + 1 ) { - armament.AIM.new(i,missile_name,missile_brevity); - armament.AIM.active[i].status = 0; - armament.AIM.active[i].search(); - } - ACTIVE_MISSILE = 0; -} - -### missile launch - -var missile_launch = func(mp, launchtime) { - if ( armament.AIM.active[ACTIVE_MISSILE].status == 1 and systime() - launchtime > lockon_time and radar_logic.isNotBehindTerrain(mp) == true ) { - armament.AIM.active[ACTIVE_MISSILE].release(); - setprop("/sim/multiplay/chat", defeatSpamFilter("KN-06 fired at: " ~ mp.getNode("callsign").getValue())); - ACTIVE_MISSILE = ACTIVE_MISSILE + 1; - print("Fired KN-06 #" ~ ACTIVE_MISSILE ~ " at: " ~ mp.getNode("callsign").getValue()); - return; - } - settimer( func { missile_launch(mp, launchtime); },launch_update_time); -} - -### missile set launched to false for target - -var incoming_listener = func { - var history = getprop("/sim/multiplay/chat-history"); - var hist_vector = split("\n", history); - if (size(hist_vector) > 0) { - var last = hist_vector[size(hist_vector)-1]; - var last_vector = split(":", last); - var author = last_vector[0]; - var callsign = getprop("sim/multiplay/callsign"); - if (size(last_vector) > 1 and author == callsign) { - var last1 = split(" ", last_vector[1]); - if(size(last1) > 2 and last1[size(last1)-1] == "exploded" ) { - #print("missile hitting someone"); - if (size(last_vector) > 3) { - #print("that someone is me!"); - var type = last1[1]; - #callsign = target, type = missile - last_vector[3] = right(last_vector[3],size(last_vector[3]) - 1); - if ( targets[last_vector[3]] != nil and type == "KN-06" ) { - targets[last_vector[3]].fired = false; - } - } - } - } - } -} - - -################### MISC - -var spams = 1; - -var defeatSpamFilter = func (str) { - spams += 1; - if (spams == 15) { - spams = 1; - } - str = str~":"; - for (var i = 1; i <= spams; i+=1) { - str = str~"."; - } - return str; -} - -reload(); -scan(); -setlistener("/sim/multiplay/chat-history", incoming_listener, 0, 0); \ No newline at end of file diff --git a/Frigate/Nasal/guided-missiles.nas b/Frigate/Nasal/guided-missiles.nas deleted file mode 100644 index 253224c..0000000 --- a/Frigate/Nasal/guided-missiles.nas +++ /dev/null @@ -1,1829 +0,0 @@ -########################################################################### -####### -####### Guided missiles code for Flightgear. -####### -####### License: GPL 2 -####### -####### Authors: -####### XIII, 5N1N0B1, Nikolai V. Chr. -####### -####### In addition, some code is derived from work by: -####### David Culp, Vivian Meazza -####### -########################################################################### - - -var AcModel = props.globals.getNode("payload"); -var OurHdg = props.globals.getNode("orientation/heading-deg"); -var OurRoll = props.globals.getNode("orientation/roll-deg"); -var OurPitch = props.globals.getNode("orientation/pitch-deg"); -var HudReticleDev = props.globals.getNode("payload/armament/hud/reticle-total-deviation", 1);#polar coords -var HudReticleDeg = props.globals.getNode("payload/armament/hud/reticle-total-angle", 1); -var vol_weak_track = 0.10; -var vol_track = 0.15; -var update_loop_time = 0.000; - -var FRAME_TIME = 1; -var REAL_TIME = 0; - -var TRUE = 1; -var FALSE = 0; - -var use_fg_default_hud = FALSE; - -var MISSILE_STANDBY = -1; -var MISSILE_SEARCH = 0; -var MISSILE_LOCK = 1; -var MISSILE_FLYING = 2; - -var g_fps = 9.80665 * M2FT; -var slugs_to_lbs = 32.1740485564; - -# -# The radar will make sure to keep this variable updated. -# Whatever is targeted and ready to be fired upon, should be set here. -# -var contact = nil; -# - -var AIM = { - #done - new : func (p, type = "AIM-9", sign = "Sidewinder") { - if(AIM.active[p] != nil) { - #do not make new missile logic if one exist for this pylon. - return -1; - } - var m = { parents : [AIM]}; - # Args: p = Pylon. - - m.type_lc = string.lc(type); - m.type = type; - - m.status = MISSILE_STANDBY; # -1 = stand-by, 0 = searching, 1 = locked, 2 = fired. - m.free = 0; # 0 = status fired with lock, 1 = status fired but having lost lock. - m.trackWeak = 1; - - m.prop = AcModel.getNode("armament/"~m.type_lc~"/").getChild("msl", 0 , 1); - m.SwSoundOnOff = AcModel.getNode("armament/"~m.type_lc~"/sound-on-off"); - m.SwSoundVol = AcModel.getNode("armament/"~m.type_lc~"/sound-volume"); - m.PylonIndex = m.prop.getNode("pylon-index", 1).setValue(p); - m.ID = p; - m.pylon_prop = props.globals.getNode("controls/armament").getChild("station", p+1); - m.Tgt = nil; - m.callsign = "Unknown"; - m.update_track_time = 0; - m.seeker_dev_e = 0; # Seeker elevation, deg. - m.seeker_dev_h = 0; # Seeker horizon, deg. - m.curr_tgt_e = 0; - m.curr_tgt_h = 0; - m.init_tgt_e = 0; - m.init_tgt_h = 0; - m.target_dev_e = 0; # Target elevation, deg. - m.target_dev_h = 0; # Target horizon, deg. - m.track_signal_e = 0; # Seeker deviation change to keep constant angle (proportional navigation), - m.track_signal_h = 0; # this is directly used as input signal for the steering command. - m.t_coord = geo.Coord.new().set_latlon(0, 0, 0); - m.last_t_coord = m.t_coord; - m.before_last_t_coord = nil; - #m.next_t_coord = m.t_coord; - m.direct_dist_m = nil; - m.speed_m = 0; - - # AIM specs: - m.aim9_fov_diam = getprop("payload/armament/"~m.type_lc~"/FCS-field-deg"); - m.aim9_fov = m.aim9_fov_diam / 2; - m.max_detect_rng = getprop("payload/armament/"~m.type_lc~"/max-fire-range-nm"); - m.max_seeker_dev = getprop("payload/armament/"~m.type_lc~"/seeker-field-deg") / 2; - m.force_lbs_1 = getprop("payload/armament/"~m.type_lc~"/thrust-lbf-stage-1"); - m.force_lbs_2 = getprop("payload/armament/"~m.type_lc~"/thrust-lbf-stage-2"); - m.stage_1_duration = getprop("payload/armament/"~m.type_lc~"/stage-1-duration-sec"); - m.stage_2_duration = getprop("payload/armament/"~m.type_lc~"/stage-2-duration-sec"); - m.weight_launch_lbs = getprop("payload/armament/"~m.type_lc~"/weight-launch-lbs"); - m.weight_whead_lbs = getprop("payload/armament/"~m.type_lc~"/weight-warhead-lbs"); - m.Cd_base = getprop("payload/armament/"~m.type_lc~"/drag-coeff"); - m.eda = getprop("payload/armament/"~m.type_lc~"/drag-area"); - m.max_g = getprop("payload/armament/"~m.type_lc~"/max-g"); - m.searcher_beam_width = getprop("payload/armament/"~m.type_lc~"/searcher-beam-width"); - m.arming_time = getprop("payload/armament/"~m.type_lc~"/arming-time-sec"); - m.min_speed_for_guiding = getprop("payload/armament/"~m.type_lc~"/min-speed-for-guiding-mach"); - m.selfdestruct_time = getprop("payload/armament/"~m.type_lc~"/self-destruct-time-sec"); - m.guidance = getprop("payload/armament/"~m.type_lc~"/guidance"); - m.all_aspect = getprop("payload/armament/"~m.type_lc~"/all-aspect"); - m.vol_search = getprop("payload/armament/"~m.type_lc~"/vol-search"); - m.angular_speed = getprop("payload/armament/"~m.type_lc~"/seeker-angular-speed-dps"); - m.loft_alt = getprop("payload/armament/"~m.type_lc~"/loft-altitude"); - m.min_dist = getprop("payload/armament/"~m.type_lc~"/min-fire-range-nm"); - m.rail = getprop("payload/armament/"~m.type_lc~"/rail"); - m.rail_dist_m = getprop("payload/armament/"~m.type_lc~"/rail-length-m"); - m.rail_forward = getprop("payload/armament/"~m.type_lc~"/rail-point-forward"); - m.class = getprop("payload/armament/"~m.type_lc~"/class"); - m.aim_9_model = getprop("payload/armament/models")~type~"/"~m.type_lc~"-"; - m.dt_last = 0; - # Find the next index for "models/model" and create property node. - # Find the next index for "ai/models/aim-9" and create property node. - # (M. Franz, see Nasal/tanker.nas) - var n = props.globals.getNode("models", 1); - var i = 0; - for (i = 0; 1==1; i += 1) { - if (n.getChild("model", i, 0) == nil) { - break; - } - } - m.model = n.getChild("model", i, 1); - - n = props.globals.getNode("ai/models", 1); - for (i = 0; 1==1; i += 1) { - if (n.getChild(m.type_lc, i, 0) == nil) { - break; - } - } - m.ai = n.getChild(m.type_lc, i, 1); - - m.ai.getNode("valid", 1).setBoolValue(1); - m.ai.getNode("name", 1).setValue(type); - m.ai.getNode("sign", 1).setValue(sign); - #m.model.getNode("collision", 1).setBoolValue(0); - #m.model.getNode("impact", 1).setBoolValue(0); - var id_model = m.aim_9_model ~ m.ID ~ ".xml"; - m.model.getNode("path", 1).setValue(id_model); - m.life_time = 0; - - # Create the AI position and orientation properties. - m.latN = m.ai.getNode("position/latitude-deg", 1); - m.lonN = m.ai.getNode("position/longitude-deg", 1); - m.altN = m.ai.getNode("position/altitude-ft", 1); - m.hdgN = m.ai.getNode("orientation/true-heading-deg", 1); - m.pitchN = m.ai.getNode("orientation/pitch-deg", 1); - m.rollN = m.ai.getNode("orientation/roll-deg", 1); - - m.ac = nil; - m.coord = geo.Coord.new().set_latlon(0, 0, 0); - m.last_coord = nil; - m.before_last_coord = nil; - m.s_down = nil; - m.s_east = nil; - m.s_north = nil; - m.alt = nil; - m.pitch = nil; - m.hdg = nil; - - m.density_alt_diff = 0; - m.max_g_current = m.max_g; - m.last_deviation_e = nil; - m.last_deviation_h = nil; - m.last_track_e = 0; - m.last_track_h = 0; - m.update_count = -1; - m.paused = 0; - m.last_tgt_h = nil; - m.last_tgt_e = nil; - m.old_speed_horz_fps = nil; - m.t_alt_delta_last_m = nil; - - m.dist_last = nil; - m.dist_direct_last = nil; - m.last_t_course = nil; - m.last_t_elev_deg = nil; - m.last_cruise_or_loft = 0; - m.old_speed_fps = 0; - m.last_t_norm_speed = nil; - m.last_t_elev_norm_speed = nil; - - m.dive_token = FALSE; - - # cruise missiles - m.nextGroundElevation = 0; # next Ground Elevation - m.nextGroundElevationMem = [-10000, -1]; - - #rail - m.drop_time = 0; - m.rail_passed = FALSE; - m.x = 0; - m.y = 0; - m.z = 0; - m.rail_pos = 0; - m.rail_speed_into_wind = 0; - - m.lastFlare = 0; - - m.SwSoundOnOff.setBoolValue(FALSE); - m.SwSoundVol.setDoubleValue(m.vol_search); - me.trackWeak = 1; - - return AIM.active[m.ID] = m; - }, - #done - del: func { - #print("deleted"); - me.model.remove(); - me.ai.remove(); - if (me.status == MISSILE_FLYING) { - delete(AIM.flying, me.flyID); - } else { - delete(AIM.active, me.ID); - } - }, - - # get Coord from body position. x,y,z must be in meters. - getGPS: func(x, y, z) { - # derived from Vivian's code in AIModel/submodel.cxx. - var ac_roll = getprop("orientation/roll-deg"); - var ac_pitch = getprop("orientation/pitch-deg"); - var ac_hdg = getprop("orientation/heading-deg"); - - me.ac = geo.aircraft_position(); - - var in = [0,0,0]; - var trans = [[0,0,0],[0,0,0],[0,0,0]]; - var out = [0,0,0]; - - in[0] = -x * M2FT; - in[1] = y * M2FT; - in[2] = z * M2FT; - # Pre-process trig functions: - var cosRx = math.cos(-ac_roll * D2R); - var sinRx = math.sin(-ac_roll * D2R); - var cosRy = math.cos(-ac_pitch * D2R); - var sinRy = math.sin(-ac_pitch * D2R); - var cosRz = math.cos(ac_hdg * D2R); - var sinRz = math.sin(ac_hdg * D2R); - # Set up the transform matrix: - trans[0][0] = cosRy * cosRz; - trans[0][1] = -1 * cosRx * sinRz + sinRx * sinRy * cosRz ; - trans[0][2] = sinRx * sinRz + cosRx * sinRy * cosRz; - trans[1][0] = cosRy * sinRz; - trans[1][1] = cosRx * cosRz + sinRx * sinRy * sinRz; - trans[1][2] = -1 * sinRx * cosRx + cosRx * sinRy * sinRz; - trans[2][0] = -1 * sinRy; - trans[2][1] = sinRx * cosRy; - trans[2][2] = cosRx * cosRy; - # Multiply the input and transform matrices: - out[0] = in[0] * trans[0][0] + in[1] * trans[0][1] + in[2] * trans[0][2]; - out[1] = in[0] * trans[1][0] + in[1] * trans[1][1] + in[2] * trans[1][2]; - out[2] = in[0] * trans[2][0] + in[1] * trans[2][1] + in[2] * trans[2][2]; - # Convert ft to degrees of latitude: - out[0] = out[0] / (366468.96 - 3717.12 * math.cos(me.ac.lat() * D2R)); - # Convert ft to degrees of longitude: - out[1] = out[1] / (365228.16 * math.cos(me.ac.lat() * D2R)); - # Set submodel initial position: - var alat = me.ac.lat() + out[0]; - var alon = me.ac.lon() + out[1]; - var aalt = (me.ac.alt() * M2FT) + out[2]; - - var c = geo.Coord.new(); - c.set_latlon(alat, alon, aalt * FT2M); - - return c; - }, - - release: func() { - me.status = MISSILE_FLYING; - me.flyID = rand(); - AIM.flying[me.flyID] = me; - delete(AIM.active, me.ID); - me.animation_flags_props(); - - # Get the A/C position and orientation values. - me.ac = geo.aircraft_position(); - me.ac_init = geo.Coord.new(me.ac); - var ac_roll = getprop("orientation/roll-deg");# positive is banking right - var ac_pitch = getprop("orientation/pitch-deg"); - var ac_hdg = getprop("orientation/heading-deg"); - - # Compute missile initial position relative to A/C center - - me.x = me.pylon_prop.getNode("offsets/x-m").getValue(); - me.y = me.pylon_prop.getNode("offsets/y-m").getValue(); - me.z = me.pylon_prop.getNode("offsets/z-m").getValue(); - - var init_coord = me.getGPS(me.x, me.y, me.z); - - # Set submodel initial position: - var alat = init_coord.lat(); - var alon = init_coord.lon(); - var aalt = init_coord.alt() * M2FT; - me.latN.setDoubleValue(alat); - me.lonN.setDoubleValue(alon); - me.altN.setDoubleValue(aalt); - me.hdgN.setDoubleValue(ac_hdg); - if (me.rail == FALSE) { - # align into wind (commented out since heavy wind make missiles lose sight of target.) - var alpha = getprop("orientation/alpha-deg"); - var beta = getprop("orientation/side-slip-deg");# positive is air from right - - var alpha_diff = alpha * math.cos(ac_roll*D2R) * ((ac_roll > 90 or ac_roll < -90)?-1:1) + beta * math.sin(ac_roll*D2R); - #alpha_diff = alpha > 0?alpha_diff:0;# not using alpha if its negative to avoid missile flying through aircraft. - #ac_pitch = ac_pitch - alpha_diff; - - var beta_diff = beta * math.cos(ac_roll*D2R) * ((ac_roll > 90 or ac_roll < -90)?-1:1) - alpha * math.sin(ac_roll*D2R); - #ac_hdg = ac_hdg + beta_diff; - - # drop distance in time - me.drop_time = math.sqrt(2*7/g_fps);# time to fall 7 ft to clear aircraft - } - me.pitchN.setDoubleValue(ac_pitch); - me.rollN.setDoubleValue(ac_roll); - #print("roll "~ac_roll~" on "~me.rollN.getPath()); - me.coord.set_latlon(alat, alon, aalt * FT2M); - - me.model.getNode("latitude-deg-prop", 1).setValue(me.latN.getPath()); - me.model.getNode("longitude-deg-prop", 1).setValue(me.lonN.getPath()); - me.model.getNode("elevation-ft-prop", 1).setValue(me.altN.getPath()); - me.model.getNode("heading-deg-prop", 1).setValue(me.hdgN.getPath()); - me.model.getNode("pitch-deg-prop", 1).setValue(me.pitchN.getPath()); - me.model.getNode("roll-deg-prop", 1).setValue(me.rollN.getPath()); - var loadNode = me.model.getNode("load", 1); - loadNode.setBoolValue(1); - - # Get initial velocity vector (aircraft): - - me.s_down = getprop("velocities/speed-down-fps"); - me.s_east = getprop("velocities/speed-east-fps"); - me.s_north = getprop("velocities/speed-north-fps"); - if (me.rail == TRUE) { - if (me.rail_forward == FALSE) { - # rail is actually a tube pointing upward - me.rail_speed_into_wind = -getprop("velocities/wBody-fps");# wind from below - } else { - # rail is pointing forward - me.rail_speed_into_wind = getprop("velocities/uBody-fps");# wind from nose - } - } - #print("release speed down: "~me.s_down); - - me.alt = aalt; - me.pitch = ac_pitch; - me.hdg = ac_hdg; - - #print("p1 "~ac_pitch); - if (getprop("sim/flight-model") == "jsb") { - # currently not supported in Yasim - me.density_alt_diff = getprop("fdm/jsbsim/atmosphere/density-altitude") - aalt; - } - - #print("air density diff alt = "~me.density_alt_diff); - #print("missile alt = "~aalt); - - #me.smoke_prop.setBoolValue(1); - me.SwSoundVol.setDoubleValue(0); - me.trackWeak = 1; - #settimer(func { HudReticleDeg.setValue(0) }, 2); - #interpolate(HudReticleDev, 0, 2); - #loadNode.remove(); - - me.flight(); - loadNode.remove(); - }, - - drag: func (mach) { - # Adjust Cd by Mach number. The equations are based on curves - # for a conventional shell/bullet (no boat-tail). - # - # Derived from Davic Culps code in AIBallistic - var Cd = 0; - if (mach < 0.7) { - Cd = 0.0125 * mach + me.Cd_base; - } elsif (mach < 1.2 ) { - Cd = 0.3742 * math.pow(mach, 2) - 0.252 * mach + 0.0021 + me.Cd_base; - } else { - Cd = 0.2965 * math.pow(mach, -1.1506) + me.Cd_base; - } - - return Cd; - }, - - drag_new: func (mach) { - # Nikolai V. Chr.: Made the drag calc more in line with big missiles as opposed to small bullets. - # - # When I start using this, all the drag coefficients for the missiles have to be reestimated, same for thrust. - # - var Cd = 0; - if (mach < 0.7) { - Cd = (0.0125 * mach + 0.20) * 5 * me.Cd_base; - } elsif (mach < 1.2 ) { - Cd = (0.3742 * math.pow(mach, 2) - 0.252 * mach + 0.0021 + 0.2 ) * 5 * me.Cd_base; - } else { - Cd = (0.2965 * math.pow(mach, -1.1506) + 0.2) * 5 * me.Cd_base; - } - - return Cd; - }, - - flight: func { - #print(); - if (me.Tgt.isValid() == FALSE) { - me.del(); - return; - } - var dt = getprop("sim/time/delta-sec");#TODO: find out more about how this property works (most likely time since last time nasal timers were called) - if (dt == 0) { - #FG is likely paused - me.paused = 1; - settimer(func me.flight(), 0.00); - return; - } - #if just called from release() then dt is almost 0 (cannot be zero as we use it to divide with) - # It can also not be too small, then the missile will lag behind aircraft and seem to be fired from behind the aircraft. - #dt = dt/2; - var elapsed = systime(); - if (me.paused == 1) { - # sim has been unpaused lets make sure dt becomes very small to let elapsed time catch up. - me.paused = 0; - me.dt_last = elapsed-0.02; - } - var init_launch = 0; - if (me.dt_last != 0) { - #if (getprop("sim/speed-up") == 1) { - dt = (elapsed - me.dt_last)*getprop("sim/speed-up"); - #} else { - # dt = getprop("sim/time/delta-sec")*getprop("sim/speed-up"); - #} - init_launch = 1; - if(dt <= 0) { - # to prevent pow floating point error in line:cdm = 0.2965 * math.pow(me.speed_m, -1.1506) + me.cd; - # could happen if the OS adjusts the clock backwards - dt = 0.00001; - } - } - me.dt_last = elapsed; - - - me.life_time += dt; - # record coords so we can give the latest nearest position for impact. - me.before_last_coord = geo.Coord.new(me.last_coord); - me.last_coord = geo.Coord.new(me.coord); - #print(dt); - - #### Calculate speed vector before steering corrections. - - # Rocket thrust. If dropped, then ignited after fall time of what is the equivalent of 7ft. - # If the rocket is 2 stage, then ignite the second stage when 1st has burned out. - var f_lbs = 0;# pounds force (lbf) - if (me.life_time > me.drop_time) { - f_lbs = me.force_lbs_1; - } - if (me.life_time > me.stage_1_duration + me.drop_time) { - f_lbs = me.force_lbs_2; - } - if (me.life_time > (me.drop_time + me.stage_1_duration + me.stage_2_duration)) { - f_lbs = 0; - } - if (f_lbs < 1) { - me.smoke_prop.setBoolValue(0); - } else { - me.smoke_prop.setBoolValue(1); - } - - # Get total old speed. - var d_east_ft = me.s_east * dt; - var d_north_ft = me.s_north * dt; - var d_down_ft = me.s_down * dt; - var dist_h_ft = math.sqrt((d_east_ft*d_east_ft)+(d_north_ft*d_north_ft)); - var total_s_ft = math.sqrt((dist_h_ft*dist_h_ft)+(d_down_ft*d_down_ft)); - - # get old attitude - var pitch_deg = me.pitch; - var hdg_deg = me.hdg; - - if (me.rail == TRUE and me.rail_passed == FALSE) { - var u = getprop("velocities/uBody-fps");# wind from nose - var v = getprop("velocities/vBody-fps");# wind from side - var w = getprop("velocities/wBody-fps");# wind from below - - var opposing_wind = u; - - if (me.rail_forward == TRUE) { - pitch_deg = getprop("orientation/pitch-deg"); - hdg_deg = getprop("orientation/heading-deg"); - } else { - pitch_deg = 90; - opposing_wind = -w; - hdg_deg = me.Tgt.get_bearing(); - } - - var speed_on_rail = me.clamp(me.rail_speed_into_wind - opposing_wind, 0, 1000000); - var movement_on_rail = speed_on_rail * dt; - - me.rail_pos = me.rail_pos + movement_on_rail; - if (me.rail_forward == TRUE) { - me.x = me.x - (movement_on_rail * FT2M);# negative cause positive is rear in body coordinates - } else { - me.z = me.z + (movement_on_rail * FT2M);# positive cause positive is up in body coordinates - } - #print("rail pos "~(me.rail_pos*FT2M)); - } - - # Get air density and speed of sound (fps): - #var alt_ft = me.altN.getValue(); don't declare this twice - var rs = rho_sndspeed(me.altN.getValue() + me.density_alt_diff); - var rho = rs[0]; - var sound_fps = rs[1]; - - # density for 0ft and 50kft: - #print("0:"~rho_sndspeed(0)[0]); = 0.0023769 - #print("50k:"~rho_sndspeed(50000)[0]); = 0.00036159 - # - # a aim-9j can do 22G at sealevel, 13G at 50Kft - # 13G = 22G * 0.5909 - # - # extra/inter-polation: - # f(x) = y1 + ((x - x1) / (x2 - x1)) * (y2 - y1) - # calculate its performance at current air density: - me.max_g_current = me.max_g+((rho-0.0023769)/(0.00036159-0.0023769))*(me.max_g*0.5909-me.max_g); - #print("Max G = "~me.max_g_current~" Rho = "~rho); - - var old_speed_fps = total_s_ft / dt; - #print("aim "~old_speed_fps); - #print("ac "~(getprop("velocities/groundspeed-3D-kt")*KT2FPS)); - me.old_speed_horz_fps = dist_h_ft / dt; - me.old_speed_fps = old_speed_fps; - - if (me.rail == TRUE and me.rail_passed == FALSE) { - # if missile is still on rail, we replace the speed, with the speed into the wind from nose on the rail. - old_speed_fps = me.rail_speed_into_wind; - } - - me.speed_m = old_speed_fps / sound_fps; - - var Cd = me.drag_new(me.speed_m); - - # Add drag to the total speed using Standard Atmosphere (15C sealevel temperature); - # rho is adjusted for altitude in environment.rho_sndspeed(altitude), - # Acceleration = thrust/mass - drag/mass; - var mass = me.weight_launch_lbs / slugs_to_lbs; - - var acc = f_lbs / mass; - - var q = 0.5 * rho * old_speed_fps * old_speed_fps;# dynamic pressure - var drag_acc = (Cd * q * me.eda) / mass; - - # get total new speed (minus gravity) - var speed_change_fps = acc*dt - drag_acc*dt; - - - if(me.loft_alt != 0 and me.loft_alt < 10000) - { - # detect terrain for use in terrain following - me.nextGroundElevationMem[1] -= 1; - var geoPlus2 = nextGeoloc(me.coord.lat(), me.coord.lon(), me.hdg, old_speed_fps, dt*5); - var geoPlus3 = nextGeoloc(me.coord.lat(), me.coord.lon(), me.hdg, old_speed_fps, dt*10); - var geoPlus4 = nextGeoloc(me.coord.lat(), me.coord.lon(), me.hdg, old_speed_fps, dt*20); - #var geoPlus5 = nextGeoloc(me.coord.lat(), me.coord.lon(), me.hdg, old_speed_fps, dt*30); - var e1 = geo.elevation(me.coord.lat(), me.coord.lon());# This is done, to make sure is does not decline before it has passed obstacle. - var e2 = geo.elevation(geoPlus2.lat(), geoPlus2.lon());# This is the main one. - var e3 = geo.elevation(geoPlus3.lat(), geoPlus3.lon());# This is an extra, just in case there is an high cliff it needs longer time to climb. - var e4 = geo.elevation(geoPlus4.lat(), geoPlus4.lon()); - #var e5 = geo.elevation(geoPlus5.lat(), geoPlus5.lon()); - if (e1 != nil) { - me.nextGroundElevation = e1; - } else { - print("nil terrain, blame terrasync! Cruise-missile keeping altitude."); - } - if (e2 != nil and e2 > me.nextGroundElevation) { - me.nextGroundElevation = e2; - if (e2 > me.nextGroundElevationMem[0] or me.nextGroundElevationMem[1] < 0) { - me.nextGroundElevationMem[0] = e2; - me.nextGroundElevationMem[1] = 5; - } - } - if (me.nextGroundElevationMem[0] > me.nextGroundElevation) { - me.nextGroundElevation = me.nextGroundElevationMem[0]; - } - if (e3 != nil and e3 > me.nextGroundElevation) { - me.nextGroundElevation = e3; - } - if (e4 != nil and e4 > me.nextGroundElevation) { - me.nextGroundElevation = e4; - } - #if (e5 != nil and e5 > me.nextGroundElevation) { - # me.nextGroundElevation = e5; - #} - } - - #print("alt "~alt_ft); - - #### Guidance. - - if ( me.status == MISSILE_FLYING and me.free == FALSE and me.life_time > me.drop_time) { - if (me.rail == FALSE or me.rail_passed == TRUE) { - var success = me.guide(dt); - if (success == FALSE) { - return; - } - } - #print("steering "~me.track_signal_e~" deg up"); - #Here will be set the max angle of pitch and the max angle of heading to avoid G overload - var myG = steering_speed_G(me.track_signal_e, me.track_signal_h, old_speed_fps, dt); - if(me.max_g_current < myG) - { - var MyCoef = max_G_Rotation(me.track_signal_e, me.track_signal_h, old_speed_fps, dt, me.max_g_current); - me.track_signal_e = me.track_signal_e * MyCoef; - me.track_signal_h = me.track_signal_h * MyCoef; - #print(sprintf("G1 %.2f", myG)); - var myG2 = steering_speed_G(me.track_signal_e, me.track_signal_h, old_speed_fps, dt); - #print(sprintf("G2 %.2f", myG)~sprintf(" - Coeff %.2f", MyCoef)); - #print(sprintf("Missile pulling almost max G: %.1f G", myG2)); - } - #print(sprintf("G %.1f", myG)); - if (me.all_aspect == 1 or me.rear_aspect() == 1) { - pitch_deg += me.track_signal_e; - hdg_deg += me.track_signal_h; - me.last_track_e = me.track_signal_e; - me.last_track_h = me.track_signal_h; - } else { - me.last_track_e = 0; - me.last_track_h = 0; - print("Heat seeking missile lost lock, attempting to reaquire.."); - } - #print(sprintf("%.1f deg elev command done, desired pitch: %.1f deg", me.track_signal_e, pitch_deg)); - #print(sprintf("%.1f deg bear command done", me.last_track_h)); - - #print("Still Tracking : Elevation ",me.track_signal_e,"Heading ",me.track_signal_h," Gload : ", myG ); - - } - - # If we add gravity while the missile is guiding, the gravity speed will be added to total speed, - # which next update will be added in the direction the missile points, which we do not want. - # therefore only real gravity drop is added to gravity bombs. - - var grav_bomb = FALSE; - if (me.force_lbs_1 == 0 and me.force_lbs_2 == 0) { - grav_bomb == TRUE; - } - - - #print("p "~pitch_deg); - # Break speed change down total speed to North, East and Down components. - var speed_down_fps = - math.sin(pitch_deg * D2R) * (speed_change_fps + old_speed_fps); - var speed_horizontal_fps = math.cos(pitch_deg * D2R) * (speed_change_fps + old_speed_fps); - var speed_north_fps = math.cos(hdg_deg * D2R) * speed_horizontal_fps; - var speed_east_fps = math.sin(hdg_deg * D2R) * speed_horizontal_fps; - - if (me.rail == TRUE and me.rail_passed == FALSE) { - # missile still on rail, lets calculate its speed relative to the wind coming in from the aircraft nose. - me.rail_speed_into_wind = me.rail_speed_into_wind + speed_change_fps; - } - - if (grav_bomb == TRUE) { - # true gravity acc - speed_down_fps += g_fps * dt; - } - - #var speed_down_fps = speed_down_change_fps;# + me.s_down - #var speed_north_fps = speed_north_change_fps;# + me.s_north - #var speed_east_fps = speed_east_change_fps;# + me.s_east - - #var speed_horizontal_fps = math.sqrt(speed_north_fps*speed_north_fps+speed_east_fps*speed_east_fps); - - - -#print("change: down="~speed_down_change_fps~" north="~speed_north_change_fps~" east="~speed_east_change_fps); -#print("new: down="~speed_down_fps~" north="~speed_north_fps~" east="~speed_east_fps); -#print("speed horz: "~speed_horizontal_fps~" (old: "~(dist_h_ft/dt)~")"); -#print("speed: new="~new_speed_fps~" old="~old_speed_fps); - - #if (new_speed_fps < 0) { - # drag can theoretically make the speed less than 0, this will prevent that from happening. - # new_speed_fps = 0; - #} - - # Calculate altitude and elevation velocity vector (no incidence here). - - #pitch_deg = math.atan2( speed_down_fps, speed_horizontal_fps ) * R2D; - - # this is commented, cause the missile just falls due to gravity, it doesn't pitch - # a real missile would pitch ofc. but then have to calc how fuel affects CoG and its inertia/momentum - # - #me.pitch = pitch_deg; - #pitch_deg = me.pitch; - - var dist_h_m = speed_horizontal_fps * dt * FT2M; - var alt_ft = me.altN.getValue() - ((speed_down_fps + g_fps * dt * !grav_bomb) * dt); - - var new_speed_fps = math.sqrt(speed_horizontal_fps*speed_horizontal_fps+speed_down_fps*speed_down_fps); - -#print("."); - -#print(me.s_down); -#print(speed_down_fps); -#print(me.altN.getValue()); -#print(alt_ft); - if (me.rail == FALSE or me.rail_passed == TRUE) { - # misssile not on rail, lets move it to next waypoint - me.coord.apply_course_distance(hdg_deg, dist_h_m); - me.coord.set_alt(alt_ft * FT2M); - } else { - # missile on rail, lets move it on the rail - new_speed_fps = me.rail_speed_into_wind; - me.coord = me.getGPS(me.x, me.y, me.z); - alt_ft = me.coord.alt() * M2FT; - } - - - # performance logging: - #setprop("logging/missile/dist-m", me.ac_init.distance_to(me.coord)); - #setprop("logging/missile/alt-m", alt_ft * FT2M); - #setprop("logging/missile/speed-m", me.speed_m*1000); - #setprop("logging/missile/drag-lbf", Cd * q * me.eda); - #setprop("logging/missile/thrust-lbf", f_lbs); - - - - - - - me.latN.setDoubleValue(me.coord.lat()); - me.lonN.setDoubleValue(me.coord.lon()); - me.altN.setDoubleValue(alt_ft); - me.pitchN.setDoubleValue(pitch_deg); - me.hdgN.setDoubleValue(hdg_deg); - - # log missiles to unicsv - #setprop("/logging/missile/latitude-deg", me.coord.lat()); - #setprop("/logging/missile/longitude-deg", me.coord.lon()); - #setprop("/logging/missile/altitude-ft", alt_ft); - #setprop("/logging/missile/t-latitude-deg", me.t_coord.lat()); - #setprop("/logging/missile/t-longitude-deg", me.t_coord.lon()); - #setprop("/logging/missile/t-altitude-ft", me.t_coord.alt()*M2FT); - - # set radar properties for use in selection view and HUD tracks. - var self = geo.aircraft_position(); - me.ai.getNode("radar/bearing-deg", 1).setDoubleValue(self.course_to(me.coord)); - var angleInv = me.clamp(self.distance_to(me.coord)/self.direct_distance_to(me.coord), -1, 1); - me.ai.getNode("radar/elevation-deg", 1).setDoubleValue((self.alt()>me.coord.alt()?-1:1)*math.acos(angleInv)*R2D); - me.ai.getNode("velocities/true-airspeed-kt",1).setDoubleValue(new_speed_fps * FPS2KT); - - #### Proximity detection. - if ( me.status == MISSILE_FLYING and (me.rail == FALSE or me.rail_passed == TRUE)) { - #### check if the missile can keep the lock. - if ( me.free == FALSE ) { - var g = steering_speed_G(me.track_signal_e, me.track_signal_h, old_speed_fps, dt); - -# Uncomment this line to check stats while flying: -# -#print(sprintf("Mach %02.1f", me.speed_m)~sprintf(" , time %03.1f s", me.life_time)~sprintf(" , thrust %03.1f lbf", f_lbs)~sprintf(" , G-force %02.2f", g)); -#print(sprintf("Alt %05.1f", alt_ft)); - if ( g > me.max_g_current and init_launch != 0) { - #print("lost lock "~g~"G"); - # Target unreachable, fly free. - me.free = 1; - print("Missile attempted to pull too many G, it broke."); - } - if (me.guidance == "heat") { - var flareNode = me.Tgt.getFlareNode(); - if (flareNode != nil) { - var flareString = flareNode.getValue(); - if (flareString != nil) { - var flareVector = split(":", flareString); - if (flareVector != nil and size(flareVector) == 2 and flareVector[1] == "flare") { - var flareNumber = num(flareVector[0]); - if (flareNumber != nil and flareNumber != me.lastFlare) { - # target has released a new flare, lets check if it fools us - me.lastFlare = flareNumber; - var aspect = me.aspect() / 180; - var fooled = rand() < (0.2 + 0.1 * aspect); - # 20% chance to be fooled, extra up till 10% chance added if front aspect - if (fooled) { - # fooled by the flare - print("Missile fooled by flare"); - me.free = 1; - } else { - print("Missile ignored flare"); - } - } - } - } - } - } - } - var v = me.poximity_detection(); - - if (v == FALSE) { - #print("exploded"); - # We exploded, and start the sound propagation towards the plane - me.sndSpeed = sound_fps; - me.sndDistance = 0; - me.dt_last = systime(); - me.sndPropagate(); - return; - } - - } - # record the velocities for the next loop. - me.s_north = speed_north_fps; - me.s_east = speed_east_fps; - me.s_down = speed_down_fps; - me.alt = alt_ft; - me.pitch = pitch_deg; - me.hdg = hdg_deg; - - if (me.rail == FALSE or me.rail_pos > me.rail_dist_m * M2FT) { - me.rail_passed = TRUE; - #print("rail passed"); - } - - settimer(func me.flight(), update_loop_time, REAL_TIME); - - }, - - # If is heat-seeking rear-aspect-only missile, check if it has good view on engine(s) and can keep lock. - rear_aspect: func () { - - var offset = me.aspect(); - - if (offset < 45) { - # clear view of engine heat, keep the lock - rearAspect = 1; - } else { - # the greater angle away from clear engine view the greater chance of losing lock. - var offset_away = offset - 45; - var probability = offset_away/135; - rearAspect = rand() > probability; - } - - #print ("RB-24J deviation from full rear-aspect: "~sprintf("%01.1f", offset)~" deg, keep IR lock on engine: "~rearAspect); - - return rearAspect;# 1: keep lock, 0: lose lock - }, - - aspect: func () { - var rearAspect = 0; - - var t_dist_m = me.coord.distance_to(me.t_coord); - var alt_delta_m = me.coord.alt() - me.t_coord.alt(); - var elev_deg = math.atan2( alt_delta_m, t_dist_m ) * R2D; - var elevation_offset = elev_deg - me.Tgt.get_Pitch(); - - var course = me.t_coord.course_to(me.coord); - var heading_offset = course - me.Tgt.get_heading(); - - # - while (heading_offset < -180) { - heading_offset += 360; - } - while (heading_offset > 180) { - heading_offset -= 360; - } - while (elevation_offset < -180) { - elevation_offset += 360; - } - while (elevation_offset > 180) { - elevation_offset -= 360; - } - elevation_offset = math.abs(elevation_offset); - heading_offset = 180 - math.abs(heading_offset); - - var offset = math.max(elevation_offset, heading_offset); - - return offset; - }, - - # navigation and guidance - guide: func(dt) { - if (!me.Tgt.isValid()) { - # Lost of lock due to target disapearing: - # destroy missile - #print("invalid"); - me.del(); - return FALSE; - } - #print("track"); - # Time interval since lock time or last track loop. - - if (dt != nil) { - # Status = launched. Compute target position relative to seeker head. - - # Get target position. - var t_alt = me.Tgt.get_altitude(); - me.t_coord.set_latlon(me.Tgt.get_Latitude(), me.Tgt.get_Longitude(), t_alt * FT2M); - - # Calculate current target elevation and azimut deviation. - var t_dist_m = me.coord.distance_to(me.t_coord); - var t_alt_delta_m = (t_alt - me.alt) * FT2M; - var t_elev_deg = math.atan2( t_alt_delta_m, t_dist_m ) * R2D; - me.curr_tgt_e = t_elev_deg - me.pitch; - var (t_course, dst) = courseAndDistance(me.coord, me.t_coord); - #var t_course = me.coord.course_to(me.t_coord); - me.curr_tgt_h = t_course - me.hdg; - #print(); - #print(sprintf("Altitude above launch platform = %.1f ft", M2FT * (me.coord.alt()-me.ac.alt()))); - - - # Compute gain to reduce target deviation to match an optimum 3 deg - # This augments steering by an additional 10 deg per second during - # the trajectory stage 1 seconds. - # Then, keep track of deviations at the end of these two initial 2 seconds. - var e_gain = 1; - var h_gain = 1; - - if(me.curr_tgt_h < -180) { - me.curr_tgt_h += 360; - } - if(me.curr_tgt_h > 180) { - me.curr_tgt_h -= 360; - } - - #print("tgt alt: "~t_alt~" me alt: "~me.alt); - #print(" absolute elevation: "~t_elev_deg~" relative elevation: "~me.curr_tgt_e); - #print(" absolute bearing: "~t_course~" relative bearing: "~me.curr_tgt_h); - #print(" distance along curvature: "~t_dist_m~" meter"); - - if(me.speed_m < me.min_speed_for_guiding) { - # it doesn't guide at lower speeds - e_gain = 0; - h_gain = 0; - me.update_count = -1; - #print("Not guiding (too low speed)"); - } elsif (me.guidance == "semi-radar" and me.is_painted(me.Tgt) == FALSE) { - # if its semi-radar guided and the target is no longer painted - e_gain = 0; - h_gain = 0; - me.update_count = -1; - print("Not guiding (lost radar reflection, trying to reaquire)"); - } elsif (me.curr_tgt_e > me.max_seeker_dev or me.curr_tgt_e < (-1 * me.max_seeker_dev) - or me.curr_tgt_h > me.max_seeker_dev or me.curr_tgt_h < (-1 * me.max_seeker_dev)) { - # target is not in missile seeker view anymore - print("Target is not in missile seeker view anymore"); - me.free = 1; - e_gain = 0; - h_gain = 0; - } - - - var dev_e = me.curr_tgt_e;# - var dev_h = me.curr_tgt_h;# - - #print(sprintf("curr: elev=%.1f", dev_e)~sprintf(" head=%.1f", dev_h)); - if (me.last_deviation_e != nil) { - # its not our first seeker head move - me.update_count += 1; - # calculate if the seeker can keep up with the angular change of the target - - # missile own movement is subtracted from this change due to seeker being on gyroscope - - var dve_dist = dev_e - me.last_deviation_e + me.last_track_e; - var dvh_dist = dev_h - me.last_deviation_h + me.last_track_h; - var deviation_per_sec = math.sqrt(dve_dist*dve_dist+dvh_dist*dvh_dist)/dt; - - if (deviation_per_sec > me.angular_speed) { - #print(sprintf("last-elev=%.1f", me.last_deviation_e)~sprintf(" last-elev-adj=%.1f", me.last_track_e)); - #print(sprintf("last-head=%.1f", me.last_deviation_h)~sprintf(" last-head-adj=%.1f", me.last_track_h)); - # lost lock due to angular speed limit - print(sprintf("%.1f deg/s too big angular change for seeker head.", deviation_per_sec)); - #print(dt); - me.free = 1; - e_gain = 0; - h_gain = 0; - } - } else { - me.update_count = 0; - } - - me.last_deviation_e = dev_e; - me.last_deviation_h = dev_h; - - - - - ###################################### - ### cruise, loft, cruise-missile ### - ###################################### - - var loft_angle = 15;# notice Shinobi uses 26.5651 degs, but Raider1 found a source saying 10-20 degs. - var loft_minimum = 10;# miles - var cruise_minimum = 7.5;# miles - var cruise_or_loft = 0; - - if(me.loft_alt != 0 and me.loft_alt < 10000) { - # this is for Air to ground/sea cruise missile (SCALP, Taurus, Tomahawk...) - var Daground = 0;# zero for sealevel in case target is ship. Don't shoot A/S missiles over terrain. :) - if(me.class == "A/G") { - Daground = me.nextGroundElevation * M2FT; - } - var loft_alt = me.loft_alt; - if (t_dist_m < me.old_speed_fps * 4 * FT2M and t_dist_m > me.old_speed_fps * 2.5 * FT2M) { - # the missile lofts a bit at the end to avoid APN to slam it into ground before target is reached. - # end here is between 2.5-4 seconds - loft_alt = me.loft_alt*2; - } - if (t_dist_m > me.old_speed_fps * 2.5 * FT2M) {# need to give the missile time to do final navigation - # it's 1 or 2 seconds for this kinds of missiles... - var t_alt_delta_ft = (loft_alt + Daground - me.alt); - #print("var t_alt_delta_m : "~t_alt_delta_m); - if(loft_alt + Daground > me.alt) { - # 200 is for a very short reaction to terrain - #print("Moving up"); - dev_e = -me.pitch + math.atan2(t_alt_delta_ft, me.old_speed_fps * dt * 5) * R2D; - } else { - # that means a dive angle of 22.5° (a bit less - # coz me.alt is in feet) (I let this alt in feet on purpose (more this figure is low, more the future pitch is high) - #print("Moving down"); - var slope = me.clamp(t_alt_delta_ft / 300, -5, 0);# the lower the desired alt is, the steeper the slope. - dev_e = -me.pitch + me.clamp(math.atan2(t_alt_delta_ft, me.old_speed_fps * dt * 5) * R2D, slope, 0); - } - cruise_or_loft = 1; - } elsif (t_dist_m > 500) { - # we put 9 feets up the target to avoid ground at the - # last minute... - #print("less than 1000 m to target"); - #dev_e = -me.pitch + math.atan2(t_alt_delta_m + 100, t_dist_m) * R2D; - #cruise_or_loft = 1; - } else { - #print("less than 500 m to target"); - } - if (cruise_or_loft == 1) { - #print(" pitch "~me.pitch~" + dev_e "~dev_e); - } - } elsif (me.loft_alt != 0 and t_dist_m * M2NM > loft_minimum - and t_elev_deg < loft_angle #and t_elev_deg > -7.5 - and me.dive_token == FALSE) { - # stage 1 lofting: due to target is more than 10 miles out and we havent reached - # our desired cruising alt, and the elevation to target is less than lofting angle. - # The -7.5 limit, is so the seeker don't lose track of target when lofting. - if (me.coord.alt() * M2FT < me.loft_alt) { - dev_e = -me.pitch + loft_angle; - #print(sprintf("Lofting %.1f degs, dev is %.1f", loft_angle, dev_e)); - } else { - me.dive_token = TRUE; - #print("Cruise token"); - } - cruise_or_loft = 1; - } elsif (me.rail == TRUE and me.rail_forward == FALSE and t_dist_m * M2NM > cruise_minimum and me.dive_token == FALSE) { - # tube launched missile turns towards target - - dev_e = -me.pitch + t_elev_deg; - #print("Turning, desire "~t_elev_deg~" degs pitch."); - cruise_or_loft = 1; - if (math.abs(me.curr_tgt_e) < 5) { - me.dive_token = TRUE; - #print("Is last turn, APN takes it from here..") - } - } elsif (t_elev_deg < 0 and me.life_time < me.stage_1_duration+me.stage_2_duration+me.drop_time - and t_dist_m * M2NM > cruise_minimum) { - # stage 1/2 cruising: keeping altitude since target is below and more than 5 miles out - - var attitude = math.asin((g_fps * dt)/me.old_speed_fps)*R2D; - - dev_e = -me.pitch + attitude; - #print("Cruising, desire "~attitude~" degs pitch."); - cruise_or_loft = 1; - me.dive_token = TRUE; - } - - - - - ########################################### - ### augmented proportional navigation ### - ########################################### - - var dist_curr = me.coord.distance_to(me.t_coord); - var dist_curr_direct = me.coord.direct_distance_to(me.t_coord); - if (h_gain != 0 and me.dist_last != nil and me.last_tgt_h != nil) { - # augmented proportional navigation for heading - var horz_closing_rate_fps = me.clamp((me.dist_last - dist_curr)*M2FT/dt, 0.1, 1000000);#clamped due to cruise missiles that can fly slower than target. - var proportionality_constant = 3;#ja37.clamp(me.map(me.speed_m, 2, 5, 5, 3), 3, 5);# - #setprop("payload/armament/factor-pro2", proportionality_constant); - var c_dv = t_course-me.last_t_course; - if(c_dv < -180) { - c_dv += 360; - } - if(c_dv > 180) { - c_dv -= 360; - } - var line_of_sight_rate_rps = D2R*c_dv/dt; - - - # calculate target acc as normal to LOS line: - var t_heading = me.Tgt.get_heading(); - var t_pitch = me.Tgt.get_Pitch(); - var t_speed = me.Tgt.get_Speed()*KT2FPS;#true airspeed - var t_horz_speed = t_speed - math.abs(math.sin(t_pitch*D2R)*t_speed); - var t_LOS_norm_head = t_course + 90; - var t_LOS_norm_speed = math.cos((t_LOS_norm_head - t_heading)*D2R)*t_horz_speed; - - if (me.last_t_norm_speed == nil) { - me.last_t_norm_speed = t_LOS_norm_speed; - } - - var t_LOS_norm_acc = (t_LOS_norm_speed - me.last_t_norm_speed)/dt; - - me.last_t_norm_speed = t_LOS_norm_speed; - - # acceleration perpendicular to instantaneous line of sight in feet/sec^2 - var acc_sideways_ftps2 = proportionality_constant*line_of_sight_rate_rps*horz_closing_rate_fps+proportionality_constant*t_LOS_norm_acc/2; - - # now translate that sideways acc to an angle: - var velocity_vector_length_fps = me.old_speed_horz_fps; - var commanded_sideways_vector_length_fps = acc_sideways_ftps2*dt; - dev_h = math.atan2(commanded_sideways_vector_length_fps, velocity_vector_length_fps)*R2D; - - #print(sprintf("LOS-rate=%.2f rad/s - closing-rate=%.1f ft/s",line_of_sight_rate_rps,horz_closing_rate_fps)); - #print(sprintf("commanded-perpendicular-acceleration=%.1f ft/s^2", acc_sideways_ftps2)); - #print(sprintf("horz leading by %.1f deg, commanding %.1f deg", me.curr_tgt_h, dev_h)); - - if (cruise_or_loft == 0 and me.last_cruise_or_loft == 0) { - # augmented proportional navigation for elevation - var vert_closing_rate_fps = me.clamp((me.dist_direct_last - dist_curr_direct)*M2FT/dt,0.1,1000000); - var line_of_sight_rate_up_rps = D2R*(t_elev_deg-me.last_t_elev_deg)/dt; - - # calculate target acc as normal to LOS line: (up acc is positive) - var t_approach_bearing = t_course + 180; - var t_horz_speed_away_from_missile = -math.cos((t_approach_bearing - t_heading)*D2R)* t_horz_speed; - var t_horz_comp_speed = math.cos((90+t_elev_deg)*D2R)*t_horz_speed_away_from_missile; - var t_vert_comp_speed = math.sin(t_pitch*D2R)*t_speed*math.cos(t_elev_deg*D2R); - var t_LOS_elev_norm_speed = t_horz_comp_speed + t_vert_comp_speed; - - if (me.last_t_elev_norm_speed == nil) { - me.last_t_elev_norm_speed = t_LOS_elev_norm_speed; - } - - var t_LOS_elev_norm_acc = (t_LOS_elev_norm_speed - me.last_t_elev_norm_speed)/dt; - me.last_t_elev_norm_speed = t_LOS_elev_norm_speed; - - var acc_upwards_ftps2 = proportionality_constant*line_of_sight_rate_up_rps*vert_closing_rate_fps+proportionality_constant*t_LOS_elev_norm_acc/2; - velocity_vector_length_fps = me.old_speed_fps; - var commanded_upwards_vector_length_fps = acc_upwards_ftps2*dt; - dev_e = math.atan2(commanded_upwards_vector_length_fps, velocity_vector_length_fps)*R2D; - #print(sprintf("vert leading by %.1f deg", me.curr_tgt_e)); - } - } - me.dist_last = dist_curr; - me.dist_direct_last = dist_curr_direct; - me.t_alt_delta_last_m = t_alt_delta_m; - me.last_tgt_h = me.curr_tgt_h; - me.last_tgt_e = me.curr_tgt_e; - - me.track_signal_e = dev_e * e_gain; - me.track_signal_h = dev_h * h_gain; - - #print(sprintf("%.1f deg elevate command", me.track_signal_e)); - #print(sprintf("%.1f deg bearing command, %.1f deg lead", me.track_signal_h, me.h_add)); - - me.last_t_course = t_course; - me.last_t_elev_deg = t_elev_deg; - me.last_cruise_or_loft = cruise_or_loft; - -#print ("**** curr_tgt_e = ", me.curr_tgt_e," curr_tgt_h = ", me.curr_tgt_h, " me.track_signal_e = ", me.track_signal_e," me.track_signal_h = ", me.track_signal_h); - - - } - - return TRUE; - }, - - map: func (value, leftMin, leftMax, rightMin, rightMax) { - # Figure out how 'wide' each range is - var leftSpan = leftMax - leftMin; - var rightSpan = rightMax - rightMin; - - # Convert the left range into a 0-1 range (float) - var valueScaled = (value - leftMin) / leftSpan; - - # Convert the 0-1 range into a value in the right range. - return rightMin + (valueScaled * rightSpan); - }, - - poximity_detection: func { - var cur_dir_dist_m = me.coord.direct_distance_to(me.t_coord); - # Get current direct distance. - if ( me.direct_dist_m != nil and me.life_time > me.arming_time) { - #print("distance to target_m = "~cur_dir_dist_m~" prev_distance to target_m = "~me.direct_dist_m); - if ( cur_dir_dist_m > me.direct_dist_m and cur_dir_dist_m < 250) { - #print("passed target"); - # Distance to target increase, trigger explosion. - me.explode("Passed target."); - return FALSE; - #} #elsif (cur_dir_dist_m < 15) { - # print("proximity fuse activated."); - #within killing distance, explode - #(this might not be how the real thing does, but due to this only being called every frame, might miss otherwise) - # me.explode(); - # return(0); - }# elsif (me.free == 1 and cur_dir_dist_m < m.prox_dist) { - #print("Magnetic fuse active."); - # lost lock, magnetic detector checks if close enough to explode - #me.explode(); - #return(0); - #} - if (me.life_time > me.selfdestruct_time) { - me.explode("Selfdestructed."); - return FALSE; - } - } - - ####Ground interaction - var ground = geo.elevation(me.coord.lat(), me.coord.lon()); - #print("Ground :",ground); - if(ground != nil and me.direct_dist_m != nil) - { - if(ground > me.coord.alt()) { - me.explode("Hit terrain."); - return FALSE; - } - } - me.before_last_t_coord = geo.Coord.new(me.last_t_coord); - me.last_t_coord = geo.Coord.new(me.t_coord); - me.direct_dist_m = cur_dir_dist_m; - return TRUE; - }, - - explode: func (reason) { - # Get missile relative position to the target at last frame. - var t_bearing_deg = me.last_t_coord.course_to(me.last_coord); - var t_delta_alt_m = me.last_coord.alt() - me.last_t_coord.alt(); - var new_t_alt_m = me.t_coord.alt() + t_delta_alt_m; - var t_dist_m = me.direct_dist_m; - - var min_distance = me.direct_dist_m; - var explosion_coord = me.last_coord; - #print("min1 "~min_distance); - #print("last_t to t : "~me.last_t_coord.direct_distance_to(me.t_coord)); - #print("last to current: "~me.last_coord.direct_distance_to(me.coord)); - for (var i = 0.05; i < 1; i += 0.05) { - var t_coord = me.interpolate(me.last_t_coord, me.t_coord, i); - var coord = me.interpolate(me.last_coord, me.coord, i); - var dist = coord.direct_distance_to(t_coord); - if (dist < min_distance) { - min_distance = dist; - explosion_coord = coord; - } - } - #print("min2 "~min_distance); - if (me.before_last_coord != nil and me.before_last_t_coord != nil) { - for (var i = 0.05; i < 1; i += 0.05) { - var t_coord = me.interpolate(me.before_last_t_coord, me.last_t_coord, i); - var coord = me.interpolate(me.before_last_coord, me.last_coord, i); - var dist = coord.direct_distance_to(t_coord); - if (dist < min_distance) { - min_distance = dist; - explosion_coord = coord; - } - } - } - me.coord = explosion_coord; - #print("min3 "~min_distance); - - # Create impact coords from this previous relative position applied to target current coord. - me.t_coord.apply_course_distance(t_bearing_deg, t_dist_m); - me.t_coord.set_alt(new_t_alt_m); - var wh_mass = me.weight_whead_lbs / slugs_to_lbs; - #print("FOX2: me.direct_dist_m = ", me.direct_dist_m, " time ",getprop("sim/time/elapsed-sec")); - impact_report(me.t_coord, wh_mass, "missile"); # pos, alt, mass_slug,(speed_mps) - - var phrase = sprintf( me.type~" exploded: %01.1f", min_distance) ~ " meters from: " ~ me.callsign; - print(phrase~" Reason: "~reason~sprintf(" time %.1f", me.life_time)); - if (min_distance < 650000 ) { - if (getprop("payload/armament/msg")) { - setprop("/sim/multiplay/chat", armament.defeatSpamFilter(phrase)); - } else { - setprop("/sim/messages/atc", phrase); - } - } - - me.ai.getNode("valid", 1).setBoolValue(0); - me.animate_explosion(); - me.Tgt = nil; - }, - - interpolate: func (start, end, fraction) { - var x = (start.x()*(1-fraction)+end.x()*fraction); - var y = (start.y()*(1-fraction)+end.y()*fraction); - var z = (start.z()*(1-fraction)+end.z()*fraction); - - var c = geo.Coord.new(); - c.set_xyz(x,y,z); - - return c; - }, - - # aircraft searching for lock - search: func { - if ( me.status == MISSILE_FLYING ) { - me.SwSoundVol.setDoubleValue(0); - me.SwSoundOnOff.setBoolValue(FALSE); - return; - } elsif ( me.status == MISSILE_STANDBY ) { - # Stand by. - me.SwSoundVol.setDoubleValue(0); - me.SwSoundOnOff.setBoolValue(FALSE); - me.trackWeak = 1; - return; - } elsif ( me.status > MISSILE_SEARCH ) { - # Locked or fired. - return; - } - #print("search"); - # search. - if (1==1 or contact != me.Tgt) { - #print("search2"); - if (contact != nil and contact.isValid() == TRUE and - ( (contact.get_type() == radar_logic.SURFACE and me.class == "A/G") - or (contact.get_type() == radar_logic.AIR and me.class == "A/A") - or (contact.get_type() == radar_logic.MARINE and me.class == "A/G"))) { - #print("search3"); - var tgt = contact; # In the radar range and horizontal field. - var rng = tgt.get_range(); - var total_elev = deviation_normdeg(OurPitch.getValue(), tgt.getElevation()); # deg. - var total_horiz = deviation_normdeg(OurHdg.getValue(), tgt.get_bearing()); # deg. - # Check if in range and in the (square shaped here) seeker FOV. - var abs_total_elev = math.abs(total_elev); - var abs_dev_deg = math.abs(total_horiz); - if ((me.guidance != "semi-radar" or me.is_painted(tgt) == TRUE) - and rng < me.max_detect_rng and abs_total_elev < me.aim9_fov and abs_dev_deg < me.aim9_fov ) { - #print("search4"); - me.status = MISSILE_LOCK; - me.SwSoundOnOff.setBoolValue(TRUE); - me.SwSoundVol.setDoubleValue(vol_weak_track); - me.trackWeak = 1; - me.Tgt = tgt; - - me.callsign = me.Tgt.get_Callsign(); - - var time = props.globals.getNode("/sim/time/elapsed-sec", 1).getValue(); - me.update_track_time = time; - - settimer(func me.update_lock(), 0.1); - return; - } else { - me.Tgt = nil; - } - } else { - me.Tgt = nil; - } - } - me.SwSoundVol.setDoubleValue(me.vol_search); - me.SwSoundOnOff.setBoolValue(TRUE); - me.trackWeak = 1; - settimer(func me.search(), 0.1); - }, - - # Missile locked on target - update_lock: func() { - if ( me.Tgt == nil or me.status == MISSILE_FLYING) { - return TRUE; - } - if (me.status == MISSILE_SEARCH) { - # Status = searching. - #print("search commanded"); - me.return_to_search(); - return TRUE; - } elsif ( me.status == MISSILE_STANDBY ) { - # Status = stand-by. - me.reset_seeker(); - me.SwSoundOnOff.setBoolValue(FALSE); - me.SwSoundVol.setDoubleValue(0); - me.trackWeak = 1; - return TRUE; - } elsif (!me.Tgt.isValid()) { - # Lost of lock due to target disapearing: - # return to search mode. - #print("invalid"); - me.return_to_search(); - return TRUE; - } - #print("lock"); - # Time interval since lock time or last track loop. - - var last_tgt_e = me.curr_tgt_e; - var last_tgt_h = me.curr_tgt_h; - if (me.status == MISSILE_LOCK) { - # Status = locked. Get target position relative to our aircraft. - me.curr_tgt_e = - deviation_normdeg(OurPitch.getValue(), me.Tgt.getElevation()); - me.curr_tgt_h = - deviation_normdeg(OurHdg.getValue(), me.Tgt.get_bearing()); - } - - var time = props.globals.getNode("/sim/time/elapsed-sec", 1).getValue(); - - # Compute HUD reticle position. - if ( use_fg_default_hud == TRUE and me.status == MISSILE_LOCK ) { - var h_rad = (90 - me.curr_tgt_h) * D2R; - var e_rad = (90 - me.curr_tgt_e) * D2R; - var devs = develev_to_devroll(h_rad, e_rad); - var combined_dev_deg = devs[0]; - var combined_dev_length = devs[1]; - var clamped = devs[2]; - if ( clamped ) { SW_reticle_Blinker.blink();} - else { SW_reticle_Blinker.cont();} - HudReticleDeg.setDoubleValue(combined_dev_deg); - HudReticleDev.setDoubleValue(combined_dev_length); - } - if (me.status != MISSILE_STANDBY ) { - var in_view = me.check_t_in_fov(); - if (in_view == FALSE) { - #print("out of view"); - me.return_to_search(); - return TRUE; - } - # We are not launched yet: update_track() loops by itself at 10 Hz. - var dist = geo.aircraft_position().direct_distance_to(me.Tgt.get_Coord()); - if (time - me.update_track_time > 1 and dist != nil and dist > (me.min_dist * NM2M)) { - # after 1 second we get solid track if target is further than minimum distance. - me.SwSoundOnOff.setBoolValue(TRUE); - me.SwSoundVol.setDoubleValue(vol_track); - me.trackWeak = 0; - } else { - me.SwSoundOnOff.setBoolValue(TRUE); - me.SwSoundVol.setDoubleValue(vol_weak_track); - me.trackWeak = 1; - } - if (contact == nil or (contact.getUnique() != nil and me.Tgt.getUnique() != nil and contact.getUnique() != me.Tgt.getUnique())) { - #print("oops "); - me.return_to_search(); - return TRUE; - } - settimer(func me.update_lock(), 0.1); - } - return TRUE; - }, - - return_to_search: func { - me.status = MISSILE_SEARCH; - me.Tgt = nil; - me.SwSoundOnOff.setBoolValue(TRUE); - me.SwSoundVol.setDoubleValue(me.vol_search); - me.trackWeak = 1; - me.reset_seeker(); - #print("return"); - settimer(func me.search(), 0.1); - }, - - # - check_t_in_fov: func { - - var total_elev = deviation_normdeg(OurPitch.getValue(), me.Tgt.getElevation()); # deg. - var total_horiz = deviation_normdeg(OurHdg.getValue(), me.Tgt.get_bearing()); # deg. - # Check if in range and in the (square shaped here) seeker FOV. - var abs_total_elev = math.abs(total_elev); - var abs_dev_deg = math.abs(total_horiz); - if (abs_total_elev < me.aim9_fov and abs_dev_deg < me.aim9_fov ) { - # Target out of FOV while still not launched, return to search loop. - return TRUE; - } - return FALSE; - - - # Used only when not launched. - # Compute seeker total angular position clamped to seeker max total angular rotation. - #me.seeker_dev_e += me.track_signal_e; - #me.seeker_dev_e = me.clamp_min_max(me.seeker_dev_e, me.max_seeker_dev); - #me.seeker_dev_h += me.track_signal_h; - #me.seeker_dev_h = me.clamp_min_max(me.seeker_dev_h, me.max_seeker_dev); - # Check target signal inside seeker FOV. - #var e_d = me.seeker_dev_e - me.aim9_fov; - #var e_u = me.seeker_dev_e + me.aim9_fov; - #var h_l = me.seeker_dev_h - me.aim9_fov; - #var h_r = me.seeker_dev_h + me.aim9_fov; - #if (me.status != MISSILE_FLYING and (me.curr_tgt_e < e_d or me.curr_tgt_e > e_u or me.curr_tgt_h < h_l or me.curr_tgt_h > h_r) ) { - # Target out of FOV while still not launched, return to search loop. - # return FALSE; - #} - #return TRUE; - }, - - #done - reset_steering: func { - me.track_signal_e = 0; - me.track_signal_h = 0; - }, - - is_painted: func (target) { - if(target != nil and target.isPainted() != nil and target.isPainted() == TRUE) { - return TRUE; - } - return FALSE; - }, - - reset_seeker: func { - me.curr_tgt_e = 0; - me.curr_tgt_h = 0; - me.seeker_dev_e = 0; - me.seeker_dev_h = 0; - settimer(func { HudReticleDeg.setDoubleValue(0) }, 2); - interpolate(HudReticleDev, 0, 2); - me.reset_steering() - }, - - - #done - clamp_min_max: func (v, mm) { - if ( v < -mm ) { - v = -mm; - } elsif ( v > mm ) { - v = mm; - } - return(v); - }, - - clamp: func(v, min, max) { v < min ? min : v > max ? max : v }, - - animation_flags_props: func { - # Create animation flags properties. - var msl_path = "payload/armament/"~me.type_lc~"/flags/msl-id-" ~ me.ID; - me.msl_prop = props.globals.initNode( msl_path, 1, "BOOL", 1); - var smoke_path = "payload/armament/"~me.type_lc~"/flags/smoke-id-" ~ me.ID; - me.smoke_prop = props.globals.initNode( smoke_path, 0, "BOOL", 1); - var explode_path = "payload/armament/"~me.type_lc~"/flags/explode-id-" ~ me.ID; - me.explode_prop = props.globals.initNode( explode_path, 0, "BOOL", 1); - var explode_smoke_path = "payload/armament/"~me.type_lc~"/flags/explode-smoke-id-" ~ me.ID; - me.explode_smoke_prop = props.globals.initNode( explode_smoke_path, 0, "BOOL", 1); - var explode_sound_path = "payload/armament/flags/explode-sound-on-" ~ me.ID;; - me.explode_sound_prop = props.globals.initNode( explode_sound_path, 0, "BOOL", 1); - var explode_sound_vol_path = "payload/armament/flags/explode-sound-vol-" ~ me.ID;; - me.explode_sound_vol_prop = props.globals.initNode( explode_sound_vol_path, 0, "DOUBLE", 1); - }, - - - #done - animate_explosion: func { - # a last position update to where the explosion happened: - me.latN.setDoubleValue(me.coord.lat()); - me.lonN.setDoubleValue(me.coord.lon()); - me.altN.setDoubleValue(me.coord.alt()*M2FT); - - me.msl_prop.setBoolValue(0); - me.smoke_prop.setBoolValue(0); - me.explode_prop.setBoolValue(1); - settimer( func me.explode_prop.setBoolValue(0), 0.5 ); - settimer( func me.explode_smoke_prop.setBoolValue(1), 0.5 ); - settimer( func me.explode_smoke_prop.setBoolValue(0), 3 ); - #var delay = me.Tgt.getNode("radar/range-nm").getValue()*4.689; - #settimer( func me.explode_sound_prop.setBoolValue(1), delay ); - #settimer( func me.explode_sound_prop.setBoolValue(0), delay+3 ); - }, - - sndPropagate: func { - var dt = getprop("sim/time/delta-sec"); - if (dt == 0) { - #FG is likely paused - settimer(func me.sndPropagate(), 0.01); - return; - } - #dt = update_loop_time; - var elapsed = systime(); - if (me.dt_last != 0) { - dt = (elapsed - me.dt_last) * getprop("sim/speed-up"); - } - me.dt_last = elapsed; - - me.ac = geo.aircraft_position(); - var distance = me.coord.direct_distance_to(me.ac); - - me.sndDistance = me.sndDistance + (me.sndSpeed * dt) * FT2M; - if(me.sndDistance > distance) { - var volume = math.pow(2.71828,(-.00025*(distance-1000))); - #print("explosion heard "~distance~"m vol:"~volume); - me.explode_sound_vol_prop.setDoubleValue(volume); - me.explode_sound_prop.setBoolValue(1); - settimer( func me.explode_sound_prop.setBoolValue(0), 3); - settimer( func me.del(), 4); - return; - } elsif (me.sndDistance > 5000) { - settimer(func { me.del(); }, 4 ); - } else { - settimer(func me.sndPropagate(), 0.05); - return; - } - }, - - active: {}, - flying: {}, -}; - - -# Create impact report. - -#altitde-agl-ft DOUBLE -#impact -# elevation-m DOUBLE -# heading-deg DOUBLE -# latitude-deg DOUBLE -# longitude-deg DOUBLE -# pitch-deg DOUBLE -# roll-deg DOUBLE -# speed-mps DOUBLE -# type STRING -#valid "true" BOOL - - -var impact_report = func(pos, mass_slug, string) { - - # Find the next index for "ai/models/model-impact" and create property node. - var n = props.globals.getNode("ai/models", 1); - for (var i = 0; 1; i += 1) - if (n.getChild(string, i, 0) == nil) - break; - var impact = n.getChild(string, i, 1); - - impact.getNode("impact/elevation-m", 1).setDoubleValue(pos.alt()); - impact.getNode("impact/latitude-deg", 1).setDoubleValue(pos.lat()); - impact.getNode("impact/longitude-deg", 1).setDoubleValue(pos.lon()); - impact.getNode("mass-slug", 1).setDoubleValue(mass_slug); - #impact.getNode("speed-mps", 1).setValue(speed_mps); - impact.getNode("valid", 1).setBoolValue(1); - impact.getNode("impact/type", 1).setValue("terrain"); - - var impact_str = "/ai/models/" ~ string ~ "[" ~ i ~ "]"; - setprop("ai/models/model-impact", impact_str); - -} - -var steering_speed_G = func(steering_e_deg, steering_h_deg, s_fps, dt) { - # Get G number from steering (e, h) in deg, speed in ft/s. - var steer_deg = math.sqrt((steering_e_deg*steering_e_deg) + (steering_h_deg*steering_h_deg)); - - # next speed vector - var vector_next_x = math.cos(steer_deg*D2R)*s_fps; - var vector_next_y = math.sin(steer_deg*D2R)*s_fps; - - # present speed vector - var vector_now_x = s_fps; - var vector_now_y = 0; - - # subtract the vectors from each other - var dv = math.sqrt((vector_now_x - vector_next_x)*(vector_now_x - vector_next_x)+(vector_now_y - vector_next_y)*(vector_now_y - vector_next_y)); - - # calculate g-force - # dv/dt=a - var g = (dv/dt) / g_fps; - - # old calc with circle: - #var radius_ft = math.abs(s_fps / math.sin(steer_deg*D2R)); - #var g = ( (s_fps * s_fps) / radius_ft ) / g_fps; - #print("#### R = ", radius_ft, " G = ", g); ########################################################## - return g; -} - -var semi_old_max_G_Rotation = func(steering_e_deg, steering_h_deg, s_fps, dt, gMax) { - for(var i = 1; i >= 0; i-=0.005) { - var new_g = steering_speed_G(steering_e_deg*i, steering_h_deg*i, s_fps, dt); - if (new_g < gMax) { - return i; - } - } - return 0; -} - -var max_G_Rotation = func(steering_e_deg, steering_h_deg, s_fps, dt, gMax) { - var guess = 1; - var coef = 1; - var lastgoodguess = 1; - - for(var i=1;i<25;i+=1){ - coef = coef/2; - var new_g = steering_speed_G(steering_e_deg*guess, steering_h_deg*guess, s_fps, dt); - if (new_g < gMax) { - lastgoodguess = guess; - guess = guess + coef; - } else { - guess = guess - coef; - } - } - return lastgoodguess; -} - -var old_max_G_Rotation = func(steering_e_deg, steering_h_deg, s_fps, dt,gMax) { - # Get G number from steering (e, h) in deg, speed in ft/s. - #This function is for calculate the maximum angle without overload G - - var steer_deg = math.sqrt((steering_e_deg*steering_e_deg) + (steering_h_deg*steering_h_deg)); - var radius_ft = math.abs(s_fps / math.cos((90 - steer_deg)*D2R)); - var g = ( (s_fps * s_fps) / radius_ft ) / g_fps; - - #Isolation of Radius - if (s_fps < 1) { - s_fps = 1; - } - var radius_ft2 = ( s_fps * s_fps) / (gMax * 0.95 * g_fps); - if (math.abs(s_fps / radius_ft2) < 1) { - var steer_rad_theoric = math.acos(math.abs(s_fps/radius_ft2)); - var steer_deg_theoric = 90 - (steer_rad_theoric * R2D); - } else { - var steer_rad_theoric = 1; - var steer_deg_theoric = 1; - } - - var radius_ft_th = math.abs(s_fps / math.cos((90 -steer_deg_theoric)*D2R)); - var g_th = ( (s_fps * s_fps) / radius_ft_th ) / g_fps; - - #print ("Max G ", gMax , " Actual G " , g, " steer_deg_theoric ", steer_deg_theoric, " G theoretic=", g_th); - - return (steer_deg_theoric / steer_deg); -} - - -# HUD clamped target blinker -SW_reticle_Blinker = aircraft.light.new("payload/armament/hud/hud-sw-reticle-switch", [0.1, 0.1]); -setprop("payload/armament/hud/hud-sw-reticle-switch/enabled", 1); - - - - - -var OurRoll = props.globals.getNode("orientation/roll-deg"); -var eye_hud_m = 0.6;#pilot: -3.30 hud: -3.9 -var hud_radius_m = 0.100; - -#was in hud -var develev_to_devroll = func(dev_rad, elev_rad) { - var clamped = 0; - # Deviation length on the HUD (at level flight), - # 0.6686m = distance eye <-> virtual HUD screen. - var h_dev = eye_hud_m / ( math.sin(dev_rad) / math.cos(dev_rad) ); - var v_dev = eye_hud_m / ( math.sin(elev_rad) / math.cos(elev_rad) ); - # Angle between HUD center/top <-> HUD center/symbol position. - # -90° left, 0° up, 90° right, +/- 180° down. - var dev_deg = math.atan2( h_dev, v_dev ) * R2D; - # Correction with own a/c roll. - var combined_dev_deg = dev_deg - OurRoll.getValue(); - # Lenght HUD center <-> symbol pos on the HUD: - var combined_dev_length = math.sqrt((h_dev*h_dev)+(v_dev*v_dev)); - # clamp and squeeze the top of the display area so the symbol follow the egg shaped HUD limits. - var abs_combined_dev_deg = math.abs( combined_dev_deg ); - var clamp = hud_radius_m; - if ( abs_combined_dev_deg >= 0 and abs_combined_dev_deg < 90 ) { - var coef = ( 90 - abs_combined_dev_deg ) * 0.00075; - if ( coef > 0.050 ) { coef = 0.050 } - clamp -= coef; - } - if ( combined_dev_length > clamp ) { - combined_dev_length = clamp; - clamped = 1; - } - var v = [combined_dev_deg, combined_dev_length, clamped]; - return(v); - -} - -#was in radar -var deviation_normdeg = func(our_heading, target_bearing) { - var dev_norm = our_heading - target_bearing; - while (dev_norm < -180) dev_norm += 360; - while (dev_norm > 180) dev_norm -= 360; - return(dev_norm); -} - -#was environment -var const_e = 2.71828183; - -var rho_sndspeed = func(altitude) { - # Calculate density of air: rho - # at altitude (ft), using standard atmosphere, - # standard temperature T and pressure p. - - var T = 0; - var p = 0; - if (altitude < 36152) { - # curve fits for the troposphere - T = 59 - 0.00356 * altitude; - p = 2116 * math.pow( ((T + 459.7) / 518.6) , 5.256); - } elsif ( 36152 < altitude and altitude < 82345 ) { - # lower stratosphere - T = -70; - p = 473.1 * math.pow( const_e , 1.73 - (0.000048 * altitude) ); - } else { - # upper stratosphere - T = -205.05 + (0.00164 * altitude); - p = 51.97 * math.pow( ((T + 459.7) / 389.98) , -11.388); - } - - var rho = p / (1718 * (T + 459.7)); - - # calculate the speed of sound at altitude - # a = sqrt ( g * R * (T + 459.7)) - # where: - # snd_speed in feet/s, - # g = specific heat ratio, which is usually equal to 1.4 - # R = specific gas constant, which equals 1716 ft-lb/slug/R - - var snd_speed = math.sqrt( 1.4 * 1716 * (T + 459.7)); - return [rho, snd_speed]; - -} - -var nextGeoloc = func(lon, lat, heading, speed, dt, alt=100){ - # lng & lat & heading, in degree, speed in fps - # this function should send back the futures lng lat - var distance = speed * dt * FT2M; # should be a distance in meters - #print("distance ", distance); - # much simpler than trigo - var NextGeo = geo.Coord.new().set_latlon(lon, lat, alt); - NextGeo.apply_course_distance(heading, distance); - return NextGeo; -} - -#var AIM_instance = [nil, nil,nil,nil];#init aim-9 - -var spams = 0; - -var defeatSpamFilter = func (str) { - spams += 1; - if (spams == 15) { - spams = 1; - } - str = str~":"; - for (var i = 1; i <= spams; i+=1) { - str = str~"."; - } - return str; -} \ No newline at end of file diff --git a/Frigate/Nasal/radar-logic.nas b/Frigate/Nasal/radar-logic.nas deleted file mode 100644 index dcc4cdf..0000000 --- a/Frigate/Nasal/radar-logic.nas +++ /dev/null @@ -1,424 +0,0 @@ -var clamp = func(v, min, max) { v < min ? min : v > max ? max : v } -var encode3bits = func(first, second, third) { - var integer = first; - integer = integer + 2 * second; - integer = integer + 4 * third; - return integer; -} - - -var AIR = 0; -var MARINE = 1; -var SURFACE = 2; -var ORDNANCE = 3; - -var Contact = { - # For now only used in guided missiles, to make it compatible with Mirage 2000-5. - new: func(c, class) { - var obj = { parents : [Contact]}; -#debug.benchmark("radar process1", func { - obj.rdrProp = c.getNode("radar"); - obj.oriProp = c.getNode("orientation"); - obj.velProp = c.getNode("velocities"); - obj.posProp = c.getNode("position"); - obj.heading = obj.oriProp.getNode("true-heading-deg"); -#}); -#debug.benchmark("radar process2", func { - obj.alt = obj.posProp.getNode("altitude-ft"); - obj.lat = obj.posProp.getNode("latitude-deg"); - obj.lon = obj.posProp.getNode("longitude-deg"); -#}); -#debug.benchmark("radar process3", func { - #As it is a geo.Coord object, we have to update lat/lon/alt ->and alt is in meters - obj.coord = geo.Coord.new(); - obj.coord.set_latlon(obj.lat.getValue(), obj.lon.getValue(), obj.alt.getValue() * FT2M); -#}); -#debug.benchmark("radar process4", func { - obj.pitch = obj.oriProp.getNode("pitch-deg"); - obj.speed = obj.velProp.getNode("true-airspeed-kt"); - obj.vSpeed = obj.velProp.getNode("vertical-speed-fps"); - obj.callsign = c.getNode("callsign", 1); - obj.shorter = c.getNode("model-shorter"); - obj.orig_callsign = obj.callsign.getValue(); - obj.name = c.getNode("name"); - obj.sign = c.getNode("sign",1); - obj.valid = c.getNode("valid"); - obj.painted = c.getNode("painted"); - obj.unique = c.getNode("unique"); - obj.validTree = 0; -#}); -#debug.benchmark("radar process5", func { - #obj.transponderID = c.getNode("instrumentation/transponder/transmitted-id"); -#}); -#debug.benchmark("radar process6", func { - obj.acType = c.getNode("sim/model/ac-type"); - obj.type = c.getName(); - obj.index = c.getIndex(); - obj.string = "ai/models/" ~ obj.type ~ "[" ~ obj.index ~ "]"; - obj.shortString = obj.type ~ "[" ~ obj.index ~ "]"; -#}); -#debug.benchmark("radar process7", func { - obj.range = obj.rdrProp.getNode("range-nm"); - obj.bearing = obj.rdrProp.getNode("bearing-deg"); - obj.elevation = obj.rdrProp.getNode("elevation-deg"); -#}); - obj.deviation = nil; - - obj.node = c; - obj.class = class; - - obj.polar = [0,0]; - obj.cartesian = [0,0]; - - return obj; - }, - - isValid: func () { - var valid = me.valid.getValue(); - if (valid == nil) { - valid = FALSE; - } - if (me.callsign.getValue() != me.orig_callsign) { - valid = FALSE; - } - return valid; - }, - - isPainted: func () { - if (me.painted == nil) { - me.painted = me.node.getNode("painted"); - } - if (me.painted == nil) { - return nil; - } - var p = me.painted.getValue(); - return p; - }, - - getUnique: func () { - if (me.unique == nil) { - me.unique = me.node.getNode("unique"); - } - if (me.unique == nil) { - return nil; - } - var u = me.unique.getValue(); - return u; - }, - - getElevation: func() { - var e = 0; - e = me.elevation.getValue(); - if(e == nil or e == 0) { - # AI/MP has no radar properties - var self = geo.aircraft_position(); - me.get_Coord(); - var angleInv = clamp(self.distance_to(me.coord)/self.direct_distance_to(me.coord), -1, 1); - e = (self.alt()>me.coord.alt()?-1:1)*math.acos(angleInv)*R2D; - } - return e; - }, - - getNode: func () { - return me.node; - }, - - getFlareNode: func () { - return me.node.getNode("sim/multiplay/generic/string[10]"); - }, - - setPolar: func(dist, angle) { - me.polar = [dist,angle]; - }, - - setCartesian: func(x, y) { - me.cartesian = [x,y]; - }, - - remove: func(){ - if(me.validTree != 0){ - me.validTree.setValue(0); - } - }, - - get_Coord: func(){ - me.coord.set_latlon(me.lat.getValue(), me.lon.getValue(), me.alt.getValue() * FT2M); - var TgTCoord = geo.Coord.new(me.coord); - return TgTCoord; - }, - - get_Callsign: func(){ - var n = me.callsign.getValue(); - if(n != "" and n != nil) { - return n; - } - if (me.name == nil) { - me.name = me.getNode().getNode("name"); - } - if (me.name == nil) { - n = ""; - } else { - n = me.name.getValue(); - } - if(n != "" and n != nil) { - return n; - } - n = me.sign.getValue(); - if(n != "" and n != nil) { - return n; - } - return "UFO"; - }, - - get_model: func(){ - var n = ""; - if (me.shorter == nil) { - me.shorter = me.node.getNode("model-shorter"); - } - if (me.shorter != nil) { - n = me.shorter.getValue(); - } - if(n != "" and n != nil) { - return n; - } - n = me.sign.getValue(); - if(n != "" and n != nil) { - return n; - } - if (me.name == nil) { - me.name = me.getNode().getNode("name"); - } - if (me.name == nil) { - n = ""; - } else { - n = me.name.getValue(); - } - if(n != "" and n != nil) { - return n; - } - return me.get_Callsign(); - }, - - get_Speed: func(){ - # return true airspeed - var n = me.speed.getValue(); - return n; - }, - - get_Longitude: func(){ - var n = me.lon.getValue(); - return n; - }, - - get_Latitude: func(){ - var n = me.lat.getValue(); - return n; - }, - - get_Pitch: func(){ - var n = me.pitch.getValue(); - return n; - }, - - get_heading : func(){ - var n = me.heading.getValue(); - if(n == nil) - { - n = 0; - } - return n; - }, - - get_bearing: func(){ - var n = 0; - n = me.bearing.getValue(); - if(n == nil or n == 0) { - # AI/MP has no radar properties - n = me.get_bearing_from_Coord(geo.aircraft_position()); - } - return n; - }, - - get_bearing_from_Coord: func(MyAircraftCoord){ - me.get_Coord(); - var myBearing = 0; - if(me.coord.is_defined()) { - myBearing = MyAircraftCoord.course_to(me.coord); - } - return myBearing; - }, - - get_reciprocal_bearing: func(){ - return geo.normdeg(me.get_bearing() + 180); - }, - - get_deviation: func(true_heading_ref, coord){ - me.deviation = - deviation_normdeg(true_heading_ref, me.get_bearing_from_Coord(coord)); - return me.deviation; - }, - - get_altitude: func(){ - #Return Alt in feet - return me.alt.getValue(); - }, - - get_Elevation_from_Coord: func(MyAircraftCoord) { - me.get_Coord(); - var value = (me.coord.alt() - MyAircraftCoord.alt()) / me.coord.direct_distance_to(MyAircraftCoord); - if (math.abs(value) > 1) { - # warning this else will fail if logged in as observer and see aircraft on other side of globe - return 0; - } - var myPitch = math.asin(value) * R2D; - return myPitch; - }, - - get_total_elevation_from_Coord: func(own_pitch, MyAircraftCoord){ - var myTotalElevation = - deviation_normdeg(own_pitch, me.get_Elevation_from_Coord(MyAircraftCoord)); - return myTotalElevation; - }, - - get_total_elevation: func(own_pitch) { - me.deviation = - deviation_normdeg(own_pitch, me.getElevation()); - return me.deviation; - }, - - get_range: func() { - var r = 0; - if(me.range == nil or me.range.getValue() == nil or me.range.getValue() == 0) { - # AI/MP has no radar properties - me.get_Coord(); - r = me.coord.direct_distance_to(geo.aircraft_position()) * M2NM; - } else { - r = me.range.getValue(); - } - return r; - }, - - get_range_from_Coord: func(MyAircraftCoord) { - var myCoord = me.get_Coord(); - var myDistance = 0; - if(myCoord.is_defined()) { - myDistance = MyAircraftCoord.direct_distance_to(myCoord) * M2NM; - } - return myDistance; - }, - - get_type: func () { - return me.class; - }, - - get_cartesian: func() { - return me.cartesian; - }, - - get_polar: func() { - return me.polar; - }, -}; - -var isNotBehindTerrain = func( mp ) { - -########### - var pos = mp.getNode("position"); - var alt = pos.getNode("altitude-ft").getValue(); - var lat = pos.getNode("latitude-deg").getValue(); - var lon = pos.getNode("longitude-deg").getValue(); - if(alt == nil or lat == nil or lon == nil) { - return isVisible = 0; - } - var aircraftPos = geo.Coord.new().set_latlon(lat, lon, alt*0.3048); -################# - - - var isVisible = 0; - var MyCoord = geo.aircraft_position(); - - # Because there is no terrain on earth that can be between these 2 - if(MyCoord.alt() < 8900 and aircraftPos.alt() < 8900) - { - # Temporary variable - # A (our plane) coord in meters - var a = MyCoord.x(); - var b = MyCoord.y(); - var c = MyCoord.z(); - # B (target) coord in meters - var d = aircraftPos.x(); - var e = aircraftPos.y(); - var f = aircraftPos.z(); - var x = 0; - var y = 0; - var z = 0; - var RecalculatedL = 0; - var difa = d - a; - var difb = e - b; - var difc = f - c; - # direct Distance in meters - var myDistance = aircraftPos.direct_distance_to(MyCoord); - var Aprime = geo.Coord.new(); - - # Here is to limit FPS drop on very long distance - var L = 500; - if(myDistance > 50000) - { - L = myDistance / 15; - } - var step = L; - var maxLoops = int(myDistance / L); - - isVisible = 1; - # This loop will make travel a point between us and the target and check if there is terrain - for(var i = 0 ; i < maxLoops ; i += 1) - { - L = i * step; - var K = (L * L) / (1 + (-1 / difa) * (-1 / difa) * (difb * difb + difc * difc)); - var DELTA = (-2 * a) * (-2 * a) - 4 * (a * a - K); - - if(DELTA >= 0) - { - # So 2 solutions or 0 (1 if DELTA = 0 but that 's just 2 solution in 1) - var x1 = (-(-2 * a) + math.sqrt(DELTA)) / 2; - var x2 = (-(-2 * a) - math.sqrt(DELTA)) / 2; - # So 2 y points here - var y1 = b + (x1 - a) * (difb) / (difa); - var y2 = b + (x2 - a) * (difb) / (difa); - # So 2 z points here - var z1 = c + (x1 - a) * (difc) / (difa); - var z2 = c + (x2 - a) * (difc) / (difa); - # Creation Of 2 points - var Aprime1 = geo.Coord.new(); - Aprime1.set_xyz(x1, y1, z1); - - var Aprime2 = geo.Coord.new(); - Aprime2.set_xyz(x2, y2, z2); - - # Here is where we choose the good - if(math.round((myDistance - L), 2) == math.round(Aprime1.direct_distance_to(aircraftPos), 2)) - { - Aprime.set_xyz(x1, y1, z1); - } - else - { - Aprime.set_xyz(x2, y2, z2); - } - var AprimeLat = Aprime.lat(); - var Aprimelon = Aprime.lon(); - var AprimeTerrainAlt = geo.elevation(AprimeLat, Aprimelon); - if(AprimeTerrainAlt == nil) - { - AprimeTerrainAlt = 0; - } - - if(AprimeTerrainAlt > Aprime.alt()) - { - isVisible = 0; - } - } - } - } - else - { - isVisible = 1; - } - return isVisible; -} \ No newline at end of file diff --git a/depot/Nasal/damage.nas b/depot/Nasal/damage.nas index df199d2..6af7830 100644 --- a/depot/Nasal/damage.nas +++ b/depot/Nasal/damage.nas @@ -42,16 +42,6 @@ var warhead_lbs = { "AGM-84": 488.00, "AGM-88": 146.00, "AGM65": 200.00, - "aim-120": 44.00, - "AIM-120": 44.00, - "AIM-54": 135.00, - "aim-7": 88.00, - "AIM-7": 88.00, - "aim-9": 20.80, - "AIM-9": 20.80, - "AIM120": 44.00, - "AIM132": 22.05, - "AIM9": 20.80, "ALARM": 450.00, "AM39-Exocet": 364.00, "AS-37-Martel": 330.00, @@ -61,12 +51,13 @@ var warhead_lbs = { "FAB-250": 202.85, "FAB-500": 564.38, "GBU-12": 190.00, + "GBU-24": 945.00, "GBU-31": 945.00, "GBU12": 190.00, "GBU16": 450.00, "HVAR": 7.50,#P51 "KAB-500": 564.38, - "KH-25MP": 197.53, + "Kh-25MP": 197.53, "Kh-66": 244.71, "KN-06": 315.00, "LAU-68": 10.00, @@ -74,40 +65,14 @@ var warhead_lbs = { "M71": 200.00, "M71R": 200.00, "M90": 500.00, - "Magic-2": 27.00, - "Matra MICA": 30.00, - "Matra R550 Magic 2": 27.00, - "MATRA-R530": 55.00, - "MatraMica": 30.00, - "MatraMicaIR": 30.00, - "MatraR550Magic2": 27.00, - "Meteor": 55.00, - "MICA-EM": 30.00, - "MICA-IR": 30.00, "MK-82": 192.00, "MK-83": 445.00, "MK-84": 945.00, "OFAB-100": 92.59, - "R-13M": 16.31, - "R-27R1": 85.98, - "R-27T1": 85.98, - "R-3R": 16.31, - "R-3S": 16.31, - "R-55": 20.06, - "R-60": 6.60, - "R-60M": 7.70, - "R-73E": 16.31, - "R-77": 49.60, - "R74": 16.00, "RB-04E": 661.00, "RB-05A": 353.00, "RB-15F": 440.92, - "RB-24": 20.80, - "RB-24J": 20.80, - "RB-71": 88.00, - "RB-74": 20.80, "RB-75": 126.00, - "RB-99": 44.00, "RN-14T": 800.00, #fictional, thermobaeric replacement for the RN-24 nuclear bomb "RN-18T": 1200.00, #fictional, thermobaeric replacement for the RN-28 nuclear bomb "RS-2US": 28.66, diff --git a/groundtarget/Nasal/damage.nas b/groundtarget/Nasal/damage.nas index 8d34bee..d740cee 100644 --- a/groundtarget/Nasal/damage.nas +++ b/groundtarget/Nasal/damage.nas @@ -28,16 +28,6 @@ var warhead_lbs = { "AGM-84": 488.00, "AGM-88": 146.00, "AGM65": 200.00, - "aim-120": 44.00, - "AIM-120": 44.00, - "AIM-54": 135.00, - "aim-7": 88.00, - "AIM-7": 88.00, - "aim-9": 20.80, - "AIM-9": 20.80, - "AIM120": 44.00, - "AIM132": 22.05, - "AIM9": 20.80, "ALARM": 450.00, "AM39-Exocet": 364.00, "AS-37-Martel": 330.00, @@ -47,12 +37,13 @@ var warhead_lbs = { "FAB-250": 202.85, "FAB-500": 564.38, "GBU-12": 190.00, + "GBU-24": 945.00, "GBU-31": 945.00, "GBU12": 190.00, "GBU16": 450.00, "HVAR": 7.50,#P51 "KAB-500": 564.38, - "KH-25MP": 197.53, + "Kh-25MP": 197.53, "Kh-66": 244.71, "KN-06": 315.00, "LAU-68": 10.00, @@ -60,40 +51,14 @@ var warhead_lbs = { "M71": 200.00, "M71R": 200.00, "M90": 500.00, - "Magic-2": 27.00, - "Matra MICA": 30.00, - "Matra R550 Magic 2": 27.00, - "MATRA-R530": 55.00, - "MatraMica": 30.00, - "MatraMicaIR": 30.00, - "MatraR550Magic2": 27.00, - "Meteor": 55.00, - "MICA-EM": 30.00, - "MICA-IR": 30.00, "MK-82": 192.00, "MK-83": 445.00, "MK-84": 945.00, "OFAB-100": 92.59, - "R-13M": 16.31, - "R-27R1": 85.98, - "R-27T1": 85.98, - "R-3R": 16.31, - "R-3S": 16.31, - "R-55": 20.06, - "R-60": 6.60, - "R-60M": 7.70, - "R-73E": 16.31, - "R-77": 49.60, - "R74": 16.00, "RB-04E": 661.00, "RB-05A": 353.00, "RB-15F": 440.92, - "RB-24": 20.80, - "RB-24J": 20.80, - "RB-71": 88.00, - "RB-74": 20.80, "RB-75": 126.00, - "RB-99": 44.00, "RN-14T": 800.00, #fictional, thermobaeric replacement for the RN-24 nuclear bomb "RN-18T": 1200.00, #fictional, thermobaeric replacement for the RN-28 nuclear bomb "RS-2US": 28.66, diff --git a/missile-frigate/Nasal/damage.nas b/missile-frigate/Nasal/damage.nas index 24f3df8..cb831e3 100644 --- a/missile-frigate/Nasal/damage.nas +++ b/missile-frigate/Nasal/damage.nas @@ -28,16 +28,6 @@ var warhead_lbs = { "AGM-84": 488.00, "AGM-88": 146.00, "AGM65": 200.00, - "aim-120": 44.00, - "AIM-120": 44.00, - "AIM-54": 135.00, - "aim-7": 88.00, - "AIM-7": 88.00, - "aim-9": 20.80, - "AIM-9": 20.80, - "AIM120": 44.00, - "AIM132": 22.05, - "AIM9": 20.80, "ALARM": 450.00, "AM39-Exocet": 364.00, "AS-37-Martel": 330.00, @@ -47,12 +37,13 @@ var warhead_lbs = { "FAB-250": 202.85, "FAB-500": 564.38, "GBU-12": 190.00, + "GBU-24": 945.00, "GBU-31": 945.00, "GBU12": 190.00, "GBU16": 450.00, "HVAR": 7.50,#P51 "KAB-500": 564.38, - "KH-25MP": 197.53, + "Kh-25MP": 197.53, "Kh-66": 244.71, "KN-06": 315.00, "LAU-68": 10.00, @@ -60,40 +51,14 @@ var warhead_lbs = { "M71": 200.00, "M71R": 200.00, "M90": 500.00, - "Magic-2": 27.00, - "Matra MICA": 30.00, - "Matra R550 Magic 2": 27.00, - "MATRA-R530": 55.00, - "MatraMica": 30.00, - "MatraMicaIR": 30.00, - "MatraR550Magic2": 27.00, - "Meteor": 55.00, - "MICA-EM": 30.00, - "MICA-IR": 30.00, "MK-82": 192.00, "MK-83": 445.00, "MK-84": 945.00, "OFAB-100": 92.59, - "R-13M": 16.31, - "R-27R1": 85.98, - "R-27T1": 85.98, - "R-3R": 16.31, - "R-3S": 16.31, - "R-55": 20.06, - "R-60": 6.60, - "R-60M": 7.70, - "R-73E": 16.31, - "R-77": 49.60, - "R74": 16.00, "RB-04E": 661.00, "RB-05A": 353.00, "RB-15F": 440.92, - "RB-24": 20.80, - "RB-24J": 20.80, - "RB-71": 88.00, - "RB-74": 20.80, "RB-75": 126.00, - "RB-99": 44.00, "RN-14T": 800.00, #fictional, thermobaeric replacement for the RN-24 nuclear bomb "RN-18T": 1200.00, #fictional, thermobaeric replacement for the RN-28 nuclear bomb "RS-2US": 28.66,