Pilot Activated Lighting

From FSDeveloper Wiki
Jump to: navigation, search



Introduction

My foray into custom airport lighting began when I learned that my snazzy hi-res GMax ground polys covered up all the default airport lighting from my custom AFCAD file, and that I would have to find another way to light my airport. I read various articles and postings regarding the pros and cons of BGL_LIGHTS versus Effects and decided to go with the BGL_LIGHTS.

Since I was going to create all the lights from scratch, I decided to start by researching the real world lighting that I was trying to emulate. I reviewed the information at airnav.com and discovered that the general aviation airfield that I am modelling has (i) a two-box VASI system at either end of the runway, (ii) runway end identifier lights ("REIL"), and (iii) medium intensity runway lights ("MIRL"). The REIL lights flash and are only visible on approach. The MIRL lights at the runway end are green when viewed on approach, but are red when viewed on take-off. The lights are on from dusk to dawn. However, the tower only operates from 7:00 AM to 9:00 PM local time. Outside these hours, the pilot can activate the MIRL and REIL lights by tuning the radio to a frequency of 119.0 MHz and keying the mic a specific number of times in quick succession. The lights then remain on for 15 minutes. The challenge was how to tweak my airport lighting for all of these detailed display conditions.

After reading through all the wiki articles and the forum postings I could find on the subject, I managed to create lights that fairly closely matched the behavior of their real-world counterparts. But one aspect eluded me till the very end. I knew how to use the radio to turn the lights on. But I couldn't figure out how to get the lights to turn themselves off after 15 minutes.

This tutorial covers the process by which I programmed the conditional display of my airport lights, including the techniques that I ultimately used successfully to create pilot activated lights that turn themselves off after a fixed period of time. This tutorial does not presume that the reader is highly experienced at tweaking ASM code. In fact, I am hoping to give the non-programmers in the audience enough of the fundamentals to get a better sense of how ASM tweaking works. I hope the professional programmers reading this tutorial will forgive me my sometimes lengthy digressions into basic programming discussions.

Creating a BGL light

While this tutorial is really about ASM tweaking, I wanted to take a second to give an overview of how the initial ASM code containing our BGL_LIGHT gets created.

Drawing the light in GMax

This is well documented in the PAPI tutorial. In its simplest form, you draw a simple box in GMax and apply to it a material with a name that begins with LIGHT_NAV, LIGHT_STROBE, LIGHT_BEACON, LIGHT_LAND, or LIGHT_TAXI. When the box is exported by MakeMDL with the "Keep Files" option checked, the visible box is discarded, and the modelname_0.asm file contains lines that look like this:

 IFMSK       nolgt_1, dict_0_g_lightStates, LIGHT_NAV_MASK
 BGL_LIGHT LIGHT_NAV, -0.017, 0.067, 0.000, 20, 0.60, 0.40, 0FF000060h, 0.0, 0.0, 1.0
 nolgt_1       label BGLCODE</pre>

BGL_LIGHT fix in ASM Code

You need to remove a couple of lines from the generated code in order for it to work properly.

 IFMSK       nolgt_1, dict_0_g_lightStates, LIGHT_NAV_MASK
 BGL_LIGHT LIGHT_NAV, -0.017, 0.067, 0.000, 20, 0.60, 0.40, 0FF000060h, 0.0, 0.0, 1.0
 nolgt_1       label BGLCODE

Arno's MDL Tweaker II program can remove this code from an already compiled MDL file. If you delete these lines yourself from the modelname_0.asm file, then you do not need to use MDLTweaker for the BGL_LIGHT fix.

Compile the light object

This is well described in Introduction to ASM tweaking, but I will repeat the very basics here.

After saving the edited modelname_0.asm file, compile modelname.asm with BGLC_9 for use in FS2004, or use BGLC for FS2002. Rename the resulting modelname.bgl to modelname.mdl. BGLC_9 will generate a file with a .mdl extension if you use the -mdl command line option. Alternatively, you can use CompileHelper, which will take care of the file extension for you.

