Grand Unified FTL 4: Jump

Posted: 2024-07-28
Last Modified: 2024-11-20
Word Count: 1952
Tags: lua-code space rpg

Table of Contents

Part of the Grand Unified FTL series.

WARNING: this article may contain numbers.

Previously …

We presented hyperspace, a common FTL trope in which ships exceed the speed of light by opening a portal to a parallel space where the distances between stars are much shorter.

Next we examine a related trope: jumping between stars without crossing the space between.

How Jumps Work

The crew powers up the FTL Engine and they’re suddenly … sumeplace else.

Really, that’s how it works. There’s a lot of prepration to make sure the ship ends up in the right place, or even anyplace at all. But it’s essentially magical teleportation across parsecs.

In Traveller’s classic formulation, the “Jump Drive” has a few restrictions:

Mechanics aside, from a game play standpoint this is nearly the same as a hyperspace journey; the “jump” just avoids tedious and skippable time during the hyperspace transit. On the other hand, journeys with a Jump drive occur not as one continuous path from origin to destination, but a series of short hops through realspace with plenty of time between.

Questions About Jumps

How do ships Jump?

The crew does a bunch of calculations to effect a jump to the right star system. What those calculations are aren’t specfied.

What determines how far a ship can Jump?

In Traveller each Jump Drive has a Jump Rating, which is the maximum number of parsecs it can jump. One can imagine more complicated schemes, e.g. something similar to the “gravitational density” limits I placed on hyperspace, but for simplicity – and to make Jump more distinct – I’ll keep the absolute limit.

How long must the crew wait between Jumps, and why?

In Traveller the crew must perform “maintenance operations” after every Jump (source). In Faster Than Light: Nomad, the FTL Engine or the capacitors that feed it must literally recharge between transits.

Whatever the reason, jump drives have a “cooldown” period. This distinguishes Jump travel from hyperspace: the need to move in short hops, visiting stars in between, rather than one long ride through hyperspace.

What does the crew experience?

In classic science fiction, the Jump drive is a literal discontinuity; the crew experiences nothing. In one Asimov story about the first jump, the crew experienced hallucinations. In other stories the crew and passengers have to be sedated or put into cryo-stasis to avoid being conscious during the Jump. But the simplest answer is “nothing”.

How much time passes between a ship disappearing and reappearing?

One would think it would be no time at all, but some science fiction authors consider this too simple. In Traveller it’s 168 hours give or take, i.e. a week of time (source). One could make it shorter.

Can the Jump fail? What happens?

Jumps can fail, if the coordinates are wrong or inconsistent. Perhaps the ship goes nowhere at all. Perhaps it ends up in the wrong star system. Or perhaps it ends up as atoms smeared across space-time.

Jump Phases

Each jump involves three phases: preparation, the jump, and cooldown.

Preparation

In Traveller a ship cannot jump unless it is sufficiently far from a large mass like a planet. In Traveller this is 100 planetary diameters (source).

To not only jump but re-emerge at the right destination, the pilot or astrogator must make some detailed calculations using the ship’s computer. Ideally the astrogator would take their time, but if the ship is under fire the astrogator may have to rush things a bit.

JUMP!

And then the ship jumps … and hopefully lands where the crew intended.

To make things interesting, while the crew perceives no time has passed, in realspace a number of hours or days may have passed between the ship exiting realspace and re-entering it. In Traveller this always takes 168 hours (1 week) (source). I’ve shortened the time to be proportional to the length of a jump, given in the table below.

Jump Rating Parsecs LY Ext. Time
1 1.0 3.26 19.2 hr
2 2.0 6.52 1.6 d
3 3.0 9.78 2.4 d
4 4.0 13.04 3.2 d
5 5.0 16.30 4.0 d
6 6.0 19.56 4.8 d

Cooldown

In Traveller the cooldown time is always 16 hours, mostly for recalibration and tests. The crew can rush things, but there’s a greater chance of a mis-jump, and even then they can only shorten the time to about an hour (source).

I’ve re-imagined the Jump cooldown to be absolute; the drive won’t work until it literally cools down for a period of time. Like external Jump time above, I’ve made each Jump Drive’s cooldown time proportional to the maximum Jump divided by the actual Jump. At maximum Jump, cooldown always takes about a day.

Parsecs Jump 1 Jump 2 Jump 3 Jump 4 Jump 5 Jump 6
1.0 1.0 d 12.0 hr 8.0 hr 6.0 hr 4.8 hr 4.0 hr
2.0 1.0 d 16.0 hr 12.0 hr 9.6 hr 8.0 hr
3.0 1.0 d 18.0 hr 14.4 hr 12.0 hr
4.0 1.0 d 19.2 hr 16.0 hr
5.0 1.0 d 20.0 hr
6.0 1.0 d

Jump Experiences

In my version of Jump, the crew just blinks and they’re parsecs away. One can posit other experiences:

Jump Accidents

Sometimes the calculations are wrong, or the Jump Drive malfunctions. Traveller introduced the idea of “misjumps”. In Faster Than Light: Nomad things that can happen during misjump include:

Jump Variations

Jump Paths

Like hyperspace paths above, jump paths ignore the usual distance and Jump Drive Rating restrictions. Situate yourself in the right volume of space, calculate a Jump (maybe with extra parameters), pull the lever, and arrive a dozen or more parsecs away at your destination.

