Dear Nuts & Volts Readers,

The flight simulator ran for many years with the BASIC software included in this article.

When versions of the simulator were picked up for manufacturing, I wrote a friend and engineer (John) the following explanation of my source code to help him recode the software into c++.

The c++ source code is not available, and was highly customized for a new set of custom designed control cards built for a “ride only” version of the sim. (The commercial manufacturer only wanted people to ride in the simulator without having direct control.)

So, you can use my notes below to get a full understanding and many of my thoughts on the software to make your own implementation easier!

In addition, you’ll find my source code HEAVILY commented as well.

It’s funny rereading these as I can see my own learning curve in figuring out just what I told you in the article now in Nut & Volts!

Thanks,

Walt

Here on some notes as far as theory of operation:

With pneumatics, I KNEW I’d never get accurate within a degree or two, so I didn’t even try. (Surprisingly, it was more accurate than I’d thought!) What was important to me was the “feel” of the flight. In figuring out the numbers here, I literally sat in the cabin, flew to 30 degrees of bank on the screen, and swung the cabin to what I thought FELT like 30 degrees and noted the position given by the pot. I did not make 30 degrees on the screen equal 30 degrees in the cabin exactly. (Actually version one did, but that’s why I did away with it!)

Other forces in a real plane make a 30 degree back feel like a lot less than literally sitting in the plane while stationary and hanging at a 30 degree angle.

This probably is not all that important when designing “movie ride” software, but it will explain why you see some of the numbers you do. Also, it’s something to keep in mind for generation two, where they will want full control for the rider. (Funny that we solved the hard issues of that, and are being asked to make a machine that doesn’t do that. Well, that makes our job easier for now.)

The Weeder/Walt board gave the following readings using 2.5K pots on both axis:

Roll Full Left: 1169

Roll Full Right: 2346

Pitch Full Up: 3371

Pitch Full Down: 2302

I give variables in the software called “bumpers.” These are operator settable variables that determined how close the simulator will come to the stops. I.e. “lbumper%” would determine how far left the sim would roll left before stopping. This served two purposes: 1.) It stopped the sim from ever rolling into a mechanical stop and 2.) widening the bumpers can be used to tame the ride for little kids etc.

Scott had the idea that bumpers be settable by a rotary switch or the likes. I.e. a 3 position switch for high, medium, and low.

Actually, I ALMOST never gave any but the wildest ride! I thought any less than a good yell would be a disappointment for the rider.

A quick note here: This was one of my favorite things about the sim! I LOVED watching it WHIP around and never hit a stop!!! It just smoothly avoided them! I found this eerie to watch (the glass smooth operation of an “intelligent” and “active” system.) I don’t usually trust active systems. J

One of the MOST important things I discovered was what I called “dampening.” This little idea suddenly made the sim come alive and feel just right.

Initially what I did was have my program get the current screen position from your program. Then, my program would pick a “target” position based on what posture (pitch and roll) it felt would best simulate that screen position. It would then move the cabin to that position.

The problems I encountered with this scheme were minor, but very annoying (it actually ran in this configuration for several shows without complaint).

The first problem was the possibility of oscillation. I.e. on a small move, it was EASY for the cabin to overshoot it’s target. It would then adjust by rocking back a tad. This drove me nuts because an airplane would never do that and it didn’t feel right at all.

At first I tried to fix it by having a “fudge factor” variable, so that you had a wider “zone” (setable) in which the sim could rest without readjusting. But this was bad because even a SLIGHT widening of this zone meant greatly decreased sensitivity to motion. I.e. Small screen motions would not result in platform motions unless the screen moved beyond the fudge factor setting. This created the dreaded “lag.”

So, my “dampening” solution was to have the simulator pick a target and move to the target. As soon as the target is reached, motion would be stopped, and NO new motion was allowed to occur (no matter cabin position even in an overshoot) until a new CHANGE in position was detected in the screen position.

A quick note: With pneumatics, an excited rider shifting his weight around could even cause a small cabin motion of a degree or two.

This scheme is counter intuitive, because if you made a tiny motion, say to roll left on the screen, the cabin would probably slightly overshoot, so another tiny motion to the left would seem to land you farther out of spec. HOWEVER, though this was technically true, the FEEL was just right, because each tiny motion was accompanied by a tiny move.

This put an end to all oscillation problems, and improved the way the ride felt while allowing us to completely eliminate the “lag” that simulators are famous for. (Tricky huh?)

