1
0
Fork 0
fgdata/Aircraft/Generic/an_spn_46.nas
Richard Harrison 54165c213f Added Emesary and AN/SPN-46 ACLS
Emesary is a simple and efficient class based interobject communcation system to allow decoupled disparate parts of a system to function together without knowing about each. It allows decoupling and removal of dependencies by using notifications to cause actions or to query values.

Emesary is all about decoupling and removing dependecies, and improving the structure of code. Using Emesary you can more easily define the what rather than the how. By using what is essential an event driven system it is easy to add or remove modules, and also for extra modules to be inserted that the rest of the aircraft knows nothing about (e.g. FGCamera or the Walker).

see: http://chateau-logic.com/content/emesary-nasal-implementation-flightgear

The AN/SPN-46 is an ACLS implementation using Emesary. ACLS is the Navy's version of ILS.
2016-04-02 00:13:35 +02:00

330 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 new_class = emesary.Notification.new("ANSPN46ActiveNotification", _anspn46_system.Ident);
new_class.ANSPN46_system = _anspn46_system;
#
# Set notification properties from the ANSPN46_System.
new_class.set_from = func(_anspn)
{
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.set_from(_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)
{
print("AN/SNP46 created for "~_ident);
var new_class = emesary.Recipient.new("ANSPN46_System "~_ident);
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.Type == "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;
# print("AN/SPN 46 : update from",me.Ident," rate=",me.UpdateRate);
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;
},
}