Notice to YSPilots/YSFLIGHT

Notice to YSPilots/YSFLIGHT
Legacy Pack available under the YSFLIGHT category.
Any individual requests for a model must be made to my email address, see bottom of the page..
Enjoy!
Skippy

Sunday 7 January 2018

Computer Craft, Tinkers Construct revisited


So I left my little contraption running over night, with the hope that it'd finish emptying out my ores. Well, it was half right. I came down to a casting basin half full... This wasn't meant to happen! It was only supposed to cast when there was enough in the smeltery to fill a basin. I looked at my code again:


if quantity > 1296 then
print(quantity)
redstone.setOutput("front",true)
sleep(5)
redstone.setOutput("front",false)
print(quantity)
sleep(5)
end

Nope, that clearly says if the quantity is greater than 1294 (9 ingots, or enough for a block of that material), then it should open the valve, so why is it still opening when there is less than 9 ingots available?
Because the quantity value doesn't update.
If we refer back to our main code:

local tank = peripheral.wrap("left")
local tankTable = tank.getTankInfo()[1]
local contentsTable = tankTable["contents"]
local quantity = contentsTable["amount"]
while true do

if quantity > 1296 then
print(quantity)
redstone.setOutput("front",true)
sleep(5)
redstone.setOutput("front",false)
print(quantity)
sleep(5)
end

end



The first bit, the local variables, are only called at the start of the program - so the value does not change! I should've noticed this when I looked at the printed quantity on the screen, but truth be told, I didn't look.
So, solutions? I could just move the variables into the "while true do" so they're called every cycle - but that's not very elegant. Instead, I went for the complex approach, and made some functions while I was at it...
First off, I timed how long I actually needed for the casting table to fill, cool and empty (15 seconds), and made a function that would just open the faucet for exactly 15 seconds. The faucet is rising edge triggered - so it only goes off when the redstone input goes high, you can drop it straight back down to low again, ready for the next lot - so my function just pulses it on for 1 seconds, then switches it off again for for a set amount of time. I wanted to be able to easily reuse the function, so I didn't hardcode the time into the function.

function on(side,time)
redstone.setOutput(side,true)
sleep(1)
redstone.setOutput(side,false)
sleep(time-1)
end


What does it do?! So, the name of the function is "on" - imaginative! and it takes 2 variables to run it - the side your rednet cable is on (or whatever way you're transmitting your redstone signal), and the time you need it on for. In my case, the rednet cable was on the back of the computer, and it was on for 15 seconds, so calling the command was a simple matter of writing - on("back", 15) and it'd
switch the faucet on for exactly the right amount of time to fill, cool and empty.

So the function takes those 2 variables, and first sets the side you gave it to high for 1 second - then it goes back to low. Now it waits for the time you gave it (minus the 1 seconds it spent setting it high). That's it for that bit!
So, next function I made took care of the issue I had with the first one - that the quantity wasn't updating. in this one, I just moved all the tank/table variables into there... So it looks like this:

function getQuantity(drainObject)
local tank = drainObject.getTankInfo()[1]
local contentsTable = tank["contents"]
local quantity = contentsTable["amount"]


return quantity

end




I made a new variable - drainObject, which passes the wrapped drain block to the function. This way, if I use something like a wired modem to do it more remotely, I can just pass it that, rather than specifically giving it a side, or hardcoding the side.
So next up - it makes a bunch of local variables that are only accessible from within the function, and don't affect anything outside it. (so I could have another quantity variable used in another function or main script, and it wouldn't take the same value)

The key thing here is the return quantity bit. Basically, I can use the function getQuantity(drainObject) as a variable - so my if statement from the previous one (if quantity > 1296 then do stuff) can use the getQuantity instead of having to define another variable.

I did make another variable for the ingot/casting volume, which is 1296mB or 9 ingots. I did this so I can just change that value to 144 if I wanted to cast single ingots or 576 if I wanted to cast gears.


local ingotVol = 1296 -- 1296 is volume of a full block in mB, for ingots use 144, for gears use 576

The -- bit just is a comment, so it doesn't have any effect on the code, and is only there for the user.

Then I have my loop and if statement:

while true do

if getQuantity(drain) > ingotVol then
print("Contents is: "..getQuantity(drain))
on("back",15)
print("Contents is now: "..getQuantity(drain))
else
print("Quantity is less than 1 block")
sleep(15)
end
end


