diff --git a/library/lib_circle_nav.ks b/library/lib_circle_nav.ks new file mode 100644 index 0000000..5927a60 --- /dev/null +++ b/library/lib_circle_nav.ks @@ -0,0 +1,53 @@ +// lib_circle_nav.ks provides a set of functions that use Great Circle equations. +// Copyright © 2015,2019,2023 KSLib team +// Lic. MIT +@LAZYGLOBAL OFF. +@CLOBBERBUILTINS OFF. + +//use to find the initial bearing for the shortest path around a sphere from... +function circle_bearing { + parameter + p1, //...this point... + p2. //...to this point. + + return mod(360+arctan2(sin(p2:lng-p1:lng)*cos(p2:lat),cos(p1:lat)*sin(p2:lat)-sin(p1:lat)*cos(p2:lat)*cos(p2:lng-p1:lng)),360). +}. + +//use to find where you will end up if you travel from... +function circle_destination { + parameter + p1, //...this point... + b, // ...with this as your initial bearing... + d, // ...for this distance... + radius. // ...around a sphere of this radius. + + local lat is arcsin(sin(p1:lat)*cos((d*180)/(radius*constant():pi))+cos(p1:lat)*sin((d*180)/(radius*constant():pi))*cos(b)). + local lng is 0. + if abs(Lat) <> 90 { + set lng to p1:lng+arctan2(sin(b)*sin((d*180)/(radius*constant():pi))*cos(p1:lat),cos((d*180)/(radius*constant():pi))-sin(p1:lat)*sin(lat)). + }. + + return latlng(lat,lng). +}. + +//use to find the distance from... +function circle_distance { + parameter + p1, //...this point... + p2, //...to this point... + radius. //...around a body of this radius. (note: if you are flying you may want to use ship:body:radius + altitude). + local A is sin((p1:lat-p2:lat)/2)^2 + cos(p1:lat)*cos(p2:lat)*sin((p1:lng-p2:lng)/2)^2. + + return radius*constant():PI*arctan2(sqrt(A),sqrt(1-A))/90. +}. + +//use to find the mid point on the outside of a sphere between... +function circle_midpoint { + parameter + p1, //...this point... + p2. //...and this point. + local A is cos(p2:lat)*cos(p2:lng-p1:lng). + local B is cos(p2:lat)*sin(p2:lng-P1:lng). + + return latlng(arctan2(sin(p1:lat)+sin(p2:lat),sqrt((cos(p1:lat)+resultA)^2+resultB^2)),p1:lng+arctan2(resultB,cos(p1:lat)+resultA)). +}. diff --git a/library/lib_enum.ks b/library/lib_enum.ks new file mode 100644 index 0000000..7083384 --- /dev/null +++ b/library/lib_enum.ks @@ -0,0 +1,112 @@ +// lib_enum.ks provides a set of functions for manipulating lists, queues, and stacks using the new delegates syntax introduced in kOS version 0.19.0. +// Copyright © 2015,2016 KSLib team +// Lic. MIT + +{global Enum is lex( +"version", "0.1.1", +"all", all@, +"any", any@, +"count", count@, +"each", each@, +"each_slice", each_slice@, +"each_with_index", each_with_index@, +"find", find@, +"find_index", find_index@, +"group_by", group_by@, +"map", map@, +"map_with_index", map_with_index@, +"max", _max@, +"min", _min@, +"partition", partition@, +"reduce", reduce@, +"reject", reject@, +"reverse", reverse@, +"select", select@, +"sort", sort@ +). + +local y is true. local n is false. + +function cast{ + parameter a,b,p is 0. + if a:typename=b return a. + if b="List"{set p to list(). for i in a p:add(i). return p.} + if b="Queue"{set p to queue(). for i in a p:push(i). return p.} + if b="Stack"{ + local l is stack(). + set p to stack(). for i in a p:push(i). + for i in p l:push(i). return l.}} + +function to_l{parameter c. return cast(c,"List").} + +function all{parameter l,c. for i in l if not c(i) return n. return y.} + +function any{parameter l,c. for i in l if c(i) return y. return n.} + +function count{ + parameter l,c,p is 0. for i in l if c(i) set p to p+1. return p.} + +function each{parameter l,o. for i in l o(i).} + +function each_slice{ + parameter l,m,o,c is to_l(l),i is 0. + until i > c:length-1 { + o(cast(c:sublist(i,min(m,c:length-1)),l:typename)). set i to i+m.}} + +function each_with_index{ + parameter l,o,i is 0. for j in to_l(l) {o(j,i+1). set i to i+1.}} + +function find{parameter l,c. for i in l if c(i) return i. return n.} + +function find_index{ + parameter l,c,i is 0. for j in to_l(l) {if c(j) return i. set i to i+1.} + return -1.} + +function group_by{parameter l,t,p is lex(). for i in l{ + local u is t(i). if p:haskey(u) p[u]:add(i). else set p[u] to list(i).} + for k in p:keys set p[k] to cast(p[k],l:typename). return p.} + +function map{ + parameter l,t,p is list(). for i in to_l(l) p:add(t(i)). + return cast(p,l:typename).} + +function map_with_index{parameter l,t,p is list(), i is 0, c is to_l(l). + until i=c:length { p:add(t(c[i],i+1)). set i to i+1. } + return cast(p,l:typename).} + +function _max{parameter l,c is to_l(l). if c:length=0 return n. + local p is c[0]. for i in c if i>p set p to i. return p.} + +function _min{parameter l, c is to_l(l). if c:length=0 return n. + local p is c[0]. for i in c if i

