diff --git a/Aircraft/Instruments-3d/radar2/radar2.nas b/Aircraft/Instruments-3d/radar2/radar2.nas new file mode 100644 index 000000000..676629148 --- /dev/null +++ b/Aircraft/Instruments-3d/radar2/radar2.nas @@ -0,0 +1,250 @@ +##### radar2.nas Multiplayer radar and ECM/RWR system. + +# Alexis Bory, 2008. + +# Cycles through the list of multiplayers and tankers, then triggers +# radar or ECM/RWR computations if those features are enabled in our aircraft -set.xml file. + +# Needs radardist.nas for some visibilty computations based on radardist radar and RCS database. +# watch_aimp_models() has to be periodicaly called from one of our aircraft +# nasal files. Do not forget to init both scripts. + +# Properties: + +# instrumentation/radar/enabled (bool) (radar display) +# instrumentation/ecm/enabled (bool) (RWR display) +# At least one of these. + +# /instrumentation/radar/range : fixed limit to any computation (both radar and ECM/RWR) +# /instrumentation/radar/radar2-range : our own and current display range. +# TODO: /instrumentation/radar/symbols-enabled (bool) as we could also display raw spots on the screen. +# /instrumentation/radar/radar-standby (int), shall be transmited via sim/multiplay/generic/int[2] +# (until we get a good definition of radar and related properties that could be added to the +# standard set of MP transmited parameters). With this property set to 1, your radar is not +# updated anymore but continue to show targets as they where before entering standby and it +# enter silent mode and do not trigger any alert on other players using a RWR. + +# /instrumentation/ecm/on-off (bool) +# alert type 1: at least one weak scan detected. /instrumentation/ecm/alert-type1 (bool) +# alert type 2: at least one strong scan detected. /instrumentation/ecm/alert-type1 (bool) + +# About radar2.nas and wxradar. +# radar2.nas was intended first to permit building a radar display using a standard .xml animation. +# Then it was enhanced to provide new multiplayer feature. wxradar was already a good candidate for that +# and would have a great avantage because those standard .xml animations are really a pity to do, +# (as many as 3000 xml lines for the f-14 display). Anyway, I'm not enough skilled to write C++ and +# I also hope that radar2.nas feature will be integrated in wxradar so both can operate together in +# the same sky. + + +var watch_i = 0; +var list_count = 0; +var radar_able = nil; +var ecm_able = nil; +var impact_able = nil; +var synbols_enabled = nil; +var my_radarcorr = 0; +var Mp = props.globals.getNode("ai/models"); +var watch_list = []; + +# Our aircraft controls. +var OurRadarStandby = props.globals.getNode("instrumentation/radar/radar-standby", 1); +var RangeRadar = props.globals.getNode("instrumentation/radar/range"); +var RangeRadar2 = props.globals.getNode("instrumentation/radar/radar2-range"); +var EcmOn = props.globals.getNode("instrumentation/ecm/on-off", 1); + +var OurAlt = props.globals.getNode("position/altitude-ft"); + +# ECM warnings. +var EcmAlert1 = props.globals.getNode("instrumentation/ecm/alert-type1", 1); +var EcmAlert2 = props.globals.getNode("instrumentation/ecm/alert-type2", 1); +var ecm_alert1 = 0; +var ecm_alert2 = 0; +var ecm_alert1_last = 0; +var ecm_alert2_last = 0; + + +var init = func { + var our_ac_name = getprop("sim/aircraft"); + # Check which feature are enabled for our aircraft to avoid computing useless things. + radar_able = props.globals.getNode("instrumentation/radar/enabled").getValue(); + ecm_able = props.globals.getNode("instrumentation/ecm/enabled").getValue(); + # TODO: synbols_enabled = props.globals.getNode("instrumentation/radar/symbols_enabled"); + # Get our radar max range. + if (radar_able) { + my_radarcorr = radardist.my_maxrange( our_ac_name ); # in kilometers + } + if ( OurRadarStandby.getValue() == nil ) { + OurRadarStandby.setBoolValue(0); + } +} + + +# Main loop. +var watch_aimp_models = func { + # Cycle through an ordered list of multiplayers and tankers. + if ( watch_i == 0 ) { + list_count = get_list(); + } + var target_type = watch_list[watch_i][0]; + var target_index = watch_list[watch_i][1]; + var target_string = "ai/models/" ~ target_type ~ "[" ~ target_index ~ "]"; + target_process( target_string ); + if ( watch_i == ( list_count - 1 )) { + watch_i = 0; + } else { + watch_i += 1; + } +} + + +var get_list = func { + watch_list = []; + var raw_list = Mp.getChildren(); + foreach( var c; raw_list ) { + var type = c.getName(); + # Carriers are not well handled yet by AIBase.cxx, so we only reconize mp-carriers. + if (type == "multiplayer" or type == "tanker") { + append(watch_list, [type, c.getIndex()]); + } + } + return size(watch_list); +} + + + + + +var target_process = func ( target ) { + var TNode = props.globals.getNode(target); + var TRadar = TNode.getNode("radar"); + var TRadarStandby = TNode.getNode("sim/multiplay/generic/int[2]"); + # This propery used by ECM over MP should be standardized, + # like "ai/models/multiplayer[0]/radar/radar-standby" + var THeading = TNode.getNode("orientation/true-heading-deg"); + var TInRange = TRadar.getNode("in-range"); + var TCarrier = TRadar.getNode("carrier", 1); + var TDisplay = TRadar.getNode("display", 1); + var TEcmSignal = TRadar.getNode("ecm-signal", 1); + # Set variables. + var t_carrier = 0; + var t_display = 0; + var t_ecm_signal = 0; + var t_radar_standby = 0; + + if ( TRadarStandby != nil ) { + t_radar_standby = TRadarStandby.getValue(); + if ( t_radar_standby == nil ) { + t_radar_standby = 0; + } elsif ( t_radar_standby != 1 ) { + t_radar_standby = 0; + } + } + var our_radar_standby = OurRadarStandby.getValue(); + var t_in_range = TInRange.getValue(); + if ( t_in_range ) { + var TPosition = TNode.getNode("position"); + var TRange = TRadar.getNode("range-nm"); + var t_range = TRange.getValue(); + var TBearing = TRadar.getNode("bearing-deg"); + var t_bearing = TBearing.getValue(); + var TAlt = TPosition.getNode("altitude-ft"); + var t_alt = TAlt.getValue(); + var TDrawRangeNm = TRadar.getNode("draw-range-nm", 1); + var TRoundedAlt = TRadar.getNode("rounded-alt-ft", 1); + var t_heading = THeading.getValue(); + var range_radar = RangeRadar.getValue(); + var range_radar2 = RangeRadar2.getValue(); + var TPath = TNode.getNode("sim/model/path"); + var TACType = TNode.getNode("sim/model/ac-type"); + if (( t_bearing == nil ) or ( t_alt == nil ) or ( TPath == nil )) { + return; + } + var t_ac_type = "none"; + if ( TACType != nil ) { t_ac_type = TACType.getValue() } + if ( t_ac_type == "MP-Nimitz" or t_ac_type == "MP-Eisenhower") { + t_carrier = 1; + } + var our_alt = OurAlt.getValue(); + var horizon = radardist.radar_horizon( our_alt, t_alt ); + # RADAR stuff. + # Check if mp within our radar field (hard coded 74°) and if detectable. + if ( radar_able and t_range <= range_radar2 and !our_radar_standby ) { + var true_heading = getprop("orientation/heading-deg"); + var deviation_deg = deviation_normdeg(true_heading, t_bearing); + if ( deviation_deg > -37 and deviation_deg < 37 and radardist.radis(target, my_radarcorr) and t_range < horizon ) { + # Compute mp position in our radar display. (Horizontal situation) + if ( range_radar2 == 0 ) { range_radar2 = 0.00000001 } + var factor_range_radar = 0.15 / range_radar2; + var draw_radar = factor_range_radar * t_range; + TDrawRangeNm.setValue(draw_radar); + # Compute first digit of mp altitude rounded to nearest thousand. (labels). + var rounded_alt = rounding1000(t_alt) / 1000; + TRoundedAlt.setValue(rounded_alt); + t_display = 1; + } + } + # ECM/RWR stuff. + # Test if target has a radar. Computes if we are illuminated. + ecm_on = EcmOn.getValue(); + if ( ecm_able and ecm_on and t_radar_standby == 0 ) { + # TODO: overide display when alert. + t_path = TPath.getValue(); + var t_name = radardist.get_aircraft_name(target); + var t_maxrange = radardist.my_maxrange(t_name); # in kilometer, 0 is unknown or no radar. + if ( t_maxrange > 0 and t_range < horizon ) { + # Test if we are in its (arbitrary 120°) angular coverage or if we have a carrier. + # Compute the signal strength. + var t_reciprocal_bearing = geo.normdeg(t_bearing + 180); + var our_deviation_deg = deviation_normdeg(t_heading, t_reciprocal_bearing); + if ( our_deviation_deg < 0 ) { our_deviation_deg *= -1 } + if ( our_deviation_deg < 60 or t_carrier == 1 ) { + t_ecm_signal = ( (((-our_deviation_deg/30)+2.5)*(!t_carrier )) + (-t_range/20) + 2.6 + (t_carrier*1.8)); + } + } + # Compute global threat situation. (undiscriminant warning lights) + if ( t_ecm_signal > 1 and t_ecm_signal < 3 ) { + EcmAlert1.setBoolValue(1); + ecm_alert1 = 1; + } elsif ( t_ecm_signal >= 3 ) { + EcmAlert2.setBoolValue(1); + ecm_alert2 = 1; + } + } + } + # Outputs: + if ( ! our_radar_standby ) { + # If stanby: stop updating but do not erase targets positions. + TCarrier.setBoolValue(t_carrier); + TDisplay.setBoolValue(t_display); + } + if ( watch_i == 0 ) { + if ( ecm_alert1 == 0 and ecm_alert1_last == 0 ) { EcmAlert1.setBoolValue(0) } + if ( ecm_alert2 == 0 and ecm_alert1_last == 0 ) { EcmAlert2.setBoolValue(0) } + # Avoid alert blinking at each loop. + ecm_alert1_last = ecm_alert1; + ecm_alert2_last = ecm_alert2; + ecm_alert1 = 0; + ecm_alert2 = 0; + } + TEcmSignal.setValue(t_ecm_signal); + +} + + +# Utilities. +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); +} + +var rounding1000 = func(n) { + var a = int( n / 1000 ); + var l = ( a + 0.5 ) * 1000; + n = (n >= l) ? ((a + 1) * 1000) : (a * 1000); + return( n ); +} + +