[comp.sys.hp] HP-28S moon lander program

nungeste@hpspkla.spk.hp.com (Dick L. Nungester) (10/11/89)

This moon lander program for the HP-28S features simultaneous numeric and
overranging bar graph displays of height, velocity, fuel, and fuel flow
(thrust) all calibrated in MKS units.  It also has blinking low- and no-
fuel indicators, a success or failure tune upon landing, and a "black doom"
instrument panel display if you crash.  

The display during run-time looks like this:

hgt 1184 m   |XXXXXXXXX
vel  -14 m/s      XXX|
fuel 500 l   |XXXXXXXXX
flow   3 l/s |XXX

and updates every 1.1 seconds (on my HP-28S).  The 13 programs will take a
while to key in.  Once all exist in the same directory, run MOON.  The
first time it runs, "Stand by..." will be seen for about 90 seconds while
variable initializations take place.  On future runs this won't happen.
The program starts as the lunar lander's main retrorocket has cut off,
leaving it about 1300 meters above the lunar surface with 0 velocity and
manual thrusters off (free fall).  Once the program is running, push keys 0
through 9 as fuel flow (thrust) values.  0 means free-fall, 5 means hold
current velocity constant, and 9 means hit the jets as hard as you can.  A
displayed impact speed of 0 or 1 m/sec is a successful landing.  

Once you get proficient, try to land with height, velocity, fuel, and flow
all at "0".  The initial conditions were picked so that an optimum landing
takes about 300 liters of fuel, and you have 500 to start with.  Can you
land with over 200 liters left?  I have landed with 201 still available.
Comments are welcome.  Send to: 

Dick Nungester
[72257,2501] (CompuServe)
nungeste@hpspkla (UNIX mail)
(509) 921-3560 (work phone: Hewlett-Packard, Spokane, WA)
(509) 926-9048 (home phone, Spokane, WA)
----------------------------------------------------------------------------
Variable definitions:
=====================
Variables initialized by INIT that change value during run time and are 
purged at the end of a program run by CLOSE:

H  = Height above the lunar surface (meters, >0 is upward)
V  = Velocity (meter/sec, >0 is moving upward)
F  = Fuel remaining (liters -- arbitrary units)
R  = fuel Rate (liter/sec, >0 is spending fuel)
RP = fuel Rate at Previous display update (liter/sec)
A = Acceleration (meter/sec^2, >0 is accelerating upward)
DT = Delta Time per execution loop (sec, 1.1 for my 28S)

