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]