/*--------------------------------------------------------
RADAR.C -
purpose, simulate a radar Nike Hercules missile game in Windows
using keyboard (Fn keys), mouse inputs, also special i/o boards
which can do analog i/o and logic i/o
---------------------------------
---------------------------------
ManMachineInterface
Outputs to human
Function note
-------- ----
Missile Launched Lamp (on Launch Panel) ---------(a)
Missile Speed Meter (was Missile Ground Speed) ----- (b)
PPI display (CRT) -------------------- (c)
- Designate Circle
- Tracked Target (Target Tracking Radar)
- IFF
- Missile
- Predicted Point of Intercept
Range of Designate Circle Meter (on Designate Panel) --(d)
Target Altitude Meter ------------------(e)
Target Ground Speed Meter ---------------- (f)
Time to Predicted Intercept --------------- (g)
Control inputs
Function keyboard other note
---------- -------- ----- ----
About the program
A,a
Alert Status ---------------------- (1)
light in van
Command Missile Burst ------------------(2)
B,b switch on cmd
Designate Target (near select circle) ---------- (3)
D,d switch on az
Evasive Action by any designated target -------- (4)
E = on, e = off switch on PPI
Fast time on missile in flight ------------- (5)
F = on, f = off
History, StorageDisplayMode --------------- (6)
H = on, h = off switch on PPI
IFF (Identification Friend or Foe)( friend has 3 lines) - (7)
I = on, i = off switch in PPI
Keyboard Inputs --------------------- (8)
K - keyboard only (no external inputs)
k - also input from pots and switches
Launch a Hercules missile (must be in red alert) ---- (9)
L,l switch on cmd
Move Designate Circle ------------------ (10)
<,^,>,v (arrows) pots in az system
New Targets if less than 3 (starts east about 150,000 yd) (11)
N - on, n = off switch on PPI
PPI Display Range PPI switch ---(12)
3 = 350,000 yards
2 = 250,000 yards
1 = 150,000 yards
0 = 50,000 yards
Quit, go back to DOS
Q,q no switch
Restart (red alert, new planes, etc.) ---------- (13)
R,r switch on heater
Speed radar RPM sweep (ignored, always fast) ------(14)
Text, current event recorder data ----------- - (15)
T = yes,t = no switch on PPI
WarGame - automatic threat response ----------- (16)
W = on, w = off switch on heater
-------------------------------------------------------------
Notes
(a) Missile Launched Lamp (on Launch Panel)
- uses van -24 volt power supply as energy source
CO_3
current return to power supply
(b) Missile Speed Meter (was Missile Ground Speed)
- missile speed through air, not ground speed
(this was chosen to give feel of quick boost)
DAC_2
meter return
(c) PPI display (CRT) - 14" VGA emulation of radial electrostatic
VGA cable from computer to PPI
- Point Radar reflection (like airplane)
- small arc (about 2 degrees, less at long range)
- details
spread wide due to radar beam width
about 2 degrees (effectively wider on strong echos)
spread deep due to length of radar pulse (1 microsecond)
(167 yards per microsecond pulse length)
- ground clutter (Moving Target Indicator (MTI) not emulated)
(no MTI gives better feel of local and range)
- local clutter as bright spot in center
- to west, Farallon islands
- to north, coast to Point Reyes
- to east, Angel Island and Oakland coast
- to south, coast to Half Moon Bay
- Designate Circle
- Tracked Target (Target Tracking Radar)
- radial line, if with target, looks like a cross
- IFF
shown as fixed, unchopped 3 bar code
- Missile (normally not shown on PPI,
shown as "x" since plotting boards not connected)
- Predicted Point of Intercept (normally not shown on PPI,
shown as "#" since plotting boards not connected)
(d) Range of Designate Circle Meter (on Designate Panel)
- feed back to operator about distance of Circle
DAC_3
meter return
(e) Target Altitude Meter
- shown (instead of missiles) since plotting boards
are not connected
DAC_4
meter return
(f) Target Ground Speed Meter
DAC_1 -
meter return
(g) Time to Predicted Intercept
- shown in strange meter since plotting boards are
not connected
DAC_7
meter return
(1) Alert Status
computer reads RED ALERT lamp in van, MUST be RED to launch
CI_1
Switch ground
(2) Command Missile Burst
causes missile to be eliminated
and status to be pre-launch
CI_6 - on Switch Panel
switch ground
(3) Designate Target (near select circle)
CI_4 - on azimuth handle
switch ground
(4) Evasive Action by any designated target
causes any designated target to "randomly"
change yaw acceleration. (normally 0 g's)
This is usually an advantage to an aircraft formation
1) causes longer missile flight times and
reduces the number of targets which
can be attacked in a given time.
2) if an almost out-of-range but incoming target
turns around (runs away) it may cause the missile
to exceed its effective range, and be wasted.
CI_9 - on heater
switch ground
(5) Fast time on missile in flight
F = on, f = off
causes real time to move faster when missile in flight
(6) History, StorageDisplayMode
if ON, PPI scope acts as storage scope
if OFF, PPI scope acts as normal slow decay phosphor
if ON->OFF, erase with flash
CI_7 - on PPI
switch ground
(7) IFF (Identification Friend or Foe)( friend has 3 lines)
if ON, friend plane (top) shows 3 lines further out
and Target Tracking will not track it!
(this control is forced on if in WarGame mode)
if OFF, no IFF simulation
CI_11 - on heater
switch ground
(8) Keyboard inputs - input only from an optional keyboard
- system starts out reading van inputs, these will
over-ride keyboard inputs (like PPI Range)
- so then type "K"
- also, if ADC_4 (pot center) is near 0,
assume no van inputs,
do not read more ADCs and Contact Inputs
(9) Launch a Hercules missile (must be in red alert)
(computer always sets internal red alert)
CI_5 - on Switch Panel
switch ground
(10) Move Designate Circle
ADC_1 - Range control pot, knob in slot in az assembly
pot exitation
pot ground
(following 3 supply azmuth info, from the az circular disk)
ADC_2 - Azimuth sin function, rotate az assembly
pot exitation,
pot ground, (see ADC_1)
ADC_3 - Azimuth cos function, rotate az assembly
pot exitation,
pot ground, (see ADC_1)
ADC_4 - mid-point of sin and cos pots, no operator input
pot exitation,
pot ground, (see ADC_1)
(11) New Targets (if < 3)
(this control is forced on if in WarGame mode)
Every scan, a count of enemy targets is made
if (( count < 3) and (this switch is closed))
several aircraft (mostly enemy) are launched
from about 150,000 yards (85 miles) to the
west heading east.
This distance was chosen (instead of 350,000 yards)
to make the WarGame go faster, but is possibly
also reasonable for:
- very low flying planes to avoid radar
- submarine launched drones
- actual identification in heavy jamming
Two or three planes are launched at a time and
about 1 in 6 is a friend (possibly simulating:
- friendly fighter planes returning from
offshore battles
- civilian aircraft caught by suprise
CI_10 -
switch ground
(12) PPI Display Range ,binary switch on PPI,
4 ranges
(using binary switch to save contact inputs)
CI_2 - 2^0 value
CI_3 - 2^1 value
Switch ground -
(13) Restart (red alert, new planes, etc.)
(asserted at 'off' to 'on' transition
CI_8 - on heater
switch ground
(14) Sweep speed - (BC van value ignored, always fast)
(Slow is boooooring)
(15) Text, current event recorder data
- if OFF, no text
if ON->OFF, erase PPI to clear text
- if ON, update text in east part of scope
CI_0 on PPI
switch ground
(16) WarGame - automatic threat response
W = ON, w = OFF switch on heater
the computer acts as a Battery Commander,
shooting missiles at the incoming aircraft
(a quick exit if missile is flying is to
- turn off switch
- burst the missile)
(initialize Getting_Info, Launch_command flags to off)
(called every 100 ms)
if W_ON
force IFF and NewTargets on
if (missile flying),
if (flying over 150 seconds)
command burst
return
if (Launch_Command flag)
if (target tracked)
launch missile, clear Launch_Command flag
(should cause missile flying next 100 ms)
return
else
lost track error handling
else if (not Getting_Info flag)
clear internal tables and controls
set Getting_Info flag
if (target tracked)
save PI time into table
if (any target not in internal table)
designate target (assume instant auto tracking)
return
else (no more targets)
find shortest PI time
select that target
(assumes instant tracked and tracking data)
set Launch_Command flag
clear Getting_Info flag
return
CI_12 - on heater
ground
// -----------------------------------------------
Plan for end of phase 1
1) bill for material
2) lengthen 40 pin and 50 pin cables from 36" to 72 "
3) permanently mount termination blocks
to eliminate flexing of solid input wires
- block 1 for ADC and DAC cards (term blocks exist)
- block 2 for parallel port input (add block)
4) add termination block to PPI, add switches for
- Historical
- IFF
- Evasive action
- restart
- event recorder
- start plane
5) add switches (on heater) for
- restart
-
Ignoring
- alert status (always ready to fire)
- antenna speed (who wants a slow demo?)
Phase 2 - plotting boards (far future)
- pens
- position (computer DACs, error & volocity, motors)
- lift/down
- test position
- off sheet (paper change)
- interference logic
(inter change target/missile, lights)
- ignoring
- paper supplies
- ink and ink maintenance
- possible approaches
- use existing tube opamps/modulators/low power amps
(can probably fake it with one +- 270 volt ps)
- use silicon
+ 5 volts on pots (ADCs 0-5 volts?)
error and speed from pots
pulse time in phase B to triacs
excite motors with phases A-C
noted in schematics
1) PRF is 500 hz, 2000 us per pulse (328000- e yards?)
environment
-------------------
using microsoft c++ 1.5
options, (compiler) 386, inline 80x87 calls
desk top
800x600 16 bit color
DOS 6.0 or above (clock tick name incompatability with 5.1?)
special hardware for interface
ADC card, 40 pin cable, terminal and isolation block
4 ADC channels (0 -> +5 volts)
term board has 47K protective resistors
+ 5 volt source
8 contact inputs (TTL)
term board has pull-up resistor nets for -28 input)
8 contact outputs (TTL)
term board has darlington transistors
(for -28 v relays and lamps)
gnd
DAC card, 50 pin cable, (same block as above)
8 DAC channels (-5 -> +5 volts)
term board has 2 K ohm protective resistors
+ 5 volt source
gnd
(if no bc van, demo unit, or keyboard(and event recorder disp))
parallel port, cable, switch block
? TTL outputs
5 TTL inputs - seems to pull up to +5 volts
(block has 5 switches, no resistors for -28 volts)
gnd
(can't get parallel port base+2,c5 to go bi-directional)
? bi-directional printer port ?
reading status lines only
---------------------------
(looking into holes of female connector)
/--------------------------------------\
/ 25 24 23 22 21 20 19 18 17 16 15 14 \
/ 13 12 11 10 09 08 07 06 05 04 03 02 01 \
/--------------------------------------------\
female male
(switches) (machine)
2 25 gnd
9 15 CI_8 S3+ show event recorder
13 13 CI_9 S4+
12 12 CI_10 S5+ start plane east
11 11 CI_12 S7-
10 10 CI_11 S6+
these TTL inputs float hi
Note: all are active low (you can pull unit out w/o trouble)
Notes on stealth and detectable range
1) a big problem for the aircraft designer,
echo strength ~ effective cross section /(range^4)
or
effective detectable range ~ (effective cross section)^0.25
the same detectability at half the range means
reducing effective cross section by 16
(the radar designer has already faced the problem:
target reflected power ~
target radar cross section * radar power * antenna gain
/ range^2
power from target into receiver ~
reflected power * antenna gain / range^2
so
effective radar range ~ ((antenna gain ^2) * radar power) ^0.25
double the detectable range means increasing power by 16
External i/o interface (by i/o device)
Analog Input (RTD AD210, base address ADCboard = 0x300)
(board set to 0 to +5 volt)
ADC_1 - Target Select Range, (47K ohm series) from excited pot,
ADC_2 - Target Select Azimuth, (47K ohm series) from excited pot
sin?
ADC_3 - Target Select Azimuth
cos?
ADC_4 - Target Select pot center point (ADC 0-5 volt, no neg.)
the +5 reference voltage to excite the pots is
the input isolation is in the form of a series 47K ohm resistor
between the screw and the input line
Analog Output (RTD DA720, base address DACboard = 0x320)
(board set to +-5 volt)
DAC_1 - Target Speed, analog meter
DAC_2 - Missile Speed Meter
DAC_3 - Target Range Meter
DAC_4 - Target Altitude Meter
DAC_5
DAC_6
DAC_7 - Time to Intercept
DAC_8
the board has calibrated isolation amplifiers
? propose using 1K resistors for further isolation
Contact Input (RTD AD210, base address ADCboard = 0x300)
CI_0 -
CI_1 -
CI_2 - PPI Range, 0's bit - prog switch B (pen contact?)
CI_3 - PPI Range, 1's bit - prog switch B
CI_4 - Target Select, button, 0=null, 1=select
CI_5 - Launch Missile, button, 0=null, 1=launch
CI_6 - Manual Command Burst, button, 0=null, 1=burst
CI_7 - Show Storage Display (? PPI ?)
// following from Parallel port
CI_8 - Show Event Recorder, sw 0=don't, 1=do (? PPI ?)
CI_9 - IFF (? PPI ?)
CI_10 - Start plane east if ( #enemy < 3)
CI_11 - Target Evasion (? PPI ?)
CI_12 - start problem again (? Heater cabinet momentary switch?)
- restart initial situation (CI_0) ?
currently, a 47 K ohm pull up resistor to + 5 volt
and a 47 K ohm resistor series resistor
Contact Output (RTD AD210, base address ADCboard = 0x300)
CO_0 - Alert Status, on = Red
CO_1 - Target Tracked,
CO_2 - PI in dead zone
CO_3 - Missile in flight,
CO_4 -
CO_5 -
CO_6 -
CO_7 -
expected loads:
- 24 volt incandecent lamps (appendix says more peripheral
drivers are damaged by incandecents than any other load)
(the initial current is 9 to 10 times the steady state
current) A common technique is to use a switch bypass
resistor to warm the filament to barely glowing when the
switch is open. This reduces the surge current to maybe
2 times steady state. (Hi surge current leads to heating
in two ways
1) normal i * emitter drop (i * 1.3 v)
2) switch saturation, with added heating effects
- signal class relays 24 volts (? resistance, ? inductance)
schematics show that (all?) relays are shunted by something
proposed, either of 2:
a) used a pnp darlington with voltage divider input to base
(logic) 2500 ohm ,pnp base, 6000 ohm to -12 volts
with stubbing network.
pnp collector, 100 ohm, 1 ufd (100 v?) to digital gnd.
b) could not find National DS3687 negative peripheral driver
(looks good for driving the -24 volt based low duty relays)
(equiped with a -60 volt zener equivalent)
(both inputs +5 turn it off, any input 0 causes conductivity)
External i/o (by function)
STATUS
Alert status
Show Event Recorder
input, switch CI_8 0=don't, 1=do
ANTENNA AND PPI RANGE
Antenna Speed
PPI Range
input prog sw CI_2,3 0=50K,1=150K,2=250K,3=350K
TARGET SELECTION
Target Select Range
input ex pot ADC_1
Target Select Azimuth
input ex pots ADC_2,3
input ex pot center points
Target Select Command
input button, CI_4 0=null,1=active
TARGET CONDITION
Target Tracked
output led CO_1 0=null, 1=tracked
Target Speed
output meter DAC_1 analog meter, 0=not tracked
PREDICTED INTERCEPT
Time to Intercept
output meter DAC_2 DVM , 0=not tracked
Predicted Intercept in dead zone
output led CO_2
MISSILE CONDITION
Launch Missile
input button CI_5 0=null, 1=launch
Missile in flight
output led CO_3 0=null, 1=launched & tracking
Manual Command to Burst Missile
input button CI_6 0=null, 1=burst
-------------------------------------------------------------
0) Senario
a) situation starts in:
- Blue alert (stations manned, equipment running)
- a number of mach 0.7 and mach 1.5 planes
- flying toward San Francisco from the west
- at about 160,000 yards range
b) destroy as many as possible,
c) hints,
- you must be in Red alert status to fire a missile
- target the fast ones first
Nike system summary
-------------------
Radar summary
There were 3 main radar systems with 1 or more radars each.
Acquisition Radar - to get overall picture, select target.
The battery commander saw the display of this radar,
selected targets to the Target Tracking operators.
There were several types of these radars, with aids
to suppress ground clutter, low speed aircraft, and
several forms of jamming.
Target Tracking Radar - three operators aided tracking in
range, elevation and azimuth of the selected target.
The range, elevation angle and azimuth angle were converted
at the radar mount by sin/cosine potentiomenters into
h(altitude), x(N/S), and y(E/W).
These values were delivered to the computer as voltages.
Target Range Radar special characteristics helped the
range operator handle Electronic Counter Measures (jamming).
Missile Tracking Radar - to track and command the missile.
The missile height, x, y, values delivered to computer.
Computer
An analog computer (distances and times were voltages) used
inputs from the Target Tracking and Missile Tracking Radars.
These values (and derived target and missile velocities) were
used to calculate the remaining flight time and Predicted
Intercept point. The missile was guided to the predicted
intercept point by the computer generated steering commands
sent through the Missile Tracking Radar.
About 0.1 second before intercept, a burst command was sent
to explode the missile and hopefully disable the target.
Missile guidance summary
To keep flight times down, (and increase the effective range)
the missile is aimed ahead of the target at a calculated
Predicted Intercept point. If the aircraft flies a straight
line, the missile horizontal flight path will also be straight.
(A dog chasing a target runs directly toward the target,
involving a longer run if the target runs straight.)
The missile is launched (essentially) straight up, boosted
to about mach 1.7 in 3.4 seconds. It then turns its belly
toward the calculated Predicted Intercept (allow 1 second).
A sustainer rocket starts to increase the speed to mach 3.5.
A full dive command is sent to the missile to dive it
from vertical toward horizontal to intercept the flight path
of the target. When the missile has reached a vertical angle
that will be a good flight path to the intercept point, the
full dive command is removed and normal steering begins.
The missile is command guided by coded sequences of pulses from
the missile tracking radar to the predicted intercept point.
The predicted intercept point is constantly being updated by
the computer from data from the target tracking radar and
the missile tracking radar.
About 0.1 seconds before the missile will be closest to the
target, a missile burst command is send by a coded pulse
sequence to the missile by the missile tracking radar.
This burst command is decoded and the missile warhead exploded.
This Demo System as School
This demonstration system is basicly a Surface to Air (SAM)
shooting gallery for novice (you) battery commanders,
with simulated aircraft appearing on a display similar to that
seen by actual Nike battery commanders. (At the end of
training, and periodically thereafter, Nike crews and officers
traveled to a test site (usually to Red Canyon, New Mexico)
and fired actual missiles at tiny (8 foot wing span)
drone aircraft.
Think of this demo as the lab for college course SAM 1,
necessary (but not sufficient) to do the antiaircraft mission.
The battery commanders (and other personel) needed much more
training (especially in counter measures by the aircraft)
to begin to handle actual combat situations.
You, the student battery commander will view a CRT picture
similar to that presented by Nike acquisition radar. You
can operate switches and knobs that perform responses to the
real controls available to a battery commander.
Simulated radar tracks the selected simulated targets and
the missile launch switch starts a simulated missile.
The simulated computer generates commands which guide the
simulated missile to the simulated target.
Tools for the student battery commander
Provided for the student (as battery commander) are:
1) the acquition radar display
Plan Position Indication - (PPI) - with:
a) some targets and ground clutter
b) Identification Friend or Foe (IFF)
c) target selection circle
d) predicted intercept point
(presented on PPI, not automatic plotting boards
e) predicted time to intercept
(presented on meter, not automatic ploting boards)
f) missile position
(presented on PPI, not automatic plotting boards)
2) various other important indications and controls
a) alert status
the site is always "RED ALERT"
b) radar scan speed (fast/slow rather than the actual
5, 10, 15 RPM) a switch marked ANTENNA FAST/SLOW
c) range shown on PPI
(350,000 250,000 150,000 50,000 yards)
a rotary switch marked as above
d) target selection command button
a bush button marked "Target Select"
e) selected Target Tracked light,
from Target Tracking operators
a led marked "Target Tracked
f) target speed indicator
a meter marked in mach numbers
(assuming mach 1.0 = 750 miles per hour)
g) a meter indicating time to intercept
- before launch - flight time if launched right now
- after launch - flight time remaining
h) launch missile switch
(the higher performance Hercules is launched)
i) missile destroy in flight (operational problem)
j) "dead zone"
(can't hit target within 7,000 yards of site)
(presented as a warning light,
not on the time/elevation plotting boards)
k) event recorder
(shows mission history & estimated miss distance)
(presented by push button presenting text
no photographic film to be developed later)
Important items not present or assumed perfect are:
a) tracking antennas are correctly adjusted, that is
boresignted, leveled ( a high maintenance item),
b) all other system adjustments correct
c) system powered up sufficiently, (it takes at least
15 minutes to get the acquisition radar tubes
correctly warmed for use)
d) no communication with other batteries and headquarters
(who sees what, who shoots at what, etc)
e) communication with the launcher area,
(the launcher section always has missiles ready)
f) communication with the tracking radar operators
(target is acquired & tracked easily, for demo!)
3 target tracking operators
(azimuth, elevation and range)
1 missile tracking operator
g) the automatic plotting system is not present
(Predicted Intercept and missile are on the PPI)
(elevation is not presented in this demo)
(time to intercept is on a meter rather than the
axis of 2 plots on the automatic plotting system)
The following items help simplify the problem for the student:
a) IFF (Identification Friend or Foe) is always on
(see top aircraft)
b) The aircraft fly right in
- no turning back to get you to waste ammunition
- no multiple aircraft trying to appear as one
c) No stealth aircraft to cause suprises
d) No passive jamming (chaff) to obscure the area
e) There is no active jamming (a major aircraft defense)
f) And the aircraft don't threaten to shoot back with:
- radar seeking missiles
- pre-targeted missiles with you as the destination
g) Aircraft do not carry missiles (must drop bombs)
(just keep the enemy 10 miles away (18,000 yards)
---------------------------------------------------------------------------
also, improve predicted time to intercept
- existing was ajax at fixed mach 2.5
- this hack initial dives at mach 1.7,
then fires sustainer to mach 3.5
causing big changes in predicted intercept,
and 0.5 g horizontal g's
14) study using system tube modulators for plotting board
- +- 5 volts from DACs and across plotting board pots
(not 100 volts)
- fire up bc van filaments
- fire up bc van power supplies,
15) study effect of running analog computer
- clone simulates and displays one or more targets
- video to display
- clone picks up target select command, (video to display)
- clone slews TTR to target and sends "tracked" signal
- clone "tracks" target, supplying to bc computer
(+- 5 volts * 20)
(auto rc van target acquire)
- slant range to elevation pot
- height to computer
- ground range to az pot
- x, y to computer
- (clone simulates launch area signals? - missile select?,
ready?, etc)
- clone inputs missile fire from bc van
- simulates missile launch
- inputs steering commands from computer
- simulates missile behavior & tracking data
(missile data to computer +- 5 v *20)
- slant range to elevation pot
- height to computer
- ground range to az pot
- x, y to computer
- clone takes burst signal (allows x ms) and calculates
miss distance
- clone records "every thing" including event recorder info
- bc van does all else
- human inputs
- predicted point of intercept, (gyro),
and predicted time of intercept
- values to plotting boards
- missile steering commands
- alert status
17) if tube supply or zero set problem in computer
- use +- 5 volts for reference voltages
(op amp, ADC and DAC limits
- replace all tube op amps (+- 100v)with chopped op amps (+-5v)
(leave tube op amps there, but unscrew supply plug
into supply plug insert dual chopped op amp with
simple power supply)
- assume tubes supplied with 12 volts center tapped
- simple unfiltered +- 9 volts to chopped op amps
(totally ignore other voltages)
- if zero set amplifier works, the neon flasher can be
used to find non"zero" inputs to op amps for
diagnostics. (correction voltage
not needed as op amp is already chopped.
- need servo amplifiers to drive pots
- tube modulators for high voltages eliminates need
for fancy electronic work by me.
i/o board generalities and details -
------------------------------------
all from Real Time Devices, Inc
820 North University Drive
P. O. Box 906
State College, Pennsylvania 16804
phone (814) 234-8087
FAX (814) 234-5218
AD210
-----
ADC 4 single ended 12 bit ADC inputs (0 to +5 v factory default)
16 external TTL i/o
8 internal logic i/?
Jumpers
P3 base address 0x300 (factory default)
P4 A/D end of convert PPI PB7 (factory default)
P5 external int or PPI disabled (factory default)
P6 timer to int channel disabled (factory default)
P7 configures 8254 counter cascaded (factory default)
missile data
combined booster and body
length 39 feet
weight 10,550 pounds
body
length 27 feet
weight 5,250 pounds
body diam 32 inches
fin diam 90 inches
thrust 13,500 pounds
burn time 29 seconds
booster
length 14 feet (including 2 feet of coupling with body
weight 5300 pounds
body diam 34 inches
fin diam 138 inches
thrust 173,600 pounds
burn time 3.4 sec
(following is with delayed sustainer)
speed distance profile (estimated from weight, thrust, and distance
time (sec) speed (m/s) mach hz, km hz, miles alt, ft.
---------- ----------- ----- ------ --------- --------
0 0
3.4 633 1.82 0 0 3,336
(end of booster, assume 1/2 weight is propellant)
(details (2 g's needed for 1g of vertical speed gain)
(start of boost 17 g (16 for vert acc.)
(end of boost 21 g (20 for vert acc.) average 18g vert
4.2 633 1.82 0 0 5,270
(if minimum dive radius needed (low alt), delay sustainer t+9)
(s = start of sustainer motor, < 3% loss in altitude gain)
start and end of burn 2 g acceleration (less propellant/weight)
drag assumed constant at 3000 pounds ( ~ 1/2 g)
10 733 2.1 0.8 0.5 14,100
(missile has completed 30 degrees in initial dive)
15 933 2.6 2.8 1.7 20.5
(missile has completed 60 degrees in initial dive,
s+0 633 1.82 0 0 5,270
s+10 833 2.4
s+20 1033 2.9
s+30 1218 3.6 (end of sustainer)
s+40 1118 3.2
s+50 1018 2.9
s+60 918 2.6
s+70 818 2.3
s+80 718 2.06
---------------------------------------------------------------
general form:
center is radar/missile launch near SF, left is west
(radar sweep simulated by blanking just before scan)
- simulated long persistance display phosphor or
(no) digital memory of past target position
(most recent scan = yellow,
aged each x? degrees after scan
- missile is x
- predicted intercept point is #
- IFF response (from friendly aircraft)
- all angles start with 0==north, clockwise
- east is +x, south is -y
- visible objects in simulation
(mouse is arrow, hot spot is tip
- lift right button selects target (if
- lift left button
- system objects are from 1-9
- 1=map object (vectors)
- 2=target Tracked icon, a box
- 3=target tracking radar, a +
- 4=predicted point of intercept, a circle
- 5=missile icon, ajax, also missile tracking radar
- 6=missile icon, hercules, also missile tracking radar
- trackable objects are types OBJ_T_MIN -> OBJ_T_MAX
including
mach 1.5, OBJ_AIRCRAFT
OBJ_BIRDS,
Plotting Rules
Horizontal x,y - scale = +- 200,000 yards
plotted (target select until fire)
target and intercept
plotted (fire until intercept)
target and missile
timing marks (10 sec) left, return, up, return spikes
fire marks right, return, down, return spikes
pen ids (TARGET/MISSILE) in lower left/right corners
- a flag 'target_left' is toggled by the 'xy hit detect'
- if this flag is set, the
Altitude z,t - scale 0-100,000 feet, 0-160 seconds
timing marks (10 sec) vertical spikes
--------------------------------------------------------*/
// note: objects are stored in type sorted order
// event recorder
// recording, to file single file EVENT.TXT, restart kills previous file
// button on/off,
// 0 1 2 3 4 5 6
// 1234567890123456789012345678901234567890123456789012345678901234
// Sx Sy Sz Vx Vy Vz
// km km km m/s m/s m/s
// m_time target snnn.n snnn.n snnn.n snnnn snnnn snnnn
// missil snnn.n snnn.n snnn.n snnnn snnnn snnnn
// pre in snnn.n snnn.n snnn.n snnnn snnnn snnnn
// g ang nnn
// status m=sssssssssssssss c=sssssssssssssssss
// current limitations
// g's I cant currently calculate the g steering forces needed
// to steer a missile in its 3 dimentional space to a given point.
// So, for now, i will calculate the x,y steering forces in the
// game's x,y plane and the vertical steering forces in a plane
// through the z axis. This will be close enough.
// Displayed missile g's calculated from x,y and z
//
// time to intercept is calculated in the game's x,y plane only
//
#define STRICT // further checking gives more warnings
#define DEBUG_R 0 // record + print debug features are off(0)/on(1)
#define LPT1 0x278
// printer port 0x278 on 486
// 0x378 on NCA
//////////////////////
#define ADCboard 0x300 // base address of adc board containing 4 ADCs
// and 16 external discrete lines PA0-7 is input
// PC0-7 is output
// card takes 24. port addresses
#define ADC_1 0 // Target Select Range
#define ADC_2 1 // Target Select Azimuth, sin
#define ADC_3 2 // Target Select Azimuth, cos
#define ADC_4 3 //
//(some normal contact inputs deleted due to limited inputs
#define CI_0 0 // WarGame
#define CI_1 1 // About this program
#define CI_2 2 // PPI Range, 2^0
#define CI_3 3 // PPI Range, 2^1
#define CI_4 4 // Target Select
#define CI_5 5 // Launch Missile
#define CI_6 6 // Manual Command Burst
#define CI_7 7 // Show Storage Display
// added from parallel port
#define CI_8 8 // Show Event Recorder
#define CI_9 9 // IFF
#define CI_10 10 // Start planes east if (#enemy < 3)
#define CI_11 11 // Selected Target Evades
#define CI_12 12 // Restart
// some contact outputs added until plotting boards)
#define CO_0 0 // Alert Status 1==Red ( not connected)
#define CO_1 1 // Target Tracked ( not connected)
#define CO_2 2 // PI in dead zone ( not connected)
#define CO_3 3 // Missile in flight
#define CO_4 4 // Missile steering limit, vert ( not connected)
#define CO_5 5 // Missile steering limit, horz ( not connected)
#define CO_6 6 //
#define CO_7 7 //
///////////////////////
#define DACboard 0x320 // base address of dac board containing 8 DACs
// also viewed as 800. decimal
// card takes 16. port addresses
#define DAC_1 0 // Target Speed Meter
#define DAC_2 1 // Missile Speed Meter
#define DAC_3 2 // Target Range Meter
#define DAC_4 3 // Target Altitude Meter
#define DAC_5 4 //
#define DAC_6 5 //
#define DAC_7 6 //
#define DAC_8 7 // Time to Intercept Meter
///////////////////////
// keyboard constants
#define VK_F1 0x81
#define VK_F2 0x82
#define VK_F3 0x83
#define VK_F4 0x84
#define VK_F5 0x85
#define VK_F6 0x86
#define VK_F7 0x87
#define VK_F8 0x88
#define VK_F9 0x89
#define VK_F10 0x8a
#define VK_F11 0x8b
#define VK_F12 0x8c
#define UP_ARROW 0x91
#define RIGHT_ARROW 0x92
#define DOWN_ARROW 0x93
#define LEFT_ARROW 0x94
#define PI 3.141592653
#define DEG_PER_RAD (180.0/PI)
#define OBJ_MAP_V 1 // map line(s)
#define OBJ_TTR 2 // target tracking radar position
#define OBJ_TAR_DES 3 // target designator circle
#define OBJ_PI 4 // predicted intercept point
#define OBJ_MTR 5 // missile tracking radar
#define OBJ_EXPLOSION 6
#define OBJ_T_MIN 100
#define OBJ_T_MAX 127
#define OBJ_AIRCRAFT 100 // does not respond to IFF
#define OBJ_AIRCRAFT_F 101 // responds to IFF
#define OBJ_BIRDS 103
#define OBJ_BIG 104
#define OBJ_SMALL 105
#define TIMER_SCAN 1
#define SCAN_INTERVAL 10
#define RADIUS_IN_PIXELS 400
#define CENTER_PIXEL_X 400
#define CENTER_PIXEL_Y 300
#define PIXELS_PER_ARROW 20
// plotting board scaling
#define MAX_PLOT_HZ 300000 // +- 300,000 yards
#define MAX_PLOT_VT 50000 // 0 to +50,000 yards
// Object display structure
#define MAX_OBJ 100
#define PULSES_PER_RAD 114 // about 2 pulses/degree
#define LIM_PULSE_CNTS (int)( 2 * PI * PULSES_PER_RAD)
#define MAX_PHASES 6 // counting 0 -> 5
#define MAX_STEERING_ACC 100.0 // maximum vert or horizontal steering
// in acc meters/sec/sec // about 10 g's
// vertical space for text
#define VERT_TEXT_STEP 1
// message types
#define MY_CREATE 9901
#define MY_TIMER 9902
#define MY_KEYUP 9903
//#define MY_MOUSEMOVE 9904
//#define MY_LBUTTONUP 9905
//#define MY_LBUTTONDOWN 9906
//#define MY_RBUTTONUP 9907
//#define MY_RBUTTONDOWN 9908
//#define MY_DESTROY 9909
// define STRICT // further checking gives warnings
// #include
#include
#include
#include
#include
//#include // _ellipse
#include
#include
// following added for floating point underflow exceptions
#include
#include
#include
#include
// local subroutines
long ClearDisplayHistory();
long SetDisplayTo(int ColorNumber);
long CheckIt( char * string); // if i != -1, make box with string, ok
long ComputeMissileMiss_H_V();
long CreateInitialSituation(int);
long CreateSystemObjects(); // predict int, targ sel, missile
long DisplayObjects(int iScan);
long GetAnalog(int id, double * val, double min, double max);
// id is port 1 through 4, val in engineering units
// offset and scaling is in subroutine
// if error, return = -1, val - -9999999.9
long GetDigital (int id); // return digital channel (1 - 8), else -1
char GetPrinterPort();
long About_Ed(int); // author Ed's message
long About_Menu(int); // message menu and array
long InitializeObject(int iObj);
int KeyboardAscii() ; // convert a keyboard hit to ascii
long KeyboardInput(int wParam); // process keyboard hits
long Log(); // post the message in szLogEntry
long My_Beep (int i); // DOS does not provide a beep
void MyLine (int x1, int y1, int x2, int y2, short color);
long MyProc (int iMsgType, int wParam); // main processing area
long PaintObject( int iObj, int iPulse);
long PutAnalog(int id, double * val, double min, double max);
// id is port 1 through 8, val in engineering units
// NOTE*** the min will cause -5 volts, the max +4.99 volts
// offset and scaling is in subroutine
// if error, return = -1,
long PutDigital(int id, char val); // id=dig chan (0-7), val = 0/1
// return 0 if legal, else -1
long RecordEvent(); // write data to event recorder
long RemoveOutOfViewTargets(); // return # remaining
long SelectTarget(double x, double y);
long set_video_SVGA256 (); // set video to SuperVGA 800x600,
void Sleep( clock_t wait ); // from Help
long SpecialInput(); // input from special cards and LPT1
long SpecialOutput(); // output to special cards
long StartPlaneEast(int iSpeedy, int designate);
long UnPaint(int iObj); // remove all paint sequences of obj
long UpdatePlotPosition(int v ); // move current position into plot data
// (current quadrant)
long UpdateMissile();
long UpdateIntercept();
long UpdateTargets();
long UpdateText(int cmd);
long WarGame(); // - automatic threat response
// local globals
// ------------------
char cInputKeyboardOnly = 0; // input select for simulation
// 0=ADC & switches, 1=mouse & keyboard
char cTargetTrackLeftArm = 1; // west == left (usual expectation)
char cIFF = 0; // IFF flag (toggle)
char cWarGame = 0; // flag to cause automatic response
char cEvasiveAction = 0; // designated target random yaw accelerations
char cStartPlaneEast = 0; // start new targets if < 3 targets remain
char cAbout_Keyboard = 0; // messages from author, from keyboard
char cAbout_Switch = 0; // messages from author, from switch
char cAbout_Seq = 0; // sequence # if from switches
char cFastMissileTime = 0; // faster missile flight for fun
// not included in switches:
// - we ran out
// - not authentic, a cheap thrill
char szLogEntry[100]; // user can sprintf text here and call Log
char my_text[100];
char cDisplayEventRecorder = 0; // enable show engineering data,
char cClearMyScreen=1; // trigger clear screen
char cStartPlane=0; // start plane request
double dPixelPerMeter = RADIUS_IN_PIXELS/350000.0; // initial
double dVisibleRange;
double dOldPixelPerMeter = 0;
double dMeterCalib = 1.5/1.75; // convert units to meter readings
double dGlobal1 = 0, dGlobal2 = 0; // debug printouts
double dSecondsSim; //from the beginning of world (Jan 1,1970) no delay
// until fSeccondsSem > LSecondsUnix - lSecondsStart)
double dSecondsSys; // system time, bring fSecondsSim up to this
// before scan
double dSecondsDelta=0.1; // time to advance one simulation cycle
double dSecondsMissile=0; // 0 at start of boost
double dPIRadNorth; // predicted intercept, radians from north
char cMissile_Over_Range; // set by missile tracking radar
double fMissRange, fMiss_x,fMiss_y,fMiss_z; // calculated miss distance,
int iPulseCnt; // pulses this scan,
double dSweepRad; // = (double)iPulseCnt/PULSES_PER_RAD;
double dSweepSin; // = sin(dSweepRad);
double dSweepCos; // = cos(dSweepRad); // for sweep
int iPulsesPerTimer= 10; // controls sweep speed 10 gives about 15 RPM
// --- enumerated stati -----------
// missile status
typedef enum {eMSPreLaunch, eMSBoost, eMSInitialDive, eMSCruse,
eMSExplode} eMissileStatus;
eMissileStatus MissileStatus =eMSPreLaunch;
char *pszMissileStatus[] = {"PreLaunch","Boost","InitialDive",
"Cruse","Explode"};
// computer status
typedef enum {eCSNoTargDesig, eCSTargNoTrack, eCSTrackBadSolution,
eCSOutOfRange, eCSInRange,
eCInDeadZone} eComputerStatus;
eComputerStatus ComputerStatus=eCSNoTargDesig;
char *pszComputerStatus[] = {"NoTargDesig","NoTrack","BadSolution",
"OutOfRange","InRange","InDeadZone"};
// alert status
typedef enum {eASWhite, eASYellow, eASRed} eASAlertStatus;
eASAlertStatus AlertStatus = eASRed;
char *pszAlertStatus[] = {"White","Yellow", "Red"};
// end-- enumerated stati -----------
// set global values for predicted Intercept program, end of initial dive
double dReleaseTime, dReleaseRange, dReleaseZ, dReleaseSpeed;
double dMaxRange, dDeadZoneRange;
double dTarDesRange, dTarDesAz;
double dSecondsIntercept; // time to intercept
int iTargetTracked = 0; // object number of selected target, 0=none
double dTargSpeedMach = 0; // Target Speed in mach number
double dMissileSpeedMach = 0;
FILE *fdRE = NULL; // file descriptor of RecordEvent
char szAppName[] = "WinRadar" ;
struct sDispObj {
char iObjType; // 0=available for assignment (and not displayed)
// (dynamics)
// 1 = map line
// 2 = Target Radar (OBJ_TTR)
// 3 = target designator circle OBJ_TAR_DES
// 4 = Predicted Intercept (OBJ_PI)
// 5 = Missile (OBJ_MTR)
// OBJ_AIRCRAFT
// OBJ_BIRDS,
double dSx, dSy, dSz; // position (cartiasian)
// - begin vector x,y - line count
double dVx, dVy, dVz, dVs; // velocity (cartiasian and "speed")
// - corner xy, corner xy
double dAx, dAy; // the various ObjTypes contain the limits
// - corner xy
double dAyaw_r, dApitch_u; // acceleration, r=right, u=up
// - corner xy,
char iObjPict; // 1=dot, 2=2x2, 3=3x3, 100+ icons
// 100=missile, 101=des targ, 102=pred interc
// also used to count explosion sequences
char reflectivity; // for stealth plot characteristics
char IFF[MAX_PHASES]; // controls writing of IFF echos
int w_iPulse[MAX_PHASES]; // write plot on this pulse
// (0 pulse is north, swing east, s,w, etc.
// note: w_x[0], w_y[0] are not updated until
// w_iPulse[0] is < 0 to prevent
// multiple hi intensity writes on fast moving targets
// (the multiple do not fade).
double w_dAngRad[MAX_PHASES]; // write angle in radians,
// clockwise from north
int w_x[MAX_PHASES]; // write x postion in pixels, rel origin
int w_y[MAX_PHASES]; // write y postion in pixels, rel origin
int iEndB1x[MAX_PHASES]; // long & short lines of two intensities)
int iEndB1y[MAX_PHASES];
int iEndB2x[MAX_PHASES];
int iEndB2y[MAX_PHASES];
int iEndS1x[MAX_PHASES];
int iEndS1y[MAX_PHASES];
int iEndS2x[MAX_PHASES];
int iEndS2y[MAX_PHASES];
} stDispObj[MAX_OBJ];
short sIntensF, sIntensC, sIntens8, sIntens6,
sIntens4, sIntens2, sIntens0; // set of intensities
short sDecayArray[2*MAX_PHASES]; // above sequence,
char bStorageDisplayMode = 0;
char bStorageDisplayModeHistory = 0;
// if operating in StorageDisplayMode,
// paint Phase 1 pixels only (no fade)
// if leaving StorageDisplayMode,
// flash screen (just like Techtronics)
// all bright, all dark
char cRestartSwitch = 0; // switch input, edge triggered
char cRestart = 0; // demand
// **************************
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
int main ()
{
long lStackVal=0;
int iType = 0, i=0;
char cKill = 0, cFirstTime = 1;
clock_t OldTick, CurrentTick; // storage for clock tick values
//// My_Beep(1);
srand( 4 ); // start randomizer
set_video_SVGA256 (); // set video 800x600, 256 colors, set yellow
// set up colors
sIntens0 = 0; // black
sDecayArray[0] = sIntensF = 2; // most intense
sDecayArray[1] = sIntensC = 8;
sDecayArray[2] = sIntens8 = 14;
sDecayArray[3] = sIntens6 = 20;
sDecayArray[4] = sIntens4 = 26;
for (i=5; i<(2*MAX_PHASES); i++)
sDecayArray[i] = sIntens0;
SetDisplayTo(sIntens0); // blank screen
cKill = 0; cFirstTime = 1;
OldTick = clock();
while ( ! cKill ) // MAIN LOOP //////////
{
if (cFirstTime)
{
MyProc (MY_CREATE, 0); // pseudo Windows
cFirstTime = 0;
}
// check a keyboard hit
else if ( _kbhit() )
{
iType = KeyboardAscii();
MyProc ( MY_KEYUP, iType );
}
// check for timer count change
else if (CurrentTick = clock()) // force read in context
{
if ( (OldTick > CurrentTick) // clock roll over
||((OldTick+(CLOCKS_PER_SEC/10)) <= CurrentTick)
|| ( (MissileStatus != eMSPreLaunch) // <><>
&&(cFastMissileTime) ) // <><>
)
{ // 0.1 second or more
MyProc ( MY_TIMER, 0);
if (CurrentTick > OldTick)
OldTick += (CLOCKS_PER_SEC/10); // normal processing
else
OldTick = CurrentTick + (CLOCKS_PER_SEC/10); // roll over
if ( OldTick < (CurrentTick+10) )
OldTick = CurrentTick; // limit catch-up
if ( OldTick > (CurrentTick+10) )
OldTick = CurrentTick; // limit catch-up
}
}
// nothing to do this time in loop
else
; // could not find sleep till next tick
}
_setvideomode( _DEFAULTMODE ); // exit kindly?
return (0);
}
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
long MyProc (int iMsgType, int wParam)
{
static char info_buffer[40];
int i, iObj, j;
int x,y;
static short rbgSaveArray[RADIUS_IN_PIXELS]; // color
static int iDelayCnt=0; // for text display and setpoint output
static int iCenterDot=5; // controls width of main bang clutter
char bRptScan; // helps control modulo scanning
// x pulses per 100 ms time interval
char cSkipScan; // used in speeding simulation
if (cRestart)
{
cRestart = 0;
SetDisplayTo(sIntens0); // blank screen
for (iObj=0; iObj< MAX_OBJ; iObj++)
InitializeObject(iObj);
CreateSystemObjects(); // predict int, targ sel, missile
CreateInitialSituation(1); // application
AlertStatus = eASRed;
cIFF = 0;
cDisplayEventRecorder = 0;
MissileStatus = eMSPreLaunch;
dSecondsSim = time(0)-1;
dSecondsSys = dSecondsSim+1;
cIFF = 0; // IFF flag (toggle)
cWarGame = 0; // flag to cause automatic response
cEvasiveAction = 0; // designated target random yaw accelerations
cAbout_Keyboard = 0;
cAbout_Switch = 0;
cAbout_Seq = 0;
}
switch (iMsgType)
{
case MY_KEYUP:
KeyboardInput( wParam); // process keyboard hits
return 0;
break;
case MY_CREATE:
// null each object
for (i=0; i< MAX_OBJ; i++)
InitializeObject(i);
CreateSystemObjects(); // predict int, targ sel, missile
CreateInitialSituation(1); // application
// get time
dSecondsSim = time(0)-1;
dSecondsSys = dSecondsSim+1;
dPixelPerMeter = RADIUS_IN_PIXELS/350000.0; // initial
dVisibleRange = RADIUS_IN_PIXELS/dPixelPerMeter;
dOldPixelPerMeter = 0;
return 0;
break;
case MY_TIMER:
dSecondsSim += dSecondsDelta;
if (cAbout_Keyboard || cAbout_Switch)
{
if (cAbout_Keyboard) // assure correct sourse
About_Menu( 1 ); // even if both set
else
{
switch (cAbout_Seq)
{
case (0):
About_Ed (0);
cAbout_Seq++; // sequence to next message
break;
case (1):
About_Ed (0);
cAbout_Seq = 0; // start over again
break;
}
}
cAbout_Keyboard = 0;
cAbout_Switch = 0;
}
// do simulated radar ppi scan, with minimum resources
if ( (MissileStatus != eMSPreLaunch) && ( cFastMissileTime ) )
cSkipScan = 1;
else
{
cSkipScan = 0;
dSweepRad = (double)iPulseCnt/PULSES_PER_RAD;
dSweepSin = sin(dSweepRad);
dSweepCos = cos(dSweepRad); // for sweep
if (dVisibleRange < 60000)
iCenterDot = 8;
else if (dVisibleRange < 160000)
iCenterDot = 4;
else if (dVisibleRange < 260000)
iCenterDot = 3;
else
iCenterDot = 2;
for (i = 0;(i < RADIUS_IN_PIXELS); i++)
{
_setcolor( sIntensF);
if ( ( i < iCenterDot)
|| ( (i<25) && (!(i % 2)) )
|| ( (i<75) && (!(i % 4)) )
|| ( (i > 75) && ( i<200)&& (!(i % 6)) )
|| ( (i > 200)&&( i iCenterDot)
&& ( ( (i<75) && (!(i % 4)) )
|| ( (i<25) && (!(i % 2)) )
|| ( (i > 75) && ( i<200)&& (!(i % 6)) )
|| ( (i > 200)&&( i
#include
enum NOTES /* Enumeration of notes and frequencies */
{
C0 = 262, D0 = 296, E0 = 330, F0 = 349, G0 = 392, A0 = 440, B0 = 494,
C1 = 523, D1 = 587, E1 = 659, F1 = 698, G1 = 784, A1 = 880, B1 = 988,
EIGHTH = 125, QUARTER = 250, HALF = 500, WHOLE = 1000, END = 0
} song[] = /* Array initialized to notes of song */
{
C1, HALF, G0, HALF, A0, HALF, E0, HALF, F0, HALF, E0, QUARTER,
D0, QUARTER, C0, WHOLE, END
};
/* Sounds the speaker for a time specified in microseconds by duration
* at a pitch specified in hertz by frequency.
*/
long My_Beep( int duration )
{
int control, frequency;
frequency = 1;
/* If frequency is 0, Beep doesn't try to make a sound. It
* just sleeps for the duration.
*/
if( frequency )
{
/* 75 is about the shortest reliable duration of a sound. */
if( duration < 75 )
duration = 75;
/* Prepare timer by sending 10111100 to port 43. */
_outp( 0x43, 0xb6 );
/* Divide input frequency by timer ticks per second and
* write (byte by byte) to timer.
*/
frequency = (unsigned)(1193180L / frequency);
_outp( 0x42, (char)frequency );
_outp( 0x42, (char)(frequency >> 8) );
/* Save speaker control byte. */
control = inp( 0x61 );
/* Turn on the speaker (with bits 0 and 1). */
_outp( 0x61, control | 0x3 );
}
Sleep( (clock_t)duration );
/* Turn speaker back on if necessary. */
if( frequency )
_outp( 0x61, control );
return (0);
}
/* Pauses for a specified number of microseconds. */
void Sleep( clock_t wait )
{
clock_t goal;
goal = wait + clock();
while( goal > clock() )
;
}
long CheckIt(char * string) // if i != -1, make box with string, ok
// static variable "first_time" prevents more than 1 box
{
static char first_time = 1;
if (first_time == 1)
{
first_time = 0;
_settextcolor (2);
_settextposition(0,0);
_outtext( string);
}
return 0;
}
long CreateSystemObjects() // predict int, targ sel, missile
{
int iObj, i;
iObj = OBJ_PI;
stDispObj[iObj].iObjType = OBJ_PI; //
for (i=0; i < MAX_PHASES; i++)
stDispObj[iObj].w_iPulse[i] = -1;
stDispObj[iObj].dSx = 0; //
stDispObj[iObj].dSy = 0; //
stDispObj[iObj].dSz = 0; //
iObj = OBJ_TTR;
stDispObj[iObj].iObjType = OBJ_TTR; //
for (i=0; i < MAX_PHASES; i++)
stDispObj[iObj].w_iPulse[i] = -1;
stDispObj[iObj].dSx = 0; //
stDispObj[iObj].dSy = 0; //
stDispObj[iObj].dSz = 0; //
iObj = OBJ_TAR_DES; // PPI target designator
stDispObj[iObj].iObjType = OBJ_TAR_DES; //
for (i=0; i < MAX_PHASES; i++)
stDispObj[iObj].w_iPulse[i] = -1;
stDispObj[iObj].dSx = 40000.0; // initial values
stDispObj[iObj].dSy = 40000.0; //
stDispObj[iObj].dSz = 0; //
iObj = OBJ_MTR;
stDispObj[iObj].iObjType = OBJ_MTR; //
for (i=0; i < MAX_PHASES; i++)
stDispObj[iObj].w_iPulse[i] = -1;
stDispObj[iObj].dSx = 0; //
stDispObj[iObj].dSy = 0; //
stDispObj[iObj].dSz = 0; //
iObj = OBJ_EXPLOSION;
stDispObj[iObj].iObjType = OBJ_EXPLOSION; //
for (i=0; i < MAX_PHASES; i++)
stDispObj[iObj].w_iPulse[i] = -1;
stDispObj[iObj].dSx = 0; //
stDispObj[iObj].dSy = 0; //
stDispObj[iObj].dSz = 0; //
return 0;
}
////////////////////////////////////////////////////////
long InitializeObject(int iObj)
{
// int i;
stDispObj[iObj].iObjType = 0; // null type, nothing
stDispObj[iObj].dSx = 0; // 0 km west
stDispObj[iObj].dSy = 0; // 0 km north
stDispObj[iObj].dSz = 0; // 0 km hi
stDispObj[iObj].dVx = 0; // 0 km/hr east
stDispObj[iObj].dVy = 0; // 0 km/hr north
stDispObj[iObj].dVz = 0; // 0 m/s altitude change
stDispObj[iObj].dAyaw_r = 0; // 0 m/s/s right
stDispObj[iObj].dApitch_u = 0; // 0 m/s/s up
return 0;
}
long CreateInitialSituation(int iSituation)
{
int iObj;
// initialize objects
switch( iSituation)
{
case 1: // 2 objects flying to east, one slow, one fast
// return 0;
iObj = 30;
stDispObj[iObj].iObjType = OBJ_MAP_V; // map vectors
//(Drakes headlands) south
stDispObj[iObj].dSx = -14000; // 40 km west
stDispObj[iObj].dSy = 28000; // 40 km north
stDispObj[iObj].dSz = 3; // lines
stDispObj[iObj].dVx = -16000; // 20 km west
stDispObj[iObj].dVy = 26000; // 40 km north
stDispObj[iObj].dVz = -14000; // 20 km west
stDispObj[iObj].dVs = 24000; // 10 km north
stDispObj[iObj].dAx = -13000; // 20 km west
stDispObj[iObj].dAy = 25000; // 10 km north
stDispObj[iObj].dAyaw_r = 00000; // 20 km west
stDispObj[iObj].dApitch_u = 00000; // 10 km north
iObj = 31;
stDispObj[iObj].iObjType = OBJ_MAP_V; // map vector
//Point Reyes south
stDispObj[iObj].dSx = -13000; // 40 km west - MoveTo(x,y)
stDispObj[iObj].dSy = 25000; // 40 km north
stDispObj[iObj].dSz = 4; // lines
stDispObj[iObj].dVx = -12000; // 20 km west - LineTo(x,y)
stDispObj[iObj].dVy = 25000; // 40 km north
stDispObj[iObj].dVz = -9000; // 20 km west
stDispObj[iObj].dVs = 24000; // 10 km north
stDispObj[iObj].dAx = -7000; // 20 km west
stDispObj[iObj].dAy = 21000; // 10 km north
stDispObj[iObj].dAyaw_r = -7000; // 20 km west
stDispObj[iObj].dApitch_u = 10000; // 10 km north
iObj = 32;
stDispObj[iObj].iObjType = OBJ_MAP_V; // map vector
// Stinson Beach?
stDispObj[iObj].dSx = -7000; // 40 km west -
stDispObj[iObj].dSy = 10000; // 40 km north
stDispObj[iObj].dSz = 2; // lines
stDispObj[iObj].dVx = -4000; // 20 km west -
stDispObj[iObj].dVy = 10000; // 40 km north
stDispObj[iObj].dVz = -500; // 20 km west
stDispObj[iObj].dVs = 0; // 10 km north
stDispObj[iObj].dAx = 0000; // 20 km west
stDispObj[iObj].dAy = 0000; // 10 km north
stDispObj[iObj].dAyaw_r = 0000; // 20 km west
stDispObj[iObj].dApitch_u = 0000; // 10 km north
iObj = 33;
stDispObj[iObj].iObjType = OBJ_MAP_V; // map vector
// Seal Point south
stDispObj[iObj].dSx = -1000; // 40 km west -
stDispObj[iObj].dSy = -1500; // 40 km north
stDispObj[iObj].dSz = 4; // lines
stDispObj[iObj].dVx = -6000; // 20 km west -
stDispObj[iObj].dVy = -13000; // 40 km north
stDispObj[iObj].dVz = -8000; // 20 km west
stDispObj[iObj].dVs = -14000; // 10 km north
stDispObj[iObj].dAx = -9000; // 20 km west
stDispObj[iObj].dAy = -14500; // 10 km north
stDispObj[iObj].dAyaw_r = -10000; // 20 km west
stDispObj[iObj].dApitch_u = -14500; // 10 km north
iObj = 34;
stDispObj[iObj].iObjType = OBJ_MAP_V; // map vector
//Half Moon Bay
stDispObj[iObj].dSx = -10000; // 40 km west -
stDispObj[iObj].dSy = -14500; // 40 km north
stDispObj[iObj].dSz = 4; // lines
stDispObj[iObj].dVx = -10000; // 20 km west -
stDispObj[iObj].dVy = -15500; // 40 km north
stDispObj[iObj].dVz = -9000; // 20 km west
stDispObj[iObj].dVs = -15500; // 10 km north
stDispObj[iObj].dAx = -9000; // 20 km west
stDispObj[iObj].dAy = -17000; // 10 km north
stDispObj[iObj].dAyaw_r = -11000; // 20 km west
stDispObj[iObj].dApitch_u = -19000; // 10 km north
iObj = 35;
stDispObj[iObj].iObjType = OBJ_MAP_V; // map vector
//Tiberion & ElCerito
stDispObj[iObj].dSx = 4900; // 40 km west -
stDispObj[iObj].dSy = 5; // 40 km north
stDispObj[iObj].dSz = 4; // lines
stDispObj[iObj].dVx = 6300; // 20 km west -
stDispObj[iObj].dVy = -500; // 40 km north
stDispObj[iObj].dVz = 6000; // 20 km west
stDispObj[iObj].dVs = -1000; // 10 km north
stDispObj[iObj].dAx = 4300; // 20 km west
stDispObj[iObj].dAy = -900; // 10 km north
stDispObj[iObj].dAyaw_r = 4900; // 20 km west
stDispObj[iObj].dApitch_u = 0; // 10 km north
iObj = 36;
stDispObj[iObj].iObjType = OBJ_MAP_V; // map vector
stDispObj[iObj].dSx = 9500; // 40 km west -
stDispObj[iObj].dSy = 5; // 40 km north
stDispObj[iObj].dSz = 3; // lines
stDispObj[iObj].dVx = 11000; // 20 km west -
stDispObj[iObj].dVy = -500; // 40 km north
stDispObj[iObj].dVz = 12000; // 20 km west
stDispObj[iObj].dVs = -2000; // 10 km north
stDispObj[iObj].dAx = 12000; // 20 km west
stDispObj[iObj].dAy = -3500; // 10 km north
stDispObj[iObj].dAyaw_r = 0; // 20 km west
stDispObj[iObj].dApitch_u = 0; // 10 km north
iObj = 37;
stDispObj[iObj].iObjType = OBJ_MAP_V; // map vector
// San leandro & bridge
stDispObj[iObj].dSx = 12000; // 40 km west -
stDispObj[iObj].dSy = -3500; // 40 km north
stDispObj[iObj].dSz = 4; // lines
stDispObj[iObj].dVx = 11000; // 20 km west -
stDispObj[iObj].dVy = -6000; // 40 km north
stDispObj[iObj].dVz = 10200; // 20 km west
stDispObj[iObj].dVs = -6500; // 10 km north
stDispObj[iObj].dAx = 8300; // 20 km west
stDispObj[iObj].dAy = -6000; // 10 km north
stDispObj[iObj].dAyaw_r = 5000; // 20 km west
stDispObj[iObj].dApitch_u = -6000; // 10 km north
iObj = 38; // next 3 are Farallon islands
stDispObj[iObj].iObjType = OBJ_MAP_V; // map vector
stDispObj[iObj].dSx = -60000; // 40 km west -
stDispObj[iObj].dSy = -12000; // 40 km north
stDispObj[iObj].dSz = 4; // lines
stDispObj[iObj].dVx = -61500; // 20 km west -
stDispObj[iObj].dVy = -11500; // 40 km north
stDispObj[iObj].dVz = -61500; // 20 km west
stDispObj[iObj].dVs = -10500; // 10 km north
stDispObj[iObj].dAx = -59500; // 20 km west
stDispObj[iObj].dAy = -10500; // 10 km north
stDispObj[iObj].dAyaw_r = -60000; // 20 km west
stDispObj[iObj].dApitch_u = -12000; // 10 km north
iObj = 39;
stDispObj[iObj].iObjType = OBJ_MAP_V; // map vector
stDispObj[iObj].dSx = -65000; // 40 km west -
stDispObj[iObj].dSy = -9000; // 40 km north
stDispObj[iObj].dSz = 4; // lines
stDispObj[iObj].dVx = -66000; // 20 km west -
stDispObj[iObj].dVy = -9000; // 40 km north
stDispObj[iObj].dVz = -66000; // 20 km west
stDispObj[iObj].dVs = -8000; // 10 km north
stDispObj[iObj].dAx = -65000; // 20 km west
stDispObj[iObj].dAy = -8000; // 10 km north
stDispObj[iObj].dAyaw_r = -65000; // 20 km west
stDispObj[iObj].dApitch_u = -9000; // 10 km north
iObj = 40;
stDispObj[iObj].iObjType = OBJ_MAP_V; // map vector
stDispObj[iObj].dSx = -40000; // 40 km west -
stDispObj[iObj].dSy = 40000; // 40 km north
stDispObj[iObj].dSz = 0; // lines
stDispObj[iObj].dVx = -20000; // 20 km west -
stDispObj[iObj].dVy = 40000; // 40 km north
stDispObj[iObj].dVz = -20000; // 20 km west
stDispObj[iObj].dVs = 10000; // 10 km north
stDispObj[iObj].dAx = -20000; // 20 km west
stDispObj[iObj].dAy = 10000; // 10 km north
stDispObj[iObj].dAyaw_r = -20000; // 20 km west
stDispObj[iObj].dApitch_u = 10000; // 10 km north
// iObj = 10;
// stDispObj[iObj].iObjType = OBJ_AIRCRAFT; // subsonic jet
// stDispObj[iObj].dSx = -180000; // 80 km west
// stDispObj[iObj].dSy = 10000; // 10 km north
// stDispObj[iObj].dSz = 10000; // 10 km hi
// stDispObj[iObj].dVx = 0.5*348; // 2000 km/hr east (2*620 mph)
// stDispObj[iObj].dVy = -0.5*348; // 0 km/hr north
// stDispObj[iObj].dVz = 00000; // 0 m/s altitude change
// stDispObj[iObj].dAyaw_r = 000.0; // 0 m/s/s right
// stDispObj[iObj].dApitch_u = 0000; // 0 m/s/s up
// iObj = 12;
// stDispObj[iObj].iObjType = OBJ_AIRCRAFT; // subsonic jet
// stDispObj[iObj].dSx = -170000; // 70 km west
// stDispObj[iObj].dSy = -10000; // 10 km north
// stDispObj[iObj].dSz = 10000; // 10 km hi
// stDispObj[iObj].dVx = .5*348; // 2000 km/hr east (2*620 mph)
// stDispObj[iObj].dVy =-0.5*348; // 0 km/hr s.e.
// stDispObj[iObj].dVz = 00000; // 0 m/s altitude change
// stDispObj[iObj].dAyaw_r = 00000; // 0 m/s/s right
// stDispObj[iObj].dApitch_u = 0000; // 0 m/s/s up
iObj = 13;
stDispObj[iObj].iObjType = OBJ_AIRCRAFT; // subsonic jet
stDispObj[iObj].dSx = -170000; // 60 km west
stDispObj[iObj].dSy = 5000; // 5 km north
stDispObj[iObj].dSz = 10000; // 1 km hi
stDispObj[iObj].dVx = 1.5*348; // 250 km/hr east (155 mph)
stDispObj[iObj].dVy = 0000; // 0 km/hr north
stDispObj[iObj].dVz = 00000; // 0 m/s altitude change
stDispObj[iObj].dAyaw_r = 000.0; // 0 m/s/s right
stDispObj[iObj].dApitch_u = 0000; // 0 m/s/s up
iObj = 14;
stDispObj[iObj].iObjType = OBJ_AIRCRAFT; // subsonic jet
stDispObj[iObj].dSx = -170000; // 70 km west
stDispObj[iObj].dSy = -10000; // 10 km north
stDispObj[iObj].dSz = 10000; // 10 km hi
stDispObj[iObj].dVx = 0.7*348; // 2000 km/hr east (2*620 mph)
stDispObj[iObj].dVy =-0.1*348; // 0 km/hr north
stDispObj[iObj].dVz = 00000; // 0 m/s altitude change
stDispObj[iObj].dAyaw_r = 000.0; // 0 m/s/s right
stDispObj[iObj].dApitch_u = 0000; // 0 m/s/s up
iObj = 15;
stDispObj[iObj].iObjType = OBJ_AIRCRAFT; // subsonic jet
stDispObj[iObj].dSx = -180000; // 60 km west
stDispObj[iObj].dSy = 5000; // 5 km north
stDispObj[iObj].dSz = 10000; // 1 km hi
stDispObj[iObj].dVx = 0.7*348; // 250 km/hr east (155 mph)
stDispObj[iObj].dVy = 0000; // 0 km/hr north
stDispObj[iObj].dVz = 00000; // 0 m/s altitude change
stDispObj[iObj].dAyaw_r = -000.0; // 0 m/s/s right
stDispObj[iObj].dApitch_u = 0000; // 0 m/s/s up
iObj = 16;
stDispObj[iObj].iObjType = OBJ_AIRCRAFT; // subsonic jet
stDispObj[iObj].dSx = -190000; // 90 km west
stDispObj[iObj].dSy = -10000; // 10 km north
stDispObj[iObj].dSz = 10000; // 10 km hi
stDispObj[iObj].dVx = 0.8*348; // 2000 km/hr east (2*620 mph)
stDispObj[iObj].dVy = 0; // 0 km/hr north
stDispObj[iObj].dVz = 00000; // 0 m/s altitude change
stDispObj[iObj].dAyaw_r = 0.0; // 0 m/s/s right
stDispObj[iObj].dApitch_u = 0000; // 0 m/s/s up
// iObj = 17;
// stDispObj[iObj].iObjType = OBJ_AIRCRAFT; // subsonic jet
// stDispObj[iObj].dSx = -80000; // 60 km west
// stDispObj[iObj].dSy = 4000; // 5 km north
// stDispObj[iObj].dSz = 10000; // 1 km hi
// stDispObj[iObj].dVx = 0.7*348; // 250 km/hr east (155 mph)
// stDispObj[iObj].dVy = 0000; // 0 km/hr north
// stDispObj[iObj].dVz = 00000; // 0 m/s altitude change
// stDispObj[iObj].dAyaw_r = -000.0; // 0 m/s/s right
// stDispObj[iObj].dApitch_u = 0000; // 0 m/s/s up
//================
iObj = 20;
stDispObj[iObj].iObjType = OBJ_AIRCRAFT_F; // subsonic jet
stDispObj[iObj].dSx = -170000; // 60 km west
stDispObj[iObj].dSy = 50000; // 5 km north
stDispObj[iObj].dSz = 10000; // 1 km hi
stDispObj[iObj].dVx = 1.5*348; // 250 km/hr east (155 mph)
stDispObj[iObj].dVy = 0000; // 0 km/hr north
stDispObj[iObj].dVz = 00000; // 0 m/s altitude change
stDispObj[iObj].dAyaw_r = 000.0; // 0 m/s/s right
stDispObj[iObj].dApitch_u = 0000; // 0 m/s/s up
iObj = 21;
stDispObj[iObj].iObjType = OBJ_AIRCRAFT; // subsonic jet
stDispObj[iObj].dSx = -170000; // 60 km west
stDispObj[iObj].dSy = 25000; // 5 km north
stDispObj[iObj].dSz = 10000; // 1 km hi
stDispObj[iObj].dVx = 1.5*348; // 250 km/hr east (155 mph)
stDispObj[iObj].dVy = 0000; // 0 km/hr north
stDispObj[iObj].dVz = 00000; // 0 m/s altitude change
stDispObj[iObj].dAyaw_r = 000.0; // 0 m/s/s right
stDispObj[iObj].dApitch_u = 0000; // 0 m/s/s up
// iObj = 22;
// stDispObj[iObj].iObjType = OBJ_AIRCRAFT; // subsonic jet
// stDispObj[iObj].dSx = -170000; // 60 km west
// stDispObj[iObj].dSy = -25000; // 5 km north
// stDispObj[iObj].dSz = 10000; // 1 km hi
// stDispObj[iObj].dVx = 1.5*348; // 250 km/hr east (155 mph)
// stDispObj[iObj].dVy = 0000; // 0 km/hr north
// stDispObj[iObj].dVz = 00000; // 0 m/s altitude change
// stDispObj[iObj].dAyaw_r = 000.0; // 0 m/s/s right
// stDispObj[iObj].dApitch_u = 0000; // 0 m/s/s up
// iObj = 23;
// stDispObj[iObj].iObjType = OBJ_AIRCRAFT; // subsonic jet
// stDispObj[iObj].dSx = -170000; // 60 km west
// stDispObj[iObj].dSy = -50000; // 5 km north
// stDispObj[iObj].dSz = 10000; // 1 km hi
// stDispObj[iObj].dVx = 1.5*348; // 250 km/hr east (155 mph)
// stDispObj[iObj].dVy = 0000; // 0 km/hr north
// stDispObj[iObj].dVz = 00000; // 0 m/s altitude change
// stDispObj[iObj].dAyaw_r = 000.0; // 0 m/s/s right
// stDispObj[iObj].dApitch_u = 0000; // 0 m/s/s up
break;
default:
break;
}
return 0;
}
//////////////////////////////////////////////////////////////////
long About_Menu( int cntl)
{
// display menu, selecting a variety of displays or return
// should only be called if keyboard installed, not switch action
int i = 1;
while (i)
{
SetDisplayTo(sIntens0); // blank screen
_settextposition( 0, 0 );
sprintf (my_text, "Menu selections\n\n");
_outtext(my_text);
sprintf (my_text, "1) Greetings from author\n");
_outtext(my_text);
sprintf
(my_text, "\nSelect one of the above choices, an invalid choice returns to program \n\n");
_outtext(my_text);
i = getch();
switch (i)
{
case ('1'):
About_Ed(1);
break;
default:
i = 0; // exit, invalid choice
} // end switch statement
} // end while (i)
SetDisplayTo(sIntens0); // blank screen
_settextposition( 0, 0 );
return (0);
}
//////////////////////////////////////////////////////////////////
long About_Ed( int cntl) // message menu and array
{
// display
// on exit, use cntl to:
// if (1)
// return after key hit
// else
// return after cAbout_Switch == 0
//
int i;
SetDisplayTo(sIntens0); // blank screen
_settextposition( 0, 0 );
sprintf (my_text, "Greetings, my name is Ed Thelen, Fremont, CA, \n\n"); ;
_outtext(my_text);
sprintf (my_text, "I created this program for my 'kids' and you.\n");
_outtext(my_text);
sprintf(my_text,"('Kids' is quoted as they are professional programmers and engineers.)\n \n");
_outtext(my_text);
sprintf(my_text,"Some years ago - the early 1950s - the world was a bit more interesting:\n");
_outtext(my_text);
sprintf(my_text," - the Soviets blockading Berlin to drive the western Allies out\n");
_outtext(my_text);
sprintf(my_text," - the North Koreans invading South Korea\n");
_outtext(my_text);
sprintf(my_text," - Soviet planes, ships, and submarines playing chicken with their former allies\n");
_outtext(my_text);
sprintf(my_text," - the new H-Bomb eliminating Bikini, both sides frequently testing atomic bombs\n");
_outtext(my_text);
sprintf(my_text," - which hundreds of new Soviet long range bombers could deliver into any part of the U.S.\n");
_outtext(my_text);
sprintf(my_text," - the certain knowledge that both sides were racing for intercontental rockets\n\n");
_outtext(my_text);
sprintf(my_text,"This was the world environment in which Nike surface to air missiles were developed.\n\n");
_outtext(my_text);
sprintf(my_text,"I got a 'Greetings from Uncle Sam' letter inviting me to a draft physical exam!!\n");
_outtext(my_text);
sprintf(my_text,"Unlike a later 'president', I hurriedly enlisted in the Army for a promise of\n");
_outtext(my_text);
sprintf(my_text,"1 year in the new Nike Surface to Air Missile school and 2 years of stateside duty.\n");
_outtext(my_text);
sprintf(my_text," \n");
_outtext(my_text);
sprintf(my_text,"All enlistment promises were kept! I spent a year at the Ft. Bliss Artillery School\n");
_outtext(my_text);
sprintf(my_text,"and 2 years defending south-east Chicago from the distant Soviet bombers.\n");
_outtext(my_text);
sprintf(my_text,"(I now have satellite pictures of many Soviet bombers on airfields in Siberia, scary.)\n");
_outtext(my_text);
sprintf(my_text,"\n");
_outtext(my_text);
sprintf(my_text,"Myself and two other technicians kept the radars and battery control equipment functioning\n");
_outtext(my_text);
sprintf(my_text,"well at our site on Lake Michigan at 63rd and Outer Drive. Great location, museums, etc.\n");
_outtext(my_text);
sprintf(my_text,"Strange world! Bar civilians would impress us with secret info: changes to missile codes,\n");
_outtext(my_text);
sprintf(my_text,"equipment characteristics, IdentFriendFoe codes, ... Sometimes before we knew.\n");
_outtext(my_text);
sprintf(my_text,"\n");
_outtext(my_text);
sprintf(my_text,"Then I went to college, got married, raised 3 kids, retired, and now program for fun!!\n");
_outtext(my_text);
sprintf(my_text,"As a mammal, I feel impelled to pass on my (sometimes faulty) observations and conclusions.\n\n");
_outtext(my_text);
if (cntl)
{
sprintf (my_text, "depress any key to return to the menu \n ");
_outtext(my_text);
i = getch(); // wait for any keyboard input
}
else
{
sprintf (my_text, "set the About switch to OFF to return to the program\n ");
_outtext(my_text);
sprintf (my_text, "the next time the About switch is set to ON, the next message will appear\n ");
_outtext(my_text);
while (cAbout_Switch)
{ // wait for switch to release
SpecialInput();
}
}
SetDisplayTo(sIntens0); // blank screen
return 0;
}
//////////////////////////////////////////////////////////////////
int KeyboardAscii() // convert a keyboard hit to ascii code
// process keyboard inputs, yielding 'ascii' return value
// F1->F12 = 0x81->0x8c
// up,right,down,left arrows are 0x91,0x92,0x93,0x94
// -1 (0xffff) if unable to interpret the input
{
int in2, result;
result = getch();
// printf ("'result = %02x '%c'\n",result, result);
if ( (result == 0) || (result == 0xE0) )
{ // check for function keys (my function key codes)
in2 = getch();
// printf("2nd input = %02x '%c'\n", in2, in2);
if (result == 0)
{
if ((in2 >= 0x3b) && (in2 <= 0x44) ) // F1-F10
result = in2 + (0x81 - 0x3b); // F1 == 0x81
else if ((in2 >= 0x0085) && (in2 <= 0x0086) )// F11-F12
result = in2 + (0x008b - 0x0085);// F11 = 0x8b
else if (in2 == 0x48) // up arrow
result = 0x91;
else if (in2 == 0x4d) // right arrow
result = 0x92;
else if (in2 == 0x50) // down arrow
result = 0x93;
else if (in2 == 0x4b) // left arrow
result = 0x94;
else
result = -1;
}
else
result = -1;
}
return (result);
}
// ------------------------------------------------------------------
long KeyboardInput(int wParam) // process keyboard hits
{
int iObj;
// in ASCII collating sequence
if(wParam == '0')
{ // set ack range to 050,000 yards
dPixelPerMeter = RADIUS_IN_PIXELS / 50000.0;
dVisibleRange = RADIUS_IN_PIXELS/dPixelPerMeter;
cClearMyScreen = 1;
return 0;
}
else if(wParam == '1')
{ // set ack range to 150,000 yards
dPixelPerMeter = RADIUS_IN_PIXELS / 150000.0;
dVisibleRange = RADIUS_IN_PIXELS/dPixelPerMeter;
cClearMyScreen = 1;
return 0;
}
else if(wParam == '2')
{ // set ack range to 250,000 yards
dPixelPerMeter = RADIUS_IN_PIXELS / 250000.0;
dVisibleRange = RADIUS_IN_PIXELS/dPixelPerMeter;
cClearMyScreen = 1;
return 0;
}
else if(wParam == '3')
{ // set ack range to 350,000 yards
dPixelPerMeter = RADIUS_IN_PIXELS / 350000.0;
dVisibleRange = RADIUS_IN_PIXELS/dPixelPerMeter;
cClearMyScreen = 1;
return 0;
}
else if ((wParam == 'A') || (wParam == 'a'))
{ //
cAbout_Keyboard = 1;
return 0;
}
else if( (wParam == 'B') || (wParam == 'b') )
{
if (MissileStatus != eMSPreLaunch)
{ // ok, missile in flight, abandon it,
MissileStatus = eMSPreLaunch;
stDispObj[OBJ_MTR].dSx = 0.0;
stDispObj[OBJ_MTR].dSy = 0.0;
stDispObj[OBJ_MTR].dSz = 0.0;
stDispObj[OBJ_MTR].dVx = 0.0;
stDispObj[OBJ_MTR].dVy = 0.0;
stDispObj[OBJ_MTR].dVz = 0.0;
stDispObj[OBJ_MTR].dAyaw_r = 0.0;
stDispObj[OBJ_MTR].dApitch_u = 0.0;
dSecondsMissile = 0.0;
sprintf (szLogEntry, "Missile Manual Burst");
return 0;
}
}
else if( (wParam == 'D') || (wParam == 'd')) // designate target
{
iObj = OBJ_TAR_DES;
if (iTargetTracked =
(int)SelectTarget(stDispObj[iObj].dSx,
stDispObj[iObj].dSy))
{
sprintf (szLogEntry, "Target Tracked");
return 0;
}
else
{
My_Beep (0); // bad mouse hit
sprintf (szLogEntry, "No Target Selected");
return 0;
}
}
else if ( (wParam == 'E') || (wParam == 'e'))
{ // Evasive action by any designated target
if (wParam == 'E')
cEvasiveAction = 1;
else
cEvasiveAction = 0;
return 0;
}
else if ( (wParam == 'F') || (wParam == 'f'))
{ // Evasive action by any designated target
if (wParam == 'F')
cFastMissileTime = 1;
else
cFastMissileTime = 0;
return 0;
}
else if((wParam == 'H') || (wParam == 'h') )
{ // StorageDisplay mode
if (wParam == 'H')
{
bStorageDisplayMode = 1;
bStorageDisplayModeHistory = 1;
}
else
{ // must be 'h'
if (bStorageDisplayModeHistory)
{ // flash display
SetDisplayTo(sIntensF);
SetDisplayTo(sIntens0);
bStorageDisplayModeHistory = 0;
}
bStorageDisplayMode = 0;
}
}
else if( (wParam == 'I') || (wParam == 'i') )
{ //
if (wParam == 'i')
cIFF = 0; // IFF off
else
cIFF = 1; // IFF on
return 0;
}
else if( (wParam == 'K') || (wParam == 'k') )
{
if (wParam == 'K') // keyboard inputs only
cInputKeyboardOnly = 1;
else
cInputKeyboardOnly = 0; // read van onputs
// (switches and pots) also
return 0;
}
// also note, if ADC_4 is near 0,
// other van inputs are not input
else if( (wParam == 'L') || (wParam == 'l') )
{ // launch missile
if (AlertStatus != eASRed)
{
My_Beep(0);
sprintf (szLogEntry, "Need Red Alert to launch");
return 0;
}
if (MissileStatus != eMSPreLaunch)
{
My_Beep(0);
sprintf (szLogEntry, "Need PreLaunch Status");
return 0;
}
if (! iTargetTracked)
{
My_Beep(0);
sprintf (szLogEntry, "Need Target Tracked Status");
return 0;
}
MissileStatus = eMSBoost; // launch the missile
sprintf (szLogEntry, "Missile Launched");
return 0;
}
else if( (wParam == 'N') || (wParam == 'n') )
{ // start new airplanes from west to east
// if fewer than 3 remaining targets
if (wParam == 'N')
cStartPlaneEast = 1; // mach 1.5
else
cStartPlaneEast = 0; // mach 0.8
return 0;
}
else if( (wParam == 'Q')
||(wParam == 'q') )
{
printf ("exiting 'radar due to 'q' command\n");
exit(0); //goto bomb_out;
}
else if((wParam == 'R') || (wParam == 'r') ) // start again
{
cRestart = 1; // command restart
return 0; // ok
}
else if( (wParam == 'S') || (wParam == 's') )
{ // slow down sweep
if (wParam == 'S')
iPulsesPerTimer = 12;
else
iPulsesPerTimer = 4;
return 0;
}
else if( (wParam == 'T') || (wParam == 't'))
{ // Engineerng toggle display mode
if (wParam == 't')
{
if (cDisplayEventRecorder)
{ // clear display to get rid of old text
SetDisplayTo(sIntens0);
cDisplayEventRecorder = 0;
}
}
else
{
cDisplayEventRecorder = 1;
}
}
else if ( ( wParam == 'W') || (wParam == 'w') )
{ // WarGame - automatic response
if ( wParam == 'W')
cWarGame = 1;
else
cWarGame = 0;
}
else if ( wParam == 145) // up arrow
{ // move up 20 pixels
iObj = OBJ_TAR_DES;
if ( stDispObj[iObj].dSy
< (RADIUS_IN_PIXELS/dPixelPerMeter))
stDispObj[iObj].dSy += PIXELS_PER_ARROW/dPixelPerMeter;
else
My_Beep (0);
return 0;
}
else if ( wParam == 148) // left arrow
{ // move left 20 pixels
iObj = OBJ_TAR_DES;
if ( stDispObj[iObj].dSx
> (-RADIUS_IN_PIXELS/dPixelPerMeter))
stDispObj[iObj].dSx -= PIXELS_PER_ARROW/dPixelPerMeter;
else
My_Beep (0);
return 0;
}
else if ( wParam == 147) // down arrow
{ // move down 20 pixels
iObj = OBJ_TAR_DES;
if ( stDispObj[iObj].dSy
> (-RADIUS_IN_PIXELS/dPixelPerMeter))
stDispObj[iObj].dSy -= PIXELS_PER_ARROW/dPixelPerMeter;
else
My_Beep (0);
return 0;
}
else if ( wParam == 146) // right arrow
{ // move right 20 pixels
iObj = OBJ_TAR_DES;
if ( stDispObj[iObj].dSx
< (RADIUS_IN_PIXELS/dPixelPerMeter))
stDispObj[iObj].dSx += PIXELS_PER_ARROW/dPixelPerMeter;
else
My_Beep (0);
return 0;
}
else
{
My_Beep (0);
sprintf (szLogEntry, "Unknown Key Command");
}
return 0;
}
// ------------------------------------------------------------------
long SpecialInput()
// inputfrom special cards and LPT1
{
double dTemp, dTemp1, dTemp2, dTemp3;
long lStatus;
int iObj;
// input Analog Inputs here **********************************
dTemp3 = 0.0;
GetAnalog ( ADC_4, & dTemp3, 0.0, 5.0); // pot midpoint
// dGlobal1 = dTemp3; // ?? show until we know
if (dTemp3 < 1.0)
{
return 0; // van not connected,
// exit to avoid having bad inputs
}
dTemp = 0.9*(RADIUS_IN_PIXELS/dPixelPerMeter);// display range
GetAnalog ( ADC_1, & dTarDesRange, 0.0, dTemp); // range
// move designate circle to pot position
GetAnalog ( ADC_2, & dTemp1, 0.0, 5.0); // sin?
GetAnalog ( ADC_3, & dTemp2, 0.0, 5.0); // cos?
dTemp1 = dTemp1 - dTemp3; // center point correction
dTemp2 = dTemp2 - dTemp3;
dTarDesAz = atan2(dTemp1,dTemp2);
iObj = OBJ_TAR_DES; // PPI target designator
stDispObj[iObj].dSx = dTarDesRange * sin(dTarDesAz) ;
stDispObj[iObj].dSy = dTarDesRange * cos(dTarDesAz) ;
//#define MAX_DAC_COUNTS 127 // + 5 volts? 8 bit dac?
// dxyScale = (double) (MAX_DAC_COUNTS/200000.0);
// dtScale = (double) (MAX_DAC_COUNTS/160.0);
// dzScale = (double) (MAX_DAC_COUNTS/(100000/3));
// dtdouble = (double)stDispObj[OBJ_PI].dSx * dxyScale; //
// if ( dtdouble > MAX_DAC_COUNTS)
// dtdouble = MAX_DAC_COUNTS;
// else if ( dtdouble < MAX_DAC_COUNTS)
// dtdouble = -MAX_DAC_COUNTS;
//
// input Logic here ****************************************
lStatus = GetDigital (CI_0); // Get missile sim speed
if (lStatus)
cDisplayEventRecorder = 1;
else
{
if (cDisplayEventRecorder)
{
SetDisplayTo(sIntens0);
cDisplayEventRecorder = 0;
}
}
lStatus = GetDigital (CI_1); //
if (lStatus == 0)
AlertStatus = eASRed; // force red
else
AlertStatus = eASWhite; //
// iPulsesPerTimer = 4; // "slow"
// else
// iPulsesPerTimer = 16; // "fast"
// iPulsesPerTimer = 16; // *** force fast until inputs
lStatus = GetDigital (CI_2);// Get PPI range (low bit)
if (lStatus == -1)
My_Beep (1);
else
{
lStatus = lStatus | (GetDigital (CI_3) << 1);// (hi bit)
if ( (lStatus < 0) || ( lStatus > 3) )
My_Beep (1);
else if (lStatus == 0)
dPixelPerMeter = RADIUS_IN_PIXELS / 350000.0;
else if (lStatus == 1)
dPixelPerMeter = RADIUS_IN_PIXELS / 250000.0;
else if (lStatus == 2)
dPixelPerMeter = RADIUS_IN_PIXELS / 150000.0;
else if (lStatus == 3)
dPixelPerMeter = RADIUS_IN_PIXELS / 50000.0;
dVisibleRange = RADIUS_IN_PIXELS/dPixelPerMeter;
if (dOldPixelPerMeter != dPixelPerMeter)
{
cClearMyScreen = 1;
dOldPixelPerMeter = dPixelPerMeter;
}
}
lStatus = GetDigital (CI_4); // Get Target Designate
if (lStatus == 0)
{
iObj = OBJ_TAR_DES;
if (iTargetTracked =
(int)SelectTarget(stDispObj[iObj].dSx,
stDispObj[iObj].dSy))
{
sprintf (szLogEntry, "Target Designated");
}
else
{
My_Beep (0); //
sprintf (szLogEntry, "No Target Designated");
}
}
lStatus = GetDigital (CI_5); // Launch missile
if (lStatus == 0) // **** switch in normally open,
{ // launch missile
if (AlertStatus != eASRed)
{
My_Beep(0);
sprintf (szLogEntry, "Need Red Alert to launch");
}
else if (MissileStatus != eMSPreLaunch)
{
My_Beep(0);
sprintf (szLogEntry, "Need PreLaunch Status");
}
else if (MissileStatus == eMSBoost)
{ // allow a 3 seconds to remove signal
}
else if (! iTargetTracked)
{
My_Beep(0);
sprintf (szLogEntry, "Need Target Tracked Status");
}
else
{
MissileStatus = eMSBoost; // launch the missile
sprintf (szLogEntry, "Missile Launched");
}
}
lStatus = GetDigital (CI_6); // Manual Command Burst
if (lStatus == 1)
;
else
{
if (MissileStatus != eMSPreLaunch)
{ // ok, missile in flight, abandon it,
MissileStatus = eMSPreLaunch;
stDispObj[OBJ_MTR].dSx = 0.0;
stDispObj[OBJ_MTR].dSy = 0.0;
stDispObj[OBJ_MTR].dSz = 0.0;
stDispObj[OBJ_MTR].dVx = 0.0;
stDispObj[OBJ_MTR].dVy = 0.0;
stDispObj[OBJ_MTR].dVz = 0.0;
stDispObj[OBJ_MTR].dAyaw_r = 0.0;
stDispObj[OBJ_MTR].dApitch_u = 0.0;
dSecondsMissile = 0.0;
sprintf (szLogEntry, "Missile Manual Burst");
return 0;
}
}
lStatus = GetDigital (CI_7);
if (lStatus == 0) // storage scope requested (backward)
{
bStorageDisplayMode = 1;
bStorageDisplayModeHistory = 1;
}
else
{ // no storage scope requested
if (bStorageDisplayModeHistory)
{ // flash display
SetDisplayTo(sIntensF);
SetDisplayTo(sIntens0);
bStorageDisplayModeHistory = 0;
}
bStorageDisplayMode = 0;
}
lStatus = GetDigital (CI_8); // Restart
if (lStatus != 0)
{
if (cRestartSwitch == 0) // edge triggered
{
cRestart = 1;
cRestartSwitch = 1;
}
else
{}
}
else
cRestartSwitch = 0;
lStatus = GetDigital (CI_9); // operate IFF?
if (lStatus == 0)
cIFF = 0; // IFF off
else
cIFF = 1; // IFF on
lStatus = GetDigital (CI_10); //Start plane east if <3 targets
if (lStatus != 0)
cStartPlane = 1;
else
cStartPlane = 0;
lStatus = GetDigital (CI_11); // should it evade?
if (lStatus != 0)
cEvasiveAction = 1;
else
cEvasiveAction = 0;
lStatus = GetDigital (CI_12);
if (lStatus ) // wired backwords
cWarGame = 1;
else
cWarGame = 0;
return 0;
}
// --------------------------------------------------------------
long SpecialOutput()
{
int iObj;
char cLogic;
double dTemp, dTemp1;
#define CONSOLE_DAC_CON 1
// output DA here ********************************************
dTemp1 = dTargSpeedMach;
// dTemp1 = 0.250;
// 3 element piecewise linear approximation
// mach 1 = 796 knots
// dTemp gives needs
// 1.5 1200
// 1.0 1200 790
// 0.7 560
// 0.5 800
// 0.26 560
if (dTemp1 < 0.7)
dTemp = 0.00 + ( ((0.26-0.00)/(0.7-0.0))*(dTemp1-0.0));
else if (dTemp1 < 1.0)
dTemp = 0.26 + ( ((0.5-0.26)/(1.0-0.7))*(dTemp1-0.7));
else
dTemp = 0.50 + ( ((1.0-0.50)/(1.5-1.0))*(dTemp1-1.0));
PutAnalog(DAC_1, & dTemp, -3.0, 3.0);
dTemp1 = dMissileSpeedMach;
// dTemp1 = stDispObj[OBJ_MTR].dVx;
// dTemp = stDispObj[OBJ_MTR].dVy;
// dTemp1 = sqrt(dTemp1*dTemp1 + dTemp*dTemp); // gnd meters/sec
// dTemp1 /= 348.0; // convert to mach
dTemp = 0.250*(796.0/2100.0)*dTemp1; // meter conversion-
PutAnalog(DAC_2, & dTemp, -3.0, 3.0);
#define TARGET_RANGE_METER_SCALE 400000.0
PutAnalog(DAC_3, & dTarDesRange, -TARGET_RANGE_METER_SCALE,
TARGET_RANGE_METER_SCALE);
// output target height info here
#define TARGET_HEIGHT_SCALE 0.333
dTemp1 = stDispObj[OBJ_TTR].dSz * TARGET_HEIGHT_SCALE;
PutAnalog(DAC_4, & dTemp1, -MAX_PLOT_VT,
MAX_PLOT_VT);
dTemp = dSecondsIntercept;
#define SECONDS_METER_CONSTANT 12.0/100.0
dTemp *= SECONDS_METER_CONSTANT;
PutAnalog(DAC_7, & dTemp, -100.0, 100.0);
goto skip_dac;
if ( MissileStatus != eMSPreLaunch) // missile in flight
iObj = OBJ_MTR; // plot missile
else
iObj = OBJ_PI; // plot Predicted Intercept
PutAnalog(DAC_8, & stDispObj[iObj].dSz, -MAX_PLOT_VT,
MAX_PLOT_VT);
if (cTargetTrackLeftArm)
{
PutAnalog(DAC_3, & stDispObj[OBJ_TAR_DES].dSx, -MAX_PLOT_HZ,
MAX_PLOT_HZ);
PutAnalog(DAC_4, & stDispObj[OBJ_TAR_DES].dSy, -MAX_PLOT_HZ,
MAX_PLOT_HZ);
PutAnalog(DAC_6, & stDispObj[iObj].dSx, -MAX_PLOT_HZ,
MAX_PLOT_HZ);
PutAnalog(DAC_7, & stDispObj[iObj].dSy, -MAX_PLOT_HZ,
MAX_PLOT_HZ);
}
else
{
PutAnalog(DAC_6, & stDispObj[OBJ_TAR_DES].dSx, -MAX_PLOT_HZ,
MAX_PLOT_HZ);
PutAnalog(DAC_7, & stDispObj[OBJ_TAR_DES].dSy, -MAX_PLOT_HZ,
MAX_PLOT_HZ);
PutAnalog(DAC_3, & stDispObj[iObj].dSx, -MAX_PLOT_HZ,
MAX_PLOT_HZ);
PutAnalog(DAC_4, & stDispObj[iObj].dSy, -MAX_PLOT_HZ,
MAX_PLOT_HZ);
}
skip_dac:
// output Logic here ***************************************
if ( AlertStatus == eASRed ) // CO_0, alert status
// (red == 1)
cLogic = 1; else cLogic = 0;
PutDigital(CO_0, cLogic);
if ( iTargetTracked ) // CO_1, TargetTracked
cLogic = 1; else cLogic = 0;
PutDigital(CO_1, cLogic);
if ( ComputerStatus == eCInDeadZone )// PI in dead zone
cLogic = 1; else cLogic = 0;
PutDigital(CO_2, cLogic);
if ( MissileStatus != eMSPreLaunch )// Missile in flight
cLogic = 1; else cLogic = 0;
PutDigital(CO_3, cLogic);
if ( (stDispObj[OBJ_MTR].dApitch_u >= MAX_STEERING_ACC)
||(stDispObj[OBJ_MTR].dApitch_u <= -MAX_STEERING_ACC) )
// missile steering limit, vertical
cLogic = 1;
else cLogic = 0;
PutDigital(CO_4, cLogic);
if ( (stDispObj[OBJ_MTR].dAyaw_r >= MAX_STEERING_ACC)
||(stDispObj[OBJ_MTR].dAyaw_r <= -MAX_STEERING_ACC) )
// missile steering limit, horizontal
cLogic = 1;
else cLogic = 0;
PutDigital(CO_5, cLogic);
if ( 1 == 0 ) // not assigned ...
cLogic = 1; else cLogic = 0;
PutDigital(CO_6, cLogic);
if ( cTargetTrackLeftArm ) // cTargetTrackLeftArm
cLogic = 1; else cLogic = 0;
PutDigital(CO_7, cLogic);
return 0;
}
//////////////////////////////////////////////////////////////////
long UpdatePlotPosition(int v )
// purpose:
// set current display pulse and current x,y into index[0]
// (do not do this if w_iPulse[0] >= 0)
// (had trouble with targets moving west to east in north semi
//when single at 0)
{
int iObj; // i_p, i_x, i_y ;
double dRad; // radians, ccw starting at ease
double dRadcw_n0, dTest; // radians 0=north, clockwise
double dMeterOffsetX, dMeterOffsetY;
dMeterOffsetX = CENTER_PIXEL_X / dPixelPerMeter;
dMeterOffsetY = CENTER_PIXEL_Y / dPixelPerMeter;
dTest = CENTER_PIXEL_Y;
for (iObj=0; iObj< MAX_OBJ; iObj++)
{ // do each object
if ( stDispObj[iObj].iObjType > 0) // object enabled for display
{
if( (stDispObj[iObj].w_iPulse[0] == -1 )
// prevent multi hi intens
&&(stDispObj[iObj].w_iPulse[1] != -1 ) )
{ }
else
{ //
if ( ( stDispObj[iObj].dSx != 0.0)
&& (stDispObj[iObj].dSy != 0.0))
{
dRad = atan2( stDispObj[iObj].dSy ,
stDispObj[iObj].dSx );
dRadcw_n0 = -dRad + ( PI/2.0);
if ( dRadcw_n0 < 0.0)
dRadcw_n0 += ( PI * 2);
}
else
{
dRad = 0.0;
dRadcw_n0 = 0;
}
stDispObj[iObj].w_x[0] = (int)( stDispObj[iObj].dSx
* dPixelPerMeter)
+ CENTER_PIXEL_X ;
stDispObj[iObj].w_y[0] = - (int)( stDispObj[iObj].dSy
* dPixelPerMeter)
+ CENTER_PIXEL_Y ;
stDispObj[iObj].w_dAngRad[0] = dRad;
if ( ((int)( stDispObj[iObj].dSx))
|| ((int)( stDispObj[iObj].dSy)) ) // no
stDispObj[iObj].w_iPulse[0]
= (int)( dRadcw_n0 *PULSES_PER_RAD );
else
stDispObj[iObj].w_iPulse[0] = -1; // don't display 0,0
if (cIFF)
stDispObj[iObj].IFF[0] = 1; // show IFF
else
stDispObj[iObj].IFF[0] = 0; // don't show IFF
// i_p = stDispObj[iObj].w_iPulse[0];
// i_x = stDispObj[iObj].w_x[0];
// i_y = stDispObj[iObj].w_y[0];
} // end update object in quadrant ....
} // end object enabled for display
} // for (iObj...
return 0;
}
//////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////
long ClearDisplayHistory()
// should be followed by clear screen to clear decaying objects
{
int iObj, j;
for ( iObj=0; iObj< MAX_OBJ; iObj++)
{ // do each object
if ( (stDispObj[iObj].iObjType > 0) )
{
for (j=0; j < MAX_PHASES; j++)
stDispObj[iObj].w_iPulse[j] = -1;
}
} // for ( iObj...
return 0;
}
//////////////////////////////////////////////////////////////////
long DisplayObjects( int iPulse)
{
int iObj;
for ( iObj=0; iObj< MAX_OBJ; iObj++)
{ // do each object
if ( (stDispObj[iObj].iObjType > 0) )
{
PaintObject( iObj, iPulse);
}
} // for ( iObj...
return 0;
}
//////////////////////////////////////////////////////////////////
long PaintObject( int iObj, int iPulse)
{
int iObjType, x, y, i,j,radix, iPhase ;
int x1, x2, y1, y2;
static unsigned seed;
double dAngRad;
double dWidth, dPerpAngRad;
int iBigDeltaX, iBigDeltaY, iSmallDeltaX, iSmallDeltaY;
long i_x, i_y;
short rgb;
int iEndB1x, iEndB1y, iEndB2x, iEndB2y;
int iEndS1x, iEndS1y, iEndS2x, iEndS2y;
int iLineCnt;
int iLineToX[6], iLineToY[6];
for (i=0, j = -1; (i 4)
iLineCnt = 4; // prevent runaway
for (i = 0; i< iLineCnt; i++)
{
x2 = iLineToX[i] + CENTER_PIXEL_X ;
y2 = -iLineToY[i] + CENTER_PIXEL_Y ;
MyLine( x1, y1, x2, y2, rgb);
x1 = x2; y1 = y2; // new starting point
}
break;
case OBJ_MTR:
// a little x
_setcolor( sDecayArray[iPhase]); // bright,
_setpixel( x+2 , y+2);
_setpixel(x+1 , y+1);
_setpixel( x-1 , y-1);
_setpixel( x-2 , y-2);
_setpixel( x+2 , y-2);
_setpixel( x+1 , y-1);
_setpixel( x-1 , y+1);
_setpixel( x-2 , y+2);
break;
case OBJ_PI: // predicted intercept pt
// a box
_setcolor( sDecayArray[iPhase]); // bright,
_setpixel( x+2 , y+2);
_setpixel( x+1 , y+2);
_setpixel( x+0 , y+2);
_setpixel( x-1 , y+2);
_setpixel( x-2 , y+2);
_setpixel( x+2 , y-2);
_setpixel( x+1 , y-2);
_setpixel( x+0 , y-2);
_setpixel( x-1 , y-2);
_setpixel( x-2 , y-2);
_setpixel( x+2 , y+1);
_setpixel( x+2 , y+0);
_setpixel( x+2 , y-1);
_setpixel( x-2 , y+1);
_setpixel( x-2 , y+0);
_setpixel(x-2 , y-1);
break;
case OBJ_TTR: // radial line perpendicular to target
i_x = CENTER_PIXEL_X - x;
i_y = CENTER_PIXEL_Y - y;
if (iPhase == 0)
{ // compute useful things
iBigDeltaY = (int)(4*(sin(dAngRad)));
iBigDeltaX = -(int)(4*(cos(dAngRad)));
iEndB1x = x + iBigDeltaX;
iEndB1y = y + iBigDeltaY;
iEndB2x = x - iBigDeltaX;
iEndB2y = y - iBigDeltaY;
stDispObj[iObj].iEndB1x[1] = iEndB1x;// big line
stDispObj[iObj].iEndB1y[1] = iEndB1y;
stDispObj[iObj].iEndB2x[1] = iEndB2x;
stDispObj[iObj].iEndB2y[1] = iEndB2y;
}
else
{
iEndB1x = stDispObj[iObj].iEndB1x[iPhase]; // big line
iEndB1y = stDispObj[iObj].iEndB1y[iPhase];
iEndB2x = stDispObj[iObj].iEndB2x[iPhase];
iEndB2y = stDispObj[iObj].iEndB2y[iPhase];
}
_setcolor( sDecayArray[iPhase]); // duller
_moveto( iEndB1x, iEndB1y);
_lineto( iEndB2x, iEndB2y);
break;
// aircraft
case OBJ_AIRCRAFT:
case OBJ_AIRCRAFT_F:
// case OBJ_BIRDS:
//
i_x = CENTER_PIXEL_X - x;
i_y = CENTER_PIXEL_Y - y;
if (iPhase == 0)
{ // compute useful things
dWidth = 8 - 0.01*sqrt( (double)(i_x*i_x)
+ (double)(i_y*i_y) );
dPerpAngRad = dAngRad + (PI / 2) ;
if ( dPerpAngRad > PI )
dPerpAngRad = dPerpAngRad - (PI * 2);
iBigDeltaY = (int)(dWidth*(sin(dPerpAngRad)));
iBigDeltaX = -(int)(dWidth*(cos(dPerpAngRad)));
iSmallDeltaX = iBigDeltaX / 2;
iSmallDeltaY = iBigDeltaY / 2;
iEndB1x = x + iBigDeltaX;
iEndB1y = y + iBigDeltaY;
iEndB2x = x - iBigDeltaX;
iEndB2y = y - iBigDeltaY;
iEndS1x = x + iSmallDeltaX;
iEndS1y = y + iSmallDeltaY;
iEndS2x = x - iSmallDeltaX;
iEndS2y = y - iSmallDeltaY;
stDispObj[iObj].iEndB1x[1] = iEndB1x;// big line
stDispObj[iObj].iEndB1y[1] = iEndB1y;
stDispObj[iObj].iEndB2x[1] = iEndB2x;
stDispObj[iObj].iEndB2y[1] = iEndB2y;
stDispObj[iObj].iEndS1x[1] = iEndS1x;// short line
stDispObj[iObj].iEndS1y[1] = iEndS1y;
stDispObj[iObj].iEndS2x[1] = iEndS2x;
stDispObj[iObj].iEndS2y[1] = iEndS2y;
}
else
{
iEndB1x = stDispObj[iObj].iEndB1x[iPhase]; // big line
iEndB1y = stDispObj[iObj].iEndB1y[iPhase];
iEndB2x = stDispObj[iObj].iEndB2x[iPhase];
iEndB2y = stDispObj[iObj].iEndB2y[iPhase];
iEndS1x = stDispObj[iObj].iEndS1x[iPhase]; // short line
iEndS1y = stDispObj[iObj].iEndS1y[iPhase];
iEndS2x = stDispObj[iObj].iEndS2x[iPhase];
iEndS2y = stDispObj[iObj].iEndS2y[iPhase];
}
// create wide, dull line
_setcolor( sDecayArray[iPhase+2]); // duller
_moveto( iEndB1x, iEndB1y);
_lineto( iEndB2x, iEndB2y);
// create narrow, brighter section
_setcolor( sDecayArray[iPhase]); // bright,
_moveto( iEndS1x, iEndS1y);
_lineto( iEndS2x, iEndS2y);
if ( (stDispObj[iObj].IFF[iPhase])
&&(iObjType == OBJ_AIRCRAFT_F ) )
{ // show IFF lines
iBigDeltaX = (int)(5*cos(dAngRad));
iBigDeltaY = -(int)(5*sin(dAngRad));
iEndB1x += 2*iBigDeltaX;
iEndB1y += 2*iBigDeltaY;
iEndB2x += 2*iBigDeltaX;
iEndB2y += 2*iBigDeltaY;
_setcolor( sDecayArray[iPhase]); // use brighter col,
_moveto( iEndB1x, iEndB1y);
_lineto( iEndB2x, iEndB2y);
iEndB1x += 1*iBigDeltaX;
iEndB1y += 1*iBigDeltaY;
iEndB2x += 1*iBigDeltaX;
iEndB2y += 1*iBigDeltaY;
_moveto( iEndB1x, iEndB1y);
_lineto( iEndB2x, iEndB2y);
iEndB1x += 1*iBigDeltaX;
iEndB1y += 1*iBigDeltaY;
iEndB2x += 1*iBigDeltaX;
iEndB2y += 1*iBigDeltaY;
_moveto( iEndB1x, iEndB1y);
_lineto( iEndB2x, iEndB2y);
}
break;
case OBJ_EXPLOSION:
radix = 3; // break;
seed = (unsigned) iObj; // allow multiple simul.
srand( seed);
if (iPhase == 0)
rgb = sIntensF;
else if (iPhase == 1)
rgb = sIntens8;
else if (iPhase == 2)
rgb = sIntens4;
else if (iPhase == 3)
rgb = sIntens0;
_setcolor(rgb);
for (i=0; i< 4; i++)
{
for ( j=0; j<4; j++)
{
if ( ( i+j) < 14)
{
if (! ( ( rand()) % radix) )
_setpixel ( x+i, y+j);
if (! ( ( rand()) % radix) )
_setpixel( x-i, y+j);
if (! ( ( rand()) % radix) )
_setpixel( x+i, y-j);
if (! ( ( rand()) % radix) )
_setpixel( x-i, y-j);
}
} // end for(j...
} // end for(i...
if ( iPhase == 3)
{
InitializeObject( iObj); // unassign
}
break; // end case OBJ_EXPLOSION:
default:
break;
} // switch ( iObjType...
} // in viewable box
else // not in viewable box
{
// stDispObj[iObj].w_iPulse[0]=-1; // kill the dot
}
} // if (this pulse)
return ( 0);
}
///////////////////////////////////////////////////////////////////
long UpdateMissile( )
{
// This replaces the missile tracking radar to update:
// 1) missile position,
// 2) velocity,
// along the missile axis from boost, sustainer and drag
// and up and right from g commands sent to the missile
// & during cruse in response to g commands set into the object
// by the predicted Intercept module
//
// and also computes missile run time, and missile status,
//
// It is obviously closely tied with the predicted
// Intercept program, and end of initial dive experience must be
// fed into the predicted Intercept program.
//
// What do the acceleration commands really mean?
// There is a gyro on board, the spin axis at right angles to
// the vertical plane containing:
// a) a vertical line up from the launch site
// b) the predicted intercept point at time of launch
// Missile internal "roll" servos keep a plane containing
// a line defined as "missile down" perpendicular the spin axis
// of the gyro. A crisis develops if during steering operations,
// the axis of the missile approaches with in 20 degrees of
// the spin axis. This is called "Gymbol Limits" and if pushed,
// will cause total disorientation of the missile in the sense
// that what the commanding system calls "up" does not mean
// what the missile calls "up"
// 1) What does "accelerate negative yaw" mean to a missile?
// Move the control surfaces so that an acceloromotor whose
// sensitive direction is perpendicular to the missile axis
// and perpendicular to the missile down line will give a
// negative signal. For humans in the missile facing toward the
// nose, head end "up", this would appear "left".
//
// 2) What is "up" to a missile going straight up?
// The direction opposite the line defined as "missile down"
// passing from the missile center perpendicular to the missile
// axis passing through the "bottom" of the missile.
//
// 3) What does "accelerate positive pitch" mean to a missile?
// Move the control surfcaes so that an acceloorometer
// whose sensitive direction is opposite "missile down"
// will give a positive signal. For humans in the missile,
// facing toward the nose, head end "up", this would appear "up".
// current assumptions for hercules:
// constant speed after boost
// speed of sound for mach = 345 m/s
// prelaunch
// boost (18 g) phase
// 3.4 sec, gets 633.0 meters/sec, 1888 MPH, 3000 kM/h,
// mach <><>, at 1500 meters high (0.9 miles)
// initial dive to horizontal
// radius = 5,700 meters, quarter circle = 1.57 radians
// 15 sec to do the quarter circle,
// boost and initial dive
// takes 22.4 sec total,
// missile is 12,200 meters high (36,000 ft) (5.5 miles)
// range (in x,y) 10,800 meters
// ?radar range?=16 km (10 miles)
// cruse,
// see velocity profile
//
// if no target Tracked, set intercept at center 0,0, time = 999
static int iDiveCycle;
static int iSecondsTenths;
static eMissileStatus OldMissileStatus; // status history
// static double dBoostAcc = 18*9.8; // 18*9.8 Herk (average) boost m/s/s
static double dBoostAcc = 28*9.8; // 18*9.8 Herk (average) boost m/s/s
static double dBoostTimeLimit=3.4; // time limit of boost
// static double dSustainerAcc = +2.7*9.8; // acceleration due to sustainer
static double dSustainerAcc = +2.0*9.8; // acceleration due to sustainer
static double dDragAcc = -0.3*9.8;// no sustainer drag
static double dSzBoost, dVzBoost; // hight & speed at end of boost
static double dRadDive; // radius of initial dive, calc
static double dDeltaAngleInitialDive; // angle change each delta time, calc
static double dGyroAngle; // gyro angle at start of boost
// set from Intercept at start of boost
static double dSustainerTimeLimit=29.0; // time Herk sustainer runs
static double dSustainerTime; // length of time sustainer burning
double dTimeSq; // time squared
double dWorkSx, dWorkSy, dWorkSz;
double dMRad,dMRadPPI,dMDistance ;
double dPredIntGndRange, fSecondsIntercept, dPredIntSupElev;
static double dPredIntSuperRad;
// set global values for predicted Intercept program// change for herk
dReleaseTime = 17.2;
dReleaseRange = 5300;
dReleaseZ = 6260;
dReleaseSpeed = 850;
dMaxRange = 120000; // herk?
dDeadZoneRange = 15000;
if (stDispObj[OBJ_PI].iObjType != OBJ_PI)
{ // no target Tracked
cMissile_Over_Range = 0;
return 0;
}
// initialize at start of boost, catch transition
if ( ( MissileStatus == eMSBoost)
&&( OldMissileStatus != eMSBoost) )
{
cMissile_Over_Range = 0;
dSecondsMissile = 0;
dSustainerTime = 0;
stDispObj[OBJ_MTR].dSx = 0;
stDispObj[OBJ_MTR].dSy = 0;
stDispObj[OBJ_MTR].dSz = 0;
stDispObj[OBJ_MTR].dVx = 0;
stDispObj[OBJ_MTR].dVy = 0;
stDispObj[OBJ_MTR].dVz = 0;
stDispObj[OBJ_MTR].dAyaw_r = 0;
stDispObj[OBJ_MTR].dApitch_u = 0;
stDispObj[OBJ_MTR].dAx = 0;
stDispObj[OBJ_MTR].dAy = 0;
// capture predicted intercept angle into gyro angle
dGyroAngle=dPIRadNorth;
// gyro angle stays locked
iDiveCycle = 0; // start dive here
}
OldMissileStatus = MissileStatus;
// target is Tracked, see which mode
if ( MissileStatus == eMSPreLaunch)
{ // pre_launch
}
else if ( MissileStatus == eMSBoost)
{ // status is set by launch command
dSecondsMissile += dSecondsDelta;
dTimeSq = dSecondsMissile*dSecondsMissile;
stDispObj[OBJ_MTR].dSx = 0;
stDispObj[OBJ_MTR].dSy = 0;
stDispObj[OBJ_MTR].dSz = 0.5*dBoostAcc*dTimeSq;
stDispObj[OBJ_MTR].dVx = 0;
stDispObj[OBJ_MTR].dVy = 0;
stDispObj[OBJ_MTR].dVz = dBoostAcc*dSecondsMissile;
stDispObj[OBJ_MTR].dAyaw_r = 0;
stDispObj[OBJ_MTR].dApitch_u = 0;
iSecondsTenths = (int)( dSecondsMissile*10);
if ( dSecondsMissile >= dBoostTimeLimit)
{
MissileStatus = eMSInitialDive; // next mode
dSzBoost = stDispObj[OBJ_MTR].dSz; // hight
dVzBoost = stDispObj[OBJ_MTR].dVz; // speed
// calc initial dive radius
dRadDive = ( dVzBoost*dVzBoost)/(MAX_STEERING_ACC);
// calc change of angled each dSecondsDelta in dive
dDeltaAngleInitialDive = ( dVzBoost/dRadDive)*dSecondsDelta;
// radians
iDiveCycle = 1; // start dive here
// calculate possible early release from initial dive
dPredIntGndRange = sqrt( (stDispObj[OBJ_PI].dSx
* stDispObj[OBJ_PI].dSx)
+(stDispObj[OBJ_PI].dSy * stDispObj[OBJ_PI].dSy) );
if (fSecondsIntercept > 10.0) // apply 1/2 super elevation
dPredIntSupElev = 0.5*0.5*9.8*((fSecondsIntercept-10)
*(fSecondsIntercept-10));
else
dPredIntSupElev = 0;
dPredIntSupElev += stDispObj[OBJ_PI].dSz;
if (dPredIntSupElev > 30000)
dPredIntSupElev = 30000;
if ((dPredIntSupElev != 0.0) && (dPredIntGndRange != 0.0))
dPredIntSuperRad = atan2(dPredIntSupElev, dPredIntGndRange);
else
dPredIntSuperRad = 0.0;
if ( dPredIntSuperRad < 1.0) //// clamp for stability
dPredIntSuperRad = 1.0;
}
}
else if (MissileStatus == eMSInitialDive)
{ // status is set by eMSBoost && top of dive for now
dSecondsMissile += dSecondsDelta;
// calculate missile height
dWorkSz = dSzBoost
+ ( dRadDive*sin( dDeltaAngleInitialDive*iDiveCycle));
// calculate x,y as due West, then fix x,y by gyro angle
// relative to launch point (0,0)
dWorkSx = ( dRadDive*cos( dDeltaAngleInitialDive*iDiveCycle))
- dRadDive;
iDiveCycle +=1;
dWorkSy = -dWorkSx*cos( dGyroAngle);
dWorkSx = -dWorkSx*sin( dGyroAngle); // west is positive?
// calc velocities as (new-old)/deltatime;
stDispObj[OBJ_MTR].dVx = ( dWorkSx-stDispObj[OBJ_MTR].dSx)
/dSecondsDelta;
stDispObj[OBJ_MTR].dVy = ( dWorkSy-stDispObj[OBJ_MTR].dSy)
/dSecondsDelta;
stDispObj[OBJ_MTR].dVz = ( dWorkSz-stDispObj[OBJ_MTR].dSz)
/dSecondsDelta;
stDispObj[OBJ_MTR].dSx = dWorkSx;
stDispObj[OBJ_MTR].dSy = dWorkSy;
stDispObj[OBJ_MTR].dSz = dWorkSz;
stDispObj[OBJ_MTR].dAyaw_r = 0;
stDispObj[OBJ_MTR].dApitch_u = -MAX_STEERING_ACC;
iSecondsTenths = (int)( dSecondsMissile*10);
if ( (stDispObj[OBJ_MTR].dVz <= 0) // dive done when horizontal!?!?
||(dPredIntSuperRad <= (dDeltaAngleInitialDive*iDiveCycle) ) )
{
MissileStatus = eMSCruse; // next mode
stDispObj[OBJ_MTR].dApitch_u = 0;
}
}
else if ( MissileStatus == eMSCruse)
{
dSecondsMissile += dSecondsDelta;
// find missile horizontal direction angle
if ( (stDispObj[OBJ_MTR].dVy != 0.0)
&& (stDispObj[OBJ_MTR].dVx != 0.0))
dMRad = atan2(stDispObj[OBJ_MTR].dVy, stDispObj[OBJ_MTR].dVx);
else
dMRad = 0.0;
dMRadPPI = dMRad+PI/2;
if ( dMRadPPI >= PI)
dMRadPPI -= ( PI*2);
dMDistance = stDispObj[OBJ_MTR].dAyaw_r;
stDispObj[OBJ_MTR].dAx = dMDistance*cos( dMRadPPI);
stDispObj[OBJ_MTR].dAy = dMDistance*sin( dMRadPPI);
// update velocity from g based steering
stDispObj[OBJ_MTR].dVx += stDispObj[OBJ_MTR].dAx*dSecondsDelta;
stDispObj[OBJ_MTR].dVy += stDispObj[OBJ_MTR].dAy*dSecondsDelta;
stDispObj[OBJ_MTR].dVz += stDispObj[OBJ_MTR].dApitch_u*dSecondsDelta;
// update velocity from sustainer motor (or drag) (x,y only)
if ( dSustainerTime < dSustainerTimeLimit)
{
dSustainerTime += dSecondsDelta;
stDispObj[OBJ_MTR].dVx += cos(dMRad)*dSustainerAcc*dSecondsDelta;
stDispObj[OBJ_MTR].dVy += sin(dMRad)*dSustainerAcc*dSecondsDelta;
}
else // sustainer motor done
{
stDispObj[OBJ_MTR].dVx += cos(dMRad)*dDragAcc*dSecondsDelta;
stDispObj[OBJ_MTR].dVy += sin(dMRad)*dDragAcc*dSecondsDelta;
}
// update position
stDispObj[OBJ_MTR].dSx += stDispObj[OBJ_MTR].dVx*dSecondsDelta;
stDispObj[OBJ_MTR].dSy += stDispObj[OBJ_MTR].dVy*dSecondsDelta;
stDispObj[OBJ_MTR].dSz += stDispObj[OBJ_MTR].dVz*dSecondsDelta;
// check for flight exhaustion (50 km range exceeded)
// or missile range > intercept range
dWorkSx = sqrt( ( stDispObj[OBJ_MTR].dSx*stDispObj[OBJ_MTR].dSx)
+( stDispObj[OBJ_MTR].dSy*stDispObj[OBJ_MTR].dSy) );
if ( dWorkSx > dMaxRange) //
{
cMissile_Over_Range = 1;
}
iSecondsTenths = (int)( dSecondsMissile*10);
}
else
return ( 1);
return 0;
//////////////////////////////////////////////////////////////
}
long UpdateTargets( )
{
double dTarRad;
int iObj, iObjType, iRand ;
double dX, dY, dZ;
for ( iObj=0; iObj< MAX_OBJ; iObj++)
{ // do each object
iObjType = stDispObj[iObj].iObjType;
switch ( iObjType)
{
// aircraft
case OBJ_AIRCRAFT: //
case OBJ_AIRCRAFT_F: //
case OBJ_BIRDS:
if ( ( cEvasiveAction ) && (iObj == iTargetTracked) )
{ // vary horizontal g's, limit +- 3 g, 0.1g/0.1 sec
iRand = rand( );
stDispObj[iObj].dAyaw_r += ((double)( iRand % 3) - 1)/5;
// clamp to +- 1 g's
if ( stDispObj[iObj].dAyaw_r > 10)
stDispObj[iObj].dAyaw_r = 10;
if ( stDispObj[iObj].dAyaw_r < -10)
stDispObj[iObj].dAyaw_r = -10;
}
else
{
stDispObj[iObj].dAyaw_r = 0;
}
if ( stDispObj[iObj].dAyaw_r != 0.0)
{ // target turning, update velocity
if ( (stDispObj[iObj].dVy != 0.0)
&& (stDispObj[iObj].dVx != 0.0))
dTarRad = atan2( stDispObj[iObj].dVy, stDispObj[iObj].dVx);
else
dTarRad = 0.0;
if (stDispObj[iObj].dVs == 0.0)
{ // calculate speed once
stDispObj[iObj].dVs =
sqrt( ( stDispObj[iObj].dVx
*stDispObj[iObj].dVx )
+( stDispObj[iObj].dVy
*stDispObj[iObj].dVy ));
}
if ( stDispObj[iObj].dVs > 0.01)
{ // ok to divide
dTarRad += ( stDispObj[iObj].dAyaw_r/stDispObj[iObj].dVs);
stDispObj[iObj].dVx =
stDispObj[iObj].dVs*cos( dTarRad);
stDispObj[iObj].dVy =
stDispObj[iObj].dVs*sin( dTarRad);
}
}
// linear, update position 1 dSecondDelta time
stDispObj[iObj].dSx += stDispObj[iObj].dVx*dSecondsDelta;
stDispObj[iObj].dSy += stDispObj[iObj].dVy*dSecondsDelta;
stDispObj[iObj].dSz += stDispObj[iObj].dVz*dSecondsDelta;
if ( DEBUG_R)
{
dX = stDispObj[iObj].dSx;
dY = stDispObj[iObj].dSy;
dZ = stDispObj[iObj].dSx;
}
break;
default:
break;
}
}
return 0;
}
///////////////////////////////////////////////////
long UpdateIntercept( ) // also sends steering commands during cruise
{
// update Predicted Intercept in 3 modes
// 0) prelaunch
// 1) boost and initial dive until horizontal
// 2) cruse to kill
// - if no target Tracked, set at origin
//
// assumptions:
// constant speed after boost
// speed of sound for mach = 345 m/s
// prelaunch
// boost (25 g) phase
// 3.4 sec, gets 850.0 meters/sec, 1888 MPH, 3000 kM/h,
// mach 2.45, at 1500 meters high (0.9 miles)
// initial dive to horizontal
// radius = 10,300 meters, quarter circle = 1.57 radians
// length of quarter circle = 16,171 meters
// 19 sec to do the quarter circle,
// boost and initial dive
// takes 22.4 sec total,
// missile is 17,700 meters high (58,000 ft) (11 miles)
// range 16 km (10 miles)
// cruse,
// constant speed of 850 meters/sec until intercept
// if (dSecondsMissile < 50) and (speed < mach 2.5)
// set speed to mach 2.5
//
// if no target Tracked, set intercept at center 0,0, time = 999
int i;
double dTryPx, dTryPy, dTryPz;
double dTryMx, dTryMy, dTryMz;
double dTryTGRange, dTryTRad, dTryMGRange;
double dMx, dMy, dMRange; // missile data
double dTx, dTy, dTRange; // target data
static double dTryMTime, dTryMRad;
double dMPx, dMPy, dMPz; // midpoint between missile and PredInt
double dWTime, dWTime2;
double fMVRadN, fM2Ix,fM2Iy,fM2Ir,fMGoalRadN,fMErrorRadH;
if ( iTargetTracked == 0)
{ // no target Tracked
// move predicted point of Intercept to 0,0
stDispObj[OBJ_PI].dSx = 0;
stDispObj[OBJ_PI].dSy = 0;
stDispObj[OBJ_PI].dSz = 0;
// show impractical time value
dSecondsIntercept = -1.0;
// move target tracking radar to 0,0
stDispObj[OBJ_TTR].dSx = 0;
stDispObj[OBJ_TTR].dSy = 0;
stDispObj[OBJ_TTR].dSz = 0;
stDispObj[OBJ_TTR].dVx = 0;
stDispObj[OBJ_TTR].dVy = 0;
stDispObj[OBJ_TTR].dVz = 0;
stDispObj[OBJ_TTR].dAyaw_r = 0;
stDispObj[OBJ_TTR].dApitch_u = 0;
// make message
ComputerStatus = eCSNoTargDesig;
return 0;
}
if ( iTargetTracked < 0)
{
My_Beep(0);
return (0);
}
// target is Tracked, copy values into OBJ_TTR
i = iTargetTracked;
stDispObj[OBJ_TTR].dSx = stDispObj[i].dSx;
stDispObj[OBJ_TTR].dSy = stDispObj[i].dSy;
stDispObj[OBJ_TTR].dSz = stDispObj[i].dSz;
stDispObj[OBJ_TTR].dVx = stDispObj[i].dVx;
stDispObj[OBJ_TTR].dVy = stDispObj[i].dVy;
stDispObj[OBJ_TTR].dVz = stDispObj[i].dVz;
stDispObj[OBJ_TTR].dAyaw_r = stDispObj[i].dAyaw_r;
stDispObj[OBJ_TTR].dApitch_u = stDispObj[i].dApitch_u;
// target is Tracked, see which mode
switch (MissileStatus)
{
case eMSPreLaunch:
// check to see if present algorithm can calc it
// predict from default constants
if ( dTryMTime100)
dTryMTime = 100;
if ( dTryMRad > PI)
dTryMRad = (2*PI);
if ( dTryMRad < -PI)
dTryMRad = -PI;
for ( i=0; i<4; i++) // limit to 2 range & az iterations/cycle
{ // trial target position & range
dTryPx = stDispObj[OBJ_TTR].dSx
+ ( stDispObj[OBJ_TTR].dVx*dTryMTime);
dTryPy = stDispObj[OBJ_TTR].dSy
+ ( stDispObj[OBJ_TTR].dVy*dTryMTime);
dTryTGRange = sqrt( ( dTryPx*dTryPx) + (dTryPy*dTryPy) );
if ( (dTryPy != 0.0) && (dTryPx != 0.0))
dTryTRad = atan2( dTryPy,dTryPx);
else
dTryTRad = 0.0;
// trial missile position
dTryMGRange = dReleaseRange
+ ( dReleaseSpeed * (dTryMTime - dReleaseTime) );
dTryMx = dTryMGRange * cos( dTryMRad);
dTryMy = dTryMGRange * sin( dTryMRad);
dTryMz = dReleaseZ;
// if (i is even)do angle, else do time
if (!( i && 1))
dTryMRad = dTryMRad + ( ( dTryTRad-dTryMRad)/2);
else
dTryMTime = dTryMTime
+ ( ( ( dTryTGRange-dTryMGRange)/dReleaseSpeed)/2);
}
stDispObj[OBJ_PI].dSx = dTryPx;
stDispObj[OBJ_PI].dSy = dTryPy;
stDispObj[OBJ_PI].dSz = stDispObj[OBJ_TTR].dSz
+ ( stDispObj[OBJ_TTR].dVz*dTryMTime);;
dSecondsIntercept = dTryMTime;
dPIRadNorth = ( -dTryTRad)+(PI/2);
if (dPIRadNorth < 0)
dPIRadNorth += PI*2;
// set computer status
if ( dTryTGRange < dDeadZoneRange)
ComputerStatus = eCInDeadZone;
else if ( dTryTGRange > dMaxRange)
ComputerStatus = eCSOutOfRange;
else
ComputerStatus = eCSInRange;
break;
case eMSBoost:
// Note: no break
case eMSInitialDive:
dTryMTime -= dSecondsDelta;
if ( dTryMTime < dSecondsDelta)
dTryMTime = dSecondsDelta; // clamp
dSecondsIntercept = dTryMTime;
RecordEvent( );
break;
case eMSCruse:
// predict PredInt from missile values
dTryMTime -= dSecondsDelta; // new trial time
for (i=0; i<2; i++) // limit to 2 range iterations/cycle
{ // trial target position & range
dTryPx = stDispObj[OBJ_TTR].dSx
+( stDispObj[OBJ_TTR].dVx*dTryMTime);
dTryPy = stDispObj[OBJ_TTR].dSy
+( stDispObj[OBJ_TTR].dVy*dTryMTime);
dTryTGRange = sqrt((dTryPx*dTryPx) + (dTryPy*dTryPy));
if ( (dTryPy != 0.0)
||(dTryPx != 0.0) )
dTryTRad = atan2(dTryPy,dTryPx);
else
dTryTRad = 0.0;
// trial missile position
dTryMx = stDispObj[OBJ_MTR].dSx
+( stDispObj[OBJ_MTR].dVx*dTryMTime);
dTryMy = stDispObj[OBJ_MTR].dSy
+( stDispObj[OBJ_MTR].dVy*dTryMTime);
dTryMGRange = sqrt( ( dTryMx*dTryMx) + (dTryMy*dTryMy) );
dTryMTime = dTryMTime
+ ( ( ( dTryTGRange-dTryMGRange)/dReleaseSpeed)/2.0);
} // end itterative Predicted Intercept pointtrials
stDispObj[OBJ_PI].dSx = dTryPx; // predicted intercept point
stDispObj[OBJ_PI].dSy = dTryPy;
stDispObj[OBJ_PI].dSz = dTryPz = stDispObj[OBJ_TTR].dSz
+( stDispObj[OBJ_TTR].dVz*dTryMTime);
dSecondsIntercept = dTryMTime;
dPIRadNorth = ( -dTryTRad)+(PI/2);
if (dPIRadNorth < 0)
dPIRadNorth += PI*2;
// set computer status
if ( dTryTGRange < dDeadZoneRange)
ComputerStatus = eCInDeadZone;
else if ( dTryTGRange > dMaxRange)
ComputerStatus = eCSOutOfRange;
else
ComputerStatus = eCSInRange;
RecordEvent( );
// Generate acceleration commands to steer missile
// - General steering theory (not used in Nike)
// Steer so that the missile will pass through
// a point midway between the current missile
// and the current predicted point of Intercept.
// This should appear critically damped.
// find mid-point window (between PredInt and Missile)
dMPx = ( dTryPx + stDispObj[OBJ_MTR].dSx)/2.0;
dMPy = ( dTryPy + stDispObj[OBJ_MTR].dSy)/2.0;
dMPz = ( dTryPz + stDispObj[OBJ_MTR].dSz)/2.0;
// find mid-point missile position before steering corrections
dWTime = dTryMTime/2.0;
if ( dWTime > 0.001)
{
dTryMx = stDispObj[OBJ_MTR].dSx
+ ( stDispObj[OBJ_MTR].dVx*dWTime);
dTryMy = stDispObj[OBJ_MTR].dSy
+ ( stDispObj[OBJ_MTR].dVy*dWTime);
dTryMz = stDispObj[OBJ_MTR].dSz
+ ( stDispObj[OBJ_MTR].dVz*dWTime);
// now we have the desired position and the projected position
// basicly a=( 2*S)/(t*t)
dWTime2 = dWTime*dWTime; // time squared
// calculate vertical steering
stDispObj[OBJ_MTR].dApitch_u = (2*(dMPz-dTryMz))/ dWTime2; // up
if ( stDispObj[OBJ_MTR].dApitch_u > MAX_STEERING_ACC)
stDispObj[OBJ_MTR].dApitch_u = MAX_STEERING_ACC; // clamp
if ( stDispObj[OBJ_MTR].dApitch_u < -MAX_STEERING_ACC)
stDispObj[OBJ_MTR].dApitch_u = -MAX_STEERING_ACC;
// calculate horizontal steering
if ( (stDispObj[OBJ_MTR].dVy != 0.0)
|| (stDispObj[OBJ_MTR].dVx != 0.0) )
fMVRadN = atan2(stDispObj[OBJ_MTR].dVy,
stDispObj[OBJ_MTR].dVx);// missile angle
else
fMVRadN = 0.0;
fMVRadN += PI/2; // rotate 0 to north?
if ( fMVRadN < 0.0)
fMVRadN += PI*2; // missile flight radians from north
if ( fMVRadN >= PI*2)
fMVRadN -= PI*2; // missile flight radians from north
fM2Ix = dTryPx - stDispObj[OBJ_MTR].dSx;
fM2Iy = dTryPy - stDispObj[OBJ_MTR].dSy;
fM2Ir = sqrt ( ( fM2Ix*fM2Ix)+(fM2Iy*fM2Iy)); // m-> int
if ( (fM2Iy != 0.0) || (fM2Ix != 0.0) )
fMGoalRadN = atan2( fM2Iy, fM2Ix); //
else
fMGoalRadN = 0.0;
fMGoalRadN += PI/2;
if ( fMGoalRadN < 0.0)
fMGoalRadN += PI*2; // radians missile to intercept
if ( fMGoalRadN >= PI*2)
fMGoalRadN -= PI*2; // radians missile to intercept
fMErrorRadH = fMGoalRadN - fMVRadN; // e rad in h flight
if (fMErrorRadH > PI)
fMErrorRadH -= PI*2; // keep in bounds
if (fMErrorRadH < -PI)
fMErrorRadH += PI*2;
stDispObj[OBJ_MTR].dAyaw_r = ( 2*( fM2Ir*fMErrorRadH))
/ dWTime2;// right
if ( stDispObj[OBJ_MTR].dAyaw_r > MAX_STEERING_ACC)
stDispObj[OBJ_MTR].dAyaw_r = MAX_STEERING_ACC; // clamp steering
if ( stDispObj[OBJ_MTR].dAyaw_r < -MAX_STEERING_ACC)
stDispObj[OBJ_MTR].dAyaw_r = -MAX_STEERING_ACC;
stDispObj[OBJ_MTR].dAx = stDispObj[OBJ_MTR].dAyaw_r
*sin( fMVRadN); // from north
stDispObj[OBJ_MTR].dAy = stDispObj[OBJ_MTR].dAyaw_r
*cos( fMVRadN); // from north
}
// -----------------------------------------------------
dMx = stDispObj[OBJ_MTR].dSx;
dMy = stDispObj[OBJ_MTR].dSy;
dMRange = sqrt ((dMx*dMx)+(dMy*dMy));
dTx = stDispObj[OBJ_TTR].dSx;
dTy = stDispObj[OBJ_TTR].dSy;
dTRange = sqrt((dTx*dTx) + (dTy*dTy));
if ( ( dTRange < dMRange) // passed target
||( dSecondsIntercept <= 0.004)
||( cMissile_Over_Range ) )
{
RecordEvent( );
if ( fdRE != NULL) // close file if open
{
fclose ( fdRE);
fdRE = NULL;
}
MissileStatus = eMSPreLaunch;
// My_Beep ( 0); // missile terminated
ComputeMissileMiss_H_V();
if ( cMissile_Over_Range )
{
sprintf( szLogEntry, "missile exhausted, removed");
cMissile_Over_Range = 0;
}
else if ( ( ( dSecondsIntercept < 1.0)
||( dSecondsIntercept > -1.0) ) // HORIZONTAL
&&(fabs( stDispObj[OBJ_TTR].dSz
- stDispObj[OBJ_MTR].dSz) < 200.0) )
{
sprintf( szLogEntry, "** INTERCEPT **");
stDispObj[OBJ_MTR].iObjType = // explode the MISSILE, not target
OBJ_EXPLOSION;
stDispObj[OBJ_MTR].iObjPict = 0; // explosion sequence
UnPaint(iTargetTracked); // remove visual traces
UnPaint(OBJ_TTR); // remove visual traces
stDispObj[iTargetTracked].iObjType = 0;// remove target
iTargetTracked = 0;
} // else if ( ( ( dSec
else
{
sprintf( szLogEntry,"missile passed target, removed");
}
Log( ); // post the message in szLogEntry
if (fdRE != NULL)
{
fclose(fdRE); // close event recorder file
fdRE = NULL;
}
InitializeObject(OBJ_MTR); // unassign missile
stDispObj[OBJ_MTR].iObjType = OBJ_MTR; //
}
break;
case eMSExplode:
break;
default:
break;
}
return ( 0);
}
/////////////////////////////////////////////////////////////////
long Log() // post the message in szLogEntry
{
return 0;
}
///////////////////////////////////////////////////////////////
long RemoveOutOfViewTargets( ) // (all w_iPulse == -1)
// returns number of non IFF identified targets remaining on
{
int iObj, iLObjType;
long lTarCnt ;
// return 0;
lTarCnt = 0;
for ( iObj=0; iObj< MAX_OBJ; iObj++)
{ // do each object
iLObjType = stDispObj[iObj].iObjType;
if ( ( iLObjType >= OBJ_T_MIN) && (iLObjType <= OBJ_T_MAX) )
// aircraft
{
if ( (fabs(stDispObj[iObj].dSx) > 350000.0)
||(fabs(stDispObj[iObj].dSy) > 350000.0) )
{
if (iTargetTracked == iObj)
iTargetTracked = 0;
UnPaint (iObj); // remove video traces
InitializeObject( iObj); // unassign
}
else
{
if (cIFF && (iLObjType == OBJ_AIRCRAFT_F))
; // ignore friendly aircraft if IFF is on
else
lTarCnt++; // increment # targets remaining
}
} // if iObjType , aircraft, birds
} // for (iObj...
return lTarCnt;
}
//-----------------------------------------------------------------
long SelectTarget( double x, double y)
{
int i;
int closest_obj = 0;
double dW, dDeltaX, dDeltaY, closest_dist = 99999999.9;
// select closest visible target, but not during WarGames
// do not select if IFF is showing and friend
if ( cWarGame )
return 0; // no manual selection during WarGames
for (i = 0; i= OBJ_T_MIN)
&&(stDispObj[i].iObjType <= OBJ_T_MAX) )
{ // target correct type
if (cIFF && (stDispObj[i].iObjType == OBJ_AIRCRAFT_F))
{}
else
{
dW = sqrt( (stDispObj[i].dSx*stDispObj[i].dSx)
+(stDispObj[i].dSy*stDispObj[i].dSy) );
if (dW < dVisibleRange)
{ // target is visable, pick closest target
dDeltaX = stDispObj[i].dSx - x;
dDeltaY = stDispObj[i].dSy - y;
dW = sqrt( (dDeltaX*dDeltaX) + (dDeltaY*dDeltaY) );
if (dW < closest_dist)
{ // closest so far
closest_obj = i;
closest_dist = dW;
}
} // end visable
} // end if (cIFF && (stDisp
} // end correct type
} // end seach targets for closest mouse hit
if (closest_dist <= 200/dPixelPerMeter) // was 20 until bad pots
{ // close enough?
return closest_obj;
}
else
return 0;
}
//-----------------------------------------------------------------
long RecordEvent()
{
int iObj;
double dTemp1, dTemp2, dTemp3, dTemp4 ;
if (DEBUG_R == 0)
return 0; // do not record unless in debug
if (fdRE == NULL)
{
fdRE = fopen("EVENT.TXT","w");
if (fdRE == NULL)
{
My_Beep(0);
CheckIt ( "Unable to open file EVENT.TXT !");
exit (0);
}
// make message header
// 0 1 2 3 4 5 6 7
// 1234567890123456789012345678901234567890123456789012345678901234567890123456
fprintf(fdRE,"time item Sx Sy Sz Vx Vy Vz \n");
fprintf(fdRE," km km km m/s m/s m/s \n");
}
iObj = OBJ_TTR;
fprintf(fdRE, "%8.4f target %8.1f %8.1f %8.1f %8.1f %8.1f %8.1f %05.2 %05.2\n",
dSecondsMissile,
stDispObj[iObj].dSx, stDispObj[iObj].dSy, stDispObj[iObj].dSz,
stDispObj[iObj].dVx, stDispObj[iObj].dVy, stDispObj[iObj].dVz,
0,0);
iObj = OBJ_MTR;
fprintf(fdRE, " missil %8.1f %8.1f %8.1f %8.1f %8.1f %8.1f %05.2 %05.2\n",
stDispObj[iObj].dSx, stDispObj[iObj].dSy, stDispObj[iObj].dSz,
stDispObj[iObj].dVx, stDispObj[iObj].dVy, stDispObj[iObj].dVz,
0,0);
iObj = OBJ_PI;
fprintf(fdRE, " pre in %8.1f %8.1f %8.1f %8.1f %8.1f %8.1f %05.2 %05.2\n",
stDispObj[iObj].dSx, stDispObj[iObj].dSy, stDispObj[iObj].dSz,
stDispObj[iObj].dVx, stDispObj[iObj].dVy, stDispObj[iObj].dVz,
0,0);
dTemp1 = stDispObj[OBJ_MTR].dVx;
dTemp2 = stDispObj[OBJ_MTR].dVy;
dTemp3 = stDispObj[OBJ_MTR].dVz;
dTemp4 = sqrt((dTemp1*dTemp1)
+ (dTemp2*dTemp2)
+ (dTemp3*dTemp3)); // meters/sec
dTemp1 = stDispObj[OBJ_MTR].dSx;
dTemp2 = stDispObj[OBJ_MTR].dSy;
dTemp3 = sqrt((dTemp1*dTemp1)
+ (dTemp2*dTemp2) );
fprintf(fdRE, "t to i %4.4f, msl m/s = %6.1f, msl km xy = %6.1f\n",
dSecondsIntercept, dTemp4, dTemp3 );
fprintf(fdRE,"\n");
return 0;
}
//---------------------------------------------------------------
long UnPaint(int iObj) // remove all paint sequences of obj
{
int i, j ;
for (i=iPulseCnt; i 1.0)
fS = 1.0;
else if (fS < -1.0)
fS = -1.0;
// target position at time 0
// get 3 d miss distance
fMiss_x = (stDispObj[OBJ_TTR].dSx
+ (stDispObj[OBJ_TTR].dVx*fS) )
- (stDispObj[OBJ_MTR].dSx
+ (stDispObj[OBJ_MTR].dVx*fS) );
fMiss_y = (stDispObj[OBJ_TTR].dSy
+ (stDispObj[OBJ_TTR].dVy*fS) )
- (stDispObj[OBJ_MTR].dSy
+(stDispObj[OBJ_MTR].dVy*fS) );
fMiss_z = (stDispObj[OBJ_TTR].dSz
+ (stDispObj[OBJ_TTR].dVz*fS) )
- (stDispObj[OBJ_MTR].dSz
+(stDispObj[OBJ_MTR].dVz*fS) );
fMissRange = sqrt((fMiss_x*fMiss_x) + (fMiss_y*fMiss_y)
+ (fMiss_z*fMiss_z) );
return 0;
}
//-----------------------------------------------------
long UpdateText( int cmd)
{ // update the text details
// usually at beginning of scan after other updates complete
// cmd updated
// --- -------
// 1 text
// 2 designated target #'s
// 3 missile #'s
// 4 predicted intercetp #'s
//
// units are: km, speed km/hr, bearing in degrees rel north
// acceleration in g's
// designated target - x,y,z,speed,range,bearing,accel
// missile - x,y,z,speed,range,bearing,acceleration
// predicted intercept - x,y,z,speed,range,bearing
//
int iRelX, iRelY, iStartY, iObj, i;
int iCol1, iCol2, iCol3, iCol4;
short rgb;
double dTemp1, dTemp2, dTemp3, dTemp4;
iCol1 = 60;
iCol2 = iCol1+8;
iCol3 = iCol2+8;
iCol4 = iCol3+8;
iStartY = 10;
// set text and background colors
rgb = 2;
_settextcolor( rgb);
// set likely object
switch (cmd)
{
case 1:
iRelX=iCol1;
break;
case 2:
iRelX=iCol2;
iObj = OBJ_TTR;
break;
case 3:
iRelX=iCol3;
iObj = OBJ_MTR;
break;
case 4:
iRelX=iCol4;
iObj = OBJ_PI;
break;
}
//Line1 - header 1 , target missile & predicted intercept
switch (cmd)
{
case 1:
//123456789012345678901234567890
sprintf (my_text, "------- Event Recorder -------");
_settextposition (iStartY+(0*VERT_TEXT_STEP), iCol1 );
_outtext( my_text);
//123456789012345678901234567890
sprintf (my_text, " Targ Misl P.Int ");
_settextposition( iStartY+(1*VERT_TEXT_STEP), iCol1 );
_outtext( my_text);
break;
}
//Line2 - x in km
iRelY = iStartY+(2*VERT_TEXT_STEP);
switch (cmd)
{
case 1:
//123456789012345678901234567890
sprintf (my_text, "e-kY ");
_settextposition( iRelY, iCol1);
_outtext( my_text);
break;
case 2:
case 3:
case 4:
dTemp1 = stDispObj[iObj].dSx;
sprintf (my_text, "%7.1f", stDispObj[iObj].dSx/1000);
_settextposition( iRelY, iRelX);
_outtext( my_text);
break;
}
//Line3 - y in km
iRelY= iStartY+(3*VERT_TEXT_STEP);
switch (cmd)
{
case 1:
//123456789012345678901234567890
sprintf (my_text, "n-kY ");
_settextposition( iRelY, iRelX);
_outtext( my_text);
break;
case 2:
case 3:
case 4:
dTemp2 = stDispObj[iObj].dSy;
sprintf (my_text, "%7.1f", stDispObj[iObj].dSy/1000);
_settextposition( iRelY, iRelX);
_outtext( my_text);
break;
}
//Line4 - z in meters
iRelY = iStartY+(4*VERT_TEXT_STEP);
switch (cmd)
{
case 1:
//123456789012345678901234567890
sprintf (my_text, "h-kF ");
_settextposition( iRelY, iRelX);
_outtext( my_text);
break;
case 2:
case 3:
case 4:
sprintf (my_text, "%7.1f", stDispObj[iObj].dSz*3/1000);
_settextposition( iRelY, iRelX);
_outtext( my_text);
break;
}
//Line5 - range in kM
iRelY = iStartY+(5*VERT_TEXT_STEP);
switch (cmd)
{
case 1:
//123456789012345678901234567890
sprintf (my_text, "r-kY ");
_settextposition( iRelY, iRelX);
_outtext( my_text);
break;
case 2:
case 3:
case 4:
// note this is range in x,y only,
// useful in inter-site coordination
dTemp1 = stDispObj[iObj].dSx;
dTemp2 = stDispObj[iObj].dSy;
dTemp3 = sqrt((dTemp1*dTemp1) + (dTemp2*dTemp2));
sprintf (my_text, "%7.1f", dTemp3/1000);
_settextposition( iRelY, iRelX);
_outtext( my_text);
break;
}
//Line6 - speed in mach (mach 1 == 348 m/s)
iRelY = iStartY+(6*VERT_TEXT_STEP);
switch (cmd)
{
case 1:
//123456789012345678901234567890
sprintf (my_text, "mach ");
_settextposition(iStartY+(6*VERT_TEXT_STEP), iRelX);
_outtext( my_text);
break;
case 2:
case 3:
case 4:
dTemp1 = stDispObj[iObj].dVx;
dTemp2 = stDispObj[iObj].dVy;
dTemp3 = stDispObj[iObj].dVz;
dTemp4 = sqrt((dTemp1*dTemp1)
+ (dTemp2*dTemp2)
+ (dTemp3*dTemp3)); // meters/sec
dTemp4 = dTemp4/348.0; // mach
if (iObj == OBJ_TTR)
dTargSpeedMach = dTemp4; // update for analog output
if (iObj == OBJ_MTR)
dMissileSpeedMach = dTemp4;
sprintf (my_text, "%7.1f", dTemp4);
_settextposition( iRelY, iRelX);
_outtext( my_text);
break;
}
//Line7 - bearing in Deg
iRelY = iStartY+(7*VERT_TEXT_STEP);
switch (cmd)
{
case 1:
//123456789012345678901234567890
sprintf (my_text, "deg ");
_settextposition(iStartY+(7*VERT_TEXT_STEP), iRelX);
_outtext( my_text);
break;
case 2:
case 3:
case 4:
if ( (stDispObj[iObj].dSx != 0.0) && (stDispObj[iObj].dSy != 0.0) )
dTemp2 = DEG_PER_RAD
* atan2 (stDispObj[iObj].dSx,stDispObj[iObj].dSy);
// degrees from north
else
dTemp2 = 0.0;
if (dTemp2 < 0.0)
dTemp2 += 360.0; // degrees from north
sprintf (my_text, "%7.1f", dTemp2);
_settextposition( iRelY, iRelX);
_outtext( my_text);
break;
}
//Line8- horizontal g's
iRelY = iStartY+(8*VERT_TEXT_STEP);
switch (cmd)
{
case 1:
//123456789012345678901234567890
sprintf (my_text, "hz gs ");
_settextposition(iStartY+(8*VERT_TEXT_STEP), iRelX);
_outtext( my_text);
break;
case 2:
break;
case 3:
// no case 4, intercept
dTemp4 = stDispObj[iObj].dAyaw_r/9.8; // g's
sprintf (my_text, "%7.2f ", dTemp4);
_settextposition(iRelY, iRelX);
_outtext( my_text);
break;
}
//Line9- vertical g's
iRelY = iStartY+(9*VERT_TEXT_STEP);
switch (cmd)
{
case 1:
//123456789012345678901234567890
sprintf (my_text, "vt gs ");
_settextposition( iStartY+(9*VERT_TEXT_STEP), iRelX);
_outtext( my_text);
break;
case 2:
break;
case 3:
// no case 4, intercept
dTemp4 = stDispObj[iObj].dApitch_u/9.8; // g's
sprintf (my_text, "%7.2f ", dTemp4);
_settextposition(iRelY, iRelX);
_outtext( my_text);
break;
}
//Line10 - time to intercept
iRelY = iStartY+(10*VERT_TEXT_STEP);
switch (cmd)
{
case 1:
//123456789012345678901234567890
sprintf (my_text, "time to intercept ");
_settextposition( iRelY, iRelX);
_outtext( my_text);
break;
case 4:
sprintf (my_text, "%7.1f", dSecondsIntercept);
_settextposition(iRelY, iRelX);
_outtext( my_text);
break;
}
//Line11 - missile time
iRelY = iStartY+(11*VERT_TEXT_STEP);
switch (cmd)
{
case 1:
//123456789012345678901234567890
sprintf (my_text, "missile flight time ");
_settextposition( iRelY, iRelX);
_outtext( my_text);
break;
case 4:
sprintf (my_text, "%7.1f", dSecondsMissile );
_settextposition(iRelY, iRelX);
_outtext( my_text);
break;
}
if (cmd == 1) // update status only on end of scan
{
// line 12 miss distance
iRelY = iStartY+(12*VERT_TEXT_STEP);
//123456789012345678901234567890
sprintf (my_text, "missed %6.0f yards ",fMissRange);
_settextposition( iRelY, iCol1);
_outtext( my_text);
// line 13 miss x,y,z
iRelY = iStartY+(13*VERT_TEXT_STEP);
//123456789012345678901234567890
sprintf (my_text, " x,y,z %6.0f%6.0f%6.0f ",
fMiss_x,fMiss_y,fMiss_z);
_settextposition( iRelY, iCol1);
_outtext( my_text);
}
if (cmd == 1) // update status only on end of scan
{
//Line24- Alert Status
iRelY = iStartY+(14*VERT_TEXT_STEP);
//123456789012345678901234567890
// sprintf (my_text, "Alert Status = %s ",
// pszAlertStatus[AlertStatus]);
if (AlertStatus == eASRed)
sprintf (my_text, "Alert Status = Red Status");
else
sprintf (my_text, "Alert Status = not Red Status");
_settextposition( iRelY, iCol1);
_outtext( my_text);
}
if ((cmd == 1) || (cmd == 3))
{
//Line15- Missile Status
iRelY = iStartY+(15*VERT_TEXT_STEP);
//123456789012345678901234567890
sprintf (my_text, "Missile Status = %s ",
pszMissileStatus[MissileStatus]);
_settextposition( iRelY, iCol1);
_outtext( my_text);
}
if (cmd == 1) // update status only on end of scan
{
//Line16- Computer Status
iRelY = iStartY+(16*VERT_TEXT_STEP);
//123456789012345678901234567890
sprintf (my_text, "Comput Status = %s ",
pszComputerStatus[ComputerStatus]);
_settextposition(iRelY, iCol1);
_outtext( my_text);
}
if (cmd == 1) // log entry
{
//Line 17,18 - beep or other message
iRelY = iStartY+(17*VERT_TEXT_STEP);
//123456789012345678901234567890
sprintf (my_text, "-------- last message ------");
_settextposition(iRelY, iCol1);
_outtext(my_text);
iRelY = iStartY+(18*VERT_TEXT_STEP);
//123456789012345678901234567890
sprintf (my_text, " ");
_settextposition( iRelY, iCol1);
_outtext(my_text);
i = strlen(szLogEntry);
if (i > 28)
szLogEntry[28] = 0;
_settextposition(iRelY, iCol1);
_outtext(szLogEntry);
}
if (DEBUG_R)
{
//Line19- debug info
iRelY = iStartY+(19*VERT_TEXT_STEP);
//123456789012345678901234567890
sprintf (my_text, "dGlobal1 = %10.4f ",dGlobal1);
_settextposition( iRelY, iCol1);
_outtext( my_text);
iRelY = iStartY+(20*VERT_TEXT_STEP);
//123456789012345678901234567890
sprintf (my_text, "dGlobal2 = %10.4f ",dGlobal2);
_settextposition(iRelY, iCol1);
_outtext( my_text);
}
return 0;
}
long SetDisplayTo(int ColorNumber)
{
int y ;
_setbkcolor( 0 ); // background
_setcolor(ColorNumber); // line color
for (y = 0; y< 600; y++)
{
_moveto( 0, y);
_lineto( 799, y);
}
return 0 ;
}
//////////////////////////////////////////////////////
long GetAnalog(int id, double * val, double min, double max)
// id is port 0 through 3, val in engineering units
// offset and scaling is in subroutine
// if error, return = -1, val - -9999999.9
{
// start assembly language
unsigned char busy, msb, lsb;
int result, port, count;
if ( (id < 0)
||(id > 3) )
{
*val = -99999999.9;
return (-1); // error in call
}
// verify that busy is not set
_asm
{
mov dx,ADCboard + 1
in al,dx
mov busy,al
}
if ( busy & 128) //
return -1; // error, should not be busy
// outporttb (board+((channel-1)*2);
port = ADCboard + ((id)*2);
_asm
{
mov dx,port // set port address
mov ax,0x0 // set what to output (anything is ok)
out dx,ax //
}
// do
// {
// count++;
// busy = inportb(board+1);
// busy = busy & 128;
// }
// while ((busy >= 128) && (count != 0));
count = busy = 0; // size?, preset to 0
do
{
count++; // increment counter
_asm
{
mov dx,ADCboard + 1 //
in al,dx //
mov busy,al //
}
busy = busy & 128; //
}
while (busy >= 128); //
// if (count == 1)
// return -1; // did not go busy, error
// msb = inportb(board+1);
_asm
{
mov dx,ADCboard + 1;
in al,dx
mov msb,al
}
// lsb = inportb(board);
_asm
{
mov dx,ADCboard;
in al,dx
mov lsb,al
}
msb = msb & 0x7f; // strip busy bit
result = (msb*256)+lsb; // concatonate
// scale counts to engineering units here
*val = min + ((max * result)/4095);
return (0); // and exit
}
//////////////////////////////////////////////////////
long PutAnalog( int id, double * val, double min, double max)
// purpose, output setpoint to x,y plotters and other units
// by RTD convention, DAC units are numbered from 1 through 8
// if illegal id, return = -1
// outputs are clamped to min,max with no warning
// assumes board output set for +- 5 volts
// (matches 0 to +5 input of ADC to some extent)
// (we may wish to use +-10 volts at some future date)
// please note for purests: the voltage range is actually
// counts voltage comments
// ------ ------- --------
// 4095 +4.9976 maximum
// 2048 0.0
// 1024 -2.500
// 0 -5.000 minimum
{
// start assembly language
int w_base, count, msb, lsb;
double w_val;
if ( (id < 0) || (id > 7))
return -1;
else // channel legal, crash on
{ // clamp and scale inputs
w_val = *val;
if (w_val < min)
w_val = min;
else if (w_val > max)
w_val = max;
w_val = w_val * dMeterCalib;
count = (int)( ((w_val - min)*4095.0)/(max - min) );
msb = count >> 8;
lsb = count & 0x00ff;
w_base = DACboard + ((id)*2);
_asm
{
// outporttb (board+((channel-1)*2);
mov dx,w_base // set port address of this channel
mov ax,lsb // set to output lsb
out dx,ax // do it
inc dx // go to msb address
mov ax,msb
out dx,ax // output most significant byte
mov dx,DACboard // now read low channel to update output
in al,dx
}
}
return 0;
}
/////////////////////////////////////////////////////////
long GetDigital (int id)
// returns value of id bit (0 through 7) in low bit of long
// if error,long is set to -1
{
int port;
char lsb;
if (id < 0)
return -1;
else if (id < 8)
{ // use ADC card
port = ADCboard + 11;
_asm
{
mov dx,port // set port address
mov ax,0x92 // set all mode 0, A = input, B = input, C = output
out dx,al //
}
port = ADCboard + 8;
_asm
{
mov dx,port // set port address
in al,dx // input value
mov lsb,al // to memory
}
return ( (lsb >> (id) ) & 0x01);
}
else if (id < 13)
{ // use printer port status lines for input
port = LPT1+2; // printer port command register
_asm
{
mov dx,port // set port address
mov ax,0x00 // disable IRQ
out dx,al //
}
port = LPT1+1; // printer port status register
_asm
{
mov dx,port // set port address
in al,dx // input value
not al
xor al,0x80
mov lsb,al // to memory
}
return ( (lsb >> (id-8+3) ) & 0x01);
// return ( lsb & 0x00FF );
}
else
return -1;
}
////////////////////////////////////////////////////////////////////////
char GetPrinterPort()
// return value in char
{
char val;
int control, status, e_port, portP3;
static int port = 0x0378, cnt=0;
portP3 = port + 3;
control = port + 2;
status = port + 1;
e_port = port | 0x400;
_asm
{
mov dx,portP3
xor al,al
out dx,al // kill possible extended operations
mov dx,e_port
xor al,al
out dx,al // kill possible extended operations
mov dx,control // set for read
mov al,0x20 // sets bidirectional bit to tri-state output
out dx,al // sets bidirectional bit to tri-state output
mov dx,port // set port address
// mov dx,status // set port address
// mov ax,cnt
// out dx,al // sets
in al,dx // input value
mov val,al // to memory
}
cnt++;
return ( val );
}
///////////////////////////////////////////////////////////////////
long PutDigital(int id, char val) // id=dig chan (0-7), val = 0/1
// return 0 if legal, else -1
// proposed method of interfacing to relays operated from -26 volts
// (a voltage limiter is assumed across the coil)
// inverted logical out, + 5 is open, 0 = closed
// logic-
// | +--- gnd of - 24 v power
// | |/
// R1 (2500 ohm) |
// | |/|\ .
// +---------pnp darlington--| \ .
// | |\ --+ .
// R2 (6000 ohm) |
// | (collector to coil shunted by limiter)
// -12v - 24 volts
// (this software in inverting, 0 input causes 1 output)
{
int port;
static int w_val = 0; // history, not sure what is read, preset to 0
if ( (id < 0) || (id > 7) )
return -1; // error
else
{
w_val = w_val & (~(1<< id)); // clear bit in w_val
if ( ! val) // ** inverting output **
w_val = w_val | (1 << id); // assure bit in proper position
// update w_val
port = ADCboard + 11;
_asm
{
mov dx,port // set port address
mov ax,0x92 // set all mode 0, A = input, B = input, C = output
out dx,al //
} // ok, got the mode set,
port = ADCboard + 10;
_asm
{
mov dx,port // set port address
mov ax,w_val // value to output
out dx,al //
}
return 0; // ok
}
}
///////////////////////////////////////////////////////////////////
void MyLine (int x1, int y1, int x2, int y2, short color)
{
int inc_x, inc_y, slope64, temp;
_setcolor(color); // seems to work ok in DOS version
_moveto(x1,y1);
_lineto(x2,y2);
return;
// having trouble with stability of SDK line call - like no more lines
// normalize into draw from low x to hi x
if (x1 > x2)
{ temp = x1; x1 = x2; x2 = temp; // flip ends
temp = y1; y1 = y2; y2 = temp; }
if ( x1 == x2 )
{ // vertical or point
if ( y2 < y1)
{temp = y1; y1 = y2; y2 = temp; } // go in correct sequence
_setcolor (color);
for (inc_y = y1; inc_y <= y2; inc_y++)
_setpixel( x1, inc_y);
}
else if ( y1 == y2 )
{ // horizontal, x1 already < x2
_setcolor (color);
for (inc_x = x1; inc_x <= x2; inc_x++)
_setpixel( inc_x, y1);
}
else
{ // diagonal (all of our lines are short)
slope64 = ((y2-y1)*64)/(x2 - x1);
// if (slope64 <= 64)
{ // low slope, (<= 1) step along x
_setcolor( color);
_setpixel( x1, y1); // 1st to prevent divide by 0
for (inc_x = x1+1, inc_y = y1; inc_x <= x2; inc_x++)
{
temp = 1; // show x not printed
if (slope64 > 0)
{ // positive increments of y
_setcolor (color);
while ( ( (((inc_y - y1)*64) / (inc_x - x1)) < slope64)
&& (inc_y < y2) )
{ inc_y++; _setpixel( inc_x, inc_y); temp = 0;}
}
else
{ // negative increments of y
_setcolor (color);
while ( ( (((inc_y - y1)*64) / (inc_x - x1)) > slope64)
&& (inc_y > y2) )
{ inc_y--; _setpixel( inc_x, inc_y); temp = 0;}
}
if (temp)
{
_setcolor (color);
_setpixel( inc_x, inc_y); // need to set pixel
}
}
_setcolor (color);
_setpixel( x2, y2); // final point
}
// else
{ // hi slope, step along y
}
}
}
long set_video_SVGA256 ()
// set video to SuperVGA 800x600,
// color colors 0,1 = 0 (black)
// color colors 2,33 =31 yellow in decreasing intensity
// if error, exit(0)
{
#define PLENG 256
long int pal[PLENG];
int i;
long int c_red, c_green, c_blue;
/// long int a,b,c;
#define CYC 31
pal[0] = 0; // border color?
pal[1] = 0; // background color (black)
/* make yellow -> black */
c_red = 62; c_green = 62; c_blue = 0;
for ( i=2 ; i < CYC; i++)
{
pal[i] = c_red | (c_green << 8) | (c_blue << 16);
c_red -= 2; c_green -= 2;
}
for ( ; i < 256; i++)
pal[i] = 0;
// for ( i=2 ; i> 8 ) & 0xFFL;
// c = ( pal[i] >> 16) & 0xFFL;
// printf(" %8d, %8lX, %8lX, %8lX, %8lX \n",i,a,b,c, pal[i]);
// }
/* */
if (_setvideomode( _SRES256COLOR ) == 0)
{ printf( "Tins program requires a SVGA card.\n");
exit( 1 );
}
_remapallpalette( &(pal[0]) );
_setvieworg( 0, 0 );
return (0);
}
//________________________________________________________________-
long StartPlaneEast(int iSpeedy, int desig)
// start new airplane from west to east from
// target designate circle if desig != 0
// else
// start from mid west
{
int iObj;
for (iObj=10; iObj < MAX_OBJ; iObj++)
{ // look for unassigned object
if (stDispObj[iObj].iObjType == 0)
{ // found one
InitializeObject(iObj);
if ( rand() % 6)
stDispObj[iObj].iObjType = OBJ_AIRCRAFT;
else
stDispObj[iObj].iObjType = OBJ_AIRCRAFT_F;
if (desig)
{
stDispObj[iObj].dSx = stDispObj[OBJ_TAR_DES].dSx;
stDispObj[iObj].dSy = stDispObj[OBJ_TAR_DES].dSy;
}
else
{
stDispObj[iObj].dSx = -150000.0 + (rand()*15000.0)/RAND_MAX;
stDispObj[iObj].dSy = -30000.0 + (rand()*60000.0)/RAND_MAX;
}
stDispObj[iObj].dSz =
100 + ((rand()*15000.0)/RAND_MAX);//0.3-46k ft
if (iSpeedy)
stDispObj[iObj].dVx = 1.5*348; // mach 1.5
else
stDispObj[iObj].dVx = 0.8*348; // mach 0.8
stDispObj[iObj].dVy = 0000; // 0 km/hr north
stDispObj[iObj].dVz = 00000; // 0 m/s change
stDispObj[iObj].dAyaw_r = 000.0; // 0 m/s/s right
stDispObj[iObj].dApitch_u = 0000; // 0 m/s/s up
sprintf( szLogEntry, "started plane east");
return 0;
} // end of initializing one plane
}
My_Beep (1); // no free objects
sprintf( szLogEntry, "** cant start targ **");
return 0;
}
// ---------------------------------------------------------
long WarGame() // - automatic threat response
/* W = W_ON, w = W_OFF switch on heater
(initialize Getting_Info, Launch_command flags to off)
(called every 100 ms)
if W_ON
if (missile flying),
if (flying over 150 seconds)
command burst
return
if (Launch_Command flag)
if (target tracked)
wait 5 seconds for human to catch up
launch missile, clear Launch_Command flag
(should cause missile flying next 100 ms)
return
else
lost track error handling
else if (not Getting_Info flag)
clear internal tables and controls
set Getting_Info flag
if (target tracked)
save PI time into table
if (any target not in internal table)
designate target (assume instant auto tracking)
return
else (no more targets)
find shortest PI time
select that target
(assumes instant tracked and tracking data)
set Launch_Command flag
clear Getting_Info flag
return
*/
{
static char cGetting_Info = 0, cLaunch_Command=0;
static struct Targ_Info_Struct
{
int iTargID; // object number, preset to < 0 (empty)
int iTargPI_Time; //
eComputerStatus eTargStatus; // computer target status
} Targ_Info[MAX_OBJ];
static int iDesignTarg; // used to retain previoiusly selected targ
static int iNumber_In_Table=0; // used to retain
static double dDecisionTime; // used to delay launch for human
int i, j, k; // temporary variables
// verify that we should be here
if ( ! cWarGame)
{
cGetting_Info = 0;
return 0;
}
if (MissileStatus != eMSPreLaunch)
{ // missile flying, check for too long
if (dSecondsMissile > 150.0)
{
MissileStatus = eMSPreLaunch;
stDispObj[OBJ_MTR].dSx = 0.0;
stDispObj[OBJ_MTR].dSy = 0.0;
stDispObj[OBJ_MTR].dSz = 0.0;
stDispObj[OBJ_MTR].dVx = 0.0;
stDispObj[OBJ_MTR].dVy = 0.0;
stDispObj[OBJ_MTR].dVz = 0.0;
stDispObj[OBJ_MTR].dAyaw_r = 0.0;
stDispObj[OBJ_MTR].dApitch_u = 0.0;
dSecondsMissile = 0.0;
sprintf (szLogEntry, "WG Missile Burst");
}
return 0;
}
// if here, missile is not flying, ie. prelaunch
if (cLaunch_Command)
{
if (iTargetTracked)
{
if (dSecondsSim >= (dDecisionTime + 5.0))
{ // target is tracked, launch request
MissileStatus = eMSBoost;
sprintf (szLogEntry, "WG launch");
}
else
return 0;
}
else
{ // lost track in interval, try again
sprintf (szLogEntry, "WG lost track after launch req");
// ????
}
cLaunch_Command = 0;
cGetting_Info = 0; // get info when missile bursts
return 0; // exit, no more to do for a while
} // end if ( cLaunch_Command
else if ( ! cGetting_Info )
{ // not getting info
for (i = 0; i < MAX_OBJ; i++)
{ // clear internal tables
Targ_Info[i].iTargID = -2;
Targ_Info[i].iTargPI_Time = -2;
Targ_Info[i].eTargStatus = eCSTrackBadSolution;
}
iDesignTarg = -1; // used to retain previously selected targ
iNumber_In_Table=0; // used to retain
cGetting_Info = 1; // start gathering info
iTargetTracked = 0;
}
if (iTargetTracked > 0)
{
i = iNumber_In_Table;
Targ_Info[i].iTargID = iTargetTracked;
Targ_Info[i].iTargPI_Time = (int)dSecondsIntercept;
Targ_Info[i].eTargStatus = ComputerStatus;
iNumber_In_Table++; // increment # values in table
}
// track each target to get predicted intercept time
for (i=0; i < MAX_OBJ; i++)
{
if (stDispObj[i].iObjType == OBJ_AIRCRAFT)
{ // a target, is it in the table
for (j=0; j< iNumber_In_Table; j++)
{ // search table for match
if ( i == Targ_Info[j].iTargID)
goto next_object;
} // end inner loop, continue
// this AIRCRAFT is not in list, designate it
iTargetTracked = i;
return 0; // and exit, give TTR and Computer 100 ms
} // end if (AIRCRAFT
next_object:
;
} // end search table
// info from all targets collected, now select best target
// this system looks for greatest threat by
// picking target with smallest PI time, but
// - fast, incoming targets get top priority
// - a target that is leaving gets a low priority
j = 1000; // bad intercept time
k = -1; // invalid table index
for (i=0; i < iNumber_In_Table; i++)
{ // ignore targets in dead zone - unsightly, untidy
if ( ( Targ_Info[i].iTargPI_Time < j)
&&( ( Targ_Info[i].eTargStatus = eCSInRange)
||( Targ_Info[i].eTargStatus = eCSOutOfRange ) ) )
{
j = Targ_Info[i].iTargPI_Time; // looks better
k = i;
}
} // end for (i=0
if ( k >= 0)
{ // a valid index found
iTargetTracked = Targ_Info[k].iTargID; // designate best
stDispObj[OBJ_TAR_DES].dSx = stDispObj[iTargetTracked].dSx ;
stDispObj[OBJ_TAR_DES].dSy = stDispObj[iTargetTracked].dSy ;
if ( Targ_Info[k].eTargStatus = eCSInRange )
{
cLaunch_Command = 1; // a good chance, launch
dDecisionTime = dSecondsSim;
} // end if ( ... InRange)
} // end if ( k >= 0)
cGetting_Info = 0; // clear table
return 0;
}