So I'm using my getQuantity() function instead of making a new quantity variable, and I'm using my ingotVol variable to tell me the size of the ingot/block/gear I'm making. Then I'm just printing off the quantity again before, switching the faucet on (with our on() function), and printing the quantity again after, to verify that it went down by the correct volume (maybe I should've done a difference... nvm, next time).

Another new thing is the else bit. This is my catch for if there isn't enough metal in the smeltery to make another ingot/gear/block. If that is the case, it just writes on the screen that there isn't enough for a block, and waits for 15 seconds before trying again.


So, future plans for it? Multi- faucet maybe... and maybe get it to plan if there is enough material for full blocks, and if not, uses a ingot instead of a block, ensuring a fully empty smeltery...


Modems as well, so I can centralise it.. And maybe a monitor to show how much material there is in the tank, and how many ingots it has made? Or maybe I'll just leave it there.

Link as always: https://pastebin.com/1vFwdPNm

Friday 5 January 2018

Computer Craft and Tinkers Construct (Minecraft)

I've been experimenting a bit with Tinkers Construct and ComputerCraft in Minecraft.
If you don't know what any of those 3 things are, this post is not for you!

The problem I've been trying to solve is this: I've got masses of molten metal in the smeltery, and I want to automatically drain it into a casting basin. I could do this manually, but I've got a lot of metal!
I should also preface this by saying I know very little about Lua, the programing language of ComputerCraft, possibly hence my difficulty solving the issue.

Now, computercraft interfaces with tinkers by way of the OpenPeripherals mod - which gives access to commands such as getTankInfo().

Through OpenPeripherals, it is possible to talk to the Tinkers Smeltery - by way of the drain port for some reason.

So, this is the setup - I've got a computer next to the drain port
I've not yet implemented the actual triggering of the faucet, for now I'm just trying to read the contents of the tanks!

Now, there were a few examples online where people had done apparently similar things, but I couldn't make them work! So I started from scratch.. more or less.

In the lua commandline I could get peripheral.call("left","getTankInfo")to give me a huge long list of all the tasty goodness I wanted to know from the smeltery, but dropping this into an actual application, along the lines of:

tank = peripheral.wrap("left")

print(tank.getTankInfo())
gave me nothing, well, it gave me this: table:4d09e6ec...

So, it must be a table then!

To iterate through a table in lua is 

for key, value in pairs(yourTable) do
   print("here is your key: "..key.." and here is your value          "..value)
end

So adapting it for my use:

for key, value in pairs(tank.getTankInfo()) do
   print("here is your key: "..key.." and here is your value          "..value)
end

No. No such luck. Attempt to concatenate string and table... Its got a table, within a table?! What fresh madness is this?! Okay, lets take table 1 from that then! I read that there are 2 tables for each tank, the main tank with all the smeltery contents, and the fuel tank (lava), and the first one is the smeltery contents. So, table 1 it is!

for key, value in pairs(tank.getTankInfo()[1]) do
   print("here is your key: "..key.." and here is your value          "..value)
end
The [1] bit just lets us take the first key in the table, so the first table of tables, and then iterate through that table.

What did this give us?

here is your key: capacity and here is your value 11792
smeltery:4 attempt to concatenate string and table

WHAT?! again?! A table in a table in a table?! 
So first off, I want an actual object for the table, not tank.getTankInfo()... so I'm just going to have 
tankTable = tank.getTankInfo()[1]

So, this should give us the table for the smeltery contents, and it goes something like:
capacity: 11792
>another table containing who knows what!

Lets go back to the lua commandline again, and do our peripheral.call("left","getTankInfo")again and have a closer look:

The bit at the bottom that I can see reads:

{
  capacity = 1728,
  contents = {
    rawName = "Molten Lead",
    amount = 1728,
    name = "lead.molten",
    id = 187,
    }
}
Very annoying that blogger doesn't let me do tabs.. but never mind!
So, this is mirroring what we had been seeing, the first key is "capacity", with its value being whatever the capacity in the smeltery is, then is a table, and thats as far as it let me get before. But now we can see the contents of the table! (appropriately called, contents)

So within the contents table is the name of the material, its capacity (again, mirroring the "capacity key") , another name (lead.molten), and the ID, which I'm assuming is the minecraft item ID (nope, 187 is IC2.blockWall... no idea then!)

Anyway! We know the name of the table in the table now! So I'm going to try and get the contents table out as a separate object again, so I'm just going to go:

tankTable = tank.getTankInfo()[1]
contentsTable = tankTable["contents"]

And now, we iterate through the contents table:

for key, value in pairs(contentsTable) do
   print("here is your key: "..key.." and here is your value          "..value)
end

What do we get:

here is your key: rawName and here is your value Molten Iron
here is your key: amount and here is your value 1680
here is your key: name and here is your value iron.molten
here is your key: id and here is your value 170

It works!
Interestingly, this is the material at the bottom of the smeltery contents, if I change the material at the bottom to bronze:
raw name becomes "Molten Bronze" - so the first table tank.getTankInfo()[1] is the "active" or bottom material in the smeltery - and if I do tank.getTankInfo()[2] I get the 2nd material! Nothing to do with the fuel tanks!

So, now I've figured out how to get the table in a table in a table, I'm going to use my contentsTable object for now:

print(contentsTable["amount"])

What do we get?

1680

Woop woop! it works! I can have that as a variable now, and use that in an if statement to open and close the faucet when there is enough metal to fill a casting basin!
For that I'm going to use a rednet cable just to pass the redstone signal through to the faucet.


  1. local tank = peripheral.wrap("left")
  2. local tankTable = tank.getTankInfo()[1]
  3. local contentsTable = tankTable["contents"]
  4. local quantity = contentsTable["amount"]
  5. while true do
  6.   if quantity > 1296 then
  7.     print(quantity)
  8.     redstone.setOutput("front",true)
  9.     sleep(5)
  10.     redstone.setOutput("front",false)
  11.     print(quantity)
  12.     sleep(5)
  13.   end
  14. end

Ooo, nice code formatting! So this is copied from the pastebin I put up: https://pastebin.com/MTdVf8nz
The first bit I've explained....
From line 5. I've not explained yet.... while true do just repeats the rest infinitely.

if quantity > 1296 then
    print(quantity)
    redstone.setOutput("front",true)
    sleep(5)
    redstone.setOutput("front",false)
    print(quantity)
    sleep(5)
end

What this bit does is:
First: if the quantity value from our table is greater than 1296mB (which is 9 ingots), then it is going to do the rest:
print(quantity) is just going to write the quantity on the screen for me
redstone.setOutput("front",true) - this is where my computer placement kind of screwed up... The rednet cable came out the front... Not so pretty, but it works! So, this basically just outputs a redstone signal fron the front of the computer, if you put redstone dust on the ground, it'll light up. The signal is going through the rednet cable to the faucet, so at this point, it opens the faucet.
sleep(5) - just hang about for a bit while the faucet is doing its thing
redstone.setOutput("front", false) - switch the faucet off again, resetting it for the next load, then it prints the quantity again to the screen (was more for debugging than anything...), hang about for a bit with another sleep(5), and then do it all again!

So yea! It works nicely! Another little thing though - the casting basin normally needs emptying by hand when it's finished casting - but if you put a hopper underneath, it empties itself - put an itemduct under that going into your chest/storage, and

Sunday 12 March 2017

QGIS TPI Based Landform Classification Style

In SAGA GIS (and by extension - QGIS) there is a tool called Topographic Position Index.
It is a tool that assigns an index value based upon the land form feature - ridges and peaks are assigned positive values, and valleys are assigned negative values.

In addition to this tool - there is a TPI based landform classification - which takes that Position index, and classifies the landscape into different land forms, such as:

  • Canyons, deeply incised streams,
  • Mid-slope drainage, shallow valleys
  • Upland drainage, headwaters
  • U-shaped valleys
  • Plains
  • Open slopes
  • upper slopes/messas
  • local ridges/hills in valleys
  • midslope ridges, small hills in plains
  • Mountain tops and high ridges

Basically, I've created a QGIS style based upon the map used in the poster by A. Weiss.
3D plot over OS Terrain 50
© Crown Copyright and Database Right (2017). Ordnance Survey

Map view of TPI Landform Classification based on OS Terrain 50

The TPI Based Landform Classification is useful for determining the landform characteristics for a given area - and by classifying them into set categories, one can compare landforms in different areas. 

To use the tool you need a DEM, and QGIS. 
The tool is located in the Processing Toolbox under SAGA>Terrain Analysis - Morphology > TPI based landform classification


Friday 19 August 2016

YSPilots Podcast Releases

There have been some requests on YSFHQ for the old YS Pilots Podcast from... 2006?
I've therefore re uploaded them to the internet for future generations to learn from our mistakes.

1st Season:
YSP Podcast 1-12

2nd Season:
YSP Podcast 13-14

...and something that I think never made it to the final cut:
Seraphim's reading of part of the YS Story (I've no idea what happened to the previous parts)
YS Story: Mitchy pt 2