I suppose it could be foiled by a rider who made tiny move, after tiny move over and over in one direction. But I strongly suspect he wouldn’t get away with such a scheme because at some point his airplane on screen would be at a steep bank or climb and “fall off” which would result in a lot of screen motion and authority for the cabin to swing to a radically new position.

So, those are the main points, and they relate more to interactive programming than movie programming, but I think they still will apply.

In “movie” operation, I assume we’d use the same scheme. A joystic position would corespond to a platform position. The platform in seeking that position would move to that position, motion would cease, and new motion would be disabled until the stick moved again (even if the target had been overshot).

Computer joystics kind of suck as they have such a narrow range of motion. Maybe the John board should have it’s own joystick plug in, and we could use a dual pot joystick with more range and a defined center? Just a thought. I hate to make this any harder than it needs to be though.

O.K. Here is the code for the current sim. It’s super simple. J

Remember that the Weeder boards used 3 letter ASCII commands to turn things off and on. I used port #1, so Print#1,”AHB” would send the command “access board A, set the output of pin B to High.” You can get the manual for the analog input and digital I.O. boards from weedtech.com. (I think that is their address. If not, google will find them.)

I’ll also include the “auto setup” program I wrote so the guys in the field could have it detect it’s own stops and set it’s center points and bumpers etc. in a pinch. This program will tell you what any and all variables do in the program as well (since you use it to set each one).

VF2000 Code:

'This version is for the regular IO board and analog input.
'It adds sensitivity.

'This version includes pseudo rotation.

'this version attempts dampening

ON ERROR GOTO restart

CLS

‘*John note: variables starting with p=pitch, r=roll, l=left, r=right, u=up, d=down. I defined center normally manually rather than having the computer pitch full up, then full down and pick the midle for a center point since the middle of the two readings didn’t always feel like center.


OPEN "simstats.txt" FOR INPUT AS #1
'variables for detected platform pitch center and roll center

INPUT #1, pcenter%

INPUT #1, rcenter%

'These variables represent platform extremes or "bumpers"

'so that the platform will stop before hitting its limits.

INPUT #1, lbumper%

INPUT #1, rbumper%

INPUT #1, ubumper%

INPUT #1, dbumper%

'this is the sesitivity to software changes

INPUT #1, sensitivity2%

'this is the sensitivity to the platform position

INPUT #1, sensitivity%

'These variables are for determining the amount of motion

'you get in proportune to a software move.

INPUT #1, pitchprop%

INPUT #1, rollprop%

CLOSE

OPEN "COM1:9600,N,8,1" FOR RANDOM AS #1

‘* John note: I found had to sleep a second to give the weeder boards a chance to initialize

SLEEP 1
CLS
COLOR 1
LOCATE 1, 31
PRINT " Helitrainer 2000 "
COLOR 8
LOCATE 4, 31

PRINT "Software position: "

LOCATE 9, 31

PRINT "Platform position:"

‘* John Note: I drew windows to show all data on platform and software position readings.


'draw border for software reading
COLOR 4
LOCATE 5, 12
PRINT CHR$(201); STRING$(55, CHR$(205)); CHR$(187)

LOCATE 6, 12

PRINT CHR$(186); STRING$(55, " "); CHR$(186)

LOCATE 7, 12

PRINT CHR$(200); STRING$(55, CHR$(205)); CHR$(188)

'draw border for platform reading

LOCATE 10, 12

PRINT CHR$(201); STRING$(55, CHR$(205)); CHR$(187)

LOCATE 11, 12

PRINT CHR$(186); STRING$(55, " "); CHR$(186)

LOCATE 12, 12

PRINT CHR$(200); STRING$(55, CHR$(205)); CHR$(188)

COLOR 1

LOCATE 20, 12

PRINT "(Q) To Quit"

LOCATE 20, 33

PRINT "(M) For Manual"

LOCATE 20, 56

PRINT "(C) To Center"

COLOR 5

LOCATE 10, 26

10 COLOR 15

a$ = INKEY$

IF a$ = "Q" OR a$ = "q" THEN

GOSUB alloff

SYSTEM

END IF

'This writes zeros to the port locations to center the platform

'after the game is run. This is a toggle.

IF UCASE$(a$) = "C" THEN flag2% = 1

IF flag2% = 1 THEN

LOCATE 20, 56

PRINT "(R) To Run "

