Sunday, 09 April 2017

Another misuse of "default"

Now, here's something really egregious. I'm back to looking at the USB TMC thing, and Linux's usbtmc driver, which not only isn't the widely-documented version from HP (Agilent, whatever), but never quite seems to work right.

One obvious aggravation is the 5-second timeout on reads, which makes it impossible to use a regular terminal program to talk to a TMC device. Maybe it's an ioctl setting? I look at the source code:

So, there's our 5-second default timeout. Does this value get stored in a variable, or struct element, for later use, allowing the value to be adjusted somehow?

It does not. USBTMC_TIMEOUT gets passed directly to various functions, presumably as a timeout.

So, it's not so much a default as a hard-coded, fixed value. Which you could change by editing the source code and recompiling, I guess.

Oh, well. It seems that I need to put the read call in a loop in a thread, retrying if it times out. That appears to work, though there may turn out to be special cases. Oh, and if I don't put in enough of a delay between retries, things don't work.

There's another weirdness, maybe unrelated. Writes (at least, to the instrument I currently have connected) take for-freaking-ever to complete. Like, plural seconds. Maybe the write can't happen until the pending read times out? Ah, that seems to be it. So... I have to issue the write, then, if there was a query somewhere in there, start a read? Except that I tried that early on, and the command didn't seem to be getting through at all. But, then, there seem to be times that the driver gets a wedgie and just plain doesn't want to work.

Well, I guess if I can get the read-after-write approach working consistently, I can just have two functions: one to send a command, and one to send a query and receive the response. But that risks various modes of breakage, and must rely on the timeout to recover from breakage.

This is a really weird driver. Maybe there's a reason people use libusb instead. Perchance I need to look around for an example of that (preferably written in C rather than Python) and learn out how to do that myself, in service of my own purposes.

No, I'm not just going to use NI VISA. I'm trying to avoid any dependence on proprietary libraries, and especially that one. Some simple little LGPL'd library would be fine. Or GPL, even.

It doesn't help that the official TMC spec is about as comprehensible as any other USB standards document, with the result that implementers play the compatibility game: a driver/library works with instruments from the first couple of vendors, so subsequent vendors try to make their instruments play nice with that driver or library rather then working directly from the standard. Then later drivers try to work with equipment from some random subset of vendors, and 'round and 'round we go.

Update: The trick to write-then-read? At least with the specific instrument I'm poking at just now, there needs to be a delay between write and read. 2ms isn't enough; 5ms is, at least for some commands. Don't delay long enough, and the read will time out without ever receiving any data. In fact...

Ah. If the read issues too soon after the write, the command/query doesn't get processed. Whether that's a problem with the instrument or with the driver, I don't know at this stage. And is the required delay a function of the command and its execution time?

I'm suspecting this instrument may have Issues with receiving a string of commands in rapid succession. USB is supposed to have inherent flow control, isn't it? Combine lack of flow control with SCPI's lack of acknowledgments, and you've got a recipe for chaos (as oft seen with RS-232-connected instruments). Send a command, wait at least as long as the maximum it should take to complete that command, then send the next one....

Also, I'm getting the distinct impression that I put a lot more care into my half-baked SCPI device implementation than did some of the companies that are peddling test equipment nowadays.

Update 2: Another factor making life Interesting is the game involved in implementing SCPI. While the SCPI spec is open and freely available, it depends on the IEEE-488 spec, for which the Eye of Tripoli wants money. So, ya wanna implement SCPI properly, you got the choice of paying for an official copy of the 488 spec, or attempting to derive that information from other sources: old HP-IB tutorials, manuals for existing instruments, and so on. Also, this results in the spec not being all in one place; the SCPI standards document refers the reader to the 488 document for various important aspects of the overall spec. This leads to the same kind of fun as trying to implement a protocol defined by an accretion of RFCs, only one of the intermediate RFCs is proprietary and only available on payment of a fee.

This is yet another reason for budget instruments not following the standards so well.

Update 3: OK, so the "don't try to read until after sending a query" rule is in the USB TMC spec. The "wait a few milliseconds after writing before you read, lest the command or query get cramps and drown" rule must be a function of either the instrument at hand or the Linux driver.

The instrument in question, on closer examination, doesn't really speak proper SCPI. There's no *OPC?, for one thing. In fact, the only common commands it implements are *IDN?, *SAV, and *RCL. Also, it doesn't support sending multiple commands in one message, separated by semicolons.

This last is Interesting, because I was kind of wondering what would happen if I sent a stack of semicolon-separated queries and then read the results. Guess I'll have to try that on a different instrument. The SCPI spec seems to imply that all the results come back in a single message, separated by semicolons.