Good times.

Monday 16 May 2016

Recap360 Pro to QGIS (Quantum GIS) - The hard way

I've recently obtained Autodesk Recap360. It's basically a software package that stitches images from drones/multiple viewpoints together to make a 3D model. I think it's the slightly more advanced version of 123D Catch.

I've also got a drone, and in June of 2014 I flew a series of missions with it over my home. Since then I've not really known what to do with the large selection of images (overall, about 1000), so they've just sat on my hard drive waiting for something to happen.

Something happened when I was trying to make a bid for a drone for work. I had to price up the software available, and the hardware (...the flying bit). I looked at the usual suspects for drone imagery, Pix4D, Agisoft etc, but a new one popped up, Autodesk's Recap360.

Their system works in the cloud, so you upload your images to their servers, and tell it what to do, and about an hour later you get a lovely email saying it's done.
At the moment, their online viewing and editing tools are out of action, so I've not tried their georeferencing tools online (which is hopefully the easy way...). I went for the hard way.


When you process your data, there is the option for either a quick preview (which I guess is perfect for just making sure it'll all come out okay) or ultra (which allows you to do all the fancy stuff, including download the orthomosaics). Ultra costs credits, which I assume you top up and pay for (because I'm a teacher, I'm using it for education (it'll be used to teach my students about DEMs next year) I get it for free.)

