1
0
Fork 0
fgdata/Aircraft/Generic/an_spn_46.nas
2016-05-16 22:49:56 +02:00

358 lines
13 KiB
Text

#---------------------------------------------------------------------------
#
# 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;
},
}