Monday, March 23, 2015

Steam Linux audio problems

No audio at all from steam games?  Try 'pavucontrol'.  It'll show you when games are trying to play audio and where they're playing it to. In the "configuration" tab I had to disable built-in audio and switch my "HDA NVidia" device to "Digital Surround 5.1 (HDMI) Output" from "Digital Stereo (HDMI) Output".

Sunday, March 22, 2015

GQL to CSV exporter for Google App Engine

The App Engine admin panel will let you run GQL queries against your datastore, but it won't let you download the results as a CSV. So I wrote a [very] quick and [very] dirty handler that does, which also has the handy side effect that you can put your own access controls on it. That means you can give someone on your team read-only access to your datastore without also giving them access to the admin panel.

If you're using db instead of ndb,  I believe the main thing to change is ndb.gql() to GqlQuery().

 # Very quick and dirty example of how to provide unfettered read  
 # access to your datastore with export to CSV. Be sure to add appropriate  
 # access controls and watch out for security risks (like XSS)  
 #  
 # Don't forget to:  
 # from google.appengine.ext import ndb  
 # from google.appengine.ext.ndb import metadata  
 class GqlPage(webapp2.RequestHandler):  
  def get(self):  
   limited = True  
   row_limit = 1000  
   # Tricky to distinguish absence of 'limit' checkbox when you  
   # first hit the URL from when you submitted with an unchecked box  
   if self.request.get('download', 'nope') != 'nope' and \  
     self.request.get("limit", "nope") == "nope":  
    limited = False  
   
   query = self.request.get('query', "empty")  
   
   is_csv = False  
   if self.request.get('download') == 'Download':  
    is_csv = True  
    self.response.headers['Content-Type'] = "text/csv"  
   else:  
    self.response.write('<html><body>')  
    self.response.write('<form action="/gql" method=POST>')  
    self.response.write('<textarea name=query rows=10 cols=80 placeholder="select * from...">')  
    if query != "empty":  
     self.response.write(cgi.escape(query))  
    self.response.write('</textarea><br>')  
    self.response.write("<input type=submit name=download value=View>")  
    self.response.write('<input id=foo type=submit name=download value=Download')  
    self.response.write(' onclick="document.getElementById(\'results_div\').innerHTML=\'\';">')  
   
    self.response.write("Limit response to " + str(row_limit) + " rows:")  
    self.response.write("<input name=limit type=checkbox ")  
    if limited:  
     self.response.write("checked")  
    self.response.write('><br></form>')  
   
    self.response.write("Examples for available tables:<br>")  
    for kind in metadata.get_kinds():  
     self.response.write("select * from " + kind + "<br>")  
   
    self.response.write('<br><div id="results_div"><pre>')  
   
   if query != 'empty' and query != '':  
    results = []  
    if limited:  
     results = ndb.gql(query).fetch(row_limit)  
    else:  
     results = ndb.gql(query).fetch()  
   
    writer = csv.writer(self.response.out)  
   
    row_count = 0  
    first_row = True  
    for row in results:  
     row_dict = row.to_dict()  
     keys = sorted(row_dict.keys())  
   
     # Write column labels as first row  
     if first_row:  
      first_row = False  
      self.response.write("#")  
      writer.writerow(keys)  
   
     values = []  
     for k in keys:  
      value = str(row_dict[k])  
      if is_csv:  
       values.append(value)  
      else:  
       values.append(cgi.escape(value))  
   
     writer.writerow(values)  
   
     row_count += 1  
     if not is_csv and limited and row_count == row_limit:  
      self.response.write("\n[Truncated at " + str(row_limit) + " lines]")  
   
   if not is_csv:  
    self.response.write('</pre></div>')  
    self.response.write("</body></html>")  
   
  def post(self):  
   return self.get()  
   

Friday, March 20, 2015

Linux data acquisition with an Agilent Infiniium MSO9404A oscilloscope

I need to capture some analog data.  My coworker has a National Instruments USB data acquisition module, but as far as I can tell, the drivers are all proprietary and focused on using Labview.

So instead I used a fancy 9000-series Agilent Infiniium scope.  I plugged my laptop's ethernet cable into the scope, then used the Windows control panel on the scope to set its IPv4 address to 192.168.1.1.  I set my laptop to 192.168.1.2, and found that I could ping the scope.  So far so good.

Back in the day, test equipment was controllable over a serial GPIB bus.  Today's fancy gear uses the same conventions over TCP/IP.  There's a confusing mess of acronyms like VXI-11 and VISA, and a bunch of half-baked libraries and crappy-looking system drivers that appear to be required to use them.  pyvisa looks nice, but wants a proprietary National Instruments driver distributed as a .iso(!).  Not my cup of tea.

Then I ran across this MATLAB example that's basically just chatting with the device over TCP/IP. That led me to Agilent's Programmer's Reference guide for the 9000-series scopes.

After fighting with the 1100-page manual for a few hours, I came up with the following settings that let me get samples at a specific rate like I would from an ADC.

