-
Posts
328 -
Joined
-
Last visited
Content Type
Profiles
Forums
Developer Articles
KSP2 Release Notes
Bug Reports
Posts posted by MAFman
-
-
I found a potential bug in kOS, where I try to create a maneuver node from a list of four values, and it returns the error "Cannot iterate on an object of type kOS.Safe.Encapsulation.ScalarIntValue. What's going on?
-
I can get the orbital period of the current orbit and the transfer directly from the game. What I need help with is how to calculate what longitude I need to start the burn at.
-
How do I calculate the surface longitude where I start my transfer burn to geostationary altitude, given the surface longitude where I want my satellite to end up, my current altitude and orbital period, and my current longitude? I'm trying to write a kOS program that puts a satellite into a precise-ish slot in geostationary orbit.
My probe is powered by a single ion engine and has a wet mass of 1.726 tonnes.
-
I'm trying to revive someone else's kOS code for rover pathfinding using A*, but their code is so messy I can't make heads or tails of it. Can someone help me? The Reddit post featuring the code is here:
-
I'm trying to write a general one size fits all launch script based around Powered Explicit Guidance, but the math is way over my head, and I'm not quite patient enough to write all those formulas...
Can someone help me develop this? I want the final product to be able to pilot a ship from the launch pad to an orbit defined by apoapsis. periapsis, inclination, longitude of ascending node, and argument of periapsis, with the first two being the only mandatory arguments.
I am using a heavily modified (mostly trimmed) variant of Seth Persigehl's launch script for the in-atmospheric flight, including open loop pitch control and open loop heading control.
PEG will kick in once the second stage starts, and the rocket's engine will not shutdown or throttle significantly until cutoff - the rocket will make orbit in one long burn.
-
Is there a way to edit BV so that it drives my rover while I have it active, not just when I'm looking at it from the tracking station? Currently, when I tell it to go somewhere, and switch to the 'roving' rover, it just sits there with controls locked...Also, I found the pathfinding doesn't actually mark any of the tiles as unwalkable, leading to the rover going over mountains and through the ocean...
-
PizzaOverHead, if you're still having issues with gimbal lock comma I would suggest using quaternions. While more complicated, as they make full use of the imaginary number syste, using quaternions instead of Euler angles almost completely eliminates gimbal lock.
Can you share the Github link? I want to see if I can help.
-
I was thinking of pulling a heightmap directly from the game and using A* to search through it, using the brightness per pixel (height) and the change in altitude over the change in coordinates as a heuristic, as well as arbitrarily assigning infinite cost to points in the ocean. Currently, I have the HeightMapExtractor mod working on pulling a 32K heightmap (which it's been working on for the last two days...).
-
KerbalMaps is up again!!! http://www.kerbalmaps.com/
Say, does anyone have ideas how I could use KerbalMaps to navigate a rover autonomously with an A* search? I'd need the data to be accurate to within 10 meters so the rover can avoid cliffs and the ocean...
-
I'm trying to make a tool that takes two geocoordinates as input and spits out the shortest and safest path between the two (as a queue of points from the end to the start) kinda like GPS routing. How do I do that?
-
5 hours ago, Rocket In My Pocket said:
Sounds like mods are breaking your game.
Remove all your mods and see if the problem persists.
If getting rid of them solves your problem, you'll have to add them back in one at a time and figure out which mod or mods was causing the problem. Best of luck!
Turns out the culprit WAS RemoteTech...I wonder if I could download the source code, make some changes, and push it to the master branch...
-
IDK whether this counts as a bug report or just a help-me, but I've been having an issue lately with the escape and mapview keys.
Whenever I hit "escape" to pause the game, it doesn't pause, but instead completely freezes the camera and locks all controls. When I enter Task Manager, everything looks absolutely normal. Something similar happens when I try to go to the map, but in addition to locking everything up, it also completely breaks the map and ship view, replacing the camera with a weird, Jello-like blob that vaguely resembles Kerbin - and I can't cancel the map view unless I hit Ctrl-Alt-Delete and click "End Task" on the KSP process...
I found a possible solution. In the KSP.log file, I found that RemoteTech2 was absolutely spamming the crap out of everything with "I'm connected - no wait, I'm not...no wait...etc..." messages. Is there a method to disable this?
-
Is there a way to just hack-unlock all the levels?
-
Sorry if I'm necro-posting, but I'm working on a rover navigation / SLAM library. Does my code make sense?
BTW, lib_circle_nav is from KSLib.
@lazyglobal off. import("lib_circle_nav"). // lib_a_star.ks finds the shortest path from a start node to a target node. // Due to the nature of the algorithm, it will also avoid hills and cliffs. // Define what a node is first. function worldNode{ parameter gridX, gridY. // Example: worldNode(5, 10) gives me a point 5 grid marks to the right of the ship, and 10 marks forward. local d is 2 * planet:radius. local c is constant:pi * d. local offset is (50 * 360) / c. // The nodes are 50 meters apart. local nodeX is offset * gridX. local nodeY is offset * gridY. local there is circle_destination(circle_destination(offset, 90, body:radius), offset, 0, body:radius). return lex("X", nodeX, "Y", nodeY, "alt", there:terrainheight, "gCost", gCost(), "hCost", hCost(), "fCost", (gCost() + hCost())). } // Then create the grid. function grid local _height is 10. local _width is 19. local spacing is 50. local startPoint is ship:geoposition. // Center the grid on the ship local _grid is list(). for {local i is (-_height / 2).}{until i >= (_height / 2)}do{ for{local j is (-_width / 2).}{until j >= (_width / 2)}do{ _grid:add(worldNode(i, j)). } } return _grid. } function endNode{ parameter grid. for n in grid{ // Pick the node on the far side of the grid with the lowest line-of-sight slope to the current node. } } function gCost{ parameter start is worldNode(0, 0), thisNode. // 3D Pythagoras: Distance^2 = deltaX^2 + deltaY^2 + deltaZ^2. return sqrt((thisNode["X"] - start["X"])^2 + (thisNode["X"] - start["X"])^2 + (thisNode["alt"] - start["alt"])^2). } function hCost{ parameter thisNode, end. // 3D Pythagoras: Distance^2 = deltaX^2 + deltaY^2 + deltaZ^2. return sqrt((thisNode["X"] - end["X"])^2 + (thisNode["X"] - end["X"])^2 + (thisNode["alt"] - end["alt"])^2). } function fCost{ parameter start is worldNode(0, 0), thisNode, endNode. return gCost(thisNode) + hCost(thisNode, end). } // The actual pathfinding algorithm function pathfinding{ parameter endNode, grid is localGrid() startNode is worldNode(0, 0). local openList is list(). local closedList is list(). local path is list(). return path. } function driveTo{ parameter point. }
-
4 hours ago, Steven Mading said:
Yes. In fact that's its main purpose.
WOOHOO!! XD
So, would I need infernal robotics to make lidar units that I've seen on robots such as the Neato?
-
Is it possible to use this mod for, for example, LIDAR or SLAM-based navigation?
-
On 7/15/2014 at 3:20 AM, Steven Mading said:
LaserDist
[WIP, Plugin, Parts] (2014-10-16) LaserDist 0.5 for KSP 0.9, Alpha
Releases:
* https://github.com/Dunbaratu/LaserDist/releases
ZIP Download (See release page above):
* https://github.com/Dunbaratu/LaserDist/releases/download/v0.4/LaserDist.zip
Source Code Hosting site:
* https://github.com/Dunbaratu/LaserDist
License:
* GPL v3
Copyright © 2014 Steven Mading (aka Dunbaratu on Github)
This is a very small plugin. It makes a KSP Part that
measures straight line distances by laser.
The "Beamer 100x Disto-o-meter" Part aims a laser in a line
and then measures the distance in meters to the first object
the laser hits. The result is displayed in the right-click
menu for the part.
The laser can work over long distances - here it's measuring the
distance from a Kerbin orbit vessel to the Mun:
And even at those very long distances, the lasers can measure
rather small differences in distace, within reason (it has
an accuracy of 2 meters at this extreme range, to keep the
game from bogging down too much). In the screenshot below,
notice how the two lasers are returning different numbers for
their distance to the terrain very far away:
The direction of the laser is whichever way the laser gun is
pointed when you mounted it on the craft, as demonstrated here:
The electric draw for the laser is substantial. Each laser consumes
1 electric charge every 3 seconds that it's on.
Why does this Mod exist?
The intended purpose of this part is to be used in conjunction with
scripted autopilots like [kOS](https://github.com/KSP-KOS/KOS/releases), to
provide a way to for you to write scripted pilot software that can
see the distance to the ground (or anything else like a ship) along
the laser line. The reason this can be useful is so you can detect
things like terrain slope and mountains in the way. The default
radar altimiter in KSP only shows you the distance directly under
the craft.
In a nutshell, the purpose is to solve this problem shown in this picture:
This mod can let you read the distance along the blue line in the diagram.
Why isn't it inside kOS then?
There is more than one KSP mod project for the purpose of letting
users write scripted autopilots. Another such project currently under
development is Jebnix
My goal is to make this part script-engine-agnostic so it works with
any such mod. I've been working in kOS mostly, but I didn't want this
part to be kOS-specific because there's no particular reason it has
to be.
Information for other modders trying to use the part:
As long as the scripting autopilot is designed to be able to query
and/or set KSPFields on parts, it should be able to read the value of
the distance, and turn the laser on and off, as follows:
- KSPField: 'Distance' is a float - the number of meters being shown in the display. It's -1 if there is currently no hit.
- KSPField: 'HitName' is a string - the name of the object being hit.
- KSPField: 'Activated' is a bool - true if the measuring device is on.
- KSPField: 'DrawLaser' is a bool (called "Visible" in the GUI) - true if the laser should be drawn when it's activated or false if it should be (realistically) invisible because, hey, it's coherent light and it's not supposed to be seen from the side.
- KSPField: 'CPUGreedyPercent' is a float (called "CPU hog" in the GUI) ranging from 0.0 to 20.0. It's how much of a single physics tick of time this mod will allow itself to consume during a single Update. If it hasn't gotten a good enough answer within that much time, it will wait until the next update to continue the calculation.
-
KSPField: 'UpdateAge' is an integer - It's how many Unity Updates (animation frames, basically) it's been since the value you are seeing was calculated. Becuase of the logic of CPUGreedyPercent (see above) sometimes the value being displayed is stale by a few update ticks and shouldn't be trusted until the next time that UpdateAge becomes zero again. If you're in a situation where this mod needs to spend more than 1 update of time to get to a good answer for the distance, you'll see this value spinning a bit, quickly going 0,1,2,3,0,1,2,3,0,1,2,3...etc. When you see that, only when it hit the zeros was the distance value perfectly correct at THAT moment.
Note: The higher that CPUGreedyPercent ("CPU hog") is, the less likely it is that UpdateAge will ever be nonzero, but the bigger hit your framerate might take.
How to Mount it.
. The Beamer 100x Dist-o-Meter is located in the "Electronics" tech node of the career tech tree. It's a 300-point node on the tree so you might not have it yet in a new fresh career game.
The Laser can be mounted anywhere as a surface-mount item. Take care to
note the orientation of the laser emiiter. (KSP lets you fine-tune
the rotation of a part by using the SHIFT key while you hit the WASDQE
keys.)
The Laser will bounce back and give you a distance measurement when
it encounters ANY object, including parts of your own craft. So
take care to mount it somewhere where the laser beam will have a clear
line of sight without obstruction.
To ensure a good mounting point, you can use "tweakables" to enable the
laser and make it show up in the VAB to look and see if you have it aimed
well.
Lightspeed
Note that if you use it to measure the distance to a far away body (i.e.
like aiming it at Duna from Kerbin), the mod does take into account
lightspeed. You have to hold the laser on an object steady and unchanged
for the entire duration of time it takes for lightspeed delay to
bounce the signal back or you won't get a measurement, so using it at that
great distance will be very hard.
FUTURE EXPANSION PLANS (i.e. the reason this is a WIP)
-
Max distance isn't enforced yet: It's part of a future plan
to have different variants of the part that work better the
higher up the tech tree you go. For now, despite what it says,
the range is actually infinite. -
Other sorts of sensors?: Now that the numerical approximation behind
making a "fake raycast" that finds intersections with the planet terrain
from far away and from any angle is implemented, this opens up the
chance that other sorts of long range beam sensors could be made.
For example a "biome detector" that returns the name of the biome
where the hit occurred is a possibility, as is a "density at a distance"
measurement which might tell you the atmospheric pressure or density
at the ground level where the hit occurred. (Maybe because of something
that the laser detects about interference with the air? It's a bit
hard to justify realistically how that would work, but it's definitely
possible with the software. Just not sure if it's possible in the
real world, or whether real-world sanity is really the intent
of this mod or not.
How do I use it from my script then?
Very Soon Now the dev team of kOS (of which I'm part) plans to
have a release that lets you read the fields of any part's KSPFields
(The stuff you see in the rightclick popup user interface panels), and
manipulate any part's widgets on the rightclick menus (the buttons,
the sliders, etc).
The plan from the beginning was to make LaserDist be a good test case
of this feature that may actually be useful in its own right.
But that's not released just yet. So at the moment this is only
usable as a manual piloting aid where you leave the part menu open
and look at it by eye as you fly.
Part modeling help?
I am aware that the artwork on the model isn't pretty. I'm a
programmer, not a graphic artist, and I don't have experience
with things like Maya and Blender. In fact I just made the model
by slapping together some stretched Cube and Cylnder objects in
Unity itself, without the aid of a modeling program. The model
is good enough to work with, but I'd be happy to have someone
better at art redesign the model. I included the model in
the github directory if you want to have a look.
Is it possible or feasible to use this as a crude LIDAR syste for, example, SLAM / navigation?
-
// Ground navigation library import("lib_enum.ks"). import("lib_circle_nav.ks"). function aStarSearch{ // Will include multiple sub-functions parameter hdg. // Runs all at once, returning a path. global costS is 10. global costD is 14. createGrid(). set startPoint to startPoint(). set currentPoint to grid[startPoint:index]. set path to list(). until reachedGoal(){ // Set the current point as the parent set parentPoint to currentPoint. local childPoints is list(). // Assign eight child points from the existing grid: // 1 | 2 | 3 // ----+---------------+----- // 4 | (parent) | 5 // ----+---------------+----- // 6 | 7 | 8 // Calculate the g-, h-, and f-costs for each child for c in childPoints { gCost(c, parentPoint). hCost(c). fCost(c). } // Set the point with the lowest f-cost as the parent and add it to the path path:add(lowestFCostPoint()). set currentPoint to lowestFCostPoint(). // Rinse and repeat } return path. } function lowestFCostPoint { return . // The point whose fCost is the minimum of all points } function createGrid{ global startPoint is currentPoint(). global spacing is 25. // 25 meters apart local _height is 64. // 64 points vertically local _width is 64 // 64 points horizontally local start is 0 local iterationNum_x is 0. local iterationNum_y is 0. // Total points: 4,096 // Total height and width: 1,600 m (approx. 1 mile - total coincidence!) // Total grid area: 2,560,000 sq. m. (about 1 square mile) set grid to list(). until iterationNum_y >= _height{ until iterationNum_x >= _width{ // work on the rows first grid:add(point((start * iterationNum_x), (start * iterationNum_y), startPoint)). set iterationNum_x to iterationNum + 1. // Start a new column } grid:add(point((start * iterationNum_x), (start * iterationNum_y), startPoint)). set iterationNum_y to iterationNum_y + 1. // Start a new row } return grid. // Hopefully kOS has enough memory! (I hope my COMPUTER has enough memory!) } function reachedGoal{ parameter endPoint is endPoint(), currentPoint is currentPoint(). if currentPoint["fCost"] = 0{ return true. }else{ return false. } } function currentPoint{ // Runs every loop local here is ship:geoposition. local currentPoint is point(here:lat, here:lng). return currentpoint. } function endpoint{ local endPoint is point(). // Figures out which point is on the opposite side of the grid, with the lowest slope of points surrounding it // Runs at the same time as startpoint(). local endPoint["x"] is _width - startPoint["x"]. local endPoint["y"] is _height - startPoint["y"]. return endPoint. } function fCost{ parameter point. set point["fCost"] to point["gCost"] + point["hCost"]. } function gCost{ parameter point, start is startPoint(). set point["gCost"] to cost(start, point). } function hCost{ parameter point, end is endPoint(). set point["hCost"] to cost(point, end). } function cost{ parameter p1, p2. // From the point class local deltaX is p2["x"] - p1["x"]. local deltaY is p2["y"] - p1["y"]. local costX is deltaX * costS. local costY is deltaY * costS. local isDiagonal is false. if (deltaX and deltaY) <> 0 set isDiagonal to true. // If the point is directly diagonal, draw a diagonal line between the points // and return the length of that line times costD. local deltaDiag is round(sqrt(deltaX^2 + deltaY^2)). local costDiag is deltaDiag * costD. if isDiagonal{ return costDiag. } // Otherwise, return the sum of costX and costY else return (costX + costY). } function point{ // Poor-man's class // Points are initialized as offset relative to the point (0,0) on the planet parameter dX is 0, dY is 0, reference is latlng(0,0). local _x is (360 * dX) / (body:radius * 2 * constant:pi). local _y is (360 * dY) / (body:radius * 2 * constant:pi). local location is latlng(_x + reference:lat, _y + reference:lng). // *shrug* local _z is location:terrainheight. return lex("x", _x, "y", _y, "z", _z, "gCost", 0, "hCost", 0, "fCost", 0). }
3 minutes ago, MAFman said:// Ground navigation library import("lib_enum.ks"). import("lib_circle_nav.ks"). function aStarSearch{ // Will include multiple sub-functions parameter hdg. // Runs all at once, returning a path. global costS is 10. global costD is 14. createGrid(). set startPoint to startPoint(). set currentPoint to grid[startPoint:index]. set path to list(). until reachedGoal(){ // Set the current point as the parent set parentPoint to currentPoint. local childPoints is list(). // Assign eight child points from the existing grid: // 1 | 2 | 3 // ----+---------------+----- // 4 | (parent) | 5 // ----+---------------+----- // 6 | 7 | 8 // Calculate the g-, h-, and f-costs for each child for c in childPoints { gCost(c, parentPoint). hCost(c). fCost(c). } // Set the point with the lowest f-cost as the parent and add it to the path path:add(lowestFCostPoint()). set currentPoint to lowestFCostPoint(). // Rinse and repeat } return path. } function lowestFCostPoint { return . // The point whose fCost is the minimum of all points } function createGrid{ global startPoint is currentPoint(). global spacing is 25. // 25 meters apart local _height is 64. // 64 points vertically local _width is 64 // 64 points horizontally local start is 0 local iterationNum_x is 0. local iterationNum_y is 0. // Total points: 4,096 // Total height and width: 1,600 m (approx. 1 mile - total coincidence!) // Total grid area: 2,560,000 sq. m. (about 1 square mile) set grid to list(). until iterationNum_y >= _height{ until iterationNum_x >= _width{ // work on the rows first grid:add(point((start * iterationNum_x), (start * iterationNum_y), startPoint)). set iterationNum_x to iterationNum + 1. // Start a new column } grid:add(point((start * iterationNum_x), (start * iterationNum_y), startPoint)). set iterationNum_y to iterationNum_y + 1. // Start a new row } return grid. // Hopefully kOS has enough memory! (I hope my COMPUTER has enough memory!) } function reachedGoal{ parameter endPoint is endPoint(), currentPoint is currentPoint(). if currentPoint["fCost"] = 0{ return true. }else{ return false. } } function currentPoint{ // Runs every loop local here is ship:geoposition. local currentPoint is point(here:lat, here:lng). return currentpoint. } function endpoint{ local endPoint is point(). // Figures out which point is on the opposite side of the grid, with the lowest slope of points surrounding it // Runs at the same time as startpoint(). local endPoint["x"] is _width - startPoint["x"]. local endPoint["y"] is _height - startPoint["y"]. return endPoint. } function fCost{ parameter point. set point["fCost"] to point["gCost"] + point["hCost"]. } function gCost{ parameter point, start is startPoint(). set point["gCost"] to cost(start, point). } function hCost{ parameter point, end is endPoint(). set point["hCost"] to cost(point, end). } function cost{ parameter p1, p2. // From the point class local deltaX is p2["x"] - p1["x"]. local deltaY is p2["y"] - p1["y"]. local costX is deltaX * costS. local costY is deltaY * costS. local isDiagonal is false. if (deltaX and deltaY) <> 0 set isDiagonal to true. // If the point is directly diagonal, draw a diagonal line between the points // and return the length of that line times costD. local deltaDiag is round(sqrt(deltaX^2 + deltaY^2)). local costDiag is deltaDiag * costD. if isDiagonal{ return costDiag. } // Otherwise, return the sum of costX and costY else return (costX + costY). } function point{ // Poor-man's class // Points are initialized as offset relative to the point (0,0) on the planet parameter dX is 0, dY is 0, reference is latlng(0,0). local _x is (360 * dX) / (body:radius * 2 * constant:pi). local _y is (360 * dY) / (body:radius * 2 * constant:pi). local location is latlng(_x + reference:lat, _y + reference:lng). // *shrug* local _z is location:terrainheight. return lex("x", _x, "y", _y, "z", _z, "gCost", 0, "hCost", 0, "fCost", 0). }
I have no idea what I'm doing...
-
17 hours ago, CliftonM said:
Make sure you aren't running it in Program Files, because it'll need to run as administrator if it is. It appears to be trying to save something, but not having permission to do so.
It is installed on my desktop. I didn't think of running it as an administrator, though. I'll try that.
What does the flag "RTLEnterCriticalSection" mean, BTW?
-
I usually run the 64- bit player, and I always get a crash-to-desktop within 5 minutes of starting the game. Attached below is a screenshot of the Visual Studio debug window explaining the error. Hopefully you understand it better than me...
4 minutes ago, MAFman said:I usually run the 64- bit player, and I always get a crash-to-desktop within 5 minutes of starting the game. Attached below is a screenshot of the Visual Studio debug window explaining the error. Hopefully you understand it better than me...
This happens no matter what I am doing in-game, or even if I'm doing anything at all.
-
Ok! I did find a one-size-fits-all satellite launching script!
-
10 minutes ago, ZooNamedGames said:
First off, what's the goal of each rover? I understand that they are trying to reach a set location but why? To expand your telecom network? To gather science? To simply do that?
Lastly, oceans are going to be an almost impossible a challenge seeing as the land will merge with it anywhere from a 3° angle to 50°+. So your rover, without prior design will drive itself in the ocean. What I suggest is you calculate how far it is from the KSC to your goal and set your rover to stop at that distance. If it stops short then you drive it manually the short distance.
The goal of the rovers is to be a passive relay for the RemoteTech signal.
Also, hmmm...can I get the biome of a LATLNG()? Maybe I can do that, and if the biome of an option is "Water", it'll skip that option.
-
Improvement? I didn't change the other scripts; just the way I iterate over the lists.
PARAMETER direct. SWITCH TO 1. DELETE startup.ks. UNTIL NOT ADDONS:RT:HASCONNECTION(SHIP) { POPULATE(direct). SEARCH(options). DRIVE_TO(nextPoint). } UNTIL ADDONS:RT:HASCONNECTION(SHIP) { POPULATE(direct + 180). SEARCH(options). DRIVE_TO(nextPoint). } FUNCTION POPULATE { PARAMETER whichWay. SET here TO SHIP:GEOPOSITION. SET offSet TO (360 * 100) / (2 * CONSTANT:PI * BODY:RADIUS). SET preOptions TO LIST // An arc 270 degrees wide, 100m in radius ( LATLNG(here:LAT + offset, here:LNG), // forward LATLNG(here:LAT + (SIN(22.5) * offset), here:LNG + (COS(22.5) * offset)), // 22.5 deg. left LATLNG(here:LAT + (SIN(22.5) * offset), here:LNG - (COS(22.5) * offset)), // 22.5 deg. right LATLNG(here:LAT + (SIN(45) * offset), here:LNG + (COS(45) * offset)), // 45 deg. left LATLNG(here:LAT + (SIN(45) * offset), here:LNG - (COS(45) * offset)), // 45 deg. right LATLNG(here:LAT + (SIN(57.5) * offset), here:LNG + (COS(57.5) * offset)), // 57.5 deg. left LATLNG(here:LAT + (SIN(57.5) * offset), here:LNG - (COS(57.5) * offset)), // 57.5 deg. right LATLNG(here:LAT + (SIN(90) * offset), here:LNG + (COS(90) * offset)), // 90 deg. left LATLNG(here:LAT + (SIN(90) * offset), here:LNG - (COS(90) * offset)), // 90 deg. right LATLNG(here:LAT + (SIN(112.5) * offset), here:LNG + (COS(112.5) * offset)), // 112.5 deg. left LATLNG(here:LAT + (SIN(112.5) * offset), here:LNG - (COS(112.5) * offset)), // 112.5 deg. right LATLNG(here:LAT + (SIN(135) * offset), here:LNG + (COS(135) * offset)), // 135 deg. left LATLNG(here:LAT + (SIN(135) * offset), here:LNG - (COS(135) * offset)) // 135 deg. right ). // Now rotate the whole thing until it aligns with "whichWay" SET options TO LIST(). FOR o IN preOptions { // Do some matrix magic options:ADD(LATLNG((o:LAT * SIN(whichWay)) - (o:LNG * COS(whichWay)), (o:LAT * COS(whichWay)) + (o:LNG * SIN(whichWay)))). } RETURN options. } FUNCTION SEARCH { PARAMETER options. SET sortedBySlope TO LIST(). SET start TO 0. UNTIL start > options:LENGTH { SET here TO SHIP:GEOPOSITION. SET there TO options[start]. SET frac TO (here:TERRAINHEIGHT - there:TERRAINHEIGHT) / 100. SET slope_a TO ABS(ARCTAN(frac)). SET there_last TO options[start - 1]. // The option before this one IF there_last:INDEX > 0 { SET frac_b TO (here:TERRAINHEIGHT - there_last:TERRAINHEIGHT) / 100. SET slope_b TO ABS(ARCTAN(frac_b)). IF slope_a < slope_b // If the current slope is less than the last slope, { sortedBySlope:INSERT(0, there). // Put it into the list at the zero-th position } ELSE // If the current slope is greater than the last slope, { sortedBySlope:INSERT(0, there_last). // Put the last slope into the list at the zero-th position } SET start TO start + 1. } ELSE { SET start TO start + 1. } } SET sortedByAngle TO LIST(). SET start TO 0. UNTIL start > sortedBySlope:LENGTH - 1 { // Sort the options again, this time by angle between the point and the direction SET currentPoint TO sortedBySlope[startAgain]. SET lastPoint TO sortedBySlope[startAgain - 1]. // The option before this one IF currentPoint:INDEX > 0 { SET theta TO ABS(whichWay - currentPoint:HEADING). SET last_theta TO ABS(whichWay - lastPoint:HEADING). IF theta < last_theta { sortedByAngle:INSERT(0, currentPoint). } ELSE { sortedByAngle:INSERT(0, lastPoint). } } SET start TO startAgain + 1. } // Run through both lists, and if there is a match, // i.e. there is a point whose corresponding point // in the other list is at the same index, // set the next waypoint to the match SET ind TO 0. UNTIL ind > options:LENGTH { SET a TO sortedBySlope[ind]. SET b TO sortedByAngle[ind]. IF a = b { SET nextPoint TO sortedBySlope[ind]. RETURN nextPoint. } ELSE { SET ind TO ind + 1. } } SET nextPoint TO sortedBySlope[0]. // If none of the conditions are true, just go with the point with the lowest slope... RETURN nextPoint. } FUNCTION DRIVE_TO { PARAMETER point. SET steerPID TO PIDLOOP(). SET throttlePID TO PIDLOOP(). SET steerPID:SETPOINT TO point:HEADING. SET throttlePID:SETPOINT TO 2.5. UNTIL point:DISTANCE < 10 { SET WHEELTHROTTLE TO throttlePID:UPDATE(TIME:SECONDS, GROUNDSPEED). SET WHEELSTEER TO steerPID:UPDATE(TIME:SECONDS, SHIP:HEADING). } STOP(). } FUNCTION STOP { SET WHEELSTEER TO 0. SET WHEELTHROTTLE TO 0. IF NOT BRAKES { BRAKES ON. } WAIT UNTIL GROUNDSPEED < 0.1. AG1 OFF. NOTIFY("Arrived!", "NOMINAL"). // Turn the cherry light off and tell us we've arrived } FUNCTION PARK { STOP(). // Activate all non-dipole antennas, and deactivate all dipoles FOR p IN SHIP:PARTS { SET mods TO p:MODULES. FOR m IN mods { IF m:NAME = "ModuleRTAntenna" AND p:NAME <> "RTShortAntenna1" { m:DOEVENT("activate"). } ELSE IF m:NAME = "ModuleRTAntenna" AND p:NAME = "RTShortAntenna1" { m:DOEVENT("deactivate"). } } } }
-
I modified my script...AGAIN... but now it's throwing an error...
Boot:
// First, set the ship to a known configuration. SET SHIP:CONTROL:PILOTMAINTHROTTLE TO 0. LIGHTS OFF. GEAR OFF. FOR p IN SHIP:PARTS { IF p:TITLE = "RoveMax Model M1" OR p:TITLE = "RoveMax Model S2" OR p:TITLE = "RoveMax Model XL3" OR p:TITLE = "TR-2L Ruggedized Vehicular Wheel" { SET SHIP:TYPE TO "rover". } } IF SHIP:TYPE = "rover" { BRAKES ON. WHEN SHIP:SENSORS:LIGHT < 0.75 THEN { LIGHTS ON. PRESERVE. } WHEN SHIP:SENSORS:LIGHT > 0.75 THEN { LIGHTS OFF. PRESERVE. } FOR p IN SHIP:PARTS { IF p:NAME = "fuelCell" OR p:NAME = "fuelCellArray" { p:GETMODULE("ModuleResourceConverter"):DOACTION("Start Fuel Cell", TRUE). } } } ELSE { BRAKES OFF. } SAS OFF. RCS OFF. FUNCTION NOTIFY { PARAMETER message, priority. IF priority = "NOMINAL" { SET col TO GREEN. } ELSE IF priority = "ALERT" { SET col TO RGB((255/255), (114/255), 0). // Safety orange } ELSE IF priority = "WARNING" { SET col TO RED. } HUDTEXT("kOS: " + message, 2, 2, 30, col, FALSE). } FUNCTION ON_DRIVE { PARAMETER n, vol. SWITCH TO vol. LIST FILES IN allFiles. FOR f IN allFiles { IF f:NAME = n { SWITCH TO 1. RETURN TRUE. } } SWITCH TO 1. RETURN FALSE. } FUNCTION DELAY { SET dTime TO ADDONS:RT:DELAY(SHIP). SET accTime TO 0. // Accumulated time UNTIL accTime >= dTime { SET start TO TIME:SECONDS. WAIT UNTIL (TIME:SECONDS - start) > (dTime - accTime) OR NOT ADDONS:RT:HASCONNECTION(SHIP). SET accTime TO accTime + TIME:SECONDS - start. } } // Get a file from KSC FUNCTION DOWNLOAD { PARAMETER f. DELAY(). IF ON_DRIVE(f, 1) { DELETE f. } IF ON_DRIVE(f, 0) { SWITCH TO 0. COPY f TO 1. SWITCH TO 1. } } // Put a file on KSC FUNCTION UPLOAD { PARAMETER f. DELAY(). IF ON_DRIVE(f, 0) { SWITCH TO 0. DELETE f. SWITCH TO 1. } IF ON_DRIVE(f, 1) { COPY f TO 0. } } // Run a library, downloading it from KSC if necessary FUNCTION REQUIRE { PARAMETER lib. IF NOT ON_DRIVE(lib, 1) { DOWNLOAD(lib). } RENAME lib TO "new_library.ks". RUN new_library. RENAME "new_library.ks" TO lib. } // THE ACTUAL BOOTUP PROCESS SET updateScript TO SHIP:NAME + ".update.ks". // If we have a connection, see if there are new instructions. If so, download and run them. IF ADDONS:RT:HASCONNECTION(SHIP) { IF ON_DRIVE(updateScript, 0) // If the update script exists on the archive, download and run it. { IF ON_DRIVE("update.ks", 1) // If an old file exists on the archive { NOTIFY("An old update file already exists. Deleting old file...", "ALERT"). WAIT 5. DELETE update.ks. } DOWNLOAD(updateScript). SWITCH TO 0. DELETE updateScript. SWITCH TO 1. // Whichever situation is true, we now have an up-to-date file NOTIFY("Update file found! Executing...", "NOMINAL"). WAIT 5. RENAME updateScript TO "update.ks". RUN update.ks. DELETE update.ks. } ELSE // We don't have an update script, so do nothing. { NOTIFY("No update file found. Either you haven't created one or one is not necessary.", "ALERT"). } // If a startup.ks file exists on the disk, run that. SET bootScript TO SHIP:NAME + ".startup.ks". IF ON_DRIVE(bootScript, 1) { SWITCH TO 1. DELETE bootScript. // If we already have an old startup file, delete it. IF ON_DRIVE(startup.ks, 1) { SWITCH TO 1. DELETE startup.ks. } IF ON_DRIVE(bootScript, 0) { DOWNLOAD(bootScript). RENAME bootScript TO "startup.ks". RUN startup.ks. } ELSE { NOTIFY("Startup failed! Instructions not found.", "WARNING"). WAIT 5. REBOOT. } } ELSE IF ON_DRIVE(bootScript, 0) { IF NOT ON_DRIVE(bootScript, 1) { DOWNLOAD(bootScript). RENAME bootScript TO "startup.ks". RUN startup.ks. DELETE startup.ks. } ELSE { DELETE bootScript. DOWNLOAD(bootScript). RENAME bootScript TO "startup.ks". RUN startup.ks. DELETE startup.ks. } } } ELSE { WAIT UNTIL ADDONS:RT:HASCONNECTION(SHIP). // Avoid thrashing the CPU (when no startup.ks, but we have a persistent connection, it will continually reboot) WAIT 5. REBOOT. }
Startup:
// Relay rover startup file // Lists the number of active "relay" rovers // and bases its navigation software on that. SWITCH TO 1. DELETE boot.ks. // Download the appropriate libraries DOWNLOAD(autoNav.ks). // List the active relays LIST TARGETS IN allVessels. SET relays TO LIST(). FOR t IN allVessels { IF t:NAME = "Relay" AND t:TYPE = "rover" { relays:ADD(t). } } // Decide which direction to go IF relays:LENGTH = 0 { RUN autonav(270). } ELSE { SET iterationNum TO relays:LENGTH + 1. IF iterationNum = 1 { RUN autonav(0). } ELSE IF iterationNum = 2 { RUN autonav(270). } ELSE IF iterationNum = 3 { RUN autonav(180). } ELSE { SET iterationNum TO MOD(iterationNum, 3) + 1. // It can be 1, 2, or 3. No more, no less. } }
Autonav:
PARAMETER direct. SWITCH TO 1. DELETE startup.ks. UNTIL NOT ADDONS:RT:HASCONNECTION(SHIP) { POPULATE(direct). SEARCH(options). DRIVE_TO(nextPoint). } UNTIL ADDONS:RT:HASCONNECTION(SHIP) { POPULATE(direct + 180). SEARCH(options). DRIVE_TO(nextPoint). } FUNCTION POPULATE { PARAMETER whichWay. SET here TO SHIP:GEOPOSITION. SET offSet TO (360 * 100) / (2 * CONSTANT:PI * BODY:RADIUS). SET preOptions TO LIST // An arc 270 degrees wide, 100m in radius ( LATLNG(here:LAT + offset, here:LNG), // forward LATLNG(here:LAT + (SIN(22.5) * offset), here:LNG + (COS(22.5) * offset)), // 22.5 deg. left LATLNG(here:LAT + (SIN(22.5) * offset), here:LNG - (COS(22.5) * offset)), // 22.5 deg. right LATLNG(here:LAT + (SIN(45) * offset), here:LNG + (COS(45) * offset)), // 45 deg. left LATLNG(here:LAT + (SIN(45) * offset), here:LNG - (COS(45) * offset)), // 45 deg. right LATLNG(here:LAT + (SIN(57.5) * offset), here:LNG + (COS(57.5) * offset)), // 57.5 deg. left LATLNG(here:LAT + (SIN(57.5) * offset), here:LNG - (COS(57.5) * offset)), // 57.5 deg. right LATLNG(here:LAT + (SIN(90) * offset), here:LNG + (COS(90) * offset)), // 90 deg. left LATLNG(here:LAT + (SIN(90) * offset), here:LNG - (COS(90) * offset)), // 90 deg. right LATLNG(here:LAT + (SIN(112.5) * offset), here:LNG + (COS(112.5) * offset)), // 112.5 deg. left LATLNG(here:LAT + (SIN(112.5) * offset), here:LNG - (COS(112.5) * offset)), // 112.5 deg. right LATLNG(here:LAT + (SIN(135) * offset), here:LNG + (COS(135) * offset)), // 135 deg. left LATLNG(here:LAT + (SIN(135) * offset), here:LNG - (COS(135) * offset)) // 135 deg. right ). // Now rotate the whole thing until it aligns with "whichWay" SET options TO LIST(). FOR o IN preOptions { // Do some matrix magic options:ADD(LATLNG((o:LAT * SIN(whichWay)) - (o:LNG * COS(whichWay)), (o:LAT * COS(whichWay)) + (o:LNG * SIN(whichWay)))). } RETURN options. } FUNCTION SEARCH { PARAMETER options. SET sortedBySlope TO LIST(). FOR o IN options { // Sort the options by slope, with lowest first. SET here TO SHIP:GEOPOSITION. SET there TO o. SET frac TO (here:TERRAINHEIGHT - there:TERRAINHEIGHT) / 100. SET slope_a TO ABS(ARCTAN(frac)). SET there_last TO . // The option before this one SET frac_b TO (here:TERRAINHEIGHT - there_last:TERRAINHEIGHT) / 100. SET slope_b TO ABS(ARCTAN(frac_b)). IF slope_a < slope_b // If the current slope is less than the last slope, { sortedBySlope:INSERT(0, there). // Put it into the list at the zero-th position } ELSE // If the current slope is greater than the last slope, { sortedBySlope:INSERT(0, there_last). // Put the last slope into the list at the zero-th position } } SET sortedByAngle TO LIST(). FOR s IN sortedBySlope { // Sort the options again, this time by angle between the point and the direction SET currentPoint TO s. SET lastPoint TO . // The option before this one SET theta TO ABS(whichWay - currentPoint:HEADING). SET last_theta TO ABS(whichWay - lastPoint:HEADING). IF theta < last_theta { sortedByAngle:INSERT(0, currentPoint). } ELSE { sortedByAngle:INSERT(0, lastPoint). } } // Now run through both lists, and pick the first point whose index is the same in both // If the above condition cannot be met, pick the first point on sortedBySlope(). // Magic! :D // I really hope this works... FOR this IN sortedBySlope { SET that TO sortedByAngle[this:INDEX]. IF this:INDEX = that:INDEX { SET nextPoint TO sortedBySlope[this:INDEX]. RETURN nextPoint. } } SET nextPoint TO sortedBySlope[0]. RETURN nextPoint. } FUNCTION DRIVE_TO { PARAMETER point. SET steerPID TO PIDLOOP(). SET throttlePID TO PIDLOOP(). SET steerPID:SETPOINT TO point:HEADING. SET throttlePID:SETPOINT TO 2.5. UNTIL point:DISTANCE < 10 { SET WHEELTHROTTLE TO throttlePID:UPDATE(TIME:SECONDS, GROUNDSPEED). SET WHEELSTEER TO steerPID:UPDATE(TIME:SECONDS, SHIP:HEADING). } STOP(). } FUNCTION STOP { SET WHEELSTEER TO 0. SET WHEELTHROTTLE TO 0. IF NOT BRAKES { BRAKES ON. } WAIT UNTIL GROUNDSPEED < 0.1. AG1 OFF. NOTIFY("Arrived!", "NOMINAL"). // Turn the cherry light off and tell us we've arrived } FUNCTION PARK { STOP(). // Activate all non-dipole antennas, and deactivate all dipoles FOR p IN SHIP:PARTS { SET mods TO p:MODULES. FOR m IN mods { IF m:NAME = "ModuleRTAntenna" AND p:NAME <> "RTShortAntenna1" { m:DOEVENT("activate"). } ELSE IF m:NAME = "ModuleRTAntenna" AND p:NAME = "RTShortAntenna1" { m:DOEVENT("deactivate"). } } } }
It's saying I can't get the index of a LATLNG, although I'm trying to get the index of a list value, which just so happens to be a LATLNG().
kOS bug
in KSP1 Mods Discussions
Posted
Where would I find this log?