Now you can place this model in your scenery by following one of these approaches:

  • Edit modelname.xml (that MakeMDL should have generated) to indicate the latitude and longitude where you want the model to be placed, then compile modelname.xml with BGLCOMP,
  • Place modelname.mdl directly with ObPlacer XML, or
  • Include modelname.mdl in an object library using Library Creator XML 2.0, then place this library object with an object placing program, such as Scenery Maker, Rwy12, SBuilder, or ADE.

Conditional Display

Okay, you now have a light in your scenery. Assuming you used a NAV light, though, the light displays all the time. In a real airport, lights need to be turned on and off for all sorts of reasons. By editing the modelname_0.asm file, we can add conditions that affect when a light is displayed. The basic technique is described here. This tutorial will demonstrate some of the other ways in which conditional display techniques can be used to tweak the behavior of your lighting even further.

Conditional Branching and Flow Control

Conditional branching in BGL code conceptually works like this:

if NOT true goto label
  command1
  command2
label

The basic IFIN1 command tests whether the value of a variable is equal to or between two values. If it is NOT, then you jump to a specified label. Otherwise, you continue to the next command. Due to the syntax of the command, jumping when the test is NOT true can be confusing at times. For example, let's say you want to test whether it is nighttime using the time-of-day global variable. You might write:

IFIN1 notnight, 028Ch, 4, 4
  commands that only run at night
notnight label BGLCODE
  commands that run all the time

This command tests whether the value of variable 028Ch is equal to or between 4 and 4 (which is the value assigned to night). If true, then we simply proceed to the next line in the code. If FALSE, then we jump to the section of the code that begins with the label "notnight". So you skip lines of code by testing for when a condition is NOT met.

If you want to create two mutually exclusive sets of commands for the TRUE and FALSE states (i.e., the equivalent of an IF...THEN...ELSE construct), you will need to place a BGL_JUMP_32 command at the end of the commands for the TRUE state. For example:

IFIN1 notnight, 028Ch, 4, 4
  commands that only run at night
  BGL_JUMP_32 skipfromnight
notnight label BGLCODE
  commands that run except at night
skipfromnight label BGLCODE

IFIN1 tests whether a single variable is within a range of values. IFIN2 tests whether two variables are BOTH within their respective ranges. In other words ...

IFIN2 not_true_label, 028Ch, 4, 4, 033Bh, 0, 5000

... means IF NOT (time-of-day is night AND viewpoint is within 5km of the object) THEN jump to not_true_label

IFIN3 takes this one step further and tests condition1 AND condition2 AND condition3.

IFMSK is used to test whether specific bits of a variable are set. If they are NOT, then execution jumps to the label. For example ...

IFMSK bitsnotset, 05FCh, 0003h

... tests the first two bits of the BGL_TICK18 timer variable by AND'ing the value of variable 05FCh with a bit mask of 0003h. If both of these bits equal 0, then the condition is false and execution jumps to the label "bitsnotset". The effect of this command is that execution will skip to "bitsnotset" for one tick out of every four ticks.

Finally, the SEPARATION_PLANE command is used to test the spatial location of the viewer. The PAPI tutorial does a very good job of explaining the use of the SEPARATION_PLANE command. The only thing I will mention here is that once again the command will jump to the label when the tested condition is NOT met.

Directional Lights

As mentioned above, MIRL lights at the end of the runway look green from one side, but red from the other. A SEPARATION_PLANE command is used to test which side of the lights the viewer is on. If you are approaching the runway, the SEPARATION_PLANE will show you the green lights, but hide the red lights. If you are taking off from the runway, the SEPARATION_PLANE will show you the red lights, but hide the green lights. Here is an example:

SEPARATION_PLANE WrongWay, -21926, 0, 24351, 32768
    BGL_LIGHT LIGHT_NAV, 0.0, 0.1, 0.0, 20, 0.60, 0.40, 0FF008800h, 0.0, 0.0, 1.0  ; green light