=0 break.} + if i 255 { + set tmpNum to tmpNum - 128. + set carry to true. + } + local subStringHigh IS _exec_idString:substring(i,_exec_idString:LENGTH - i). + set subStringHigh to subStringHigh:remove(0,1). + local subStringLow is _exec_idString:substring(0,i). + set _exec_idString to subStringLow + char(tmpNum) + subStringHigh. + + } + if carry { set _exec_idString to char(128) + _exec_idString. } + //end of string incrementing + set filePath to path("1:/_execute_" + _exec_idString + ".tmp"). + _past_exec_strings:ADD(command,filePath). + } + log_run_del(command,filePath). +} + +function evaluate { + parameter expression. + + execute("global _evaluate_result is " + expression + "."). + local result is _evaluate_result. + if defined _evaluate_result { + unset _evaluate_result. + } + return result. +} + +function evaluate_function { + parameter + function_name, + parameter_list. + + global _exec__param_list is parameter_list. + local expression is "". + local separator is "". + local index is 0. + until index = parameter_list:length { + set expression to expression + separator + "_exec__param_list[" + index + "]". + set separator to ", ". + set index to index + 1. + } + set expression to function_name + "(" + expression + ")". + local result is evaluate(expression). + if defined _exec__param_list { + unset _exec__param_list. + } + return result. +} + +function get_suffix { + parameter + structure, //the structure to get the suffix of + suffix, //the suffix to get + parameter_list IS false. //if the suffix is a function call this is the list of parameters for the suffix + + local filePath is "1:/_get_suffix" + suffix. + local logStr IS "global _evaluate_result is { parameter o. return o:" + suffix. + if parameter_list:istype("list") { + set filePath to filePath + parameter_list:length. + local separator is "(". + global _exec__param_list IS parameter_list. + local i IS 0. + until i >= parameter_list:length + { + set logStr to logStr + separator + "_exec__param_list[" + i + "]". + set separator to ", ". + set i to i + 1. + } + set logStr to logStr + ")". + } + set filePath to path(filePath + ".tmp"). + log_run_del(logStr + ". }.",filePath). + local result is _evaluate_result:call(structure). + if defined _exec__param_list { + unset _exec__param_list. + } + return result. +} + +function set_suffix { + parameter + structure, //the structure to set the suffix of + suffix, //the suffix to set + val. //the value to set the suffix to + + local filePath is path("1:/_set_suffix" + suffix + ".tmp"). + local logStr IS "global _evaluate_result is { parameter o,s. set o:" + suffix + " to s. }.". + log_run_del(logStr,filePath). + local result is _evaluate_result:call(structure,val). + unset _evaluate_result. + return result. +} + +local function log_run_del { + parameter + log_string,//the string to be executed + file_path. //the path to where the string should be stored temporarily so it can be executed. + + if exists(file_path) { + deletepath(file_path). + } + log log_string to file_path. + wait 0. + runpath(file_path). + deletepath(file_path). +} diff --git a/library/lib_file_exists.ks b/library/lib_file_exists.ks new file mode 100644 index 0000000..4b6d967 --- /dev/null +++ b/library/lib_file_exists.ks @@ -0,0 +1,20 @@ +// lib_file_exists.ks is a means of checking for optional dependencies, or checking whether a file exists before attempting to run it. +// Copyright © 2015,2023 KSLib team +// Lic. MIT + +@LAZYGLOBAL off. +@CLOBBERBUILTINS off. + +function file_exists{ + parameter fileName. + local fileList is list(). + local found is false. + list files in fileList. + for file in fileList { + if file = fileName { + set found to true. + break. + } + } + return found. +} diff --git a/library/lib_geodec.ks b/library/lib_geodec.ks new file mode 100644 index 0000000..eff936c --- /dev/null +++ b/library/lib_geodec.ks @@ -0,0 +1,35 @@ +// lib_geodec.ks provides two functions to convert between geographic coordinates (latitude, longitude) and cartesian coordinates (x, y, z). +// Copyright © 2015,2019,2023 KSLib team +// Lic. MIT +@LAZYGLOBAL off. +@CLOBBERBUILTINS off. + +function geo2dec { + parameter + _____vlat, //geoposition:lat + _____vlng, //geoposition:lng + _____valt. //altitude + local _____talt is body:radius+_____valt. + local _____tlat is _____vlat+90. + local _____tsin is sin(_____tlat). + local _____vdec is list( + _____talt*_____tsin*cos(_____vlng), + _____talt*_____tsin*sin(_____vlng), + _____talt*cos(_____tlat) + ). + return _____vdec. +}. + +function dec2geo { + parameter + _____vx, //x from geo2dec array[0] + _____vy, //y from geo2dec array[1] + _____vz. //z from geo2dec array[2] + local _____tsqrt is sqrt(_____vx*_____vx+_____vy*_____vy+_____vz*_____vz). + local _____vgeo is list( + arccos(_____vz/_____tsqrt)-90, + arctan2(_____vy,_____vx), + _____tsqrt-body:radius + ). + return _____vgeo. +}. diff --git a/library/lib_gui_box.ks b/library/lib_gui_box.ks new file mode 100644 index 0000000..75f8fc2 --- /dev/null +++ b/library/lib_gui_box.ks @@ -0,0 +1,68 @@ +// lib_gui_box.ks contains functions to draw boxes in the terminal. +// Copyright © 2015,2019 KSLib team +// Lic. MIT +// This file is based on lib_window.ks from akrOS by akrasuski1. + +function draw_custom_gui_box { + parameter + x, y, w, h, + horizontal_char, + vertical_char, + corner_char. + +// Start Input Sanitization + + if x < 0 or x >= terminal:width { + set x to max(0,min(terminal:width - 1,x)). + HUDTEXT("Error: [draw_custom_gui_box] X value outside terminal.", 10, 2, 30, RED, FALSE). + } + + if y < 0 or y >= (terminal:height - 1) { + set y to max(0,min(terminal:height - 2,y)). + HUDTEXT("Error: [draw_custom_gui_box] Y value outside terminal", 10, 2, 30, RED, FALSE). + } + + if w < 1 or x + w > terminal:width { + set w to max(1,min(terminal:width - x,w)). + HUDTEXT("Error: [draw_custom_gui_box] W value outside terminal.", 10, 2, 30, RED, FALSE). + } + + if h < 1 or y + h >= terminal:height { + set h to max(1,min(terminal:height - 1 - y,h)). + HUDTEXT("Error: [draw_custom_gui_box] H value outside terminal.", 10, 2, 30, RED, FALSE). + } + + // End Input Sanitization + + local horizontal_str is "". + local i is 1. + until i > w { + if i = 1 or i = w { + set horizontal_str to horizontal_str + corner_char. + } else { + set horizontal_str to horizontal_str + horizontal_char. + } + set i to i + 1. + } + print horizontal_str at(x, y). + print horizontal_str at(x, y + h - 1). + set i to 1. + until i >= h - 1 { + print vertical_char at(x , y + i). + print vertical_char at(x + w - 1, y + i). + set i to i + 1. + } +} + +function draw_gui_box { + parameter + x, y, w, h. + draw_custom_gui_box(x, y, w, h, "-", "|", "+"). +} + +function draw_one_char_gui_box { + parameter + x, y, w, h, + border_char. + draw_custom_gui_box(x, y, w, h, border_char, border_char, border_char). +} diff --git a/library/lib_hyperbolic_trigonometry.ks b/library/lib_hyperbolic_trigonometry.ks new file mode 100644 index 0000000..25189ed --- /dev/null +++ b/library/lib_hyperbolic_trigonometry.ks @@ -0,0 +1,45 @@ +// lib_hyperbolic_trigonometry.ks provides the usual hyperbolic functions and their inverses. +// Copyright © 2021,2023 KSLib team +// Lic. MIT + +@lazyglobal off. +@clobberbuiltins off. + +function cosh { + parameter x. + + local toRadian to x * constant:degToRad. + return (constant:e^toRadian + constant:e^(-toRadian)) / 2. +} + +function sinh { + parameter x. + + local toRadian to x * constant:degToRad. + return (constant:e^toRadian - constant:e^(-toRadian)) / 2. +} + +function tanh { + parameter x. + + local toRadian to x * constant:degToRad. + return (1 - constant:e^(-2 * toRadian)) / (1 + constant:e^(-2 * toRadian)). +} + +function arccosh { + parameter n. + + return ln(n + sqrt(n + 1) * sqrt(n - 1)) * constant:radToDeg. +} + +function arcsinh { + parameter n. + + return ln(n + sqrt(1 + n^2)) * constant:radToDeg. +} + +function arctanh { + parameter n. + + return (ln((1 + n) / (1 - n)) / 2) * constant:radToDeg. +} diff --git a/library/lib_input_string.ks b/library/lib_input_string.ks new file mode 100644 index 0000000..75d91f5 --- /dev/null +++ b/library/lib_input_string.ks @@ -0,0 +1,182 @@ +// lib_input_string.ks provides a method of inputting custom strings while a script is running via an on-screen keyboard. +// Copyright © 2015,2019,2023 KSLib team +// Lic. MIT +@LAZYGLOBAL off. +@CLOBBERBUILTINS off. + +function input_string +{ + parameter + line, // top edge of the keyboard + x0, y0, // where to display the input on screen + hidden, // set to true to display "*" instead of character + help. //true or false (the info above the keyboard). + +////////////////// internal functions //////////////////////////////// + + function refresh_board // this is done as 1 large print as drawing the boxes individually lags. + { + if shift = 0 { + print "+---+---+---+---+---+---+---+---+---+---+---+---+---+" at (0,line). + print "| ` | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0 | - | = |" at (0,line+1). + print "+---+---+---+---+---+---+---+---+---+---+---+---+---+" at (0,line+2). + print "| q | w | e | r | t | y | u | i | o | p | [ | ] |Ent|" at (0,line+3). + print "+---+---+---+---+---+---+---+---+---+---+---+---+---+" at (0,line+4). + print "| a | s | d | f | g | h | j | k | l | ; | ' | # |Cap|" at (0,line+5). + print "+---+---+---+---+---+---+---+---+---+---+---+---+---+" at (0,line+6). + print "| \ | z | x | c | v | b | n | m | , | . | / | |" at (0,line+7). + print "+---+---+---+---+---+---+---+---+---+---+---+---+" at (0,line+8). + } else if shift = 1 { + print "+---+---+---+---+---+---+---+---+---+---+---+---+---+" at (0,line). + print "|N/A| ! | "+quote+" |N/A| $ | % | ^ | & | * | ( | ) | _ | + |" at (0,line+1). + print "+---+---+---+---+---+---+---+---+---+---+---+---+---+" at (0,line+2). + print "| Q | W | E | R | T | Y | U | I | O | P | { | } |Ent|" at (0,line+3). + print "+---+---+---+---+---+---+---+---+---+---+---+---+---+" at (0,line+4). + print "| A | S | D | F | G | H | J | K | L | : | @ | ~ |Cap|" at (0,line+5). + print "+---+---+---+---+---+---+---+---+---+---+---+---+---+" at (0,line+6). + print "| | | Z | X | C | V | B | N | M | < | > | ? | |" at (0,line+7). + print "+---+---+---+---+---+---+---+---+---+---+---+---+" at (0,line+8). + } + refresh_position("N/A",0). + } + + function refresh_position + { + parameter dir, increment. + if dir = "row" { + set row to mod(row+increment+keyboard[shift]:length, keyboard[shift]:length). + set col to mod(col+keyboard[shift][row]:length, keyboard[shift][row]:length). + } else if dir = "col" { + set col to mod(col+increment+keyboard[shift][row]:length, keyboard[shift][row]:length). + } + print "+---+" at (4*oldCol,2*oldRow+line). + print "|" at (4*oldCol,2*oldRow+line+1). + print "|" at (4*(oldCol+1),2*oldRow+line+1). + print "+---+" at (4*oldCol,2*oldRow+line+2). + print "#===#" at (4*col,2*row+line). + print "H" at (4*col,2*row+line+1). + print "H" at (4*(col+1),2*row+line+1). + print "#===#" at (4*col,2*row+line+2). + set oldrow to row. + set oldCol to col. + } + + function string_add { + if keyboard[shift][row][col] = "Cap" { + set shift to mod(shift+1,2). + refresh_board(). + } else if keyboard[shift][row][col] = "Ent" { + set Enter to True. + } else { + string:add(keyboard[shift][row][col]). + if hidden { + print "*" at (x0+string:length+2,y0). + } else { + print keyboard[shift][row][col] at (x0+string:length+2,y0). + } + } + } + function string_remove { + if not string:empty { + string:remove(string:length-1). + print " " at (x0+string:length+3,y0). + } + } + + +/////////////function body///////////////////////////////////// + + local col is 0. + local row is 0. + local oldCol is 0. + local oldRow is 0. + local shift is 0. + local controlMap is ship:control. + local oldTop is controlMap:pilottop. + local oldStar is controlMap:pilotstarboard. + local oldFore is controlMap:pilotfore. + local Enter is False. + local string is list(). + local returnString is "". + local charr is 0. + local oldT is 0. + local blink is false. + local quote is char(34). + + local keyboard is list( + list( //lower case. + list("`","1","2","3","4","5","6","7","8","9","0","-","="), + list("q","w","e","r","t","y","u","i","o","p","[","]","Ent"), + list("a","s","d","f","g","h","j","k","l",";","'","#","Cap"), + list("\","z","x","c","v","b","n","m",",",".","/"," ") + ), + list( //upper case. + list("¬","!",quote,"£","$","%","^","&","*","(",")","_","+"), + list("Q","W","E","R","T","Y","U","I","O","P","{","}","Ent"), + list("A","S","D","F","G","H","J","K","L",":","@","~","Cap"), + list("|","Z","X","C","V","B","N","M","<",">","?"," ") + ) + ). + + local oldHeight is terminal:height. + local oldWidth is terminal:width. + if oldwidth < 53 { + set terminal:width to 53. + } + if oldHeight < line+9 { + set terminal:height to line+9. + } + refresh_board(). + if help { + Print "Navigate using the IJKL keys. Use the H and N" at (3,line-3). + Print "keys to add and remove characters respectively." at (3,line-2). + }. + + until Enter + { + if oldt + 0.3 < time { + if blink { + print " " at (x0+string:length+3,y0). + toggle blink. + } else { + print "_" at (x0+string:length+3,y0). + toggle blink. + } + set oldT to time. + }. + if (controlMap:PILOTTOP <> oldTop) or (controlMap:PILOTSTARBOARD <> oldStar) or (controlMap:PILOTFORE <> oldFore) + { + if help { + print " " at (3,line-3). + print " " at (3,line-2). + set help to false. + } + if controlMap:pilottop > 0 + { + refresh_position("row",+1). + } else if controlMap:pilottop < 0 { + refresh_position("row", -1). + } + if controlMap:pilotstarboard > 0 { + refresh_position("col", +1). + } else if controlMap:pilotstarboard < 0 { + refresh_position("col", -1). + } + if controlMap:pilotfore > 0 { + string_add(). + } else if controlMap:pilotfore < 0 { + string_remove(). + } + SET oldTop TO controlMap:PILOTTOP. + SET oldStar TO controlMap:PILOTSTARBOARD. + SET oldFore TO controlMap:PILOTFORE. + } + } + print " " at (x0+string:length+3,y0). + set terminal:width to oldWidth. + set terminal:height to oldHeight. + for charr IN string { + set returnString to returnString + charr. + } + return returnString. +} diff --git a/library/lib_input_terminal.ks b/library/lib_input_terminal.ks new file mode 100644 index 0000000..452c508 --- /dev/null +++ b/library/lib_input_terminal.ks @@ -0,0 +1,196 @@ +// lib_input_terminal.ks provides functions for getting user entered strings or numbers though terminal inputs. +// Copyright © 2020,2023 KSLib team +// Lic. MIT +@LAZYGLOBAL OFF. +@CLOBBERBUILTINS OFF. + +LOCAL termIn IS TERMINAL:INPUT. +LOCAL backChar IS termIn:BACKSPACE. +LOCAL delChar IS termIn:DELETERIGHT. +LOCAL enterChar IS termIn:ENTER. +LOCAL bellChar IS CHAR(7). + +FUNCTION terminal_input_string { + PARAMETER col, + row, + maxLength IS (TERMINAL:WIDTH - col), + inValue IS "", + cursorBlink IS TRUE. + + IF inValue:ISTYPE("scalar") { + SET inValue TO scalar_to_string(inValue). + } + RETURN input_loop(col,row,maxLength,0,inValue,cursorBlink,string_concatnation@). +} + +FUNCTION terminal_input_number { + PARAMETER col, + row, + maxLength IS (TERMINAL:WIDTH - col), + inValue IS " ", + cursorBlink IS TRUE. + + IF inValue:ISTYPE("string") { + IF inValue:TOSCALAR(0) = 0 { + SET inValue TO " ". + } + } + IF inValue:ISTYPE("scalar") { + SET inValue TO scalar_to_string(inValue). + } + + IF (inValue:LENGTH < 1) OR (NOT inValue[0]:MATCHESPATTERN("[ -]")) { + SET inValue TO " ". + } + RETURN number_protect(input_loop(col,row,maxLength,1,inValue,cursorBlink,number_concatnation@)). +} + +FUNCTION input_loop { + PARAMETER col, + row, + maxLength, + minLength, + workingStr, + cursorBlink, + concatenator. + + IF workingStr:LENGTH > maxLength { + SET workingStr TO workingStr:SUBSTRING(0,maxLength). + } + + LOCAL blinkInter IS CHOOSE 0.5 IF cursorBlink ELSE 1000. + LOCAL blinkNextState IS TIME:SECONDS. + LOCAL blinkChar IS "_". + + LOCAL doPrint IS TRUE. + LOCAL done IS FALSE. + UNTIL done { + IF blinkNextState < TIME:SECONDS { + SET blinkNextState TO blinkNextState + blinkInter. + IF cursorBlink { + SET blinkChar TO CHOOSE "█" IF blinkChar <> "█" ELSE "_". + SET doPrint TO TRUE. + } + } + IF termIn:HASCHAR { + LOCAL cha IS termIn:GETCHAR(). + SET doPrint TO TRUE. + + IF cha = backChar { + IF workingStr:LENGTH > minLength { + SET workingStr TO workingStr:REMOVE(workingStr:LENGTH - 1,1). + } ELSE { + PRINT bellChar. + } + } ELSE IF cha = delChar { + SET workingStr TO "". + PRINT bellChar. + } ELSE IF cha = enterChar { + SET done TO TRUE. + SET blinkChar TO " ". + } ELSE { + SET workingStr TO concatenator(workingStr,cha,maxLength). + } + } + IF doPrint { + SET doPrint TO FALSE. + LOCAL padChar IS (CHOOSE blinkChar IF workingStr:LENGTH < maxLength ELSE ""). + PRINT (workingStr + padChar):PADRIGHT(maxLength) AT(col,row). + } + WAIT UNTIL (termIn:HASCHAR) OR (blinkNextState < TIME:SECONDS) OR done. + } + RETURN workingStr. +} + +LOCAL FUNCTION scalar_to_string { + PARAMETER scalar. + LOCAL signChar IS CHOOSE "-" IF scalar < 0 ELSE " ". + LOCAL returnStr TO ABS(scalar):TOSTRING(). + IF returnStr:CONTAINS("E") { + LOCAL strSplit IS returnStr:SPLIT("E"). + LOCAL mantissa IS strSplit[0]. + LOCAL exponent IS strSplit[1]:TOSCALAR(). + IF mantissa:CONTAINS(".") { + LOCAL splitMant IS mantissa:SPLIT("."). + SET mantissa TO splitMant[0] + splitMant[1]. + SET exponent TO exponent - splitMant[1]:LENGTH. + } + IF exponent < 0 { + SET returnStr TO "0." + (" ":PADRIGHT(ABS(exponent + 1))):REPLACE(" ","0") + mantissa. + } ELSE IF exponent > 0 { + SET returnStr TO mantissa + (" ":PADRIGHT(exponent)):REPLACE(" ","0"). + } ELSE { + SET returnStr TO mantissa. + } + } + RETURN signChar + returnStr. +} + +LOCAL FUNCTION number_protect { + PARAMETER curentStr. + IF curentStr:LENGTH <= 1 { + RETURN " 0". + } + IF curentStr[curentStr:LENGTH - 1] = "." { + RETURN number_protect(curentStr:REMOVE(curentStr:LENGTH - 1,1)). + } + IF curentStr:TOSCALAR(0) = 0 { + RETURN " 0". + } + RETURN curentStr. +} + +LOCAL FUNCTION number_concatnation { + PARAMETER curentStr,//expects " " as the base string to start with + cha, + maxLength. + IF curentStr:LENGTH < 1 { + SET curentStr TO " ". + } + IF cha:MATCHESPATTERN("[0-9-.+]") { + IF curentStr:LENGTH < maxLength { + IF cha:MATCHESPATTERN("[0-9]") { + RETURN curentStr + cha. + } ELSE IF cha = "." { + IF NOT curentStr:CONTAINS(".") { + RETURN curentStr + cha. + } + } + } + IF cha = "-" OR cha = "+" { + IF curentStr:CONTAINS("-") OR cha = "+" { + RETURN " " + curentStr:REMOVE(0,1). + } ELSE { + RETURN cha + curentStr:REMOVE(0,1). + } + } + } + PRINT bellChar. + RETURN curentStr. +} + +LOCAL toIgnore IS LIST( + CHAR(127), + delChar, + backChar, + termIn:UPCURSORONE, + termIn:DOWNCURSORONE, + termIn:LEFTCURSORONE, + termIn:RIGHTCURSORONE, + termIn:HOMECURSOR, + termIn:ENDCURSOR, + termIn:PAGEDOWNCURSOR, + termIn:PAGEUPCURSOR +). +LOCAL FUNCTION string_concatnation { + PARAMETER curentStr, + cha, + maxLength. + IF (UNCHAR(cha) > 31) AND (NOT toIgnore:CONTAINS(cha)) { + IF curentStr:LENGTH < maxLength { + RETURN curentStr + cha. + } + } + PRINT bellChar. + RETURN curentStr. +} \ No newline at end of file diff --git a/library/lib_lazcalc.ks b/library/lib_lazcalc.ks new file mode 100644 index 0000000..7601818 --- /dev/null +++ b/library/lib_lazcalc.ks @@ -0,0 +1,101 @@ +// lib_lazcalc.ks - provides the user with a launch azimuth based on a desired target orbit altitude and inclination and can continued to be used throughout ascent to update the heading. It bases this calculation on the vessel's launch and current geoposition. +// Copyright © 2015,2017,2023 KSLib team +// Lic. MIT + +//~~Version 2.2~~ +//~~Created by space-is-hard~~ +//~~Updated by TDW89~~ +//~~Auto north/south switch by undercoveryankee~~ + +//To use: RUN LAZcalc.ks. SET data TO LAZcalc_init([desired circular orbit altitude in meters],[desired orbital inclination; negative if launching from descending node, positive otherwise]). Then loop SET myAzimuth TO LAZcalc(data). + +@LAZYGLOBAL OFF. +@CLOBBERBUILTINS OFF. + +FUNCTION LAZcalc_init { + PARAMETER + desiredAlt, //Altitude of desired target orbit (in *meters*) + desiredInc. //Inclination of desired target orbit + + PARAMETER autoNodeEpsilon IS 10. // How many m/s north or south + // will be needed to cause a north/south switch. Pass zero to disable + // the feature. + SET autoNodeEpsilon to ABS(autoNodeEpsilon). + + //We'll pull the latitude now so we aren't sampling it multiple times + LOCAL launchLatitude IS SHIP:LATITUDE. + + LOCAL data IS LIST(). // A list is used to store information used by LAZcalc + + //Orbital altitude can't be less than sea level + IF desiredAlt <= 0 { + PRINT "Target altitude cannot be below sea level". + SET launchAzimuth TO 1/0. //Throws error + }. + + //Determines whether we're trying to launch from the ascending or descending node + LOCAL launchNode TO "Ascending". + IF desiredInc < 0 { + SET launchNode TO "Descending". + + //We'll make it positive for now and convert to southerly heading later + SET desiredInc TO ABS(desiredInc). + }. + + //Orbital inclination can't be less than launch latitude or greater than 180 - launch latitude + IF ABS(launchLatitude) > desiredInc { + SET desiredInc TO ABS(launchLatitude). + HUDTEXT("Inclination impossible from current latitude, setting for lowest possible inclination.", 10, 2, 30, RED, FALSE). + }. + + IF 180 - ABS(launchLatitude) < desiredInc { + SET desiredInc TO 180 - ABS(launchLatitude). + HUDTEXT("Inclination impossible from current latitude, setting for highest possible inclination.", 10, 2, 30, RED, FALSE). + }. + + //Does all the one time calculations and stores them in a list to help reduce the overhead or continuously updating + LOCAL equatorialVel IS (2 * CONSTANT():Pi * BODY:RADIUS) / BODY:ROTATIONPERIOD. + LOCAL targetOrbVel IS SQRT(BODY:MU/ (BODY:RADIUS + desiredAlt)). + data:ADD(desiredInc). //[0] + data:ADD(launchLatitude). //[1] + data:ADD(equatorialVel). //[2] + data:ADD(targetOrbVel). //[3] + data:ADD(launchNode). //[4] + data:ADD(autoNodeEpsilon). //[5] + RETURN data. +}. + +FUNCTION LAZcalc { + PARAMETER + data. //pointer to the list created by LAZcalc_init + LOCAL inertialAzimuth IS ARCSIN(MAX(MIN(COS(data[0]) / COS(SHIP:LATITUDE), 1), -1)). + LOCAL VXRot IS data[3] * SIN(inertialAzimuth) - data[2] * COS(data[1]). + LOCAL VYRot IS data[3] * COS(inertialAzimuth). + + // This clamps the result to values between 0 and 360. + LOCAL Azimuth IS MOD(ARCTAN2(VXRot, VYRot) + 360, 360). + + IF data[5] { + LOCAL NorthComponent IS VDOT(SHIP:VELOCITY:ORBIT, SHIP:NORTH:VECTOR). + IF NorthComponent > data[5] { + SET data[4] TO "Ascending". + } ELSE IF NorthComponent < -data[5] { + SET data[4] to "Descending". + }. + }. + + //Returns northerly azimuth if launching from the ascending node + IF data[4] = "Ascending" { + RETURN Azimuth. + + //Returns southerly azimuth if launching from the descending node + } ELSE IF data[4] = "Descending" { + IF Azimuth <= 90 { + RETURN 180 - Azimuth. + + } ELSE IF Azimuth >= 270 { + RETURN 540 - Azimuth. + + }. + }. +}. diff --git a/library/lib_list_dialog.ks b/library/lib_list_dialog.ks new file mode 100644 index 0000000..a2a98e7 --- /dev/null +++ b/library/lib_list_dialog.ks @@ -0,0 +1,58 @@ +// lib_list_dialog.ks helps when you want the user to choose a value from a long list. +// Copyright © 2015,2023 KSLib team +// Lic. MIT +@LAZYGLOBAL OFF. +@CLOBBERBUILTINS OFF. + +run lib_menu. + +function open_list_dialog { + parameter + title, + option_list. + + local action_list is list("Next page", "Previous page"). + return _list_dialog(title, option_list, action_list, -1). +} + +function open_cancelable_list_dialog { + parameter + title, + option_list. + + local action_list is list("Next page", "Previous page", "Cancel"). + return _list_dialog(title, option_list, action_list, -2). +} + + +// this function is internal and should not be used outside lib_list_dialog +function _list_dialog { + parameter + title, + option_list, + action_list, + internal_action_offset. + + local inaccessible_lines is 11. + local page_start is 0. + local result is internal_action_offset. + until result > internal_action_offset { + local page_height is terminal:height - inaccessible_lines - action_list:length. + set page_start to page_start - mod(page_start, page_height). + local menu_list is action_list:copy. + for option in option_list:sublist(page_start, page_height) { + menu_list:add(option). + } + local title is title + " [Page: " + (page_start / page_height + 1) + " / " + (ceiling(option_list:length/page_height)) + "]". + set result to open_menu_indexed(title, menu_list) - action_list:length. + if result = internal_action_offset - 1 { // next + set page_start to min(page_start + page_height, option_list:length - 1). + } else if result = internal_action_offset { // previous + set page_start to max(0, page_start - page_height). + } + } + if result >= 0 { + set result to page_start + result. + } + return result. +} diff --git a/library/lib_location_constants.ks b/library/lib_location_constants.ks new file mode 100644 index 0000000..da4452c --- /dev/null +++ b/library/lib_location_constants.ks @@ -0,0 +1,119 @@ +// lib_location_constants.ks provides geolocations for places that would be relatively easy to locate for players flying manually (i.e., without the use of scripting mods such as kOS). +// Copyright © 2020,2023 KSLib team +// Lic. MIT +@LAZYGLOBAL off. +@CLOBBERBUILTINS off. + +if not (defined location_constants) { +global location_constants is lex(). +//runways should be added in this pattern "name_runway_##_start" where name is the name of the runway and ## is the runway number +// If not added with this pattern then the automatic alias creation that add an end position based on the start of the same name but opposite number will fail + +if bodyexists("Sun") and body("Sun"):radius = 261_600_000 and bodyexists("Kerbin") {//check for if the craft is in the stock solar system + local kerbinLocations is lex(). + // vertical landing locations + kerbinLocations:add("launchpad", Kerbin:GeoPositionLatLng(-0.0972, -74.5577)). + kerbinLocations:add("woomerang_launchpad", Kerbin:GeoPositionLatLng(45.2896, 136.1100)). + kerbinLocations:add("desert_launchpad", Kerbin:GeoPositionLatLng(-6.5604, -143.9500)). + kerbinLocations:add("VAB", Kerbin:GeoPositionLatLng(-0.0968, -74.6187)). + location_constants:add("launchpad",kerbinLocations["launchpad"]). + + // horizontal landing locations + kerbinLocations:add("runway_09_start", Kerbin:GeoPositionLatLng(-0.0486, -74.7247)). + kerbinLocations:add("runway_09_overrun", Kerbin:GeoPositionLatLng(-0.0502, -74.4880)). // runway "lip" + kerbinLocations:add("runway_27_start", Kerbin:GeoPositionLatLng(-0.0502, -74.4925)). + kerbinLocations:add("runway_27_overrun", Kerbin:GeoPositionLatLng(-0.0486, -74.7292)). // runway "lip" + kerbinLocations:add("l1_runway_09_start", Kerbin:GeoPositionLatLng(-.0489, -74.7101)). + kerbinLocations:add("l1_runway_27_start", Kerbin:GeoPositionLatLng(-.0501, -74.5076)). + kerbinLocations:add("l2_runway_09_start", Kerbin:GeoPositionLatLng(-.0486, -74.7134)). + kerbinLocations:add("l2_runway_27_start", Kerbin:GeoPositionLatLng(-.0501, -74.5046)). + kerbinLocations:add("island_runway_09_start", Kerbin:GeoPositionLatLng(-1.5177, -71.9663)). + kerbinLocations:add("island_runway_27_start", Kerbin:GeoPositionLatLng(-1.5158, -71.8524)). + kerbinLocations:add("desert_runway_36_start", Kerbin:GeoPositionLatLng(-6.5998, -144.0409)). + kerbinLocations:add("desert_runway_18_start", Kerbin:GeoPositionLatLng(-6.4480, -144.0383)). + + location_constants:add("runway_start",kerbinLocations["runway_09_start"]). + location_constants:add("reverse_runway_start",kerbinLocations["runway_27_start"]). + + location_constants:add("kerbin",kerbinLocations). +} + +// JNSQ locations +if bodyexists("Sun") and body("Sun"):radius = 175_750_000 and bodyexists("Kerbin") { + local kerbinLocations is lex(). + // vertical landing locations + kerbinLocations:add("launchpad", Kerbin:GeoPositionLatLng(-1.75133971218638E-08,-91.7839867917987)). + kerbinLocations:add("woomerang_launchpad", Kerbin:GeoPositionLatLng(45.2898572995131,136.109991969089)). + kerbinLocations:add("desert_launchpad", Kerbin:GeoPositionLatLng(-6.56014471162071,-143.949999962632)). + kerbinLocations:add("VAB", Kerbin:GeoPositionLatLng(-3.59779685817755E-05,-91.8082182694506)). + location_constants:add("launchpad",kerbinLocations["launchpad"]). + + // horizontal landing locations + kerbinLocations:add("runway_09_start", Kerbin:GeoPositionLatLng(0.0177914364838286,-91.8485588846624)). + kerbinLocations:add("runway_09_overrun", Kerbin:GeoPositionLatLng(0.017771315430981,-91.7560165023844)). // runway "lip" + kerbinLocations:add("runway_27_start", Kerbin:GeoPositionLatLng(0.0177872750049561,-91.758043638339)). + kerbinLocations:add("runway_27_overrun", Kerbin:GeoPositionLatLng(0.0177981075373338,-91.8503058587583)). // runway "lip" + kerbinLocations:add("l1_runway_09_start", Kerbin:GeoPositionLatLng(0.0177201817416171,-91.8418633582038)). + kerbinLocations:add("l1_runway_27_start", Kerbin:GeoPositionLatLng(0.0177610480469539,-91.7644480917721)). + kerbinLocations:add("l2_runway_09_start", Kerbin:GeoPositionLatLng(0.0178308568526199,-91.8434577819091)). + kerbinLocations:add("l2_runway_27_start", Kerbin:GeoPositionLatLng(0.0178110871731228,-91.7632143441808)). + kerbinLocations:add("island_runway_09_start", Kerbin:GeoPositionLatLng(-1.5323269901046,-71.9321971014811)). + kerbinLocations:add("island_runway_27_start", Kerbin:GeoPositionLatLng(-1.53151635515729,-71.8874890013379)). + kerbinLocations:add("desert_runway_36_start", Kerbin:GeoPositionLatLng(-6.55057590520998,-144.040382288094)). + kerbinLocations:add("desert_runway_18_start", Kerbin:GeoPositionLatLng(-6.49233191848109,-144.039374890239)). + + location_constants:add("runway_start",kerbinLocations["runway_09_start"]). + location_constants:add("reverse_runway_start",kerbinLocations["runway_27_start"]). + + location_constants:add("kerbin",kerbinLocations). +} + +// RealSolarSystem locations +if bodyexists("Sun") and body("Sun"):radius = 696_342_000 and bodyexists("Earth") { + // local earthLocations is lex(). + // TODO: populate at least the most commonly used of the RealSolarSystem locations. +} + +// aliases +until not aliasing(location_constants) {}. + +local function aliasing { + parameter locationConstants. + local didChange is false. + for key in locationConstants:keys { + local bodyLex is locationConstants[key]. + if bodyLex:istype("lexicon") { + local bodyKeys is bodyLex:keys:copy. + for currentKey in bodyKeys { + if currentKey:contains("desert") {//desert to dessert aliasing + local alias to currentKey:replace("desert", "dessert"). + if not bodyLex:haskey(alias) { + bodyLex:add(alias,bodyLex[currentKey]). + set didChange to true. + } + } + + if currentKey:matchespattern("runway_\d{1,2}_start$") {//runway_##_end aliasing + local alias is currentKey:replace("start", "end"). + // now find the key that `alias` is an alias of + if not bodyLex:haskey(alias) { + local splitKey is currentKey:split("_"). + local currentNum is splitKey[splitKey:length - 2]. + local reverseNum is MOD(currentNum:toscalar(0) + 18,36):tostring. + if reverseNum = "0" { set reverseNum to "36". } + local reverseStr is currentKey:replace(currentNum,reverseNum). + if not bodyLex:haskey(reverseStr) { + set reverseStr to currentKey:replace(currentNum,"0" + reverseNum). + } + if bodyLex:haskey(reverseStr) { + bodyLex:add(alias,bodyLex[reverseStr]). + set didChange to true. + } + } + } + } + } + } + return didChange. +} +} diff --git a/library/lib_math.ks b/library/lib_math.ks new file mode 100644 index 0000000..b6befd5 --- /dev/null +++ b/library/lib_math.ks @@ -0,0 +1,15 @@ +declare function lerp{ + parameter a. + parameter b. + parameter t. + return a + (b - a) * t. +} + +declare function map{ + parameter a1. + parameter b1. + parameter a2. + parameter b2. + parameter v. + return a2 + (v - a1) * (b2 - a2) / (b1 - a1). +} \ No newline at end of file diff --git a/library/lib_menu.ks b/library/lib_menu.ks new file mode 100644 index 0000000..fe80752 --- /dev/null +++ b/library/lib_menu.ks @@ -0,0 +1,62 @@ +// lib_menu.ks - select one of the options and return it to calling script. +// Copyright © 2015,2023 KSLib team +// Lic. MIT + +@lazyglobal off. +@clobberbuiltins off. + +run lib_gui_box. + +function open_menu_indexed{ + parameter title. + parameter list_of_names. + + if list_of_names:empty{ + print "error: list_of_names should not be empty". print 1/0. + } + + local current_option is 0. + local len is list_of_names:length(). + + clearscreen. + print title at(2,2). + local i is 0. + until i=len{ + print "[ ] "+list_of_names[i] at(2,i+4). + set i to i+1. + } + draw_gui_box(0, 0, terminal:width, len + 6). + print "AG7/8 - move up/down, AG9 - select" at(2,len+7). + draw_gui_box(0, len + 5, terminal:width, 5). + print "*" at(3,4). + + until false{ + local last_up is ag7. + local last_down is ag8. + local last_sel is ag9. + wait until ag7<>last_up or ag8<>last_down or ag9<>last_sel. + if ag7<>last_up{ + print " " at(3,4+current_option). + set current_option to mod(current_option-1+len,len). + print "*" at(3,4+current_option). + } + else if ag8<>last_down{ + print " " at(3,4+current_option). + set current_option to mod(current_option+1,len). + print "*" at(3,4+current_option). + } + else if ag9<>last_sel{ + break. + } + } + + clearscreen. + return current_option. +} + +function open_menu{ + parameter title. + parameter list_of_names. + + return list_of_names[open_menu_indexed(title, list_of_names)]. +} diff --git a/library/lib_navball.ks b/library/lib_navball.ks new file mode 100644 index 0000000..3f49dd1 --- /dev/null +++ b/library/lib_navball.ks @@ -0,0 +1,122 @@ +// lib_navball.ks - A library of functions to calculate navball-based directions. +// Copyright © 2015,2017,2019,2023 KSLib team +// Lic. MIT + +@lazyglobal off. +@clobberbuiltins off. + +function east_for { + parameter ves is ship. + + return vcrs(ves:up:vector, ves:north:vector). +} + +function compass_for { + parameter ves is ship,thing is "default". + + local pointing is ves:facing:forevector. + if not thing:istype("string") { + set pointing to type_to_vector(ves,thing). + } + + local east is east_for(ves). + + local trig_x is vdot(ves:north:vector, pointing). + local trig_y is vdot(east, pointing). + + local result is arctan2(trig_y, trig_x). + + if result < 0 { + return 360 + result. + } else { + return result. + } +} + +function pitch_for { + parameter ves is ship,thing is "default". + + local pointing is ves:facing:forevector. + if not thing:istype("string") { + set pointing to type_to_vector(ves,thing). + } + + return 90 - vang(ves:up:vector, pointing). +} + +function roll_for { + parameter ves is ship,thing is "default". + + local pointing is ves:facing. + if not thing:istype("string") { + if thing:istype("vessel") or pointing:istype("part") { + set pointing to thing:facing. + } else if thing:istype("direction") { + set pointing to thing. + } else { + print "type: " + thing:typename + " is not recognized by roll_for". + } + } + + if vang(pointing:topvector,ves:up:vector) < 0.2 {//this is the dead zone for roll when within 0.2 degrees of vertical + return 0. + } else { + local trig_x is vdot(pointing:topvector,ves:up:vector). + local vec_y is vcrs(ves:up:vector,ves:facing:forevector). + local trig_y is vdot(pointing:topvector,vec_y). + return arctan2(trig_y,trig_x). + } +} + +function compass_and_pitch_for { + parameter ves is ship,thing is "default". + + local pointing is ves:facing:forevector. + if not thing:istype("string") { + set pointing to type_to_vector(ves,thing). + } + + local east is east_for(ves). + + local trig_x is vdot(ves:north:vector, pointing). + local trig_y is vdot(east, pointing). + local trig_z is vdot(ves:up:vector, pointing). + + local compass is arctan2(trig_y, trig_x). + if compass < 0 { + set compass to 360 + compass. + } + local pitch is arctan2(trig_z, sqrt(trig_x^2 + trig_y^2)). + + return list(compass,pitch). +} + +function bearing_between { + parameter ves,thing_1,thing_2. + + local vec_1 is type_to_vector(ves,thing_1). + local vec_2 is type_to_vector(ves,thing_2). + + local fake_north is vxcl(ves:up:vector, vec_1). + local fake_east is vcrs(ves:up:vector, fake_north). + + local trig_x is vdot(fake_north, vec_2). + local trig_y is vdot(fake_east, vec_2). + + return arctan2(trig_y, trig_x). +} + +function type_to_vector { + parameter ves,thing. + if thing:istype("vector") { + return thing:normalized. + } else if thing:istype("direction") { + return thing:forevector. + } else if thing:istype("vessel") or thing:istype("part") { + return thing:facing:forevector. + } else if thing:istype("geoposition") or thing:istype("waypoint") { + return (thing:position - ves:position):normalized. + } else { + print "type: " + thing:typename + " is not recognized by lib_navball". + } +} diff --git a/library/lib_navigation.ks b/library/lib_navigation.ks new file mode 100644 index 0000000..bbf133e --- /dev/null +++ b/library/lib_navigation.ks @@ -0,0 +1,266 @@ +// lib_navigation.ks provides a plethora of useful functions to aid in writing navigational scripts, whether it's space navigation or surface navigation. +// Copyright © 2019,2020,2023 KSLib team +// Lic. MIT +@LAZYGLOBAL OFF. +@CLOBBERBUILTINS OFF. + +// Same as orbital prograde vector for ves +function orbitTangent { + parameter ves is ship. + + return ves:velocity:orbit:normalized. +} + +// In the direction of orbital angular momentum of ves +// Typically same as Normal +function orbitBinormal { + parameter ves is ship. + + return vcrs((ves:position - ves:body:position):normalized, orbitTangent(ves)):normalized. +} + +// Perpendicular to both tangent and binormal +// Typically same as Radial In +function orbitNormal { + parameter ves is ship. + + return vcrs(orbitBinormal(ves), orbitTangent(ves)):normalized. +} + +// Vector pointing in the direction of longitude of ascending node +function orbitLAN { + parameter ves is ship. + + return angleAxis(ves:orbit:LAN, ves:body:angularVel:normalized) * solarPrimeVector. +} + +// Same as surface prograde vector for ves +function surfaceTangent { + parameter ves is ship. + + return ves:velocity:surface:normalized. +} + +// In the direction of surface angular momentum of ves +// Typically same as Normal +function surfaceBinormal { + parameter ves is ship. + + return vcrs((ves:position - ves:body:position):normalized, surfaceTangent(ves)):normalized. +} + +// Perpendicular to both tangent and binormal +// Typically same as Radial In +function surfaceNormal { + parameter ves is ship. + + return vcrs(surfaceBinormal(ves), surfaceTangent(ves)):normalized. +} + +// Vector pointing in the direction of longitude of ascending node +function surfaceLAN { + parameter ves is ship. + + return angleAxis(ves:orbit:LAN - 90, ves:body:angularVel:normalized) * solarPrimeVector. +} + +// Vector directly away from the body at ves' position +function localVertical { + parameter ves is ship. + + return ves:up:vector. +} + +// Angle to ascending node with respect to ves' body's equator +function angleToBodyAscendingNode { + parameter ves is ship. + + local joinVector is orbitLAN(ves). + local angle is vang((ves:position - ves:body:position):normalized, joinVector). + if ves:status = "LANDED" { + set angle to angle - 90. + } + else { + local signVector is vcrs(-body:position, joinVector). + local sign is vdot(orbitBinormal(ves), signVector). + if sign < 0 { + set angle to angle * -1. + } + } + return angle. +} + +// Angle to descending node with respect to ves' body's equator +function angleToBodyDescendingNode { + parameter ves is ship. + + local joinVector is -orbitLAN(ves). + local angle is vang((ves:position - ves:body:position):normalized, joinVector). + if ves:status = "LANDED" { + set angle to angle - 90. + } + else { + local signVector is vcrs(-body:position, joinVector). + local sign is vdot(orbitBinormal(ves), signVector). + if sign < 0 { + set angle to angle * -1. + } + } + return angle. +} + +// Vector directed from the relative descending node to the ascending node +function relativeNodalVector { + parameter orbitBinormal. + parameter targetBinormal. + + return vcrs(orbitBinormal, targetBinormal):normalized. +} + +// Angle to relative ascending node determined from args +function angleToRelativeAscendingNode { + parameter orbitBinormal. + parameter targetBinormal. + + local joinVector is relativeNodalVector(orbitBinormal, targetBinormal). + local angle is vang(-body:position:normalized, joinVector). + local signVector is vcrs(-body:position, joinVector). + local sign is vdot(orbitBinormal, signVector). + if sign < 0 { + set angle to angle * -1. + } + return angle. +} + +// Angle to relative descending node determined from args +function angleToRelativeDescendingNode { + parameter orbitBinormal. + parameter targetBinormal. + + local joinVector is -relativeNodalVector(orbitBinormal, targetBinormal). + local angle is vang(-body:position:normalized, joinVector). + local signVector is vcrs(-body:position, joinVector). + local sign is vdot(orbitBinormal, signVector). + if sign < 0 { + set angle to angle * -1. + } + return angle. +} + +// Orbital phase angle with assumed target +// Positive when you are behind the target, negative when ahead +function phaseAngle { + local common_ancestor is 0. + local my_ancestors is list(). + local your_ancestors is list(). + + my_ancestors:add(ship:body). + until not(my_ancestors[my_ancestors:length-1]:hasBody) { + my_ancestors:add(my_ancestors[my_ancestors:length-1]:body). + } + your_ancestors:add(target:body). + until not(your_ancestors[your_ancestors:length-1]:hasBody) { + your_ancestors:add(your_ancestors[your_ancestors:length-1]:body). + } + + for my_ancestor in my_ancestors { + local found is false. + for your_ancestor in your_ancestors { + if my_ancestor = your_ancestor { + set common_ancestor to my_ancestor. + set found to true. + break. + } + } + if found { + break. + } + } + + local vel is ship:velocity:orbit. + local my_ancestor is my_ancestors[0]. + until my_ancestor = common_ancestor { + set vel to vel + my_ancestor:orbit:velocity:orbit. + set my_ancestor to my_ancestor:body. + } + local binormal is vcrs(-common_ancestor:position:normalized, vel:normalized):normalized. + + local phase is vang( + -common_ancestor:position:normalized, + vxcl(binormal, target:position - common_ancestor:position):normalized + ). + local signVector is vcrs( + -common_ancestor:position:normalized, + (target:position - common_ancestor:position):normalized + ). + local sign is vdot(binormal, signVector). + if sign < 0 { + return -phase. + } + else { + return phase. + } +} + +// Average Isp calculation +function _avg_isp { + local burnEngines is list(). + list engines in burnEngines. + local massBurnRate is 0. + for e in burnEngines { + if e:ignition { + set massBurnRate to massBurnRate + e:availableThrust/(e:ISP * constant:g0). + } + } + local isp is -1. + if massBurnRate <> 0 { + set isp to ship:availablethrust / massBurnRate. + } + return isp. +} + +// Burn time from rocket equation +function getBurnTime { + parameter deltaV. + parameter isp is 0. + + if deltaV:typename() = "Vector" { + set deltaV to deltaV:mag. + } + if isp = 0 { + set isp to _avg_isp(). + } + + local burnTime is -1. + if ship:availablethrust <> 0 { + set burnTime to ship:mass * (1 - CONSTANT:E ^ (-deltaV / isp)) / (ship:availablethrust / isp). + } + return burnTime. +} + +// Instantaneous azimuth +function azimuth { + parameter inclination. + parameter orbit_alt. + parameter auto_switch is false. + + local shipLat is ship:latitude. + if abs(inclination) < abs(shipLat) { + set inclination to shipLat. + } + + local head is arcsin(cos(inclination) / cos(shipLat)). + if auto_switch { + if angleToBodyDescendingNode(ship) < angleToBodyAscendingNode(ship) { + set head to 180 - head. + } + } + else if inclination < 0 { + set head to 180 - head. + } + local vOrbit is sqrt(body:mu / (orbit_alt + body:radius)). + local vRotX is vOrbit * sin(head) - vdot(ship:velocity:orbit, heading(90, 0):vector). + local vRotY is vOrbit * cos(head) - vdot(ship:velocity:orbit, heading(0, 0):vector). + set head to 90 - arctan2(vRotY, vRotX). + return mod(head + 360, 360). +} diff --git a/library/lib_num_to_formatted_str.ks b/library/lib_num_to_formatted_str.ks new file mode 100644 index 0000000..43ab4a7 --- /dev/null +++ b/library/lib_num_to_formatted_str.ks @@ -0,0 +1,159 @@ +// lib_num_to_formatted_str.ks provides several functions for changing numbers (scalers) into strings with specified formats +// Copyright © 2019,2020,2021,2023 KSLib team +// Lic. MIT + +@LAZYGLOBAL OFF. +@CLOBBERBUILTINS OFF. + +LOCAL FUNCTION time_converter { + PARAMETER timeValue, // the time in seconds to convert + places. // how many time places (e.g HH:MM:SS has 3 places) + SET timeValue TO TIMESPAN(timeValue). + + IF timeValue:MINUTES < 1 OR places = 1 { + RETURN LIST(ROUND(timeValue:SECONDS,2)). + + } ELSE IF timeValue:HOURS < 1 OR places = 2 { + RETURN LIST(ROUND(MOD(timeValue:SECONDS,60),2), timeValue:MINUTES). + + } ELSE IF timeValue:DAYS < 1 OR places = 3 { + RETURN LIST(ROUND(MOD(timeValue:SECONDS,60),2), timeValue:MINUTE, timeValue:HOURS). + + } ELSE IF timeValue:YEARS < 1 OR places = 4 { + RETURN LIST(ROUND(MOD(timeValue:SECONDS,60),2), timeValue:MINUTE, timeValue:HOUR, timeValue:DAYS). + + } ELSE { + RETURN LIST(ROUND(MOD(timeValue:SECONDS,60),2), timeValue:MINUTE, timeValue:HOUR, timeValue:DAY, timeValue:YEARS). + } +} + +//adding list of format types +//formats should be a list of two items +// the first item is the fixed number of places to show ie a 2 will always show 2 places be it MM:SS or HH:MM or DDD:HH +// the second item is a list of strings to include after the neumeric value of the place ie "s" to show after the number or seconds +// the order is seconds, minutes, hours, days, years +// it must also be at least as long as the fixed places number +LOCAL timeFormats IS LIST(). +timeFormats:ADD(LIST(0,LIST("s","m ","h ","d ","y "),2)). +timeFormats:ADD(LIST(0,LIST("",":",":"," Days, "," Years, "),2)). +timeFormats:ADD(LIST(0,LIST(" Seconds"," Minutes, "," Hours, "," Days, "," Years, "),2)). +timeFormats:ADD(LIST(0,LIST("",":",":"),2)). +timeFormats:ADD(LIST(3,timeFormats[3][1],2)). +timeFormats:ADD(LIST(2,LIST("s ","m ","h ","d ","y "),0)). +timeFormats:ADD(LIST(2,LIST(" Seconds "," Minutes "," Hours "," Days "," Years "),0)). + +LOCAL leading0List IS LIST(2,2,2,3,3).//presumed maximum leading zeros applied to sec,min,hour,day,year values + +FUNCTION time_formatting { + PARAMETER timeSec, // the time in seconds to be formatted + formatType IS 0, // type of format to use, range 0 to 6 + rounding IS 0, // rounding for the seconds place, range 0 to 2 + prependT IS FALSE, // prepend a T- or T+ to format (T- or T if showPlus is FALSE) + showPlus IS prependT. // by default only display "+" when prependT is TRUE + + LOCAL timeFormat IS timeFormats[formatType]. + LOCAL fixedPlaces IS timeFormat[0]. // number of required time places (e.g. HH:MM:SS has 3 fixedPlaces) + LOCAL stringList IS timeFormat[1]. // separators for each time place (must have at least fixedPlaces elements!) + + // start by rounding the input so we don't have to "carry the one" in time_converter + LOCAL roundingList IS LIST(MIN(rounding,timeFormat[2]), 0, 0, 0, 0). + SET timeSec TO ROUND(timeSec, roundingList[0]). + + LOCAL maxPlaces IS stringList:LENGTH. + LOCAL timeList IS time_converter(ABS(timeSec), maxPlaces). + LOCAL maxLength IS MIN(timeList:LENGTH, maxPlaces). + LOCAL returnString IS "". + + // fill in missing time places until format is reached + // e.g. for HH:MM:SS, given 4s, put 0 in HH and MM place + IF fixedPlaces > 0 { + UNTIL timeList:LENGTH >= fixedPlaces { + timeList:ADD(0). + } + SET maxLength TO MIN(timeList:LENGTH, maxPlaces). + } ELSE { + SET fixedPlaces TO maxLength. + } + + // add padding to each element in timeList and prepend to returnString + FROM {LOCAL i IS maxLength - fixedPlaces.} + UNTIL i >= maxLength STEP {SET i TO i + 1.} DO { + LOCAL paddedStr IS padding(timeList[i], leading0List[i], roundingList[i], FALSE, 1). + SET returnString TO paddedStr + stringList[i] + returnString. + } + + // the prependT strings have one space padding + IF prependT SET returnString TO returnString:INSERT(0, " "). + + // all time_string formats have either '+','-', or ' ' for padding + IF timeSec < 0 { + SET returnString TO returnString:INSERT(0, "-"). + } ELSE IF showPlus { + SET returnString TO returnString:INSERT(0, "+"). + } ELSE { + SET returnString TO returnString:INSERT(0, " "). + } + + IF prependT SET returnString TO returnString:INSERT(0, "T"). + + RETURN returnString. +} + + +LOCAL siPrefixList IS LIST(" y"," z"," a"," f"," p"," n"," μ"," m"," "," k"," M"," G"," T"," P"," E"," Z"," Y"). + +FUNCTION si_formatting { + PARAMETER num, // number to format, should be in range 10^-24 to 10^24 + unit IS "". // user supplied unit, output will prepend SI prefix + + IF num = 0 { + RETURN padding(num,1,3) + " " + unit. + } ELSE { + LOCAL powerOfTen IS MAX(MIN(FLOOR(LOG10(ABS(num))),26),-24). + LOCAL SIfactor IS FLOOR(powerOfTen / 3). + LOCAL trailingLength IS 3 - (powerOfTen - SIfactor * 3). + + SET num TO ROUND(num/1000^SIfactor,trailingLength) * 1000^SIfactor. + + SET powerOfTen TO MAX(MIN(FLOOR(LOG10(ABS(num))),26),-24). + SET SIfactor TO FLOOR(powerOfTen / 3). + SET trailingLength TO 3 - (powerOfTen - SIfactor * 3). + + LOCAL prefix IS siPrefixList[SIfactor + 8]. + RETURN padding(num/1000^SIfactor,1,trailingLength,TRUE,0) + prefix + unit. + } +} + + +LOCAL roundingFunctions IS LIST(ROUND @,FLOOR @,CEILING @). + +FUNCTION padding { + PARAMETER num, // number to be formatted + leadingLength, // minimum digits to the left of the decimal + trailingLength, // digits to the right of the decimal + positiveLeadingSpace IS TRUE, // whether to prepend a single space to the output + roundType IS 0. // 0 for normal rounding, 1 for floor, 2 for ceiling + + LOCAL returnString IS ABS(roundingFunctions[roundType](num,trailingLength)):TOSTRING. + + IF trailingLength > 0 { + IF returnString:CONTAINS(".") { + LOCAL splitString IS returnString:SPLIT("."). + SET returnString TO (splitString[0]:PADLEFT(leadingLength) + "." + splitString[1]:PADRIGHT(trailingLength)):REPLACE(" ","0"). + } ELSE { + SET returnString TO (returnString:PADLEFT(leadingLength) + "." + "0":PADRIGHT(trailingLength)):REPLACE(" ","0"). + } + } ELSE IF returnString:LENGTH < leadingLength { + SET returnString TO returnString:PADLEFT(leadingLength):REPLACE(" ","0"). + } + + IF num < 0 { + RETURN "-" + returnString. + } ELSE { + IF positiveLeadingSpace { + RETURN " " + returnString. + } ELSE { + RETURN returnString. + } + } +} \ No newline at end of file diff --git a/library/lib_num_to_str.ks b/library/lib_num_to_str.ks new file mode 100644 index 0000000..3a7b7cc --- /dev/null +++ b/library/lib_num_to_str.ks @@ -0,0 +1,48 @@ +// lib_num_to_str.ks can be used to to format a number in a string of constant length for use in a display. +// Copyright © 2015,2023 KSLib team +// Lic. MIT +@LAZYGLOBAL off. +@CLOBBERBUILTINS off. + +function num_to_str { + parameter + number, //input number + ip, //number of digits before the decimal point. + dp. //number of decimal places + + local string is "". + local padder is "". + local absNumber is abs(number). + local index is ip-1. + local firstNum is false. + until firstNum or index = 0 { // stop adding spacers when the first number is found + if mod(floor(absNumber/10^index),10) = 0 { + set padder to padder +" ". + } + else { + set firstNum to true. + } + set index to index-1. + }. + if dp = 0 { + set string to string +round(absNumber). + }. + else { +// set index to index-1. + set string to string +floor(absNumber). + set index to -1. + set string to string +".". + until index = -dp { + set string to string +mod(floor(absNumber/10^index),10). + set index to index-1. + }. + set string to string + mod(round(absNumber/10^index),10). + }. + if number < 0 { + set string to padder +"-" +string. + } + else { + set string to padder +" " +string. + }. + return string. +}. diff --git a/library/lib_number_dialog.ks b/library/lib_number_dialog.ks new file mode 100644 index 0000000..7bdc316 --- /dev/null +++ b/library/lib_number_dialog.ks @@ -0,0 +1,53 @@ +// lib_number_dialog.ks provides a dialog where the user can enter any number (including negative and fractional). +// Copyright © 2015 KSLib team +// Lic. MIT +run lib_gui_box. + +function open_number_dialog +{ + parameter title. + parameter number. + + local increment is 1. + + clearscreen. + + draw_gui_box(0, 0, terminal:width, 10). + + print "6/7 - number -/+" at (2, 5). + print "8/9 - increment -/+" at (2, 6). + print "0 - enter" at (2, 7). + + local old_decrease is ag6. + local old_increase is ag7. + local old_div10 is ag8. + local old_mul10 is ag9. + local old_enter is ag10. + local spaces is " ". + + until old_enter <> ag10{ + print title + " "+number+spaces at (2, 2). + print "Increment: " + increment+spaces at (2, 3). + + if old_increase <> ag7{ + set old_increase to ag7. + set number to number + increment. + } + if old_decrease <> ag6{ + set old_decrease to ag6. + set number to number - increment. + } + if old_div10 <> ag8{ + set old_div10 to ag8. + set increment to increment / 10. + } + if old_mul10 <> ag9{ + set old_mul10 to ag9. + set increment to increment * 10. + } + } + + clearscreen. + + return number. +} diff --git a/library/lib_pid.ks b/library/lib_pid.ks new file mode 100644 index 0000000..7c26e1c --- /dev/null +++ b/library/lib_pid.ks @@ -0,0 +1,87 @@ +// lib_pid.ks provides routines to implement a simple generic PID controller. +// Copyright © 2015,2016,2019,2023 KSLib team +// Lic. MIT +@LAZYGLOBAL off. +@CLOBBERBUILTINS off. + +HUDTEXT("lib_pid.ks has been superseded by the kOS inbuilt PIDloop() function.", 10, 2, 30, RED, FALSE). +wait 0.5. +HUDTEXT("It is maintained for example purposes only, please see the kOS documentation for more.", 10, 4, 30, RED, FALSE). + +function PID_init { + parameter + Kp, // gain of position + Ki, // gain of integral + Kd, // gain of derivative + cMin, // the bottom limit of the control range (to protect against integral windup) + cMax. // the the upper limit of the control range (to protect against integral windup) + + local SeekP is 0. // desired value for P (will get set later). + local P is 0. // phenomenon P being affected. + local I is 0. // crude approximation of Integral of P. + local D is 0. // crude approximation of Derivative of P. + local oldT is -1. // (old time) start value flags the fact that it hasn't been calculated + local oldInput is 0. // previous return value of PID controller. + + // Because we don't have proper user structures in kOS (yet?) + // I'll store the PID tracking values in a list like so: + // + local PID_array is list(Kp, Ki, Kd, cMin, cMax, SeekP, P, I, D, oldT, oldInput). + + return PID_array. +}. + +function PID_seek { + parameter + PID_array, // array built with PID_init. + seekVal, // value we want. + curVal. // value we currently have. + + // Using LIST() as a poor-man's struct. + + local Kp is PID_array[0]. + local Ki is PID_array[1]. + local Kd is PID_array[2]. + local cMin is PID_array[3]. + local cMax is PID_array[4]. + local oldS is PID_array[5]. + local oldP is PID_array[6]. + local oldI is PID_array[7]. + local oldD is PID_array[8]. + local oldT is PID_array[9]. // Old Time + local oldInput is PID_array[10]. // prev return value, just in case we have to do nothing and return it again. + + local P is seekVal - curVal. + local D is oldD. // default if we do no work this time. + local I is oldI. // default if we do no work this time. + local newInput is oldInput. // default if we do no work this time. + + local t is time:seconds. + local dT is t - oldT. + + if oldT < 0 { + // I have never been called yet - so don't trust any + // of the settings yet. + } else { + if dT > 0 { // Do nothing if no physics tick has passed from prev call to now. + set D to (P - oldP)/dT. // crude fake derivative of P + local onlyPD is Kp*P + Kd*D. + if (oldI > 0 or onlyPD > cMin) and (oldI < 0 or onlyPD < cMax) { // only do the I turm when within the control range + set I to oldI + P*dT. // crude fake integral of P + }. + set newInput to onlyPD + Ki*I. + }. + }. + + set newInput to max(cMin,min(cMax,newInput)). + + // remember old values for next time. + set PID_array[5] to seekVal. + set PID_array[6] to P. + set PID_array[7] to I. + set PID_array[8] to D. + set PID_array[9] to t. + set PID_array[10] to newInput. + + return newInput. +}. diff --git a/library/lib_raw_user_input.ks b/library/lib_raw_user_input.ks new file mode 100644 index 0000000..f55d418 --- /dev/null +++ b/library/lib_raw_user_input.ks @@ -0,0 +1,58 @@ +// lib_raw_user_input.ks provides a low level library to do user input via action groups. I believe it supports AGX to. +// Copyright © 2015,2020,2023 KSLib team +// Lic. MIT +// Originally developed by abenkovskii +@LAZYGLOBAL OFF. +@CLOBBERBUILTINS OFF. + +run lib_exec. + +function wait_for_action_groups +{ + parameter ag_list. // list of strings + + function first_diff + { + parameter list_a. + parameter list_b. + + local iter_a is list_a:iterator. + local iter_b is list_b:iterator. + + until not (iter_a:next and iter_b:next) + { + if iter_a:value <> iter_b:value + { + break. + } + } + local result is iter_a:index. + if iter_a:atend or iter_b:atend + { + set result to -1. + } + return result. + } + + local arg_string is "". + local separator is "". + + for ag_name in ag_list + { + set arg_string to arg_string + separator + ag_name. + set separator to ", ". + } + + lock _raw_input_ag_list to -1. + execute("lock _raw_input_ag_list to list(" + arg_string + ")."). + local old_values is _raw_input_ag_list. + + local result is -1. + until result <> -1 + { + set result to first_diff(old_values, _raw_input_ag_list). + wait 0. + } + + return result. +} diff --git a/library/lib_realchute.ks b/library/lib_realchute.ks new file mode 100644 index 0000000..1585f08 --- /dev/null +++ b/library/lib_realchute.ks @@ -0,0 +1,13 @@ +// lib_realchute.ks - adds support for RealChute +// Copyright © 2015,2023 KSLib team +// Lic. MIT + +@LAZYGLOBAL OFF. +@CLOBBERBUILTINS OFF. + +declare function R_chutes { + parameter event. + for RealChute in ship:modulesNamed("RealChuteModule") { + RealChute:doevent(event). + }. +}. diff --git a/library/lib_running_average_filter.ks b/library/lib_running_average_filter.ks new file mode 100644 index 0000000..e6f61e5 --- /dev/null +++ b/library/lib_running_average_filter.ks @@ -0,0 +1,50 @@ +// lib_running_average_filter.ks provides a function that will filter out noise from a dataset by storing a number of previous values of that dataset and outputting the average (mean) of those values. +// Copyright © 2015,2023 KSLib team +// Lic. MIT +//Authored by space_is_hard + +//These functions will set up and implement a running +//average filter that will help filter out noise in a +//continuously-updating set of data. It's useful for +//removing noise from a dataset that is expected to remain +//relatively stable. It will lag behind any large or +//continuous changes in the dataset + +@LAZYGLOBAL OFF. +@CLOBBERBUILTINS OFF. + +//Builds the list that will be passed on to the filter +FUNCTION running_average_filter_init { + PARAMETER + maxLength, //How many previous values should be stored and averaged + initValue. //What starting values to use to build the list of [maxLength] units long + + LOCAL inputList TO LIST(). + + FROM {LOCAL i TO 0.} UNTIL i >= maxLength STEP {SET i TO i + 1.} DO { + inputList:ADD(initValue). + }. + + RETURN inputList. + +}. + +//Takes in a new value and spits out an average of it and the previous values +FUNCTION running_average_filter { + PARAMETER + inputList, //The list we built with PA_filter_init + newValue. //The next value to tack onto the list + + inputList:REMOVE(0). //Removes the first item and bumps the rest down + inputList:ADD(newValue). //Adds the new value onto the end + + //Adds all of the values in the list together + LOCAL sum TO 0. + FOR item IN inputList { + SET sum TO sum + item. + }. + + //Returns the average of all of the values in the list + RETURN sum / inputList:LENGTH. + +}. \ No newline at end of file diff --git a/library/lib_seven_seg.ks b/library/lib_seven_seg.ks new file mode 100644 index 0000000..14bde01 --- /dev/null +++ b/library/lib_seven_seg.ks @@ -0,0 +1,89 @@ +// lib_seven_seg.ks prints a seven segment display on the terminal showing the input value at the location specified. +// Copyright © 2015,2019 KSLib team +// Lic. MIT +function seven_seg { + parameter + num, + col, + row. + if num:istype("scalar") { + if num > -1 { + if num < 5 { + if num < 2 { + if num < 1 { + print "_" at (col+1,row). + print "| |" at (col,row+1). + print "|_|" at (col,row+2). + } else { + print " " at (col+1,row). + print " |" at (col,row+1). + print " |" at (col,row+2). + } + } else { + if num < 4 { + if num < 3 { + print "_" at (col+1,row). + print " _|" at (col,row+1). + print "|_ " at (col,row+2). + } else { + print "_" at (col+1,row). + print " _|" at (col,row+1). + print " _|" at (col,row+2). + } + } else { + print " " at (col+1,row). + print "|_|" at (col,row+1). + print " |" at (col,row+2). + } + } + } else { + if num < 7 { + if num < 6 { + print "_" at (col+1,row). + print "|_ " at (col,row+1). + print " _|" at (col,row+2). + } else { + print "_" at (col+1,row). + print "|_ " at (col,row+1). + print "|_|" at (col,row+2). + } + } else { + if num < 9 { + if num < 8 { + print "_" at (col+1,row). + print " |" at (col,row+1). + print " |" at (col,row+2). + } else { + print "_" at (col+1,row). + print "|_|" at (col,row+1). + print "|_|" at (col,row+2). + } + } else if num < 10 { + print "_" at (col+1,row). + print "|_|" at (col,row+1). + print " |" at (col,row+2). + } else { + hudtext("ERROR: [lib_seven_seg.ks] number: " + num +" is out of bounds high" , 10, 2, 30, RED, FALSE). + } + } + } + } else { + hudtext("ERROR: [lib_seven_seg.ks] number: " + num +" is out of bounds low" , 10, 2, 30, RED, FALSE). + } + // special cases + } else if num:ISTYPE("string") { + if num = "b" { //"b" = blank + print " " at (col+1,row). + print " " at (col,row+1). + print " " at (col,row+2). + } else if num = "-" { //"-" = minus + print " " at (col+1,row). + print " _ " at (col,row+1). + print " " at (col,row+2). + } else {// else if num = // add more special cases here. + hudtext("ERROR: [lib_seven_seg.ks] unrecognized string: " +num, 10, 2, 30, RED, FALSE). + } + } else { + hudtext("ERROR: [lib_seven_seg.ks] unrecognized type: " +num:typename, 10, 2, 30, RED, FALSE). + } +} diff --git a/library/lib_str_to_num.ks b/library/lib_str_to_num.ks new file mode 100644 index 0000000..7a6ec53 --- /dev/null +++ b/library/lib_str_to_num.ks @@ -0,0 +1,63 @@ +// lib_str_to_num.ks can be used to convert a stringified number back into a legitimate number for use in computation. +// Copyright © 2015,2023 KSLib team +// Lic. MIT +@LAZYGLOBAL off. +@CLOBBERBUILTINS off. + +local num_lex is lexicon(). + +num_lex:add("0", 0). +num_lex:add("1", 1). +num_lex:add("2", 2). +num_lex:add("3", 3). +num_lex:add("4", 4). +num_lex:add("5", 5). +num_lex:add("6", 6). +num_lex:add("7", 7). +num_lex:add("8", 8). +num_lex:add("9", 9). + +function str_to_num { + parameter s. + + // Handle negative numbers + if s:startswith("-") { + return str_to_num(s:substring(1,s:length-1)) * -1. + } + + // Scientific Notation + local e is s:find("e"). + if e <> -1 { + local m is s:substring(e+1,1). + if m <> "+" and m <> "-" { return "NaN". } + local p is s:split("e" + m). + if p:length <> 2 { return "NaN". } + local p0 is str_to_num(p[0]). + local p1 is str_to_num(p[1]). + if p0 = "NaN" or p1 = "NaN" { return "NaN". } + if m = "+" { + return p0 * 10^p1. + } else { + return (p0 / 10^p1). + } + } + + // Decimals + if s:contains(".") { + local p is s:split("."). + if p:length <> 2 { return "NaN". } + local p0 is str_to_num(p[0]). + local p1 is str_to_num(p[1]). + if p0 = "NaN" or p1 = "NaN" { return "NaN". } + return p0 + (p1 / (10^p[1]:length)). + } + + // Integers (match on tokens, and bit-shift) + local val is 0. + for i IN s:split(""):sublist(1,s:length) { + if num_lex:haskey(i) { set val to val + num_lex[i]. } else { return "NaN". } + set val TO val * 10. + } + return val / 10. + +} diff --git a/library/lib_term.ks b/library/lib_term.ks new file mode 100644 index 0000000..b878998 --- /dev/null +++ b/library/lib_term.ks @@ -0,0 +1,66 @@ +// lib_term.ks - terminal manipulations +// Copyright © 2015,2023 KSLib team +// Lic. MIT +// Original starting work by Github user: Dunbaratu + +@lazyglobal off. +@clobberbuiltins off. + +// ----------- +// char_line +// ----------- +// Perform a low level line drawing algorithm +// with characters on the terminal. +function char_line { + parameter + ch, // string (character) to draw with. + x0, y0, x1, y1. // locations to draw from/to + + local dX is x1-x0. + local dY is y1-y0. + local len is sqrt(dX^2+dY^2). + local incSize is 2. // make loop only execute once if it's just a point. + if len > 0 { + set incSize to 1/len. // or let it execute a number of times if it's not. + } + local d is 0. + until d > 1 { + print ch at (d*dX+x0, d*dY+y0). + set d to d + incSize. + }. +} + + +// Draw an arc of an ellipse, using characters. +// Note that since the screen's Y axis goes bigger going +// DOWN, the result may be inverted from what you'd imagine. +function char_ellipse_arc { + parameter + ch, // string (character) to draw with. + x0, y0, xRad, yRad, deg0, deg1. + + // swap if arc degrees are given backward: + if deg0 > deg1 { + local tmp is deg0. + set deg0 to deg1. + set deg1 to tmp. + }. + // incSize is the biggest number of degrees that is guaranteed + // to be just barely small enough not to skip a character when drawing. + local longRad is max(xRad,yRad). + local incSize is arcsin(1/longRad). + + local d is deg0. + until d > deg1 { + print ch at (xRad*cos(d)+x0, yRad*sin(d)+y0). + set d to d + incSize. + }. +}. + +// Draw a circle. This is a special case of char_ellipse_arc. +function char_circle { + parameter + ch, + x0, y0, rad. + char_ellipse_arc(ch, x0, y0, rad, rad, 0, 360). +}. diff --git a/poweredLanding.ks b/poweredLanding.ks new file mode 100644 index 0000000..4708bb7 --- /dev/null +++ b/poweredLanding.ks @@ -0,0 +1,58 @@ + +//-126 +function DeOrbit{ + parameter burnLng, deltaV. + + lock pos to SHIP:geoposition. + + lock curLng to pos:lng. + + DECLARE LOCAL tgtLng IS burnLng - 5. + if curLng > burnLng or curLng < tgtLng { + SET WARPMODE TO "RAILS". + SET WARP TO 3. + print "warping to " + tgtLng. + WAIT UNTIL curLng > tgtLng and curLng < burnLng. + SET WARP TO 0. + print "arived at dst " + tgtLng. + print "burn at " + burnLng. + } + print "currently at" + curLng. + + BRAKES ON. + RCS ON. + LOCK steering to retrograde. + wait until curLng >= burnLng. + +} + +function CalculateBurnDuration{ + parameter dv. + + declare local isp is GetIsp(). + declare local finalMass is mass * exp(-dv / (isp * constant:g0)). + declare local twr is ship:maxThrust / (((mass + finalMass)/2) * constant:g0). + + + local t is dv / (twr * constant.g0). + return t. +} + +function GetIsp{ + LIST ENGINES IN allEngines. + declare local totalThrust is ship:maxThrust. + local sum is 0. + local weights is 0. + for eng in allEngines{ + if eng:IGNITION and not eng:flameout{ + local w is eng:AVAILABLETHRUST / totalThrust. + local ispW is eng:isp * w. + set sum to sum + ispW. + set weights to weights + w. + } + } + return sum / weights. +} + +// DeOrbit(-126, 250). +print CalculateBurnDuration(250). \ No newline at end of file diff --git a/radenLaunch.ks b/radenLaunch.ks new file mode 100644 index 0000000..3c9a673 --- /dev/null +++ b/radenLaunch.ks @@ -0,0 +1,52 @@ +run "library/lib_math". + +clearScreen. +BRAKES on. +print "Preparing to launch". +from { local c is 5.} until c = 0 step {set c to c - 1.} do { + print c. + wait 1. +} +print "Launching!". +lock throttle to 100. +lock steering to heading(90, 0, 0). + +print "Phase: Takeoff". +wait until surfaceSpeed > 140. + +print "Phase: Rotate". +lock steering to heading(90, 3, 0). +wait 1. +print "Gear up". +gear off. + +print "Phase: Athmospheric Climb". +lock steering to heading(90, 15, 0). +wait until altitude > 20000. + +print "Phase: Mode Switch". +ag1 on. //Switch engine mode +lock steering to heading(90, 25, 0). +wait until apoapsis > 80000. + +print "Phase: Sub-Orbital Coast". +lock throttle to 0. +lock steering to prograde. + +when altitude > 70000 then { + print "Deploying Solar". + ag2 on. //deploy solar + print "Opening Docking Port". + ag3 on. //open docking port +} + +lock distToApo to apoapsis - altitude. +wait until distToApo < 100. +print "Phase: Circularize". +lock throttle to 100. +wait until periapsis > 79000. +unlock throttle. + +print "In orbit. Releasing controls". + +