Variables created by MVRS that stay from run to run of the program and 
never change value.  (Doing MVRS's one-time setup speeds execution.)

FST1 = Fuel STring #1 -- used to blink "fuel" when < 100 liter remain
FST2 = Fuel STring #2 -- used to blink "fuel   0" when fuel is gone
ISTR = Inversion STRing -- used to invert whole display when you crash

GBL = General Bar List (all height, fuel, and flow bar displays)
VBL = Velocity Bar List (all velocity bar displays)

Important equations:

Given H, V, F, and constant A and R for period DT, new values of H, V, 
and F after time DT are:

H = H + V*DT + A*SQ(DT)/2
V = V + A*DT
F = F - R*DT

Fuel flow ("R", 0-9 l/s), and acceleration ("A", m/s^2) are linearly
related, with the following 2 points defining the relationship: 

  R          A        Notes
======  ============  =================
  0        -1.62      free fall, 0 fuel flow   
  5          0        constant velocity, arbitrary fuel flow of 5

This means that upon sensing key 0 through 9 ("R"):

A = 1.62*(R/5 - 1)
--------------------------------------------------------------------------
Here are the programs.  They are listed from "high-level" to low.  Entering
them into the 28S in the reverse order given leaves MOON on the leftmost
key of the USER menu (most accessable position).  The following strings 
represent single characters in the 28S character set: "<=", ">=", "->", 
"<-", "<<", ">>", "`root`" (square root).  

MOON			MOON main program.
<< INIT
  DO VIEW KSCN EQS
  UNTIL H 0 <=
  END CLOSE
>>

INIT                    INITialize all variables.  To make every
                        program run slightly different, height is +/- 50
                        meters about 1296.  Existence of 'FST1' is used to
                        determine if all permanent varibles need creation.
<< RAND .5 - 100 *
1296 + 'H' STO 0 'V'
STO 500 'F' STO 0
'R' STO 1 'RP' STO
-1.62 'A' STO 1.1
'DT' STO 49 CF 50 CF	STD display mode
CLLCD
  IFERR 'FST1' RCL
  THEN MVRS
  END DROP
>>

MVRS			Make permanent VaRiableS.
<< "Stand by..." 1
DISP 274 0 SBLD DUP
24 255 SBLD + 250 0
SBLD + 'FST1' STO 48	FST1 = 274 CHR(0)'s + 24 CHR(255)'s + 250 CHR(0)'s
255 SBLD + 226 0
SBLD + 'FST2' STO	FST2 = 274 CHR(0)'s + 48 CHR(255)'s + 226 CHR(0)'s
548 255 SBLD 'ISTR'	ISTR = 548 CHR(255)'s
STO 10 1 MBL 'GBL'	GBL = 10 char bar graph with "0" in char #1
STO 10 9 MBL 'VBL'   	VBL = 10 char bar graph with "0" in char #9
STO
>>

SBLD			String BuiLD.  Return a string made up of n
                        CHR(chr) characters.
<< -> n chr
  << "" 1 n
    START chr CHR +
    NEXT
  >>
>>

MBL			Make Bar List.  Accept the 's'ize of the bar list
                        in chararters and the location within the string of
                        the 'z'ero axis.  Return a list of s+2 bar strings,
                        one for each possible value position plus overrange
                        and underrange.
<< -> s z
  << 0 s 1 +
    FOR v "" 1 s	v = 'v'alue represented by each bar string
      FOR t             t = 't'his char within each bar string
        IF 't==1
 AND v==0' EVAL
        THEN "<<" +	underrange; ("<<" is a single character)
        ELSE
          IF 't==s
 AND v==s+1' EVAL
          THEN ">>" +	overrange; (">>" is a single character)
          ELSE
            IF 't==z
' EVAL
            THEN 124	"|" character (to indicate zero location)
CHR +
            ELSE
              IF 't<
z AND t>=v OR t>z
 AND t<=v' EVAL
              THEN
127 CHR +		"dark" part of bar
              ELSE
" " +			"light" part of bar
              END
            END
          END
        END
      NEXT
    NEXT s 2 + ->LIST
  >>
>>

VIEW			VIEW current status.
<<
  IF F 100 <		Low or no fuel?
  THEN LCD->
    IF F 0 >
    THEN FST1		Low fuel.  Invert "fuel".
    ELSE FST2		No fuel.  Invert "fuel   0"
    END XOR ->LCD	Invert display pixels.
  END "hgt " H 4 FMT
+ " m   " + H 0 1296
'GBL' 10 BAR + 1
DISP "vel " V 4 FMT
+ " m/s " + V -40 5
'VBL' 10 BAR + 2
DISP "fuel " F 3 FMT
+ " l   " + F 0 500
'GBL' 10 BAR + 3
DISP			If inverted above, this restores normal look.
  IF R RP #		Only update "flow..." if needed.  "#" = "not equal".
  THEN R 'RP' STO
"flow   " R 1 FMT +
" l/s " + R 0 9
'GBL' 10 BAR + 4
DISP
  END
>>

FMT			ForMaT a number for display.  Round to the nearest
                        integer value and return a string padded with
                        blanks to total 'n' characters.  Assumes STD
                        display mode (as set in INIT).
<< -> n
  << .5 + FLOOR ->STR
    WHILE DUP SIZE n
<
    REPEAT " " SWAP
+
    END
  >>
>>

BAR			select a BAR graph string.  Accept a 'val'ue, the
                        'lo'w bar limit (number representing the center of 
                        the leftmost character), the 'hi'gh bar limit
                        (number representing the middle of the rightmost
                        character), which 'blist' (bar list) to select
                        from, and the size of the strings in 'blist'.
                        Return the appropriate bar string from within
                        'blist'.
<< -> val lo hi blist
size
  << '2+(size-1)*((
val-lo)/(hi-lo))'
EVAL
    IF DUP 1 <
    THEN DROP 1
    ELSE
      IF DUP size 2
+ >
      THEN DROP size
2 +
      END
    END .5 + FLOOR
blist SWAP GET
  >>
>>

KSCN			Keyboard SCaN.
<<
  IF KEY
  THEN
    IF F 0 ==		If fuel is gone, ignore keyboard input.
    THEN DROP
    ELSE STR-> DUP
'R' STO 5 / 1 - 1.62	else store new Rate and Acceleration.
* 'A' STO
    END
  END
>>

EQS			old status to new status EQuationS.
<< 'H' 'V*DT+A*SQ(DT)
/2' EVAL STO+ 'V' 'A
*DT' EVAL STO+ 'F' '
R*DT' EVAL STO-
  IF F 0 <		Clip fuel at 0 (can't go negative).
  THEN 0 'F' STO 0
'R' STO -1.62 'A'	If out of fuel, free-fall until impact.
STO
  END
>>

CLOSE			CLOSE the display and the program.
<< '-`root`(V^2-2*A*H)'	Find impact velocity. "`root`" = "square root".
EVAL 'V' STO 0 'H'	End with height = 0 always.
STO VIEW		View final status.
  IF V -1.5 >=		If display is "0" or "-1" m/s, 
  THEN SBP		success!  Sound the victory charge.
  ELSE LCD-> ISTR XOR	else failure (blacken display and embarass user).
->LCD FBP
  END { H V F R RP A	Purge all temporary variables.
DT } PURGE
  WHILE KEY		Purge any queued-up before-landing keystrokes.
  REPEAT DROP
  END
>>			End.

SBP			Success BeeP.  (The "victory charge")
<< 523 .1 BEEP 659 .1
BEEP 784 .1 BEEP
1047 .1 BEEP .1 WAIT
784 .1 BEEP 1047 .8
BEEP
>>

FBP			Failure BeeP.  (The "death dirge".)
<< 1 3
  START 220 .1 BEEP
.1 WAIT
  NEXT 175 .8 BEEP
>>