BGL_JUMP_32 endMIRL
WrongWay label BGLCODE
    BGL_LIGHT LIGHT_NAV, 0.0, 0.1, 0.0, 20, 0.60, 0.40, 0FFAA0000h, 0.0, 0.0, 1.0  ; red light
endMIRL label BGLCODE

The SEPARATION_PLANE command tests if the viewer is on the side that the normal vector of the plane points at (in this case a vector on a heading of 318 degrees). If NOT, then execution jumps to the Wrongway label and the second BGL_LIGHT, the red light, is displayed. If the condition is true, then the first BGL_LIGHT, the green light, is displayed, then execution jumps to the endMIRL label so that the red light does not display.

For a very thorough discussion of the use of the SEPARATION_PLANE command, have a look at the PAPI tutorial.

Blinking a Strobe

REIL lights are also directional; they are only visible on approach and therefore require a SEPARATION_PLANE. In addition, they blink. To get a light to blink, we need to use a timer to turn the light on and off. Here is the code for my REIL strobes:

SEPARATION_PLANE endREIL, -21926, 0, 24351, 32768
IFMSK blinklight, 05FCh, 000Fh
    BGL_JUMP_32 endREIL
blinklight label BGLCODE
    BGL_LIGHT LIGHT_NAV, 10.0, 0.5, 0.0, 40, 0.60, 0.40, 0FFFFFFFFh, 0.0, 0.0, 1.0
    BGL_LIGHT LIGHT_NAV, -10.0, 0.5, 0.0, 40, 0.60, 0.40, 0FFFFFFFFh, 0.0, 0.0, 1.0
endREIL label BGLCODE

The SEPARATION_PLANE skips the lights completely unless viewed on approach. The IFMSK command masks the first four bits of the BGL_TICK18 variable. This condition is NOT true only for 1 out of every 16 ticks. The jump to the blinklight label therefore only flashes the two lights for 1/18 of a second before they are turned off for 15/18 of a second.

Dimming the Lights

Runway edge lights look pretty good up close. But as you get further away from them, they do not dim as much as I would expect them to. This is because the lights reinforce each other and appear brighter in the aggregate when they start to overlap each other when viewed from a distance. Therefore, I created sets of runway edge lights with progressively darker colors so that I can switch to dimmer lights as the airport gets further away. I have used a similar approach for my blue taxiway lights. The code for the runway edge lights looks like this:

IFIN1 RWYlight1, 033bh, 0, 1000  ; 0-1 km
  BGL_LIGHT LIGHT_NAV, 10.0, 0.0, -5.0, 20, 0.60, 0.40, 0FFFFFFFFh, 0.0, 0.0, 1.0
  BGL_LIGHT LIGHT_NAV, -10.0, 0.0, -5.0, 20, 0.60, 0.40, 0FFFFFFFFh, 0.0, 0.0, 1.0
  BGL_LIGHT LIGHT_NAV, 10.0, 0.0, -25.0, 20, 0.60, 0.40, 0FFFFFFFFh, 0.0, 0.0, 1.0
  BGL_LIGHT LIGHT_NAV, -10.0, 0.0, -25.0, 20, 0.60, 0.40, 0FFFFFFFFh, 0.0, 0.0, 1.0
  BGL_JUMP_32 endRWYlights
RWYlight1 label BGLCODE
IFIN1 RWYlight2, 033bh, 1000, 3000  ; 1-3 km
  BGL_LIGHT LIGHT_NAV, 10.0, 0.0, -5.0, 20, 0.60, 0.40, 0FF999999h, 0.0, 0.0, 1.0
  BGL_LIGHT LIGHT_NAV, -10.0, 0.0, -5.0, 20, 0.60, 0.40, 0FF999999h, 0.0, 0.0, 1.0
  BGL_LIGHT LIGHT_NAV, 10.0, 0.0, -25.0, 20, 0.60, 0.40, 0FF999999h, 0.0, 0.0, 1.0
  BGL_LIGHT LIGHT_NAV, -10.0, 0.0, -25.0, 20, 0.60, 0.40, 0FF999999h, 0.0, 0.0, 1.0
  BGL_JUMP_32 endRWYlights