Note that you can also interactively talk to the scope using nc or telnet to port 5025 while you're experimenting.

 #!/usr/bin/python  
   
 # Using ethernet to talk to an Agilent Infiniium MSO9404A  
 # See also the "Infiniium 9000A Programmer's Reference"  
   
 import socket  
 import sys  
   
 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  
 sock.connect(("192.168.1.1", 5025))  
   
 def cmd(s):  
  sock.send(s + "\n")  
   
 def resp():  
  r = sock.recv(1048576)  
  while not r.endswith("\n"):  
   r += sock.recv(1048576)  
  return r  
   
 print "Querying scope"  
   
 cmd("*IDN?")  
 print "Scope identifies as: ", resp(),  
 cmd("*RST")  
   
 # This doesn't seem to affect the samples we receive  
 cmd(":timebase:range 1E-6")  
 cmd(":timebase:delay 0")  
   
 cmd(":channel1:range 5")  
 cmd(":channel1:offset 2.5")  
 cmd(":channel1:input dc")  
   
 cmd(":trigger:mode edge")  
 cmd(":trigger:slope positive")  
 cmd(":trigger:level chan1,2.5")  
   
 cmd(":system:header off")  
   
 cmd(":acquire:mode rtime") # Realtime mode; don't average multiple passes  
 cmd(":acquire:complete 100")  
   
 cmd(":waveform:source channel1")  
 cmd(":waveform:format ascii")  
   
 cmd(":acquire:count 1")  
 cmd(":acquire:srate:auto off")  
 cmd(":acquire:srate 4000")  
 cmd(":acquire:points:auto off")  
 cmd(":acquire:points 16")  
 # This was on by default, and took me a long time to figure out why  
 # I was getting ~16x the number of samples I requested.  
 cmd(":acquire:interpolate off")  
   
 cmd(":digitize channel1")
# This should block until the capture is done, since we used :digitize
 cmd(":waveform:data?")  
   
 sample_string = resp().rstrip().rstrip(",")  
 ascii_samples = sample_string.split(",")  
   
 samples = []  
 for f in ascii_samples:  
  try:  
   samples.append(float(f))  
  except:  
   print "Couldn't convert:", f  
   
 print "Got ", len(samples), " samples. Values 1-10:", samples[:10]  
   

Monday, February 16, 2015

Empirically testing the "three hats game"

After reading the brain teaser here, I couldn't convince myself that the solution was correct:
http://www.futilitycloset.com/2015/02/17/the-three-hats-game/
It still seems impossible to me that any player should be able to do better than 50% at guessing the color of his own hat. So I wrote a C program to try out the strategy across 10000 games and report what it found. And indeed it does seem to work. I'd paste the code, but blogger sucks at code formatting. So here it is instead:
https://gist.github.com/anonymous/b3e0a653097e61bec498

Friday, February 06, 2015

Ubuntu: use syndaemon disable touchpad while typing

As this page describes, syndaemon solves the problem of trying to type on a laptop and having the cursor jump somewhere else because it thinks you touched the touchpad:

http://www.webupd8.org/2009/11/ubuntu-automatically-disable-touchpad.html

Wednesday, January 28, 2015

Intuition for Young's Modulus

Today I was trying to remember how to use Young's Modulus.  I eventually figured it out (I think), but when I went to check my intuition, nobody else was describing it in these terms.  So it's odd that this wouldn't be a well-known intuitive aid, which I guess means I might just be wrong about it.  If so, please point that out in the comments.

But the mental shortcut is this: Young's Modulus (aka the Elastic Modulus) tells us how much pressure we have to apply to a material to double its length.

For example: Aluminum has a modulus of elasticity of 10 million PSI.  So if you had a piece of aluminum bar stock 1 inch square, and you pulled on it with a force of 10 million pounds, it'd end up twice as long as it started.  

Now, in reality the bar would tear before it'd stretch that far, because the maximum elongation for aluminum tends to be less than 30%.  But remembering that "E = pressure required to double the length" gets you into the right place to use the units correctly (for example, to say that 1% of 10 million PSI would elongate our bar by 1%).

Friday, January 23, 2015

Paperclip Maximizer 1.0

The AI does not hate you, nor does it love you, but you are made out of atoms which it can use for something else. 
—Eliezer Yudkowsky
A Paperclip Maximizer is an artificial intelligence whose sole purpose is to make as many paperclips as possible, which to the chagrin of its creator, ends up destroying the world in the process.  It's a way of explaining why the folks at MIRI think it's important to study ways of making AI that will benefit rather than harm us.
I have several friends who study AI, so I decided to creatively misunderstand the notion of a Paperclip Maximizer, and thus conclude that AI researchers are inexplicably preoccupied with paper clips.  So to ensure they never wanted for fasteners, I ordered a whole panoply of paperclips from around the internet and had them shipped to their desks over the course of a few weeks.

This amused me greatly, but after a few weeks it was time to take it to the next level.  Thus was born Paperclip Maximizer 1.0:


Here it is in action, with early prototype clips on display that didn't quite have that je ne sais quoi:





The mechanism is relatively simple, but taught me a lot about the subtleties of wire bending.  Two GWS S125 1T sail winch servos provide all the locomotion, controlled by an Arduino Leonardo and about 2 pages of code.

