Skip to content

Commit

Permalink
script-compile: first stab at transcript compilation and JSON output.
Browse files Browse the repository at this point in the history
  • Loading branch information
issa-tseng committed May 1, 2017
1 parent 0c7865b commit ecdfe6d
Show file tree
Hide file tree
Showing 6 changed files with 173 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.DS_Store
*.swp
*.swo
node_modules/
52 changes: 52 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
Apollo 13 real-time
===================

This project consists of two efforts:

* One is a **full digital transcription** of the Apollo 13 Flight Director's Loop, generously provided to the public by NASA through the efforts of [John Stoll](https://archive.org/details/Apollo13Audio). The full transcript may be found under the `script/` directory. Timing is based on [corrected playback audio](https://www.youtube.com/watch?v=KWfnY9cRXO4) done by [ulysses777x](https://www.youtube.com/user/ulysses777x) on YouTube.
* The second is a web player for this audio which plays the audio side-by-side with the transcript, with as much additional interactive information as we can provide to help contextualize the loop chatter for curious folk who are unfamiliar with Apollo systems and terminology.

These six hours are a priceless artifact, shedding light on the strength and style of leadership of NASA's flight directors as well as the resourcefulness, knowledge, and grace under pressure of the astronauts and flight controllers alike—but also on the moment-to-moment realities of spaceflight. By listening to this audio, you begin to understand what life was like for the crew in space and the controllers on the ground, as they execute burns and solve problems, as well as the mechanics of spaceflight in the Apollo era.

Transcription
-------------