RWYlight2 label BGLCODE
  BGL_LIGHT LIGHT_NAV, 10.0, 0.0, -5.0, 20, 0.60, 0.40, 0FF555555h, 0.0, 0.0, 1.0
  BGL_LIGHT LIGHT_NAV, -10.0, 0.0, -5.0, 20, 0.60, 0.40, 0FF555555h, 0.0, 0.0, 1.0
  BGL_LIGHT LIGHT_NAV, 10.0, 0.0, -25.0, 20, 0.60, 0.40, 0FF555555h, 0.0, 0.0, 1.0
  BGL_LIGHT LIGHT_NAV, -10.0, 0.0, -25.0, 20, 0.60, 0.40, 0FF555555h, 0.0, 0.0, 1.0
endRWYlights label BGLCODE

This code tests whether the viewer is within 1 km of the lights. If so, then the first set of bright white lights is displayed. If NOT, then execution jumps to the first label, and the code checks if the viewer is between 1 km and 3 km from the lights. If so, then the second set of light grey lights is displayed. If NOT, then execution jumps to the second label, and the third set of dark grey lights is displayed. The idea for this dimming code came from this thread.

Time of Day

Turning the lights off during the day is a very common request, and is well described in the existing conditional display tutorial. Here is the code for this condition:

IFIN1 nolights, 028ch, 2, 4
  BGL_LIGHT LIGHT_NAV, 0.0, 0.0, 0.0, 20, 0.60, 0.40, 0FFFFFFFFh, 0.0, 0.0, 1.0
nolights label BGLCODE

Hours of Operation

My airport lights need a bit finer control than is afforded by the time-of-day variable. While I do need to turn them off from dawn till dusk, I also need to turn them off from 9:00 PM till 7:00 AM local time. There appears to be a global variable called "hours", but so far I have not seen this used successfully. There is another global variable that will provide you with the current hour ... in GMT. To get local time, you need to offset the GMT hours reading by the time zone offset of the airport location. The problem this creates, though, is that the time zone offset is one hour different when shifting from standard time to daylight savings time. As I discovered recently, there is also a global variable for the local time zone offset that will work for tweaking the display of lights. It is measured in minutes West of GMT. Therefore, Pacific Standard Time has an offset of 480, while Pacific Daylight Time has an offset of 420. By first testing the time zone offset, we can check the current local time, regardless of whether the airport is currently in standard or daylight savings time. Here is the code that I use to turn the lights off between 9:00 PM and 7:00 AM local time.

IFIN1 DSToff, 038eh, 420, 420   ; DST if offset is 7 hr x 60 min West of GMT
IFIN1 lightson, 0389h, 4, 13    ; GMT from 4:00 to 13:59:59 (21:00-07:00 PDT)
BGL_JUMP_32 DSTend
DSToff label BGLCODE
IFIN1 lightson, 0389h, 5, 14    ; GMT from 5:00 to 14:59:59 (21:00-07:00 PST)
DSTend label BGLCODE
BGL_JUMP_32 nolights

This code first checks to see if daylight savings time is in effect. If NOT, then the current hour is tested to see if it falls between 5:00 AM and 2:00 PM GMT, which will be true from 9:00 PM to 7:00 AM PST. If daylight savings time is in effect, the GMT hours that are tested are shifted back by one, which corresponds to 9:00 PM to 7:00 AM PDT.

Using the Radio to turn on the Lights

As described in the existing conditional display tutorial, you can use the radio to control the display of objects. The code to turn a light on when COM1 is tuned to 119.0 MHz looks like this:

IFIN1 nolights, 07beh, 1900h, 1900h
  BGL_LIGHT LIGHT_NAV, 0.0, 0.0, 0.0, 20, 0.60, 0.40, 0FFFFFFFFh, 0.0, 0.0, 1.0
nolights label BGLCODE

I took this one step further and required that the pilot be within 5km of the lights in order for the radio trigger to work. The IFIN2 command lets us test both conditions at the same time:

