Thanks to the fancy new trigonometry functions in NTSL, I could finally port the calculator to NTSL, something I've been dying to do for a while. This might be of rather limited usability, since Telescience might be removed and it's a lot easier to just use the standalone calculator, but at least it's not an external program anymore!

It's pretty much a direct port of the original tool, and it's even possible to transfer the offsets from one to the other. However, thanks to the execution limit of NTSL, it may have to spread computation across several runs of the script.

The NTSL Telescience Calculator™ has the exact same features as the original tool, with the bonus of a "help" command, since there is no fancy GUI. Now you can telescience everything without any external tools!

Runs on frequency 144.9 by default.

The script (also on pastebin):

- Code: Select all
`// NTSL Telescience Calculator!`

//

// Does all the Telescience work for you,

// without any external program accusations.

// Bruteforces through everything, so is

// guaranteed to be 100% accurate.

//

// Change $opfreq to whatever frequency you

// want to use it on.

//

// Commands:

//

// "reset"

//

// Resets the calibration data for

// this run. This is called the first time

// the program is run.

//

// "help"

//

// Displays (more concise) usage information

// on these commands.

//

// "calibrate <bearing> <elevation> <power>

// <result_x> <result_y> <source_x> <source_y"

//

// Calibrates on the given data. Will report

// on the currently achievable accuracy when

// finished. Result X and Y are where the GPS

// device ended up, and Source X and Y are the

// coordinates of the Telepad.

//

// Because NTSL only accepts a certain amount

// of function calls per run, calibrating will

// require several runs at the start. Just trigger

// it by saying anything to have it continue.

//

// "target <dest_x> <dest_y> <power> <source_x>

// <source_y>"

//

// Computes the Telescience settings needed

// to reach (dest_x, dest_y) with the given

// power, and the given telepad location.

// Will report if unreachable.

//

// "apply <power_offset> <bearing_offset>"

//

// Takes the given offsets as truth and applies

// them. Interoperability!

//

// "get"

//

// Gets the determined offsets. Only works if

// it's calibrated properly!

//

// Be sure to use appropiate precision! This

// means limiting the Elevation to one digit

// after the period, and Bearing to two digits

// after the period.

//

// Made by Braincake, pretty much a direct

// port of the existing C# calculator.

$opfreq = 1449;

// Don't change this unless they change the NTSL

// function call restrictions

$max_calcs = 30;

// Degrees to radians

def toRadian($angle) {

return $angle * (PI/180);

}

// Radians to degrees

def toDegree($angle) {

return $angle * (180/PI);

}

// Tangent function, in radians

def tan($angle) {

return sin($angle) / cos($angle);

}

// Inverse tangent function, in radians

def atan($angle) {

return toRadian(asin($angle / sqrt($angle^2 + 1)));

}

// Atan2

def atan2($y, $x) {

if ($x > 0) {

return atan($y / $x);

}

if ($y >= 0 && $x < 0) {

return atan($y / $x) + PI;

}

if ($y < 0 && $x < 0) {

return atan($y / $x) - PI;

}

if ($y > 0 && $x == 0) {

return PI/2;

}

if ($y < 0 && $x == 0) {

return 0-PI/2;

}

}

// Modulus function, $a % $b

def mod($a, $b) {

return $a - $b * floor($a / $b);

}

// BYOND degree to scientific degree

def toScientific($angle) {

return mod(360 - $angle + 90, 360);

}

// Scientific degree to BYOND degree

def fromScientific($angle) {

return mod(360 - $angle + 90, 360);

}

// Sends a message

def send($message) {

broadcast($message, $opfreq, "Telescience", "Calculator");

}

// Clear and create the initial offsets

def reset() {

// Boundaries

$pow_min = -4;

$pow_max = 0;

$ha_min = -10;

$ha_max = 10;

// Keep track

$pow_offsets = vector();

$ha_offsets = vector();

$pow = $pow_min;

// Fill them

while ($pow <= $pow_max) {

$ha = $ha_min;

while ($ha <= $ha_max) {

push_back($pow_offsets, $pow);

push_back($ha_offsets, $ha);

$ha += 1;

}

$pow += 1;

}

// Store them, overwrite existing

mem("pow_offsets", $pow_offsets);

mem("ha_offsets", $ha_offsets);

mem("total_offsets", 105);

// Done, report

send("Offsets initialized!");

}

// Apply specified calibration settings

