2016-04-01 22:13:35 +00:00
|
|
|
|
#---------------------------------------------------------------------------
|
|
|
|
|
#
|
|
|
|
|
# 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 <20> 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)
|
|
|
|
|
{
|
2016-04-09 19:39:57 +00:00
|
|
|
|
var ident="none";
|
|
|
|
|
if (_anspn46_system != nil)
|
|
|
|
|
ident=_anspn46_system.Ident;
|
|
|
|
|
|
|
|
|
|
var new_class = emesary.Notification.new("ANSPN46ActiveNotification", ident);
|
|
|
|
|
|
2016-04-01 22:13:35 +00:00
|
|
|
|
new_class.ANSPN46_system = _anspn46_system;
|
2016-04-09 19:39:57 +00:00
|
|
|
|
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 ???
|
2016-04-01 22:13:35 +00:00
|
|
|
|
|
|
|
|
|
#
|
|
|
|
|
# Set notification properties from the ANSPN46_System.
|
|
|
|
|
new_class.set_from = func(_anspn)
|
|
|
|
|
{
|
2016-04-09 19:39:57 +00:00
|
|
|
|
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 ???
|
|
|
|
|
}
|
|
|
|
|
print("\nANSPN46ActiveNotification::set_from: ", me.Ident);
|
2016-04-01 22:13:35 +00:00
|
|
|
|
};
|
2016-04-09 19:39:57 +00:00
|
|
|
|
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);
|
2016-04-01 22:13:35 +00:00
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
print("AN/SNP46 created for "~_ident);
|
|
|
|
|
|
2016-04-09 19:39:57 +00:00
|
|
|
|
var new_class = emesary.Recipient.new(_ident~".ANSPN46");
|
2016-04-01 22:13:35 +00:00
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
{
|
2016-04-12 21:25:11 +00:00
|
|
|
|
if (notification.NotificationType == "ANSPN46ActiveResponseNotification")
|
2016-04-01 22:13:35 +00:00
|
|
|
|
{
|
|
|
|
|
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;
|
2016-04-09 19:39:57 +00:00
|
|
|
|
print("AN/SPN 46 : update msg: ",me.msg.Ident," sys.rate=",me.UpdateRate);
|
2016-04-01 22:13:35 +00:00
|
|
|
|
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;
|
|
|
|
|
},
|
|
|
|
|
}
|