315 lines
8.4 KiB
Text
315 lines
8.4 KiB
Text
|
# helpers for working with GoFlight input devices
|
||
|
|
||
|
# map decimal digits 0..9 to standard 7-segment LCD pattern
|
||
|
var translateDigitToSevenSegment = [0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x67];
|
||
|
|
||
|
var formatFrequencyMHz = func(freqMhz, fieldWidth)
|
||
|
{
|
||
|
return bytesForString(sprintf("%.3f", freqMhz), fieldWidth);
|
||
|
}
|
||
|
|
||
|
var formatFrequencyKHz = func(freqKhz, fieldWidth)
|
||
|
{
|
||
|
return bytesForString(sprintf("%6.2f", freqKhz), fieldWidth);
|
||
|
}
|
||
|
|
||
|
var bytesForString = func(s, fieldWidth)
|
||
|
{
|
||
|
var padCount = fieldWidth - size(s);
|
||
|
var r = "";
|
||
|
|
||
|
while (padCount > 0) {
|
||
|
r ~= chr(0);
|
||
|
padCount -=1;
|
||
|
}
|
||
|
|
||
|
for (var i=0; i < size(s); i += 1) {
|
||
|
if (s[i] == `.`) {
|
||
|
# set the high bit to correspond to the decimal
|
||
|
var lastIndex = size(r) - 1;
|
||
|
r[lastIndex] = r[lastIndex] + 0x80;
|
||
|
} else if (s[i] == ` `) { # spaces
|
||
|
r ~= chr(0);
|
||
|
} else if (s[i] == `-`) { # negative
|
||
|
r ~= chr(0x40);
|
||
|
} else {
|
||
|
var digitCode = s[i] - `0`;
|
||
|
r ~= chr(translateDigitToSevenSegment[digitCode]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return r;
|
||
|
}
|
||
|
|
||
|
# TEST
|
||
|
# STBY
|
||
|
# OFF
|
||
|
# XPDR
|
||
|
# TA
|
||
|
# TA/RA
|
||
|
|
||
|
|
||
|
var translateTo14Segment = {
|
||
|
32: [0x0, 0x0], # space
|
||
|
65: [0x22, 0x37], # A
|
||
|
66: [0x0A, 0x8f],
|
||
|
67: [0x00, 0x39],
|
||
|
68: [0x08, 0x8f], # uppercase D
|
||
|
69: [0x22, 0x39],
|
||
|
70: [0x20, 0x31], # F
|
||
|
77: [0x00, 0x00], # upper M
|
||
|
78: [0x00, 0x00], # upper N
|
||
|
79: [0x00, 0x3f], # upper O
|
||
|
80: [0x22, 0x33], # upper P
|
||
|
82: [0x26, 0x33], # upper R
|
||
|
83: [0x22, 0x2d], # upper S
|
||
|
84: [0x08, 0x81], # T
|
||
|
88: [0x15, 0x40], # X
|
||
|
89: [0x09, 0x40] # Y
|
||
|
};
|
||
|
|
||
|
var formatFourteenSegment = func(s, fieldWidth)
|
||
|
{
|
||
|
var r = [];
|
||
|
for (var i=0; i < size(s); i += 1) {
|
||
|
var ch = s[i];
|
||
|
if (!contains(translateTo14Segment, ch)) {
|
||
|
debug.dump('No 14 segment mapping for:', ch);
|
||
|
} else {
|
||
|
var t = translateTo14Segment[s[i]];
|
||
|
append(r, t[0]);
|
||
|
append(r, t[1]);
|
||
|
}
|
||
|
}
|
||
|
return r;
|
||
|
}
|
||
|
|
||
|
var reverseBytes = func(bytes)
|
||
|
{
|
||
|
var r=[];
|
||
|
for (var i = size(bytes) - 1; i >=0; i -=1) {
|
||
|
append(r, bytes[i]);
|
||
|
}
|
||
|
return r;
|
||
|
}
|
||
|
|
||
|
var MFRController = {
|
||
|
|
||
|
new: func(prefix)
|
||
|
{
|
||
|
var m = {
|
||
|
parents: [MFRController]
|
||
|
};
|
||
|
|
||
|
# m._hideTimer = maketimer(m.DELAY, m, Tooltip._hideTimeout);
|
||
|
# m._hideTimer.singleShot = 1;
|
||
|
|
||
|
return m;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
var mcp = {
|
||
|
init: func()
|
||
|
{
|
||
|
me._speedKnotsProp = props.globals.getNode("/autopilot/settings/target-speed-kt", 1);
|
||
|
me._speedMachProp = props.globals.getNode("/autopilot/settings/target-speed-mach", 1);
|
||
|
me._altitudeFtProp = props.globals.getNode("/autopilot/settings/target-altitude-ft", 1);
|
||
|
me._vsFPMProp = props.globals.getNode("/autopilot/settings/vertical-speed-fpm", 1);
|
||
|
me._headingProp = props.globals.getNode("/autopilot/settings/heading-bug-deg", 1);
|
||
|
me._course1Prop = props.globals.getNode("/instrumentation/nav[0]/radials/selected-deg", 1);
|
||
|
me._course2Prop = props.globals.getNode("/instrumentation/nav[1]/radials/selected-deg", 1);
|
||
|
|
||
|
me._useMach = 0;
|
||
|
me._refreshProp = props.globals.getNode("/input/goflight/mcp/refresh", 1);
|
||
|
me._refreshHeadingProp = props.globals.getNode("/input/goflight/mcp/refresh-headings", 1);
|
||
|
|
||
|
me._blankVSWindow = props.globals.getNode("/input/goflight/mcp/blank-vs-window", 1);
|
||
|
|
||
|
me._ledProps = [];
|
||
|
for (var l=0; l<4; l+=1) {
|
||
|
var node = props.globals.getNode("/input/goflight/mcp/led[" ~ l ~ "]", 1);
|
||
|
node.setIntValue(0);
|
||
|
append(me._ledProps, node);
|
||
|
}
|
||
|
|
||
|
setlistener(me._speedKnotsProp, func { me.doRefresh(); } );
|
||
|
setlistener(me._speedMachProp, func { me.doRefresh(); });
|
||
|
setlistener(me._altitudeFtProp, func { me.doRefresh(); });
|
||
|
setlistener(me._vsFPMProp, func { me.doRefresh(); });
|
||
|
setlistener(me._blankVSWindow, func { me.doRefresh(); });
|
||
|
|
||
|
setlistener(me._headingProp, func { me.doRefreshHeading(); });
|
||
|
setlistener(me._course1Prop, func { me.doRefreshHeading(); });
|
||
|
setlistener(me._course2Prop, func { me.doRefreshHeading(); });
|
||
|
|
||
|
me.doRefresh();
|
||
|
me.doRefreshHeading();
|
||
|
|
||
|
print("GoFlight MCP init done");
|
||
|
},
|
||
|
|
||
|
setAltitudeFtProp: func(path)
|
||
|
{
|
||
|
me._altitudeFtProp = props.globals.getNode(path, 1);
|
||
|
setlistener(me._altitudeFtProp, func { me.doRefresh(); });
|
||
|
me.doRefresh();
|
||
|
},
|
||
|
|
||
|
doRefresh: func()
|
||
|
{
|
||
|
me._refreshProp.setIntValue(0);
|
||
|
},
|
||
|
|
||
|
doRefreshHeading: func()
|
||
|
{
|
||
|
me._refreshHeadingProp.setIntValue(0);
|
||
|
},
|
||
|
|
||
|
setMachMode: func(useMach)
|
||
|
{
|
||
|
me._useMach = useMach;
|
||
|
me.doRefresh();
|
||
|
},
|
||
|
|
||
|
altitudeData: func()
|
||
|
{
|
||
|
# if window is blanked, return empty data
|
||
|
var alt = me._altitudeFtProp.getValue();
|
||
|
return bytesForString(sprintf("%d", alt), 5);
|
||
|
},
|
||
|
|
||
|
vsData: func()
|
||
|
{
|
||
|
# if window is blanked, return empty data
|
||
|
if (me._blankVSWindow.getValue()) {
|
||
|
return bytesForString(" ", 5);
|
||
|
}
|
||
|
|
||
|
var vs = me._vsFPMProp.getValue();
|
||
|
return bytesForString(sprintf("%d", vs), 5);
|
||
|
},
|
||
|
|
||
|
speedData: func()
|
||
|
{
|
||
|
if (me._useMach) {
|
||
|
var mach = me._speedMachProp.getValue();
|
||
|
return bytesForString(sprintf("%0.3f ", mach), 5);
|
||
|
}
|
||
|
|
||
|
var spd = me._speedKnotsProp.getValue();
|
||
|
return bytesForString(sprintf("%d", spd), 5);
|
||
|
},
|
||
|
|
||
|
adjustSpeed: func(val)
|
||
|
{
|
||
|
if (me._useMach) {
|
||
|
var mach = me._speedMachProp.getValue();
|
||
|
me._speedMachProp.setDoubleValue(mach + (val * 0.01));
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
var spd = me._speedKnotsProp.getValue();
|
||
|
me._speedKnotsProp.setIntValue(spd + val);
|
||
|
},
|
||
|
|
||
|
adjustAltitude: func(val)
|
||
|
{
|
||
|
var alt = me._altitudeFtProp.getValue();
|
||
|
me._altitudeFtProp.setIntValue(alt + val);
|
||
|
},
|
||
|
|
||
|
headingData: func()
|
||
|
{
|
||
|
var h = me._headingProp.getValue();
|
||
|
return bytesForString(sprintf("%0d", h), 3);
|
||
|
},
|
||
|
|
||
|
course1Data: func()
|
||
|
{
|
||
|
var h = me._course1Prop.getValue();
|
||
|
return bytesForString(sprintf("%0d", h), 3);
|
||
|
},
|
||
|
|
||
|
course2Data: func()
|
||
|
{
|
||
|
var h = me._course2Prop.getValue();
|
||
|
return bytesForString(sprintf("%0d", h), 3);
|
||
|
},
|
||
|
|
||
|
_ledNames: {
|
||
|
'SPEED': [1, 0],
|
||
|
'LVL-CHG': [1, 1],
|
||
|
'HDG-SEL': [1, 2],
|
||
|
'APP': [1,3],
|
||
|
'ALT-HLD': [1,4],
|
||
|
'V/S': [1,5],
|
||
|
'F/O F/D': [1,7],
|
||
|
# bank 2
|
||
|
'CWS A': [2,1],
|
||
|
'CWS B': [2,2],
|
||
|
'CAP F/D': [2,6],
|
||
|
'N1': [2, 7],
|
||
|
# bank 3
|
||
|
'VNAV': [3,0],
|
||
|
'LNAV': [3,1],
|
||
|
'CMD A': [3,2],
|
||
|
'CMD B': [3,3],
|
||
|
'A/T ARM': [3,4],
|
||
|
'VOR-LOC': [3,7],
|
||
|
},
|
||
|
|
||
|
watchPropertyForLED: func(prop, ledName)
|
||
|
{
|
||
|
if (!contains(me._ledNames, ledName)) {
|
||
|
printlog('warn', 'Unknown GoFlight MCP LED:' ~ ledName);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
var ledData = me._ledNames[ledName];
|
||
|
setlistener(prop, func(n) { me.setLED(ledData, n.getValue()); });
|
||
|
},
|
||
|
|
||
|
setLED: func(data, b)
|
||
|
{
|
||
|
# data is a pair of ints; the LED node and the bit within
|
||
|
var node = me._ledProps[data[0]];
|
||
|
var ledBits = node.getValue();
|
||
|
node.setIntValue(bits.switch(ledBits, data[1], b));
|
||
|
}
|
||
|
};
|
||
|
|
||
|
var flapPositions = [];
|
||
|
var flapsNode = nil;
|
||
|
|
||
|
_setlistener("/sim/signals/nasal-dir-initialized", func {
|
||
|
mcp.init();
|
||
|
|
||
|
# build flap axis quantisation data
|
||
|
flapsNode = props.globals.getNode("/controls/flight/flaps");
|
||
|
|
||
|
foreach (var c; props.globals.getNode("/sim/flaps").getChildren("setting")) {
|
||
|
var step = c.getValue();
|
||
|
append(flapPositions, step);
|
||
|
}
|
||
|
|
||
|
});
|
||
|
|
||
|
var flapsAxisQuantized = func(val)
|
||
|
{
|
||
|
var normVal = (val + 1) * 0.5;
|
||
|
var numSteps = size(flapPositions);
|
||
|
for (var i=1; i<numSteps; i+=1) {
|
||
|
var midPoint = (flapPositions[i-1] + flapPositions[i]) * 0.5;
|
||
|
if (normVal < midPoint) {
|
||
|
#print('flap Q:' ~ normVal ~ " to " ~ flapPositions[i-1]);
|
||
|
flapsNode.setDoubleValue(flapPositions[i-1]);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
#print('flap Q:' ~ normVal ~ " to " ~ flapPositions[numSteps-1]);
|
||
|
# if we fall off the end, we're at the final value
|
||
|
flapsNode.setDoubleValue(flapPositions[numSteps-1]);
|
||
|
}
|
||
|
|
||
|
print("GoFlight Nasal module load done");
|