def calibrate($bearing, $elevation, $power, $result_x, $result_y, $source_x, $source_y) {

// Retrieve remaining offsets

$old_pow_offsets = mem("pow_offsets");

$old_ha_offsets = mem("ha_offsets");

$old_total_offsets = mem("total_offsets");

// Keep track of the new stuff

$new_pow_offsets = vector();

$new_ha_offsets = vector();

$new_total_offsets = 0;

$index = 1;

$count = 1;

// Do we need to continue a computation?

if (mem("continue")) {

$new_pow_offsets = mem("temp_pow_offsets");

$new_ha_offsets = mem("temp_ha_offsets");

$new_total_offsets = mem("temp_total_offsets");

$index = mem("temp_index");

}

// Bruteforce through them

while ($index <= $old_total_offsets) {

// Are we at the quota yet?

if ($count > $max_calcs) {

// Store the temporary information, then break

mem("temp_pow_offsets", $new_pow_offsets);

mem("temp_ha_offsets", $new_ha_offsets);

mem("temp_total_offsets", $new_total_offsets);

mem("temp_index", $index);

mem("continue", 1);

break;

}

// Get some information

$current_pow_offset = at($old_pow_offsets, $index);

$current_ha_offset = at($old_ha_offsets, $index);

// Assume that this offset is true, then run the numbers

// Get the actual bearing

$actual_bearing = mod($bearing + $current_ha_offset, 360);

// Convert it to scientific radians

$actual_ha = toRadian(toScientific($actual_bearing));

// Convert the elevation to scientific radians

$actual_va = toRadian($elevation);

// Get the actual velocity

$actual_vel = $power + $current_pow_offset;

// Compute the range, G = 10

$range = $actual_vel^2 * sin(toDegree(2 * $actual_va)) / 10;

// Compute delta X and Y

$delta_x = $range * cos(toDegree($actual_ha));

$delta_y = $range * sin(toDegree($actual_ha));

// Compute destination X and Y

$dest_x = round($source_x + $delta_x);

$dest_y = round($source_y + $delta_y);

// Check if this matches the provided coordinates

if ($dest_x == $result_x && $dest_y == $result_y) {

// We got a match! Add it

push_back($new_pow_offsets, $current_pow_offset);

push_back($new_ha_offsets, $current_ha_offset);

$new_total_offsets += 1;

}

// Next iteration

$index += 1;

$count += 1;

}

// If we're really done, store everything and report

if ($index > $old_total_offsets) {

mem("pow_offsets", $new_pow_offsets);

mem("ha_offsets", $new_ha_offsets);

mem("total_offsets", $new_total_offsets);

mem("continue", 0);

// Report

$report = tostring($new_total_offsets) + " offsets left! ";

if ($new_total_offsets == 0) {

$report += "Impossible, please reset!";

mem("reset", 0);

}

if ($new_total_offsets == 1) {

$report += "Perfectly accurate!";

}

if ($new_total_offsets > 1) {

$report += "Not quite accurate yet!";

}

send($report);

}

}

// Computes the bearing and elevation required for the given destination

def target($dest_x, $dest_y, $power, $source_x, $source_y) {

// Retrieve the best offsets

$pow_offsets = mem("pow_offsets");

$ha_offsets = mem("ha_offsets");

$pow_offset = at($pow_offsets, 1);

$ha_offset = at($ha_offsets, 1);

// Compute delta X and Y

$delta_x = $dest_x - $source_x;

$delta_y = $dest_y - $source_y;

// Compute the distance we need

$dist = sqrt($delta_x^2 + $delta_y^2);

// Get the actual power

$actual_pow = $power + $pow_offset;

// Get the maximum distance achievable

$max_range = $actual_pow^2 * sin(2 * 45) / 10;

// Check

if ($dist > $max_range) {

send("That target is out of range!");

} else {

// Compute the bearing we need

$atan2_result = atan2($delta_y, $delta_x);

$bearing = mod(toDegree($atan2_result), 360);

// Convert it

$bearing = fromScientific($bearing);

// Modify it with the offset

$bearing -= $ha_offset;

// Round it to two decimals

$bearing = round($bearing * 100) / 100;

// Compute the vertical angle we need

$elevation = 0.5 * asin(10 * $dist / $actual_pow^2);

// Round it as well

$elevation = round($elevation * 10) / 10;

// All done! Report it

$report = "Bearing = " + tostring($bearing) + " , Elevation = " + tostring($elevation);

send($report);

}

}

// Applies a given power and bearing offset

def apply($pow_offset, $ha_offset) {

$pow_offsets = vector($pow_offset);

$ha_offsets = vector($ha_offset);

$total_offsets = 1;

mem("reset", 1);

mem("pow_offsets", $pow_offsets);

mem("ha_offsets", $ha_offsets);

mem("total_offsets", $total_offsets);

send("Applied given offsets! Assuming they were accurate, you can now target!");

}

// Gets the offsets and displays them

def get() {

$total_offsets = mem("total_offsets");

if ($total_offsets == 1) {

$pow_offsets = mem("pow_offsets");

$ha_offsets = mem("ha_offsets");

$pow_offset = at($pow_offsets, 1);

$ha_offset = at($ha_offsets, 1);

$report = "Power offset: " + tostring($pow_offset) + " , Bearing offset: " + tostring($ha_offset);

send($report);

} else {

send("We're not accurate enough to have proper offsets!");

}

}

// Turn a list of <command, num1, num2, ...> into a number vector

def tonums($command_vec) {

$index = 2;

$total = length($command_vec);

$result = vector();

while ($index <= $total) {

$textv = at($command_vec, $index);

$numv = tonum($textv);

push_back($result, $numv);

$index += 1;

}

return $result;

}