These particular servos have a lot more range of motion than normal servos, and are also easy to modify for continuous rotation.  That was important for the wire feed servo, which always needs to turn the same direction.  When you disassemble this particular servo, you can just remove the gear that connects the axle to the potentiometer.  Then when you drive it, if you tell it to move clockwise of where you left the pot, it'll turn clockwise forever, and vice versa.

The feed wheel is a piece of brass rod I knurled on the lathe to give it some bite.  It's pressed onto the servo, which means I needed to cut down a servo horn to a perfect cylinder.  To do that, I wrote a program for the Arduino to make it rotate continuously, then clamped the servo upright in the mill.  Then while the servo was spinning, I moved in with a spinning end mill.

Next to the feed wheel, you can see a bearing of about the same diameter.  Its job is to hold the wire against the feed wheel.  Originally I bolted the bearing in place, but the wire wouldn't feed consistently.  Turns out there's enough eccentricity in the feed wheel that I needed to let the bearing move in and out slightly.  So that's why you can see a big coil spring overhead, tensioning the bearing against the feed wheel and compressing the wire.  If you watch the video carefully, you can see the arm move in and out slightly on the long feeds, broadcasting to the world exactly how bad my lathe skills are.


Next comes the feed block.  Its job is to keep the wire straight and hold it in place while the bending unit bends the wire.  Like the rest of the project, this turns out to be much more constrained than I expected: if there's too much space between the block and the feed wheel, then when the bender pushes back on the wire, it can bow and kink.  And if there's too much space between the block and the bender, you can't get a tight bend in the wire.

I'd have liked to make the feed block longer to give me more room for the other components.  But it needs a small diameter hole to pass the wire, and small drill bits also tend to be short.  I think I even had to drill the block from both ends to reach all the way through with the bit I had available.

The bending head itself ended up also being pretty cramped.  It needed to extend high enough to leave room for the bearing that does the bending.  It's hard to see, but imagine the head starting out twice as thick as you see here:


I milled out all but a collar from the bottom half of the piece, and that's where it's attached to the servo.  You can just barely see that collar between the head and the servo, to the right of the bearing.  Then it had to reach over to the center of the bearing, but not crash into the feed block.  So I had the mill cut that nice curved shape that you see leading to the bearing nut.  But it wasn't a particularly critical dimension, so grinding the shape by hand would have worked just as well.  Then the head on the 1/4"-20 bolt that holds the bearing turned out to be a bit too thick, so I had to grind that to about half its normal thickness.

If you watch real wire bending machines, you'll see that they create different curves by feeding the wire while they adjust the benders.


Turns out it takes a lot of force to feed wire into a tight bend, and my feeder wasn't up to it.  So that's why I have to alternate feeding and bending, which I actually find quite delightful, since it adds to the quirky personality of the machine.  If I had known at the start that I wouldn't be able to feed while bending, I could have designed the machine to use a fixed bending finger instead of a bearing.  That would have made it a lot easier to make the tight bends needed for a paper clip -- since the bearing is round, it likes to deflect the wire back through the block as it approaches.

Since everything had to be jammed in close together, I was glad I chose to tap the mounting holes for the servos.  If I had to do the project over again, I'd save a lot of time by using acrylic on a laser.  But then I suppose I'd have to rearrange things, tap the acrylic, or maybe use some sort of press-in insert.



What about the big DC motor sitting on top of the bender?   That's the cutoff wheel:



I pressed down the factory plastic gear on the motor to expose more of the shaft, then hot glued on a dremel cutoff wheel.  The motor itself is held onto the bending head with double stick tape; not a great long-term solution.

The way it was supposed to work was to not reset the bender on the last bend, but instead keep moving past the wire.  Then I'd feed out the last leg of the paperclip, and then keep rotating the bending head clockwise until it brought around the cutoff wheel in to finish the job.

That's why you can see a dished out spot on top of the feed block -- I had to spend about 15 minutes manually and very gradually feeding the spinning wheel into the block so that it'd clear when moving into place, yet still end up close enough to the block to cut the wire instead of just deflecting it out of the way.

It's too bad that feature didn't work out, since the sparks you get with steel MIG welder wire make a great grand finale to the process!  But the motor is easy to stall against the wire, which generally also browns out the Arduino.  And about half the time, right at the end, the tiny sliver of remaining wire bends instead of cutting, leaving the finished paperclip attached to the next one.  Oh, and the cut also ends up being razor sharp: I lightly grazed my fingertip with one of the cutoff pieces of wire and ended up with a very clean, deep cut.  So that was the final nail in the coffin for the cutoff wheel.

Writing the Arduino code was one of the easiest parts of the project.  I invited my AI researcher friend over on a Saturday afternoon and we coded and tuned it over the course of an hour or two, making lots of misshapen proto-clips that you can see littered around in the video.  Everything is open loop: move the servo, wait, then move again.  For the wire feed, there are one or two values where the servo will stop because that's where I left the potentiometer when I modified the servo.  But the much better solution is to tell the servo library to detach, which stops the PWM signal to the servo, stopping it no matter where it thinks it is.