Because my University seems to have worse internet connectivity than my house
During my day job as sys-admin for the a Particle Physics Group, I recently was wrapped on the knuckles by central IT for one of our users saturating the whole university's bandwidth. My first reaction was of surprise that they didn't have traffic throttling in place already but this turned to incredulity when I learnt that they only had a 1Gb/s connection for the WHOLE CAMPUS and one of our guys just downloading some LHC data from a couple of fast sites in the UK had brought the entire system to it's knees. Consequently, I was asked to stop people doing this (!) until the network had been upgraded to 10Gb/s. I decided that setting up some traffic shaping on our machines was probably a better idea.
A quick search led to this page that described how to use the tc command provided by the iptables package to do exactly what I needed and even provided a nice bash script to do the job - problem solved! At some point in the future I may update this post with the actual ins and outs of how the script works (when I've figured it out myself!) but until then, just grab the script, change the download/upload limits as you wish and off you go :)
Because 800x600 is too low-res even for the console
In this day and age, I thought that plugging my laptop into a KVM switch with a known monitor on the other end it would just work but apparently I had over estimated our current level of technology and I was left with a very nice 1920x1080 monitor running at a ludicrous 800x600. Whatever the switch was doing, it meant that Mint couldn't detect the monitor and allow me to select a sensible resolution.
So how do you tell Mint to stop being stupid and run a monitor at a given resolution? This post on the Linux Mint Community site had the answer. In summary, do the following:
First, create a 'modeline' using cvt - this is the configuration line that will be added to the monitor settings and contains info on refresh rate, vsync, etc. Note that this use the VESA standard and so should be compatible with pretty much everything.
This setting should now be added to the list of default options given for the monitor. Note that this isn't permanent and won't survive a reboot - however, I very rarely reboot my laptop anyway (yay linux!) and the original blog post has info about how to do this if you want to give it a try.
So in the first part of this post, I got the cube to respond to mouse movements in so you can rotate around it. Inspiring stuff. Next, I want to add keyboard control using good old WASD movement. The movement I want to recreate is your typical RTS style mouse movement of having the camera rotate around a point (done) and then move that point over a plane using the keyboard (definitely not done).
First things first: How do we check for keyboard input? This is actually not as quite as simple as just triggering on a key press event as if you hold down a key as these don't fire often enough. It is also very difficult (maybe impossible) to poll the actual keyboard hardware in an OS-independent way. However, we can use the Qt-provided functions keyPressEvent and keyReleaseEvent to track the state of the keyboard and act accordingly. To do this, just override these functions in the MainWindow object (this is what gets the keyboard events by default) and update a QMap to the status of each key. The actual code I've added is quite simple and is shown below:
cpp file:
void MainWindow::keyPressEvent(QKeyEvent *e)
{
// if we're quitting, then fine
if (e->key() == Qt::Key_Escape)
{
close();
return;
}
// otherwise update the keyboard map
keyboardMap_[ e->key() ] = true;
}
void MainWindow::keyReleaseEvent(QKeyEvent *e)
{
// update the keyboard map
keyboardMap_[ e->key() ] = false;
}
bool MainWindow::isKeyDown(int key)
{
// check in the map to see if the key is down
if (keyboardMap_.contains(key))
return keyboardMap_[ key ];
else
return false;
}
So we grab any key press or release events and then simply update the QMap with state for this key code. After that, all we need is an access function to allow the widget to query the key state and move the view accordingly.
Now, to actually move the view accordingly takes a little bit of thought. We have to be a little careful as to where we put the translation given by the keyboard movement in order to give the RTS style rotate-around-a-point camera we're looking for. At present, we have:
// move into the screen
glTranslatef(0.0f, 0.0f, -6.0f);
// rotate the cube by the rotation value
glRotatef(rotValue_.y(), 1.0f, 0.0f, 0.0f);
glRotatef(rotValue_.x(), 0.0f, 1.0f, 0.0f);
To apply the lateral movement, we need to think about which order to perform these translations and movements in to get the affect we want, remembering that we are transforming the world relative to the camera. This turns out to be:
Translate back by the zoom factor
Rotate the coordinate system around the origin (equivalent to rotating the camera)
Translate the view to the current focus point position
Applying these gives the following code:
// reset the view to the identity
glLoadIdentity();
// move everything back by the zoom factor
glTranslatef(0.0f, 0.0f, -zoomValue_);
// rotate everything
glRotatef(rotValue_.y(), 1.0f, 0.0f, 0.0f);
glRotatef(rotValue_.x(), 0.0f, 1.0f, 0.0f);
// finally offset by the current viewing point
glTranslatef(posValue_.x(), posValue_.y(), 0.0f);
This almost gives us the keyboard control we were looking for. However, as it stands, if you just polled the key status and increased or decreased the x and y values, you would always be moving on those axes. What we really want is to move relative to the direction we're facing. Unfortunately, this is where we can't avoid some trigonometry as we need to take the movement speed and angle of rotation around the vertical axis to give the change in x and y values needed. Long story short, this code in a new 'mainLoop' function does the job:
// check for keyboard movement
if (parentWin_->isKeyDown(65)) // A
{
posValue_.setY( posValue_.y() + (0.05 * sin( PI * rotValue_.x() / 180.0) ) );
posValue_.setX( posValue_.x() + (0.05 * cos( PI * rotValue_.x() / 180.0) ) );
}
if (parentWin_->isKeyDown(68)) // D
{
posValue_.setY( posValue_.y() - (0.05 * sin( PI * rotValue_.x() / 180.0) ) );
posValue_.setX( posValue_.x() - (0.05 * cos( PI * rotValue_.x() / 180.0) ) );
}
if (parentWin_->isKeyDown(87)) // W
{
posValue_.setY( posValue_.y() + (0.05 * cos( PI * rotValue_.x() / 180.0) ) );
posValue_.setX( posValue_.x() - (0.05 * sin( PI * rotValue_.x() / 180.0) ) );
}
if (parentWin_->isKeyDown(83)) // S
{
posValue_.setY( posValue_.y() - (0.05 * cos( PI * rotValue_.x() / 180.0) ) );
posValue_.setX( posValue_.x() + (0.05 * sin( PI * rotValue_.x() / 180.0) ) );
}
Note the conversion from degrees (as accepted by glRotatef) and radians (as accepted by sin/cos).
Things to note:
I've added mouse wheel zoom by overloading mouseWheelEventand clamping the zoom value.
In order to poll the keyboard state and a fast enough rate, I've added a mainLoop slot function that is attached to the timer and then calls the updateGL function.
In order to call into the parent window's keyboard map, you need to make the widget aware of it and I personally prefer to store this pointer in a member variable through the constructor rather than having a global variable or static singleton type framework.
The rotation/translation order can be difficult to get your head around - try to remember that the camera is static and the transformations apply to the coordinate system!
And we're done! We now have a mouse and keyboard controlled scene to zoom around.
Clearly I just have to start remembering more IP addresses
So last week I needed to go abroad (to CERN as it happens) and when I got there, my Mint 13 running laptop decided to throw a paddy and stop talking to the DNS. I could ping usual Google IPs (8.8.8.8 for example) but DNS just hung despite being reported to correctly (and pingable) in the Network Settings. Having a look at my resolv.conf, I could see that my DNS settings were set to my Uni one. I couldn't remember if these were put in automatically by the ethernet connection in my office or I'd just dumped that there randomly for some reason, but tellingly, these DNSs were NOT pingable (I guess they had fallen over or something).
So, I thought, not a problem - just change the DNSs to Google's and all should be well. Except it wasn't. The laptop was still refusing to talk to anyone by name. Luckily, my phone was not having these troubles and some frantic searching led me to the following Blog. It appears someone in Ubuntu land was trying to be clever and started using dnsmasq instead of resolv.conf. I don't know why this was done and really can't be bothered to find out, but in this occasion it meant I could not find whatever black magic was needed to force the Google DNS to be used.
By *mostly* following the blog, I discovered the following worked like a charm to get control of the DNS back to resolv.conf where (in my humble - and probably naive - opinion) it should still reside. First up, (and be aware, this should all be done under su/root), stop Mint from using dnsmasq by going to:
/etc/NetworkManager/NetworkManager.conf
And commenting out the dns line:
#dns=dnsmasq
Next, get resolvconf to sort out it's links and bring back resolv.conf:
dpkg-reconfigure resolvconf
Just to be sure, do a few more housekeeping bits and pieces and follow up with a restart:
And now you should be able to edit /etc/resolv.conf as usual and have Mint listen to you. Note that you may want to also change:
/etc/resolvconf/resolv.conf.d/original
for a more permanent setting as I believe on restart resolvconf will recreate the resolv.conf with this.
Last thing to note: While messing around, I tried to select 'Automatic (DHCP) addresses only' under IPv4 Settings for the wireless I was using. THIS WAS A BAD IDEA! It stopped the above working for other reasons I didn't understand. I plan to revisit all this at some point to try to better understand how DNS is handled in Mint/Ubuntu as I'm sure there was probably a better way around this...
A friend recently asked how to go about changing the contents of an install DVD with freely available tools that didn't have a size limit. This was something I hadn't explicitly done before and sounded like quite a fun thing to try (you may not entirely agree with the use of the word 'fun' there) so I thought I'd look into it and try to hack my Win7 install DVD a bit. For this I used Ubuntu (though I'm guessing almost any Linux install would work), dd, mount, mkisofs and a script called geteltorito.pl (available here). I was going to use ISO Master but hit problems which I will get to later. If you're of the Windows persuasion, I'm not sure how to do this using free tools, however there's nothing to stop you creating a live boot CD/USB stick of a Linux distro, going into that and just going into some area on your existing HD.
Anyway, the first step was to create an ISO image of the DVD. This was fairly easy using:
markwslater@markwslater-System-Product-Name:~$ dd if=/dev/cdrom of=~/win7_image.iso
6298216+0 records in
6298216+0 records out
3224686592 bytes (3.2 GB) copied, 229.392 s, 14.1 MB/s
markwslater@markwslater-System-Product-Name:~$ ls -ltrh ~/win7_image.iso
-rw-rw-r-- 1 markwslater markwslater 3.1G Mar 5 23:26 /home/markwslater/win7_image.iso
So we now have the ISO image which we could (in theory) start messing with through various tools. Apparently (though I didn't confirm this) most of the free ones or trials for Windows have an upper limit on the size of ISO you can mess with (around CD size by all accounts). Looking around I happened across ISO Master and thought this would do the job. Unfortunately, plugging the above ISO image in only ended up with a README that said:
This disc contains a "UDF" file system and requires an operating system
that supports the ISO-13346 "UDF" file system specification.
Which was not the most helpful. So, after a bit of searching I found the following thread that explained how to do things with basic Linux tools. The key thing is to preserve the master boot record of the ISO and recreate it with this intact. The aforementioned geteltorito.pl perl script did a grand job of this:
markwslater@markwslater-System-Product-Name:~$ ./geteltorito.pl win7_image.iso > ~/boot.bin
Booting catalog starts at sector: 22
Manufacturer of CD: Microsoft Corporation
Image architecture: x86
Boot media type is: no emulation
El Torito image starts at sector 734 and has 8 sector(s) of 512 Bytes
Image has been written to stdout ....
markwslater@markwslater-System-Product-Name:~$
It's then quite easy to mount the existing ISO image (read only), copy this elsewhere, change the permissions and do what's necessary:
A few weeks ago I finally managed to repartition a Windows Vista laptop so I could install Ubuntu Studio on it and try my hand at recording again, something I haven't done for quite some years. For those unaware, Ubuntu Studio is a real time version of Ubuntu (shocker) that comes bundled with a whole host of very useful music making and production software.
To install Ubuntu Studio, the first step (not surprisingly) was to download the ISO image (here is the download page). Now came the first wrinkle, which was that I seemed to have great trouble having the BIOS recognise large USB sticks. I would usually just dd the image to a stick and boot from it but this seemed to fail with the 3-4 sticks I had lying around. I therefore resorted to the tried and tested method and burnt a DVD. This worked without problems and I breezed through the install process (I seem to remember a question asking about including non-open source software which I said yes to. That was the only thing I had to actually think about).
So now I had a working install of Ubuntu Studio dual booting on my old Vista laptop. Next step: Getting my StealthPlug working on it. Now this is where the audio system in Ubuntu Studio requires a bit of explanation (and note that I'm by no means an expert here!). It seems the best method to use to give the smallest latency is Jack. This is very clever bit of software that 'registers' any inputs and outputs (both physical and software created) and allows you to link between any and all of these as you like (like putting jack leads between them, which I guess is where it gets it's name. Or that could just be a massive coincidence). As long as the Jack software is running you can hotplug these as much as you want.
So, how to get this to recognise the StealthPlug? Well plugging it in seemed to make it appear in both /dev and in the main UI. However, by default Jack runs with the main sound card and the inputs/outputs supplied don't show up. The secret of this is in the setup panel. So after starting Jack (Audio Production -> QJackCtl), go to setup and you should see something like this:
If you change the selected hardware device to the plugged in device (the arrow next to Interface will tell you which - /dev/hw1 for my Stealthplug for example) you should be away. To test it, fire up Guitarix (I rather cool open source amp simulator), select a sound (I went for HighGainSolo here) and then wire up the Jack controls something like the following:
and it should start making noise. Well I say that - make sure you're using the headphone output of the Stealthplug otherwise you won't hear anything!
So I now have a low latency monitor solution running with very little trouble. Next job: Direct output to the main card while still using the StealthPlug as input and add in MIDI and a USB mic as well.
In another 200 years I might have built the terminator
Last Christmas, I was the very lucky recipient of the incredibly awesome Lego Mindstorms kit. Here's a picture of all that awesome:
Yes indeed - computer controlled Lego. If I'd got this 20 years ago I wouldn't have seen daylight until I had to leave home.
Now the way this works is that there is a microprocessor controller brick that can have up to 3 motors and 4 sensors connected to it. In theory you build your robot (or whatever) using the included Lego (and any other bits you have lying around), design your program for it using the LabView based language included, download it to the control brick and away you go.
Now this is all well and good and gives you quite a bit of control. Here's a case in point:
However, though I appreciate the benefits of Labview, I'm more of a C++ kind of guy. I also have a long term plan of using another of my presents this year, a Raspberry Pi, as the main controller and maybe throw in an Arduino as well for a bit more flexibility.
This will therefore necessitate an API interface to the controller. A quick bit of googlage pointed me at a promising looking Python based version: NXT-python. This not only allowed all the file access and compilation options I could want, but also (and this was the important bit) had a direct, real time control option. What was even better was that in my Mint install had in the software manager (search for 'nxt'). A couple of clicks later and it was ready to try out. Awesome.
Or not. The version in the repo is a bit behind the main release (V.2.2.1-2 instead of V2.2.2) and contains a rather critical Ultrasonic sensor bug. However, I was still able to plug the brick in via USB (after building the basic tracked vehicle in the instructions), turn it on, and use the following code to get it move rather drunkenly around:
Obviously, this requires you to plug motors into ports B and C :)
This code was shamelessly nicked from the examples that came with the nxt-python install and can (probably) be found here:
/usr/share/doc/python-nxt/examples/
These contain code for using the speaker and reading the sensors, the latter of which required a bit of hacking to fix for the ultrasonic one. If you run it as is, you get the error:
sensor = Ultrasonic( BRICK, inPort)
File "nxt-my\nxt\sensor\generic.py", line 95, in __init__
super(Ultrasonic, self).__init__(brick, port, check_compatible)
File "nxt-my\nxt\sensor\digital.py", line 73, in __init__
sensor = self.get_sensor_info()
File "nxt-my\nxt\sensor\digital.py", line 156, in get_sensor_info
version = self.read_value('version')[0].split('\0')[0]
File "nxt-my\nxt\sensor\digital.py", line 143, in read_value
raise I2CError, "read_value timeout"
As this it's basically saying, there is a timeout issue when reading the ultrasonic sensor. Again, google came to my rescue and pointed me Here. After doing the correction suggested (i.e. increasing the loop count up to 30 on line 84), all was right with the world.
So I now have a computer controlled robot (sort of) that can be told what to do through python. This is certainly a start but if I'm going to control it with the kind of code I have in mind, I'm going to need something a bit more heavy duty. Next job: running python from C++.