IFIN2 nolights, 07beh, 1900h, 1900h, 033bh, 0, 5000  ; COM1 on 119.0 within 5 km
  BGL_LIGHT LIGHT_NAV, 0.0, 0.0, 0.0, 20, 0.60, 0.40, 0FFFFFFFFh, 0.0, 0.0, 1.0
nolights label BGLCODE

Keeping the Lights On

In real life, pilot activated lights turn on when the pilot keys the mic a certain number of times at a particular frequency and turn off after a fixed period of time unless reactivated. Since there is no equivalent of keying the mic in flight simulator, the most common technique used to simulate this is to trigger the light when one of the radios is tuned to the target frequency. Using the techniques discussed so far, we can turn a light on by tuning the radio to a particular frequency, but the light goes out immediately if we change the frequency. What we need is some sort of timer that can keep the lights on for a fixed period of time after we change the frequency.

First, a bit of explanation of the programming techniques we can use in the BGL code to create a countdown timer.

Variables

We need a few variables to store some values. Variables in BGL code are simply offsets to positions in memory where values are stored. So to reference a variable, we need to know (i) the base offset in memory where our variable space begins, and (ii) the offset from the beginning of the variable space at which our variable begins. There are global variables, which have a predefined space in memory, and local variables, which only exist if we define them.

Global variables, like those we have already seen above, are stored in a predefined location in memory. Therefore, the base offset is already known. The offset to a particular global variable is the relative address (expressed in hex notation) within the global variable space where that variable is always stored. For example, the offset within the global variable space to the time-of-day global variable is 028Ch.

Among the many global variables are five "user variables" that we can use. However, as has been pointed out elsewhere, at least three of these so-called user variables are routinely used by flight simulator for animations. And it is always possible that the other two might have been used by some other third-party add-on. Therefore, it is a better practice to create and use local variables, which are unique to your model.

Local variables, as mentioned above, are just offsets to memory locations. Unlike global variables, however, there is no predefined area of memory set aside for local variables. So the first step is to declare these variables and initialize them. The declaration should look something like this:

some_sort_of_label_name label word
   dw  0
   dw  0
   dw  0

This code declares three 16-bit local variables and sets them to zero. The label on line one is used to reference the base offset of the area of memory where these local variables are stored.

To make your local variables available for use, you need to specify the base offset that should be used for referencing local variables like this:

LOCAL_BASE_32 some_sort_of_label_name

Once you have set up your local variable space, you can reference your local variables by their offset (in hex notation) from the base offset of the local variable space. For example, the three local variables that we created above are all 2-byte variables and thus would be referred to as 0h, 2h, and 4h.

You typically can only reference one variable space at a time. Therefore, before trying to use either a global or local variable, you need to make sure that you are pointing at the base offset of the correct variable space. To specify that you will be using the global variable space, use this command before using global variables:

VAR_BASE_32 VAR_BASE_GLOBAL

To specify that you will be using the local variable space, use this command before using local variables:

VAR_BASE_32 VAR_BASE_LOCAL

If you want to temporarily switch to the global variable space for one command only, use this command:

VAR_BASE_OVERRIDE VAR_BASE_GLOBAL

Likewise, if you want to temporarily switch to the local variable space for one command only, use this command:

VAR_BASE_OVERRIDE VAR_BASE_LOCAL

VAR_BASE_LOCAL is an identifier that can only refer to one local variable space at a time. If you have more than one local variable space, then you will have to use this command with the appropriate label each time that you want to switch from one local variable space to another:

LOCAL_BASE_32 some_other_label_name

A few BGL commands (mostly animation commands) allow or require you to specify the variable base offset in the command itself. We will see an example of this later with the BGL_INTERPOLATE command.

Assigning Values to Variables

Assigning a value to a variable is done like this:

SETWRD variable, value

For example, to assign the value 3 to our first local variable, we would write:

SETWRD 0h, 3