// Initialisation

if (!mem("init")) {

mem("continue", 0);

mem("reset", 0);

mem("init", 1);

}

// Command processing

if ($freq == $opfreq) {

// If we're still computing, make a nice progress report, then continue

if (mem("continue")) {

$report = "Still computing, currently at ";

$total = mem("total_offsets");

$current = mem("temp_index");

$percent = round($current / $total * 100);

$report += tostring($percent) + "%!";

send($report);

$params = mem("calibrate_params");

$bearing = at($params, 1);

$elevation = at($params, 2);

$power = at($params, 3);

$result_x = at($params, 4);

$result_y = at($params, 5);

$source_x = at($params, 6);

$source_y = at($params, 7);

calibrate($bearing, $elevation, $power, $result_x, $result_y, $source_x, $source_y);

} else {

// Check for commands

$commanded = 0;

$words = explode($content, " ");

$first = at($words, 1);

$first = lower($first);

if ($first == "reset") {

reset();

mem("reset", 1);

$commanded = 1;

}

if ($first == "help") {

send("NTSL Telescience Calculator! To view this line, use 'help'. To reset the calibration, use 'reset'. To calibrate, use 'calibrate bearing elevation power result_x result_y source_x source_y'. To compute, use 'target dest_x dest_y power source_x source_y'. To transfer, use 'apply power_offset bearing_offset'. Have fun!");

$commanded = 1;

}

if ($first == "calibrate") {

if (mem("reset")) {

send("Calibrating!");

$params = tonums($words);

mem("calibrate_params", $params);

$bearing = at($params, 1);

$elevation = at($params, 2);

$power = at($params, 3);

$result_x = at($params, 4);

$result_y = at($params, 5);

$source_x = at($params, 6);

$source_y = at($params, 7);

calibrate($bearing, $elevation, $power, $result_x, $result_y, $source_x, $source_y);

} else {

send("You should reset before calibrating!");

}

$commanded = 1;

}

if ($first == "target") {

if (mem("reset")) {

$params = tonums($words);

$dest_x = at($params, 1);

$dest_y = at($params, 2);

$power = at($params, 3);

$source_x = at($params, 4);

$source_y = at($params, 5);

target($dest_x, $dest_y, $power, $source_x, $source_y);

} else {

send("You should calibrate properly first!");

}

$commanded = 1;

}

if ($first == "apply") {

$params = tonums($words);

$pow_offset = at($params, 1);

$ha_offset = at($params, 2);

apply($dest_x, $dest_y, $power, $source_x, $source_y);

$commanded = 1;

}

if ($first == "get") {

if (mem("reset")) {

get();

} else {

send("You should initialize first!");

}

$commanded = 1;

}

// Display generic help if the command is unknown

if (!$commanded) {

send("Unknown command! Use 'help' for a list of commands and their usage!");

}

}

}

Links to a few of these were occasionally mentioned in OOC once every few months or so, but since that is a terrible method of distribution, this thread might be better.

Remember: This may or may not be illegal, so try to use it carefully™ until something gets sorted out. But since this has lasted for a good month or two, don't expect much, even if Telescience is not removed alltogether.

I've made a Telescience calculator and various coordinate maps to go with it. The maps now contain pretty much everything, and will ofcourse also work alongside any other calculator.

Calculator -> Telescience v5.zip

Coordinate Maps -> Coordinate Maps v2.zip

Calculator

Sourcecode (without the form definition) can be found here: pastebin

It requires .NET 4.0 or higher to run.

This program was originally conceived during the brief timespan where Telescience had 3 random variables instead of 2. Nevertheless, it now has a few neat features:

- It brute-forces over every possible combination of the random variables in Telescience. As a result, calibration only requires exactly as many test cases as needed to narrow the options down to a single set of values.
- It's usually possible to get 100% accuracy with a single test case at 25 power, and even more likely at higher powers, though lower power values can require as many as 3.
- Once the definitive set of values is found, these are displayed, and may be given to someone else to be entered directly, without further calibration required.
- It always displays how close you are to full accuracy. This will most likely be either the maximum or 2 during calibration, though.
- Variable telepad location, power, etc. Fields only display and accept values up to an accuracy supported by Telescience itself.

Coordinate Maps

Migraine warranty for the tiny font and color not included.

These were all generated from sewn-together screenshots of the locations, or existing maps. They are accurate as of today (6 November 2014), though now that maintenance items are randomized, it will vary between rounds. Each dock has its respective shuttle.

The archive at the top of the thread will be convenient to download, but here are the individual maps for the sake of completion:

z-level 1 (aka the station itself): boxstation - metastation

z-level 3 (aka the old telecommunications sat): tcommsat - abandoned teleporter - white shuttle

z-level 4 (aka the derelict): derelict - derelict teleporter - clown shuttle - DJ station

z-level 5 (aka mining): main outpost - west outpost - north outpost - labor camp - alien-weed-covered part