When it's processed, you've got the option to download the products, such as OBJ files (I guess you could use it for making 3D prints of stuff too, which is cool), but more interesting is the .TIF download option.

What you get is 2 TIFs, one orthomosaic and one elevation model.

I use QGIS 2.8.1 as my main GIS software, which can happily open Tiffs. so it made sense to import them into that.
This is where it got a bit harder.
The orthomosaic went in just fine, 3 layers, red green and blue, just fine. They all looked good in the GIS (Apart from being mirrored, and un-georeferenced). Both of these problems were easily rectified by georeferencing it to Google Maps.
It referenced pretty well - with only 4 points, I had pretty good accuracy. I didn't do any serious work on it, just a quick 4 points in the corners, and checked it against where the roads were on an OS map, and it looked reasonable.
There are some oddities, such as the fence bordering on the road, but this is actually because the OS Street View map I'm using is an old one, and we moved the fence between the road and the path.
Apart from that, the fences line up well, etc etc. Not bad for 4 control points!
So far so easy.
Now things got a little trickier.
The Digital Surface Model.
I imported it the same way, it's a Tiff too, but I was greeted with this:
There were no values assigned to it, and when I opened the properties, I ended up with "bad allocation" and no property box. I tried exporting and reimporting it, no joy, still unchanged.
In the end, I thought, "I'll do this manually" and saved it as a .asc file. If you don't know, an .asc, or ASCII file is basically a text file, you can open it in Notepad, and edit it, but when you import it into a GIS, it is an elevation/raster file again. It's basically just a text file full of pixel values.
Because it is a text file, you can edit it easily in Notepad. The problem, I suspected, was the fact that the transparent parts of the image were set to "#INF", rather than -9999 which they are often set to, I guess QGIS doesn't like #INF as a numerical value, and maybe this was the reason it was throwing an exception.
So, find "#INF" and replace with "-9999". Suddenly, QGIS was quite happy opening this new file with -9999 as the transparent colour. 
I could now change all the colours in QGIS, and georeference it as I pleased, but.... it ran on a scale from -3.7 to 2.8. So the lowest bit of the map was -3.7m below sea level, and the highest was just 2.8m. This is the North Yorkshire Moors! It should be higher than that by at least 100!
It was also reading -3.7 for the highest place, and 2.8 for the lowest.... 
I used QGIS's Raster Calculator to do "1 - the DSM layer" to invert it, so now it was the right way, but the scale was still wrong. 
If I'd paid more attention in maths, I would know an easier way of doing this, but I didnt... So instead I compared another digital elevation model of the same place, and compared 2 areas: I looked at what my drone DSM said, and what the real one did, and came up with:
DSM value of -2.7083 corresponded with an actual height of 175.889m,
DSM value of 0.913 corresponded with an actual height of 249.6m.
Now I just had to figure out how to scale them both up, and now my brain left me.
So, I had to do it a slightly more cheaty, boring way, I made a graph.