The most important point to take away from this section is that you use the SETWRD command to assign values to variables. The SETWRD command does not allow you to copy the value of one variable into another variable. The FS2000 Scenery SDK describes a few commands (MOVEWORD, MOVE_L2G, and MOVE_G2L) that might be able to do this. But I have not tested them. So for now, we will stick to the SETWRD command.

Creating a Counting "Function" with BGL_INTERPOLATE

There are no math operations or other functions in the conventional programming sense in BGL code. In most high-level programming languages, you might implement a counter like this:

x=0
while x<10 do
{
   commands
   x=x+1
}

There is no built-in addition function in BGL code. However, the BGL_INTERPOLATE command provides the means to emulate many simple math functions. BGL_INTERPOLATE effectively maps a set of input values to a set of output values. So if we want to add 1 to our input value, we could set up a table like this:

INPUT OUTPUT
0 1
1 2
2 3
3 4
4 5

BGL_INTERPOLATE will only cover the range of values specified in our table, but will interpolate (thus the name) any values that fall between those in the table. Therefore, to use BGL_INTERPOLATE to add 1 to a number, we need to put a table in our code like this:

some_label_addone label word
   dw  2
   dw  0,1,999,1000

The first line after the label specifies that there are two entries in the table. The second line provides these two entries. Each entry is a pair of values representing input and output. So an input of 0 maps to an output of 1, and an input of 999 maps to an output of 1000. BGL_INTERPOLATE can use this table to add 1 to any number between 0 and 999. To do so, we use this command:

BGL_INTERPOLATE VAR_BASE_LOCAL, 2h,\
                VAR_BASE_LOCAL, 4h,\
                VAR_BASE_LOCAL, (offset some_label_addone - some_sort_of_label_name)

"VAR_BASE_LOCAL, 2h" is the input variable. "VAR_BASE_LOCAL, 4h" is the output variable. "VAR_BASE_LOCAL, (offset some_table_addone - some_sort_of_label_name)" specifies where to find the table to use. BGL_INTERPOLATE looks up the value of the input variable in the table, then writes the corresponding output value from the table into the output variable. Within the range of input values in the table, this command effectively says: 4h = 2h + 1

To use this as a counter, all we need to do is use the same local variable for both input and output.

So now that we have a counter, what shall we count? What about the BGL_TICK18 timer? I use IFMSK to see whether a particular bit in the BGL_TICK18 global variable is set to 1. If true, then I use BGL_INTERPOLATE to increment my counter variable. I also set another local variable to 1 so that I don't increment my counter again until the IFMSK command tests false then true again. IFIN1 is used to test the counter and keep the lights on until the counter reaches the target count. The total time that the lights stay on equals the length of one true-false-true cycle of the chosen bit of the BGL_TICK18 timer times the number of counts. I chose to mask the fifth bit of BGL_TICK18, which cycles every 1.78 seconds. By counting a few more than 500 cycles, this code should turn the lights off after a delay of roughly 15 minutes.

IFIN2 timelights, 07beh, 1900h, 1900h, 033bh, 0, 5000  ; COM1 on 119.0 within 5 km
VAR_BASE_32 VAR_BASE_LOCAL
SETWRD 0h, 1                    ; turn the switch on
SETWRD 2h, 0                    ; reset the flag
SETWRD 4h, 0                    ; reset the counter
BGL_JUMP_32 lightson

timelights label BGLCODE
VAR_BASE_32 VAR_BASE_LOCAL
IFIN1 nolights, 0h, 1, 1        ; check the light switch
VAR_BASE_OVERRIDE VAR_BASE_GLOBAL
IFMSK holdlights, 05FCh, 0010h  ; add one to the count every 1.78 seconds
IFIN1 skipcount, 2h, 0, 0       ; flag skips the counter until tick18 completes cycle
BGL_INTERPOLATE VAR_BASE_LOCAL, 4h,\
                VAR_BASE_LOCAL, 4h,\
                VAR_BASE_LOCAL, (offset light_table_addone - light_table_localvars)
SETWRD 2h, 1

