#--------------------------------------------------------------------------- # # Title : AN/SPN-46 Precision Approach Landing System (PALS) # # File Type : Implementation File # # Description : Representative emulation of the functions of the AN/SPN-46 using emesary # : Where an AN/SPN-46 is required it is sufficient to instantiate a ANSPN46_System connected to the instantiator model. # : Register with an instance of a Transmitter, and provide a Receive method, periodically send out Active messages # : expecting ActiveResponse messages which may result in a Communication. # : This is implemented as a set of messages that models the operation of the PALS # # References : http://trace.tennessee.edu/cgi/viewcontent.cgi?article=3297&context=utk_gradthes # : http://www.navair.navy.mil/index.cfm?fuseaction=home.displayPlatform&key=E8D18768-14B6-4CF5-BAB5-12B009070CFC # : http://www.afceaboston.com/documents/events/cnsatm2010/Briefs/4%20-%20Friday/03-Navy_Landing_Systems_Roadmap-(CDR%20Easler).pdf # # Author : Richard Harrison (richard@zaretto.com) # # Creation Date : 29 January 2016 # # Version : 4.8 # # Copyright © 2016 Richard Harrison Released under GPL V2 # #---------------------------------------------------------------------------*/ #Message Reference: #---------------------------------------------------------------------------*/ # Notification 1 ANSPN46ActiveNotification from carrier to aircraft # Transmitted via GlobalTransmitter at 1hz # - carrier position # - beam start # - beam angle # - channel / frequency information # - beam range / power # # Notification 2 - from aircraft to carrier # - aircraft position # - radar return size # - aircraft altitude, heading, velocity # - respose indicating tuned or not # # Notification 3 - from carrier to aircraft # - lateral deviation # - vertical deviation # - LSO information / messages # - system status # - lateral # #Operation: #--------------------- # The ANSPN64 system will send periodically send out a ANSPN46ActiveNotification # the rest of the logic within this system related to aircraft will be handled when # the ANSPN46ActiveResponseNotification - which itself in turn will send out a ANSPN46CommunicationNotification #---------------------- # NOTE: to avoid garbage collection all of the notifications that are sent out are created during construction # and simply modified prior to sending. This works because emesary is synchronous and therefore the state # and lifetime are known. # # Notification(1) from carrier to any aircraft within range. # var ANSPN46ActiveNotification = { # Create a new active notification notification. Aircraft will respond to this. # param(_anspn46_system): instance of ANSPN46_System which will send the notification new: func(_anspn46_system) { var ident="none"; if (_anspn46_system != nil) ident=_anspn46_system.Ident; var new_class = emesary.Notification.new("ANSPN46ActiveNotification", ident); new_class.ANSPN46_system = _anspn46_system; new_class.Position = nil; new_class.BeamPosition = nil; new_class.BeamAngle = 35; new_class.Channel = 2; new_class.BeamRange = 35; ##nm new_class.BeamPower = 999; ## mw ??? # # Set notification properties from the ANSPN46_System. new_class.set_from = func(_anspn) { if (_anspn != nil) { me.Ident = _anspn.Ident; me.Position = _anspn.GetCarrierPosition(); me.BeamPosition = _anspn.GetTDZPosition(); me.BeamAngle = 35; me.Channel = _anspn.GetChannel(); me.BeamRange = 35; ##nm me.BeamPower = 999; ## mw ??? } }; new_class.bridgeProperties = func { return [ { getValue:func{return emesary.TransferCoord.encode(new_class.Position);}, setValue:func(v){new_class.Position=emesary.TransferCoord.decode(v);}, }, { getValue:func{return emesary.TransferByte.encode(new_class.Channel);}, setValue:func(v){new_class.Channel=emesary.TransferByte.decode(v);}, }, ]; }; new_class.set_from(new_class.ANSPN46_system); return new_class; }, }; # Notification(2) - from aircraft to carrier sent in response to Notification(1) above # var ANSPN46ActiveResponseNotification = { new: func(_ident) { var new_class = emesary.Notification.new("ANSPN46ActiveResponseNotification", _ident); new_class.Respond = func(_msg) { new_class.Position = geo.aircraft_position(); new_class.Heading = getprop("orientation/heading-deg"); new_class.RadarReturnStrength = 1; # normalised value based on RCS beam power etc. new_class.Tuned = 0; # 0 or 1 new_class.ufps = getprop("velocities/uBody-fps"); return me; } return new_class; }, }; # Notification 3 - from carrier to aircraft as a result of active response notification # - only sent if the aircraft is set to the same channel that we are transmitting on # var ANSPN46CommunicationNotification = { new: func(_ident, _anspn46_system) { var new_class = emesary.Notification.new("ANSPN46CommunicationNotification", _ident); new_class.Model = _anspn46_system.Model; new_class.set_from = func(_ident, _msg, _anspn46_system) { var carrier_ara_63_position = _anspn46_system.GetCarrierPosition(); var carrier_heading = _anspn46_system.GetCarrierHeading(); var carrier_ara_63_heading = 0; # relative offset of the course to the tdz # according to my measurements the Nimitz class is 8.1362114 degrees (measured 178 vs carrier 200 allowing for local magvar -13.8637886) # i.e. this value is from tuning rather than calculation if (carrier_heading != nil) carrier_ara_63_heading = carrier_heading.getValue() - 8.1362114; var range = _msg.Position.distance_to(carrier_ara_63_position); var bearing_to = _msg.Position.course_to(carrier_ara_63_position); var deviation = bearing_to - carrier_ara_63_heading; deviation = deviation *0.1; me.ReturnPosition = _msg.Position; me.ReturnBearing = getprop("orientation/heading-deg"); # the AN/SPN 46 has a 20nm range with a 3 degree beam; ref: F14-AAD-1 17.3.2 if (range < 37000 and abs(deviation) < 3) { var FD_TAN3DEG = math.tan(3.0 / 57.29577950560105); var deck_height=20; var gs_height = ((range*FD_TAN3DEG)) + deck_height; var gs_deviation = (gs_height - _msg.Position.alt()) / 42.0; if (gs_deviation > 1) gs_deviation = 1; else if (gs_deviation < -1) gs_deviation = -1; # if not in range message will not be transmitted. me.InRange = 1; # calculate the deviation from the ideal approach me.VerticalAdjustmentCommanded = gs_deviation; me.HorizontalAdjustmentCommanded = deviation; me.LateralDeviation = deviation; me.VerticalDeviation = gs_deviation; me.Distance = range; me.SignalQualityNorm = 1; # # work out the rough ETA for the 10 seconds light, and use this # to decide whether or not to waveoff var eta = range / (_msg.ufps / 3.281); if(eta <= 10 and range < 800 and range > 150) { me.TenSeconds = 1; if(math.abs(deviation) > 0.2 or math.abs(gs_deviation) > 0.2) { me.WaveOff = 1; } else { me.WaveOff = 0; } } else { me.TenSeconds = 0; me.WaveOff = 0; } me.LSOMessage = ""; me.SystemStatus = "OK"; # Wave off, error, etcc. } else { # # No response will be sent when not in range; so ensure values are all cleared. me.InRange = 0; me.VerticalAdjustmentCommanded = 0; me.HorizontalAdjustmentCommanded = 0; me.SignalQualityNorm =0; me.Distance = -10000000000; me.TenSeconds = 0; me.WaveOff = 0; me.InRange = 0; me.VerticalDeviation = 0; me.LateralDeviation = 0; me.LSOMessage = ""; me.SystemStatus = ""; } }; return new_class; }, }; # # The main AN/SPN46 system implemented using emesary. # Periodically the Update method should be called, which will # send out a notification via the global transmitter so that aircraft within range # can respond. This is similar to a radar transmit and return. # There should be an instance of this class created in the nasal section of the model xml # Once an aircraft is within range it will receive guidance that can be displayed. # It is the responsibility of the AN/SPN system to decided whether a craft is within range. # It is the responsibility of the aircraft receiver to indicate whether it is tuned in or not # if it the aircraft is not tuned into the right channel the receiver (e.g. ARA-63) will not receive anything; however # the AN/SPN system (being a radar) will still have guidance information that could be relayed over the # comms channel or displayed on a radar display on the carrier. # var ANSPN46_System = { new: func(_ident,_model) { var new_class = emesary.Recipient.new(_ident~".ANSPN46"); new_class.ara_63_position = geo.Coord.new(); new_class.Model = _model; new_class.communicationNotification = ANSPN46CommunicationNotification.new(new_class.Ident, new_class); new_class.Channel = 2; new_class.UpdateRate = 10; #------------------------------------------- # Receive override: # Iinterested in receiving ANSPN46ActiveResponseNotification. When we get # one of these we can respond with a CommunicationNotification new_class.Receive = func(notification) { if (notification.NotificationType == "ANSPN46ActiveResponseNotification") { if (notification.Tuned) { me.communicationNotification.set_from(me.Ident, notification, me); if(me.communicationNotification.InRange) { me.UpdateRate = 0.2; emesary.GlobalTransmitter.NotifyAll(me.communicationNotification); } } return emesary.Transmitter.ReceiptStatus_OK; } return emesary.Transmitter.ReceiptStatus_NotProcessed; } # # Interface methods #----------------------------- # Required interface to get the current carrier position new_class.GetCarrierPosition = func() { var x = me.Model.getNode("position/global-x").getValue() + 88.7713542; var y = me.Model.getNode("position/global-y").getValue() + 18.74631309; var z = me.Model.getNode("position/global-z").getValue() + 115.6574875; me.ara_63_position.set_xyz(x, y, z); return me.ara_63_position; }; # # Interface to get the carrier heading new_class.GetCarrierHeading = func() { return me.Model.getNode("orientation/true-heading-deg"); }; # # offset of the TDZ (wires) from the carrier centre new_class.GetTDZPosition = func { return me.ara_63_position; }; # # radar beam angle new_class.GetUpdateRate = func { return me.UpdateRate; }; new_class.BeamAngle = func { return 30; }; # # currently transmitting channel number. new_class.GetChannel = func { return me.Channel; }; new_class.SetChannel = func(v) { me.Channel = v; }; # # main entry point. The object itself will manage the update rate - but it is # up to the caller to use this rate new_class.Update = func { # fill in properties of message me.msg.set_from(me); # # manage the update rate; increase each frame until we get to 10 seconds # this will be reset if we receive something back from the aircraft. if (me.UpdateRate < 10) me.UpdateRate = me.UpdateRate+1; return emesary.GlobalTransmitter.NotifyAll(me.msg); }; # # create the message that will be used to notify of an active carrier. This needs to be done after the methods # have been created as it references them. Implemented like this to reduce garbage collection new_class.msg = ANSPN46ActiveNotification.new(new_class); emesary.GlobalTransmitter.Register(new_class); return new_class; }, }