OUT 129, 0

OUT 130, 0

OUT 131, 0

END IF

IF UCASE$(a$) = "R" THEN

COLOR 1

LOCATE 20, 56

PRINT "(C) To Center"

flag2% = 0

END IF

IF a$ = "M" OR a$ = "m" THEN

CLS

COLOR 4

LOCATE 6, 28

PRINT "::::: Manual Mode :::::"

COLOR 7

LOCATE 9, 20

PRINT "(U)P (D)own (L)eft (R)ight (H)old (E)xit"

GOSUB alloff

DO

PRINT #1, "aS"

header$ = INPUT$(1, #1)

INPUT #1, pitch%, roll%, rotation%, d%, e%, f%, g%, h%

LOCATE 11, 19

PRINT "Pitch: "; " "; pitch%; " Roll: "; roll%; " Rotation: "; rotation%; " "

b$ = INKEY$

IF UCASE$(b$) = "H" THEN GOSUB alloff

IF UCASE$(b$) = "U" THEN

PRINT #1, "ALA"

GOSUB receive

PRINT #1, "AHB"

GOSUB receive

END IF

IF UCASE$(b$) = "D" THEN

PRINT #1, "ALB"

GOSUB receive

PRINT #1, "AHA"

GOSUB receive

END IF

IF UCASE$(b$) = "R" THEN

PRINT #1, "ALC"

GOSUB receive

PRINT #1, "AHD"

GOSUB receive

END IF

IF UCASE$(b$) = "L" THEN

PRINT #1, "ALD"

GOSUB receive

PRINT #1, "AHC"

GOSUB receive

END IF

IF UCASE$(b$) = "E" THEN

GOSUB alloff

RUN

END IF

LOOP

END IF

'Port memory locations &H81 through &H86 are usable.

'This reads the first 3 for variables pitch% roll% & rotation%

softpitch% = INP(129)

softroll% = INP(130)

softrotation% = INP(131)

'print out the software values

LOCATE 6, 20

PRINT "Pitch: "; " "; softpitch%; " Roll: "; softroll%; " Rotation: "; softrotation%; " "

'This reads the first 3 pots on the analog input card.

'signal card to send data

PRINT #1, "aS"

'receive data

header$ = INPUT$(1, #1)

INPUT #1, pitch%, roll%, rotation%, d%, e%, f%, g%, h%

LOCATE 11, 18

PRINT "Pitch: "; " "; pitch%; " Roll: "; roll%; " Rotation: "; rotation%; " "

'find out where the platform needs to go

'pposition is the desired position to be indicated by space age pot.

IF softpitch% > 128 THEN pposition% = ((255 - softpitch%) * pitchprop%) + pcenter%

IF softpitch% < 128 THEN pposition% = pcenter% - (softpitch% * pitchprop%)

IF softroll% > 128 THEN rposition% = ((255 - softroll%) * rollprop%) + rcenter%

IF softroll% < 128 THEN rposition% = rcenter% - (softroll% * rollprop%)

'seek that position

COLOR 7

'once the platform pitch is in position, this routine

'checks to see if there is an actual change in software position

'before allowing pitch movement again

IF pchange% + sensitivity2% > softpitch% AND pchange% - sensitivity2% < softpitch% AND flagpitch% = 2 THEN GOTO roll

flagpitch% = 0

pchange% = softpitch%

'flag% stays equal to 0 if the platform is already positioned

flag% = 0

'next line turns off pitch up if at the pitch bumper

IF pitch% > ubumper% THEN GOTO poff1

IF pitch% < pposition% - sensitivity% THEN

flag% = 1

LOCATE 15, 20

PRINT "Tilting Back "

LOCATE 16, 20

PRINT "Target "; pposition%; " "

'turn off pitch forward light if on

PRINT #1, "ALA"

GOSUB receive

'turn on pitch back light

PRINT #1, "AHB"

GOSUB receive

END IF

poff1:

'next line turns off pitch down if at bumper

IF flag% = 0 AND pitch% < dbumper% THEN GOTO poff

IF pitch% > pposition% + sensitivity% THEN

flag% = 1

LOCATE 15, 20

PRINT "Tilting Forward"

LOCATE 16, 20

PRINT "Target "; pposition%; " "

'turn off pitch back light

PRINT #1, "ALB"

GOSUB receive

'turn on pitch forward light

PRINT #1, "AHA"

GOSUB receive

END IF