skipcount label BGLCODE
IFIN1 lightson, 4h, 512, 1000   ; takes roughly 15 minutes to count to 512
SETWRD 0h, 0                    ; turn the switch off
SETWRD 2h, 0                    ; reset the flag
SETWRD 4h, 0                    ; reset the counter
BGL_JUMP_32 nolights

holdlights label BGLCODE
SETWRD 2h, 0                    ; reset the flag

lightson label BGLCODE

Take note of all the switches between the global and local variable base offsets. Because we are mixing a bunch of global and local variables together in this code, you need to be careful to have the correct base offset selected before trying to use a particular variable. The exception is the BGL_INTERPOLATE command, which requires you to specify in the command itself the base offset that you are using for each of the parameters.

Putting it all Together

Now that we have all the pieces, here is the final code for my conditional airport lighting:

objectname_MasterScale_1 label BGLCODE

BGL_JUMP_32 skiptables

light_table_localvars label word
   dw  0  ; switch
   dw  0  ; flag
   dw  0  ; counter

light_table_addone label word
   dw  2
   dw  0,1,999,1000

skiptables label BGLCODE

LOCAL_BASE_32 light_table_localvars

VAR_BASE_32 VAR_BASE_GLOBAL

IFIN1 nolights, 028ch, 2, 4     ; time of day is not daytime

IFIN1 DSToff, 038eh, 420, 420   ; DST if offset is 7 hr x 60 min West of GMT
IFIN1 lightson, 0389h, 4, 13    ; GMT from 4:00 to 13:59:59 (21:00-07:00 PDT)
BGL_JUMP_32 DSTend
DSToff label BGLCODE
IFIN1 lightson, 0389h, 5, 14    ; GMT from 5:00 to 14:59:59 (21:00-07:00 PST)
DSTend label BGLCODE

IFIN2 timelights, 07beh, 1900h, 1900h, 033bh, 0, 5000  ; COM1 on 119.0 within 5 km
VAR_BASE_32 VAR_BASE_LOCAL
SETWRD 0h, 1                    ; turn the switch on
SETWRD 2h, 0                    ; reset the flag
SETWRD 4h, 0                    ; reset the counter
BGL_JUMP_32 lightson

timelights label BGLCODE
VAR_BASE_32 VAR_BASE_LOCAL
IFIN1 nolights, 0h, 1, 1        ; check the light switch
VAR_BASE_OVERRIDE VAR_BASE_GLOBAL
IFMSK holdlights, 05FCh, 0010h  ; add one to the count every 1.78 seconds
IFIN1 skipcount, 2h, 0, 0       ; flag skips the counter until tick18 completes cycle
BGL_INTERPOLATE VAR_BASE_LOCAL, 4h,\
                VAR_BASE_LOCAL, 4h,\
                VAR_BASE_LOCAL, (offset light_table_addone - light_table_localvars)
SETWRD 2h, 1

skipcount label BGLCODE
IFIN1 lightson, 4h, 512, 1000   ; takes roughly 15 minutes to count to 512
SETWRD 0h, 0                    ; turn the switch off
SETWRD 2h, 0                    ; reset the flag
SETWRD 4h, 0                    ; reset the counter
BGL_JUMP_32 nolights

holdlights label BGLCODE
SETWRD 2h, 0                    ; reset the flag

lightson label BGLCODE

VAR_BASE_32 VAR_BASE_GLOBAL

; REIL lights
SEPARATION_PLANE endREIL, -21926, 0, 24351, 32768
IFMSK blinklight, 05FCh, 000Fh
    BGL_JUMP_32 endREIL
blinklight label BGLCODE
    BGL_LIGHT LIGHT_NAV, 10.0, 0.5, 0.0, 40, 0.60, 0.40, 0FFFFFFFFh, 0.0, 0.0, 1.0
    BGL_LIGHT LIGHT_NAV, -10.0, 0.5, 0.0, 40, 0.60, 0.40, 0FFFFFFFFh, 0.0, 0.0, 1.0
endREIL label BGLCODE