While there is an [official transcription](https://www.jsc.nasa.gov/history/mission_trans/apollo13.htm) of the air-ground loop provided by NASA, one does not exist for the Flight Director's Loop. In addition, the official transcript doesn't always line up with the audio timing, and does not provide timestamps for the _end_ of each transmission.

The two loops are transcribed independently based on the timing-corrected version of John Stoll's upload (see above). Each is in its own text file within the `/script` directory. The transcripts follow a common format:

[55 05 00 - 55 05 12] FLIGHT
This is a 12-second line from the Flight Director that began at 55 hours and 5 minutes flat.

[55 05 14] EECOM
When the end timestamp is omitted, the line is a short utterance of around a second or less.
> This is a commentary note on the line as a whole.

[55 05 21 -] FLIGHT
When there is a dash but no end time -

[55 05 25] GNC
{Negative,} FLIGHT.
1> A numbered commentary note relates to the {bracketed text} in the line. Multiple brackets and numbers may be used.

[- 55 05 30] FLIGHT
\- a single line was interrupted briefly by another line in a way that was hard to separate.

This raw transcript format integrates all the interactive transcript display in the web player, such that anybody can use the GitHub text editor to recommend changes or additions.

Compilation scripts in the `/script` directory compile this raw format into two separate artifacts: one is a JSON-formatted file for the benefit of the web player and other programmatic purposes, and the second is a raw transcript with all annotation removed and formatted for maximum plain-text readability, for those who wish to just have the transcribed text to read.

Web player
----------

To be developed and settled down: content to follow here.

License
=======

As the raw audio is provided by NASA as a public service, and the transcription and development work herein is done under the spirit of public service, the entire contents of this repository are provided under the most generous license available: either Public Domain, or [CC Zero](https://creativecommons.org/publicdomain/zero/1.0/), depending on the licensee's legal preferences.

This includes all contributed work: contributors, please be aware that you agree to this by contributing to this repository. All contributors will be recognized in the web player.

Attribution is, of course, appreciated—but not required.

16 changes: 16 additions & 0 deletions script/compile/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
default: all

SRC = $(shell find .. -name "*.txt" -type f -maxdepth 1 | sort)
JSON = $(SRC:../%.txt=../%.json)

../%.json: ../%.txt node_modules
node node_modules/livescript/bin/lsc txt-to-json.ls $< > $@

node_modules:
npm install

all: node_modules $(JSON)

clean:
rm -f ../*.json

7 changes: 7 additions & 0 deletions script/compile/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "apollo13-realtime",
"version": "0.1.0",
"private": true,
"devDependencies": { "livescript": "~1.5" }
}

90 changes: 90 additions & 0 deletions script/compile/parse-txt.ls
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
{ read-file-sync } = require(\fs)

hms = (hh, mm, ss) -> parse-int(hh) * 60 * 60 + parse-int(mm) * 60 + parse-int(ss)

attach-line-annotation = (line, annotation) -> line.[]annotations.push(annotation)
attach-token-annotation = (line, idx, annotation) -> line.{}tokens[idx] = annotation

parse-file = (path) ->
result = []

# we read line-by-line as a state machine so that we can track source line numbers.
incr = 0
by-id = {}
by-last-source = {}
current-source = null
for line, nr in read-file-sync(path, \utf8).split('\n')
if (parse = /^\[(\d\d) (\d\d) (\d\d) - (\d\d) (\d\d) (\d\d)\] (.+)$/.exec(line))?
# a full line. create.
[ _, start-hh, start-mm, start-ss, end-hh, end-mm, end-ss, source ] = parse
start = hms(start-hh, start-mm, start-ss)
end = hms(end-hh, end-mm, end-ss)
id = incr++

throw new Error("Message goes back in time (#{nr + 1} -> #line)") if start > end

current-source = source
line = by-last-source[current-source] = by-id[id] = { id, start, end, source, line: nr + 1 }
result.push(line)

else if (parse = /^\[(\d\d) (\d\d) (\d\d)\] (.+)$/.exec(line))?
# a full burst line. create.
[ _, hh, mm, ss, source ] = parse
start = hms(hh, mm, ss)
end = start
id = incr++

current-source = source
line = by-last-source[current-source] = by-id[id] = { id, start, end, source, line: nr + 1 }
result.push(line)

else if (parse = /^\[(\d\d) (\d\d) (\d\d) -\] (.+)$/.exec(line))?
# a starting partial fragment. create.
[ _, start-hh, start-mm, start-ss, source ] = parse
start = hms(start-hh, start-mm, start-ss)
id = incr++

current-source = source
line = by-last-source[current-source] = by-id[id] = { id, start, source, line: nr + 1 }
result.push(line)

else if (parse = /^\[ - \] (.+)$/.exec(line))?
# a middle partial fragment. track and create.
[ _, source ] = parse
id = by-last-source[source].id

current-source = source
line = by-last-source[current-source] = { id, source, line: nr + 1 }
result.push(line)

else if (parse = /^\[- (\d\d) (\d\d) (\d\d)\] (.+)$/.exec(line))?
# an ending partial fragment. track and create, and fill in an endstamp on the originating line.
[ _, end-hh, end-mm, end-ss, source ] = parse
id = by-last-source[source].id
by-id[id].end = hms(end-hh, end-mm, end-ss)

throw new Error("Message goes back in time (#{nr + 1} -> #line) (start: #{by-id[id].start}; end: #{by-id[id].end})") if by-id[id].start > by-id[id].end

current-source = source
line = by-last-source[current-source] = by-id[id] = { id, source, line: nr + 1 }
result.push(line)

else if (parse = /^> (.+)$/.exec(line))?
# a full-line annotation. attach.
[ _, message ] = parse
attach-line-annotation(by-last-source[current-source], message)

else if (parse = /^(\d+)> (.+)$/.exec(line))?
# a token annotation. attach.
[ _, idx, message ] = parse
attach-token-annotation(by-last-source[current-source], parse-int(idx), message)

else if line.length > 0
# a message line. attach.
throw new Error("Trying to attach a message where one already exists (#{nr + 1} -> #line) (extant: #{by-last-source[current-source].message})") if by-last-source[current-source].message?
by-last-source[current-source].message = line

result

module.exports = { parse-file }

4 changes: 4 additions & 0 deletions script/compile/txt-to-json.ls
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{ parse-file } = require('./parse-txt')

process.argv.2 |> parse-file |> JSON.stringify |> process.stdout.write

0 comments on commit ecdfe6d

Please sign in to comment.