I went off under my own steam a few weeks ago and started to develop an accurate representation of the T5 Lightning DC electrical system, all in Nasal. All properties in the property tree being created and all logic being performed by the Nasal script.
I was aware that the Lightning model uses a generic electrical system by 'Buckaroo', as I had seen the Nasal file associated with it, and I didn't look much further. I then later discovered an XML file too which appeared to be creating the properties in the tree, and leaving the logic to the Nasal file. So I gained an understanding of the programming of the current system after investing a lot of time into the new system.
I know there are many ways to skin a cat, but why was this system built in this manner? Is it more efficient to build the properties up with an XML configuration file, or was it just the author's style preference?
I'd like to bin off the generic system and just go with the one I have been working on, but am worried I may break it by placing the whole system in Nasal, ie: is the mix of Nasal and XML important?
Bit of a bummer if the best practice is as per Buckaroo's generic system, as I have written, tested, and debugged, nearly 1000 lines of code so far in Nasal, and that's just the DC side! I am sill to add in a bit more switching and generator logic, but the DC basic structure is in place so far.
I have also gone super-anal with comments, so each fuse and circuit ID is explained well, and also accurate references are made to pages of Lightning manuals and circuit diagrams to show the information sources. This way other people can pick it up and understand it easier.
Why do this?
Modelling the electrical distribution system accurately turned out to be quite important, as I am modelling a lot of other systems with a high level of accuracy to the real aircraft, and the electrical system has an impact on almost all other systems, so it needed to be correct in order to also have the other systems built correctly... like building a house on a solid foundation.
So far I have built up a hierarchy of the DC busbars, generators, contactors, and detailed every fuse that links busbars, and also feeds out to individual circuits. I thought the fuses was probably a step too far, but in doing so, they not only can be used for generating random faults accurately, but also gives me the right circuit reference to supply other systems (instead of the generic "dcok" one size fits all method).
I have not started the AC distribution system yet (which actually will be a bit simpler), as I thought I would have this discussion first as to weather I am doing it in an acceptable manner or not..
Ideally I'd like to hear that there is no difference and it was just personal preference to use an XML config to build up the properties, so my efforts won't have been wasted, but I am braced for the bad news if it's best not to do it all in Nasal.
I have attached the Nasal file compressed to help give an idea of what I have been doing:
I haven't looked at your full electrical system yet, but a quick look at the existing one gives me the idea that aside from the initial removal of the paths to clear the default electrical system, you could setup the rest from nasal
I would just try it out & see tbh - the only issue you might see are perhaps the system expecting some electrical systems properties on startup & them not being there until the nasal subsystem starts up causing an issue
I'm glad you haven't thought of any glaringly obvious reason for doing it that way, apart from as you said, some other systems may have an initial hiccup, but I can move the set.xml call for this electrical system to the top of the file to cal it as one of the first things, before the other things that rely on it..
I currently have my system working alongside the generic system, as its writing to properties in a different part of the tree, there are no conflicts. One thing I will probably do is to slowly modify all other systems to reference the new system, and I can turn off the generic system by commenting out the nasal script call in the set.xml file and see which bits then die, will help me to find anything else that needs moving across...
It's going to be a bit of open heart surgery, but another great learning experience!!
I could also think of completely wiping the old system including all if it's XML and nasal files. This would create a clear slate with only your system.
Sure some of the old systems would need to be redone in your code but that would produce a much clearer code overall. ,
There is still a vast amount of the old stuff that I don't yet understand as I've not had time to fully figure it out.
I've also not yet played with the "autopilot" way of scripting things, Algy told me it's the way to go with anything needing PID control etc..
Algy did a lot of work in shoehorning in some generic stuff. Last time I spoke to him we agreed on re-writing the (currently generic) engine simulation code to build a specific Avon engine (as can be used on many other aircraft too, Hunter, Sea Vixen, Canberra, etc..).
Through work I acquired some data logs of typical industrial Avon startups on diesel, which will be in the right ball park for modelling what we want, for example the relationship between engine speed and exhaust gas temperature during a startup, but this will all be sea level data, but it's better than what's there now. I did contact BAE Systems heritage group for aircraft engine test data, but at the time Covid restrictions stopped them going in to the archives, may be worth trying again now we've had our "Covid freedom day".
Bet it could, as I guess anything is possible, but it'd be a question of what the advantages are?
Algy told me the Autopilot system was great for dynamic stuff, calculating analogue variables, PID, interpolation, etc.... The electrical system I have so far developed is mainly bools (fuse health, relays and contactors), and fixed voltages from generators and batteries. I guess one good application for the Autopilot system as a bolt-on to what I have done would be to simulate the battery voltage during discharge using interpolation down a discharge curve... hmm, got me thinking about that now!
Yes please, any help in understanding the autopilot system would really be very helpful, thank you.
I was thinking again about the comment of not using Nasal, but placing all the code into autopilot... One thought; I read that Autopilot rules execute either at FDM or frame rate, meaning lots of work for the computer's processor. However, a nasal script will execute as fast as you tell it to, such as 25ms, 100ms, etc.. so would shifting it all into autopilot remove the possibility of saving processor loading where fast execution isn't needed?
One example here is when I worked in industrial gas turbine control systems, which use PLC's to control them. The fastest part of the program was for governing the fuel to the engine, by setting a valve position, and setting the VIGV angle. The fastest I have seen this code execute at was 20ms, any faster than that you'd be into the territory of crashing the processor. There was one unit I worked on where the fuel governor ran at 50ms, and the engine was still quite happy.
For variables that affect on-screen graphics (such as an indicator pointer position), I can see the need for frame rate updates to make it as smooth as possible. I thin I read about the FDM rate being useful for combating instability, but for things like cockpit switch events, does it need to be so quick? Maybe it does if you are animating the movement of a toggle switch?
1. FDM rate (usually 120Hz) - this is what you get when loading as autopilot tag - this is mainly for anything to do with real time control of the aircraft (FCS etc)
2. Frame rate (variable) - this is what you get when loading as property-rule tag - this can be used for pretty much anything else, usually animations
Because both are working on properties, you can have both read/use each others property sets
Nasal can go even slower as you noted, and can even be spun out to it's own thread for offline computing of data for some stuff (the shuttle uses this for trajectory calculations I believe)
I don't think anything in the cockpit needs FDM rate, but if it can affect the flight control systems then it should run at FDM rate
Think about what the systems needing to run at FDM will see if accessing a property that they expect to be continuously variable between 2 values - a prop running at frame rate will appear to be a step function which in some cases might cause instabilities (there is a filter that can get rid of this I think though)
I wrote a Stability Augmentation System in nasal ages ago on top of someone else's Nasal control law system (I didn't know any better) - when I rewrote the SAS in AP logic the difference was like night & day: where the nasal system became unstable at low frame rates & with time speedup, the AP logic was rock solid
Others (timi, algy) understand AP logic better than me though - I always feel like I'm just mucking about =)
Personally I've only seen performance issues with scripting heavy aircraft when running them on a Raspberry Pi 4. And using the AP system doesn't seem to make a dent on it. So my thoughts on optimising things with it might be slightly overkill/irrelevant for your typical desktop. So whatever works I guess. But sounds like the battery capacity curve is something that could be fairly easily done with it at least.
I am in a bit of a fortunate position as with the hardware gauges I am operating you can get away with small steps, as the pointers are mechanically damped, so will not be as "jumpy" as an on-screen pointer responding to step changes.. so it's something I had not considered too much.
For example I set up the Total Air Temperature calculation in Nasal, which was fine for my needs, but this would be a nice simple one to have a go at doing with an AP config to help with creating the on-screen indicator. It looks at Mach number and air temperature to calculate the TAT.
# Gather current outside air temperature and mach number
var t_static = getprop("/environment/temperature-degc");
var mach_no = getprop("/velocities/mach");
#Calculate total air temperature as seen by ram air probe
var t_total = t_static * (1 + (0.2 * math.pow(mach_no, 2)));
#Set property in tree
Let me give you a short introduction to the autopilot system and how I use it for sound design using interpolation tables.
What I need is a method of interpolating a value along a certain base value to be able to create something like an ADSR, a logarithmic curve to be able to fade in audio, sustain it over a certain range of values and fade it out over a range.
For that I register a new autopilot definition in the set file by pointing at the XML file in the aicraft folder. I normally put them into the Systems folder. If everything is written well, it will be activated automatically at simulation start.
The screenshot shows the involved files to create an autopilot definition and where they reside in the filsystem
So basically, you point at the new XML definition for your autopilot in the set file
In the autopilot definition file it is just a matter of creating a new property list of filter nodes. In such a filter you will define the calcualtion type as "gain" and it's magnitude (I always choose 1.0 as I can then work with known numbers in the tables and dont have to multiply by the gain fraction all the time)
A typical filter using interpolation tables
In the input section of a filter you define the calclulation. Several expressions can be defined to calculate the desired output value. With tables I go with one expression that contains atable definition.
In the table definition I define the input property and entries, that define my table points where I want the ouput property to be of a certain value. All values between are interpolated in a smooth curve.
And it will be faster than doing it in Nasal as Nasal is in fact being transformed into an AST (abstract syntax tree) and the looped over that as often as the functions embedded in that AST are being called. All instructions are being interpreted in real time and then run.
XML defined expressions are pieces of machine code, already compiled and turned into machine code by the c++ compiler, that accept inputs and produce outputs. For instance an expression like "Sum" is nothing more than a c++ function that has been compiled and will sum up its inputs. This code does not need to be interpreted in real time - it can just be executed and will run as machine code directly.
The definitions in that XML file are being saved in an appropriate structure in memory and then being looped over. Every time an expression is needed, the C++ function for that expression is being called and te output is saved in the structure for the next expression to be executed. So the XML defined expression tree is worked out.
This is still a bit of overhead code to go over the expression tree but it is significantly faster than interpreting and running byte code like Nasal.
Thank you for that example Timi, I never thought I'd say this, but it's much easier to follow the maths in Nasal!! However, this example is great, and something I can learn from, I will try implementing this in the Lightning and removing my Nasal TAT script to see if it works.