; MIRL lights
SEPARATION_PLANE WrongWay, -21926, 0, 24351, 32768
    BGL_LIGHT LIGHT_NAV, 5.0, 0.1, 0.0, 20, 0.60, 0.40, 0FF008800h, 0.0, 0.0, 1.0  ; green light
    BGL_LIGHT LIGHT_NAV, 0.0, 0.1, 0.0, 20, 0.60, 0.40, 0FF008800h, 0.0, 0.0, 1.0  ; green light
    BGL_LIGHT LIGHT_NAV, -5.0, 0.1, 0.0, 20, 0.60, 0.40, 0FF008800h, 0.0, 0.0, 1.0  ; green light
BGL_JUMP_32 endMIRL
WrongWay label BGLCODE
    BGL_LIGHT LIGHT_NAV, 5.0, 0.1, 0.0, 20, 0.60, 0.40, 0FFAA0000h, 0.0, 0.0, 1.0  ; red light
    BGL_LIGHT LIGHT_NAV, 0.0, 0.1, 0.0, 20, 0.60, 0.40, 0FFAA0000h, 0.0, 0.0, 1.0  ; red light
    BGL_LIGHT LIGHT_NAV, -5.0, 0.1, 0.0, 20, 0.60, 0.40, 0FFAA0000h, 0.0, 0.0, 1.0  ; red light
endMIRL label BGLCODE

; Runway edge lights
IFIN1 RWYlight1, 033bh, 0, 1000  ; 0-1 km
  BGL_LIGHT LIGHT_NAV, 10.0, 0.0, -5.0, 20, 0.60, 0.40, 0FFFFFFFFh, 0.0, 0.0, 1.0
  BGL_LIGHT LIGHT_NAV, -10.0, 0.0, -5.0, 20, 0.60, 0.40, 0FFFFFFFFh, 0.0, 0.0, 1.0
  BGL_LIGHT LIGHT_NAV, 10.0, 0.0, -25.0, 20, 0.60, 0.40, 0FFFFFFFFh, 0.0, 0.0, 1.0
  BGL_LIGHT LIGHT_NAV, -10.0, 0.0, -25.0, 20, 0.60, 0.40, 0FFFFFFFFh, 0.0, 0.0, 1.0
  BGL_JUMP_32 endRWYlights
RWYlight1 label BGLCODE
IFIN1 RWYlight2, 033bh, 1000, 3000  ; 1-3 km
  BGL_LIGHT LIGHT_NAV, 10.0, 0.0, -5.0, 20, 0.60, 0.40, 0FF999999h, 0.0, 0.0, 1.0
  BGL_LIGHT LIGHT_NAV, -10.0, 0.0, -5.0, 20, 0.60, 0.40, 0FF999999h, 0.0, 0.0, 1.0
  BGL_LIGHT LIGHT_NAV, 10.0, 0.0, -25.0, 20, 0.60, 0.40, 0FF999999h, 0.0, 0.0, 1.0
  BGL_LIGHT LIGHT_NAV, -10.0, 0.0, -25.0, 20, 0.60, 0.40, 0FF999999h, 0.0, 0.0, 1.0
  BGL_JUMP_32 endRWYlights
RWYlight2 label BGLCODE
  BGL_LIGHT LIGHT_NAV, 10.0, 0.0, -5.0, 20, 0.60, 0.40, 0FF555555h, 0.0, 0.0, 1.0
  BGL_LIGHT LIGHT_NAV, -10.0, 0.0, -5.0, 20, 0.60, 0.40, 0FF555555h, 0.0, 0.0, 1.0
  BGL_LIGHT LIGHT_NAV, 10.0, 0.0, -25.0, 20, 0.60, 0.40, 0FF555555h, 0.0, 0.0, 1.0
  BGL_LIGHT LIGHT_NAV, -10.0, 0.0, -25.0, 20, 0.60, 0.40, 0FF555555h, 0.0, 0.0, 1.0
endRWYlights label BGLCODE

nolights label BGLCODE

BGL_RETURN