Known Jump Paths tend to attract commercial and military interests who want to either exploit easy access to a star system far away or prevent others from doing so. The volumes of space tend to be too large to control effectively, but that doesn’t mean people (and things) haven’t tried.

Jump Points

Ships can’t jump from any point in space to any other point in space. Perhaps the “jump points” are near each star, like Diaspora or Coriolis. Or perhaps only a specific volume of space contains the portal to another star system.

On the plus side the jump point leads directly to its destination, no matter what Jump Rating one’s ship has, much like Jump Paths above. On the minus side known jump points become valuable resources that governments want to control, commercial enterprises want to profit from, and hostile space navies will likely blockade.

Light Speed Jump

At the opposite end of the spectrum, the speed of light could limit even Jumps. The ship and crew would experience no passage of time, but when they popped back into realspace every LY they traveled translates to one year of delay. The “quantum jump” from Neptune’s Brood seems to fit this pattern, and the Elders of my Dominion of Man setting probably use something like this.

Jumps are still valuable: the ship moves light-years while consuming finite amounts of energy, and the crew does not age a second. Returning home, however, means that years, decades, or centuries have passed while the main cast remained suspended in timeless stasis.

Next

We examine standing or temporary portals between stars.


Appendix B: Jump Travel Tables

This Lua program creates the jump tables above.

#!/usr/bin/env lua

--[[
Calculate distances and travel times based on a fictional
but well defined "jump rating", inspired by _Traveller_.
]]

-- The number of light years in a parsec
local LY_PER_PARSEC <const> = 3.26

-- The external time to jump 1 parsec in seconds
local JUMP_TIME_PER_PARSEC <const> = 7 * 23 * 42 * math.pi * LY_PER_PARSEC

-- The cooldown period for a maximum jump in seconds
local COOLDOWN <const> = 60 * 60 * 24

-- Derive the maximum distance in parsecs for jump factor 'jf'
local function jump(jf)
    return jf
end

-- Derive the jump factor required to move `d` parsecs
local function jump_for_distance(d)
    return math.ceil(d)
end

-- Derive the externally observed time to jump `d` parsecs
local function jump_time(d)
    return d * JUMP_TIME_PER_PARSEC
end

-- Calculate the cooldown in seconds after travelling `d` parsecs
-- using a Jump `jf` drive.
local function jump_cooldown(d, jf)
    local jd = jump_for_distance(d)
    return COOLDOWN * jd/jf
end

--[[
DISPLAY ROUTINES BEYOND THIS POINT
]]

-- Seconds in a year (average)
local S_IN_Y <const> = 60*60*24*365.25

-- The shortest interval possible in physics
local PLANCK_TIME = 5.39e-44

-- Seconds per time unit
local S_PER_UNIT <const> = {
   { 60*60*24,         "d"   }, -- seconds per day
   { 60*60,            "hr"  }, -- seconds per hour
   { 60,               "min" }, -- seconds per day
   { 1,                "s"   }, -- seconds per second (!)
}

-- Convert `s` seconds into the largest sensible time unit
local function largest_unit(s)
    local u, ut
    for i, unit in ipairs(S_PER_UNIT) do
        ut, u = unit[1], unit[2]
        if ut <= s then
            break;
        end
    end
    return string.format("%4.1f %-3s", s/ut, u, s)
end

local function print_md_table_line(head)
    local linebuf = {}
    for i, t in ipairs(head) do
        if #t == 0 then
            table.insert(linebuf, "")
        elseif #t <= 3 then
            table.insert(linebuf, ":-:")
        else
            table.insert(linebuf, ':' .. string.rep('-', #t-2) .. ':')
        end
    end
    print(table.concat(linebuf, '|'))
end

local function print_cooldown_header(max)
    local headbuf = { "", " Parsecs " }
    for i = 1, max do
        str = string.format(" Jump %d ", i)
        table.insert(headbuf, str)
    end
    table.insert(headbuf, "")
    print(table.concat(headbuf, '|'))
    print_md_table_line(headbuf)
end

local function print_cooldown_row(d, max)
    local rowbuf = {
        string.format("%3.1f", d),
    }
    local min = jump_for_distance(d)
    for jf = 1, min-1 do
        table.insert(rowbuf, string.rep(" ", 8))
    end
    for jf = min, max do
        local cool = jump_cooldown(d, jf)
        table.insert(rowbuf, largest_unit(cool))
    end
    print(table.concat(rowbuf, " | "))
end

local function print_cooldown(max)
    print("### Cooldown Times ###")
    print_cooldown_header(max)

    for d = jump(1), jump(max)  do
        print_cooldown_row(d, max)
    end
end

local function print_jump_table(max)
    local head = { "",
        " Jump Rating ",
        " Parsecs ",
        " LY ",
        " Ext. Time ",
    "" }
    print(table.concat(head, '|'))
    print_md_table_line(head)

    for jf = 1, max do
        local d = jump(jf)
        print(string.format("| %d | %3.1f | %5.2f | %8s |",
                            jf,
                            d,
                            d * LY_PER_PARSEC,
                            largest_unit(jump_time(d))));
    end
end

local max = 6

print_jump_table(max)
print_cooldown(max)