Very kindly, excel can put the formula on the graph too, and this is the formula I could use to calculate all heights from the weird values on the DSM.
y (real height) = 20.352 x the value on the DSM + 231.02. I'm sure there was an easier way of doing this, but it worked.
From this, I could calculate what the actual minimum and maximum values should be, from the -3.7 and +2.8. I thought it might allow me to simply put this formula into the Raster Calculator, and just create a new layer from that, but the result had the same "nan" and the error....
Luckily QGIS has a tool for the job. Enter "Grid Normalisation".
It obviously allows me to normalise the data, and luckily it does it from a known maximum and minimum, which we'd calculated using the formula.
This done, and my DSM was finally showing the correct height values! It was still in the wrong place, and mirrored, but hey! at least it was the right value! (+/- a few meters...)

Now it was just a matter of georeferencing it (which was done against the outline of the orthomosaic, as it is a hell of a job trying to georeference a flat field on a DSM... it is just featureless....).
The resolution is quite a bit lower on the DSM than the Ortho - the ortho is about 11cm, the DSM is 50cm. But, overall, it made a pretty little map, so I'm happy!

This was just from 150 of the photos, so I may try and run it again with more. ReCap360 has an upper limit of 250 photos, so it isn't going to be many more, but I might try and chose some from areas that aren't so well represented or are a little fuzzy.
The orthomosaic also has some odd artefacts around trees, where they look oddly pixelated, I think it is because of the way it is constructed. The images are formed into a 3D model, with flat faces, so if there is a slight lack of detail, the model is incomplete, and when viewed from above, it looks slightly like a model from a Playstation 2 game.
I can live with it though, for now.

I'm not sure what I'm going to use my newly georeferenced orthomosaic for...We shall see... but it's nice to have found a method that can potentially process drone images in an occasionally reliable way...
Now I just need to get the drone flying again

Wednesday 4 February 2015

Fitting a DCC decoder to an Arnold 2242 Chassis

So, I’ve got an Arnold 2242 Chassis under a Five79 Quarry Hunslet body,

(See here: http://www.five79.co.uk/ RC-4) and I want to convert it to DCC control, so.. Here goes.

What I’ve got is a LokPilot V4.0 Micro (Which I got from ebay), and my Arnold 2242 chassis. So. Lets begin.

image

Once the body is removed, make sure the two contacts (Marked above) are straightened out (And ensure the motor is insulated from the)

image

Then, solder the red and black wires onto the bronze contacts. They should go on pretty easily, and as long as you dont hang about with the soldering iron, it’ll be reet!

Will look like this:

 image

The orange and grey wires from the decoder are the next, and these need to be soldered onto the motor contacts.

image

This is how they should look after being soldered on.

Watch out on this bit. This part of the motor is made of plastic! You’ll need to be very quick with the soldering iron to make sure you don’t melt it!

That’s pretty much it! I routed the wires under the motor, and secured them in place with regular PVA glue in case I need to get them out again.

image

These can then be routed up into the cab, where I’ve got the decoder.

image

The only down side of this is that the cab looks like a rats nest…. But hey! It somehow works!

At the moment I dont have my DCC controller, so I’m unable to test it.

Wednesday 25 June 2014

Dinorwic Office Building scale drawing

Here is my second drawing. It is an office on Australia Level in Dinorwic quarry. The internal wall has a large opening, possibly for a counter or similar. Walls are slate slabs, similar to other buildings, but rendered over the top.

DSC_7106

^ My interpretation

image

DSC_7093

Pete Wilson’s original drawings ^

 

==-- Download Drawing Here (PDF)--==