Perl is used quite a bit at NORAD, which gave up ADA long ago when it became evident that ADA programmers weren't the sort of people you want creating missile guidance systems. (Same goes for C++.) At first, my supervisor balked at Perl, as he does at anything new, but he softened to the idea when I told him that it had been designed for exactly this purpose and that PERL itself stood for "Precision Entry and Reentry Launchings," a lie that would later be repeated over and over again at my court-martial.
Regrettably, my ex-employers don't want me talking about my work: software systems for missile flight calibrations. But a few stories should be okay, since I think they'll tell you more about Perl than about national security. I don't mean to alarm anyone by what follows, but you should know just how close we came to a nuclear armageddon because of my misunderstanding of basic Perl concepts. Sorry.
In March 1996, I was told to write a program to calculate the effect on thrust from a proposed modification to the alloy composition of Nike missiles. The first step was parsing a file full of rivet locations that looked like this:
RIVET294 3/8 004 14.25 14.375 DORS18-LEFT TH15/16 (TOLERANCE .0002) RIVET295 3/8 004 14.625 14.75 DORS18-LEFT TH15/16 (TOLERANCE .0002)
Line 1 tells us that rivet 294 is three-eighths of an inch long and can be found 14.25 inches above the lower left corner of plate 004. The second line has more information identifying the location of the rivet, and the third line contains the tolerance (.0002 inch) allowed in the placement and rivet size. We machine all our own rivets, of course: even if Ace Hardware could supply rivets meeting our specifications, their shareholders will rest easier knowing that Ace's off-the-shelf $4.99 Sooper Rivet 12-Pak won't be held responsible for starting World War III. (Just imagine the lawsuits.) Anyway, extracting these fields is easy - simply split() on spaces:
while (<>) { @fields = split(/\s+/, $_) if /^RIVET/;
Fortunately, Perl's support for regular expressions makes the tolerance easy to parse too:
$tolerance = /TOLERANCE\s+(\.\d+)/ if /TOLERANCE/; # rest of code will be declassified in 25 years }
Oops. When a match is evaluated in a scalar context, it yields 1 for success and UNDEF for failure. My program unwittingly set every $tolerance to 1 because I forgot the parentheses:
($tolerance) = /TOLERANCE\s+(\.\d+)/ if /TOLERANCE/;
On any software project having to do with nukes, a team of fierce Quality Control Professionals apply their canon of software verification techniques to detect possible bugs. Luckily - and surely this will restore your faith in the military - they discovered the error before the milling machine produced rivets destined to fall out in the frigid upper atmosphere.
Later investigation revealed that I had thirteen of these errors peppered throughout my source tree. All were fixed quickly and there was never any serious threat.
I wasn't so lucky with the missile targeting software. If you remember your high-school physics, you know that an object thrown into the air will follow a parabola. Guided missiles like the Nike can't simply be lobbed into the air like footballs, much as that would please certain brigadier generals who will remain unnamed. Guided missiles need to generate their own thrust.
The flight path of ICBMs is high enough that the gravitational pull of the Earth is measurably less, but not enough to make a difference. Far worse are the battery of climatic hazards, each of which cause a missile to deviate from its intended violence vector. Wind shear, precipitation, the contraction of metallic components due to frigid temperatures, the accumulation of grit and particulate matter from pollutants and debris - all pose little unintended threats to our Big Intended Threat.
The unpredictability of each of these situations means that missiles have to be reactive. They have to make continuous and minute adjustments to their flight, and the calculations have to be performed with speed and precision. An error of as little as one-hundredth of a percent is the difference between levelling a city and a nearby farm; an error of one-tenth of a percent is the difference between Baghdad and Haifa; an error of ten percent is the difference between the North Pole and Boston.
Further complicating the motion dynamics is the fact that the missile loses mass as it flies. As liquid propellant undergoes combustion and is expelled from the rear of the missile, the rocket becomes lighter and easier to propel.
The thrust of a rocket is the initial speed, , multiplied by the mass of gas flowing out of the rocket during a time dt. ( If you don't know calculus, here's how it works: dM doesn't mean d times M, but rather a teensy amount of M (mass). Likewise, dt is a really short time. dM/dt is thus a description of how mass changes as time passes. Now, if you graph mass versus time it might be a funny curve, or a straight line, or a zigzag, depending on whether you're talking about rockets or radium or a bulimic. dM/dt is a graph of that graph's slopes. Computing dM/dt is called taking a derivative and if you can do that you can say you know differential calculus. If you can do the opposite - calculate the relationship between M and t given dM/dt - then you can compute integrals, which entitles you to use the sign in equations, academic papers, and bawdy limericks.)
The mass of propellant flowing out, dM, is the propellant density () times the cross-sectional area of the exhaust orifice (A0) times the speed (v0) times the change in time dt. The dt's cancel, yielding our equation of thrust:
which can be expressed in Perl as:
$thrust = $rho * $A0 * ($v0 ** 2);
When the orifice is very small, the speed at which the propellant is ejected can be approximated as
The Nike, however, needs to eject a lot of propellant very quickly. Using a small orifice would increase the pressure inside the fuel tank to unacceptable levels, so the orifice is widened to the point where the above approximation doesn't quite hold. Instead of the square root (that is, raising to the power of 0.5) we need to raise it to the power of 0.52 or 0.53. Like any good programmer, I recognized that this number might be changed as the approximation is refined, so I hid it behind a constant. Most people don't realize that Perl lets you create constants like so:
*expo = \0.52;
This defines $expo to be 0.52, and prevents any later statement from changing it. $expo++ results in a fatal error:
Modification of a read-only value attempted at ./targetting line 1260.
Anyway, here's how I used $expo in my Perl program:
$thrust = $rho * $A0 * (2 * ($p-$p0) / $rho) ** $expo ** 2);
Do you see my mistake? If so, there's a job for you in avionics quality assurance, assuming we still have computers after screwups like these precipitate the post-Holocaust age.
Here's the problem: The ** operator is right-associative. (Go look it up in the perlop documentation if you don't believe me.) What this means is that
$a ** $b ** $c
is not equivalent to
($a ** $b) ** $c
as I had assumed, but
$a ** ($b ** $c)
That's a big problem when your $a, or 2 * ($p - $p0) / $rho in my code, is about two million, your $b is 0.52, and your $c is 2. Perl ends up disagreeing with physics by a factor of more than four, and in such battles physics wins. This error went undetected as my Perl code was translated into C using the Perl Compiler, from C into machine code, and then was burned into missile EPROMs. Six weeks later, the missiles were interred in their silos with inertial guidance systems based on my code. If fired, they would have missed their destination by thousands of miles.
Neither -w nor use strict catches this error. I learned of the mistake only after beginning a new project involving scanning large databases of text for phrases pertaining to national security. My commanding officer said that this was for organizing and categorizing the forty gigabytes of text data NORAD wants put on its intranet, and I believed him, until I found out that he was making monthly trips to Cisco headquarters in San Jose. Turns out that my scanning software has secretly been installed on a large percentage of Cisco routers, so that the DoD could keep tabs on potential espionage suspects.
Eventually, after making sure I wasn't secretly confiding in those "hippie civil libertarians," my commander filled me in. I don't think it was because the higher-ups trusted me, but simply because the droids at the Pentagon were sick of my program alerting them to increasingly popular faux mail fields such as the last line of this header:
From: liberal@bleeding-heart.org To: commie@progressive.edu Subject: The Man keeps putting us down X-NSA: Ortega SDI genetic Khaddafi bullion Cocaine munitions
So all of you radicals who think you're so rebellious and antiestablishmentarian using those headers? Who think you're thwarting Big Brother's sinister attempt to police the millions of messages zipping from liberal to liberal every day? I have three words for you (well, two words and a regexp):
next if /^X-NSA/;
It was during this project that I discovered my mistake with the exponentiation operator. In particular, I was developing heuristics to handle misspellings a little more robustly. You'd be amazed how many people misspell "nuclear", not to mention "klystron". I was using the String::Approx module, which for a given string returns a list of similar strings which you can then feed into a regexp. The longer the string, the greater the number of possible near matches. Luckily, the relation between string length and quantity of near matches is exponential. Otherwise the bug in the targeting software might have remained undetected to this day. Sleep tight.
Sometimes I think working for NORAD is like being a systems administrator. As long as you do your job well, everyone else ignores you with impunity. It's only when crises occur that people notice you, and not many pleasantries get exchanged when that happens.
However, if you ever get the chance to observe see a full-blown military crisis firsthand, I recommend the experience. It's kind of like when the fire alarm goes off in high school. You're pretty sure it's a false alarm. You act like it's a big joke. But there's a little jittery part of you worrying that you will soon be engulfed in a huge scholastic inferno and miss the prom.
When I discovered my error, I couldn't just say "Whoops, guess I really borked that one. I'll just turn off all the nuclear missiles while I fix my code - ah, that's the switch over there on the wall, next to the lights." Nope, deactivating our defense weaponry is a Big Deal, since you don't want spies or moles or lovelorn romantics getting a little poky and paralyzing our nuclear stockpile. But at the same time, the logistical machinery for resolving these crises needs to be fast. Our nuclear missiles were effectively offline, and if a nuclear power suddenly decided to lob a few enriched uranium surprises our way, we'd be impotent.
I told my commander, and he told his colonel, and he barked at one of his henchmen who tapped something into some computer that I'm not even allowed to see. A SPAM was called. SPAM stands for "Standby Potential Armageddon Meeting", presided over by the NORAD shift commander. With every SPAM comes whooping sirens, flashing epilepsy-inducing red lights, and phone calls direct to the vacation cabins of four-star generals. Every SPAM triggers an automatic escalation to DEFCON 3, which means a phone call direct to the President's Chief of Staff. (I e-mailed an apology to his whitehouse.gov address, but he never replied.)
The shift commander, bless his disciplined heart, understood that there was a problem with "EPROMs" and began the discussion asking where we could procure new EPROMs. My captain explained that it wasn't that simple, that you don't just go down to the NORAD general store to buy new EPROMs from the vending machine. He explained that you need to create a program and burn it into a new EPROM, and then he patiently explained that "burn" here had nothing to do with napalm.
He went on to explain that there might be other instances of the bug, since it was due to my misunderstanding of Perl and not a mere typographical error. The question arose of how we could detect and correct all of those bugs immediately. The shift commander went around the table, asking each of us for our recommendation. One officer suggested that we reuse old EPROMs that weren't tainted by the scourge that is Perl; my captain disagreed, saying that the old targetting system was too inaccurate, that we had no choice but to fix our current targetting software by manually checking every one of the thirty-eight programs I had written. I meekly suggested that the task of finding and fixing the bugs could be automated by a Perl script, and it was then that they asked me to leave the room.
_ _END_ _