Jump to content

MAFman

Members
  • Posts

    328
  • Joined

  • Last visited

Posts posted by MAFman

  1. 11 minutes ago, Steven Mading said:

    It's impossible to tell from that information.  Can you post the log to see the rest of the error dump, and what the kerboscript code was doing?  The full dump won't just show the error message, but will show where in the code it happened.

     

    Where would I find this log?

  2. 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?

  3. 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.

  4. 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.

  5. 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...

  6. 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...).

  7. 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...

  8. 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?

  9. 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.
    }

     

  10. 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)

    [email protected]

    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.

    8pMJOLA.png

    The laser can work over long distances - here it's measuring the

    distance from a Kerbin orbit vessel to the Mun:

    Qrq5Ttl.png

    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:

    WL1FOyG.png

    The direction of the laser is whichever way the laser gun is

    pointed when you mounted it on the craft, as demonstrated here:

    XvLJIHn.png

    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:

    GI3y7yt.png

    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:

    NwzwldV.png

     

    • 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.

    Electronics.png. 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?

  11. fa5.jpg

    // 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:

    fa5.jpg

    
    // 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...

  12. 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?

  13. 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...

    Error Image

    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...

    Error Image

    This happens no matter what I am doing in-game, or even if I'm doing anything at all.

  14. 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.

  15. 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").
    			}
    		}
    	}
    }

     

  16. 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().

×
×
  • Create New...