Skip to content

Instantly share code, notes, and snippets.

@amicuscertus
Created July 9, 2018 06:04
Show Gist options
  • Save amicuscertus/951e112df8defd590cc287a7dfbde63c to your computer and use it in GitHub Desktop.
Save amicuscertus/951e112df8defd590cc287a7dfbde63c to your computer and use it in GitHub Desktop.
/*
* owondump.c 0.3 A userspace USB driver that interrogates an Owon
* PDS digital oscilloscope and gets it to dump its trace memory for all
* channels, in both vectorgram format, and/or in .BMP bitmap format.
*
* The vectorgram is also parsed into a tabulated
* text format that can be plotted using GNUplot or a similar package.
*
* The code now has an elegant decodeTimebase
* function, courtesy of Michel Poullet!
*
* Copyright June 2009, Michael Murphy
* <[email protected]>
*/
/* slightly modified by Spinor on July 9, 2018. */
#define FNAME_LENGTH 256
#define FNAME_DEFAULT "owondata"
#include "owondump.h"
#include <arpa/inet.h> // for htonl() macro
#include <asm/errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <usb.h>
int debug = 0; // set to 1 for channel data hex dumps
struct usb_device *usb_locks[MAX_USB_LOCKS];
int locksFound = 0;
char filename[FNAME_LENGTH];
int text = 1; // tabulated text output as well as raw data output
int channelcount = 0; // the number of channels in the data dump
struct channelHeader headers[10]; // provide for up to ten scope channels
int decodeVertSensCode(long int i) {
// This is the one byte vertical sensitivity code
char c = (char)i;
int vertSensitivity = -1;
switch (c) {
case 0x01:
vertSensitivity = 5; // 5mV
break;
case 0x02:
vertSensitivity = 10;
break;
case 0x03:
vertSensitivity = 20;
break;
case 0x04:
vertSensitivity = 50;
break;
case 0x05:
vertSensitivity = 100;
break;
case 0x06:
vertSensitivity = 200;
break;
case 0x07:
vertSensitivity = 500;
break;
case 0x08:
vertSensitivity = 1000;
break;
case 0x09:
vertSensitivity = 2000;
break;
case 0x0A:
vertSensitivity = 5000; // 5V
break;
}
return vertSensitivity;
}
long long int decodeTimebase(long int i) {
int factor[] = {5, 10, 25};
long long timebase = factor[i % 3];
i /= 3;
while (i--)
timebase *= 10LL;
return timebase;
}
// decode the contents of the vectorgram data header - providing us with the
// timebase and voltage values for the channel data hdrBuf has already been
// stripped of the 10 byte vectorgram file header that begins "SPBV......"
// returns the size of the channel data block in bytes
struct channelHeader decodeVectorgramBufferHeader(char *hdrBuf) {
struct channelHeader header;
// if not a bitmap then must be a vectorgram
memcpy(&header.channelname, hdrBuf, 3);
header.channelname[3] = '\0';
memcpy(&header.blocklength, hdrBuf + 3, 4);
memcpy(&header.samplecount1, hdrBuf + 7, 4);
memcpy(&header.samplecount2, hdrBuf + 11, 4);
memcpy(&header.unknown3, hdrBuf + 15, 4);
memcpy(&header.timebasecode, hdrBuf + 19, 4);
memcpy(&header.unknown4, hdrBuf + 23, 4);
memcpy(&header.vertsenscode, hdrBuf + 27, 4);
memcpy(&header.unknown5, hdrBuf + 31, 4);
memcpy(&header.unknown6, hdrBuf + 35, 4);
memcpy(&header.unknown7, hdrBuf + 39, 4);
memcpy(&header.unknown8, hdrBuf + 43, 4);
memcpy(&header.unknown9, hdrBuf + 43, 4);
header.vertSensitivity =
decodeVertSensCode(header.vertsenscode); // 5mV through 5000mV (5V)
header.timeBase =
decodeTimebase(header.timebasecode); // in nanoseconds (10E-9)
printf("Channel: %4s samples: %6d sensitivity: %6d mV timebase: %8d us (coce "
"%ld)\n",
header.channelname, (int)header.samplecount1, header.vertSensitivity,
(int)(header.timeBase / 1000), (long int)header.timebasecode);
if (debug) {
printf(" data block length: %08x (%d) bytes\n", (int)header.blocklength,
(int)header.blocklength);
printf(" samplecount1: %08x (%d)\n", (int)header.samplecount1,
(int)header.samplecount1);
printf(" samplecount2: %08x (%d)\n", (int)header.samplecount2,
(int)header.samplecount2);
printf(" unknown3: %08x (%d)\n", (int)header.unknown3,
(int)header.unknown3);
printf(" timebase code: %08x (%d)\n", (int)header.timebasecode,
(int)header.timebasecode);
printf(" unknown4: %08x (%d)\n", (int)header.unknown4,
(int)header.unknown4);
printf(" vert sens code: %08x (%d)\n", (int)header.vertsenscode,
(int)header.vertsenscode);
printf(" unknown5: %08x (%d)\n", (int)header.unknown5,
(int)header.unknown5);
printf(" unknown6: %08x (%d)\n", (int)header.unknown6,
(int)header.unknown6);
printf(" unknown7: %08x (%d)\n", (int)header.unknown7,
(int)header.unknown7);
printf(" unknown8: %08x (%d)\n", (int)header.unknown8,
(int)header.unknown8);
printf(" unknown9: %08x (%d)\n", (int)header.unknown9,
(int)header.unknown9);
printf("\n");
printf("-------------------------------\n");
printf("\n");
}
return header;
}
// add the device locks to an array.
void found_usb_lock(struct usb_device *dev) {
if (locksFound < MAX_USB_LOCKS) {
usb_locks[locksFound++] = dev;
}
}
struct usb_device *devfindOwon() {
struct usb_bus *bus;
struct usb_dev_handle *dh;
int buses = 0;
int devs = 0;
buses = usb_find_busses();
devs = usb_find_devices();
for (bus = usb_busses; bus; bus = bus->next) {
struct usb_device *dev;
for (dev = bus->devices; dev; dev = dev->next)
if (dev->descriptor.idVendor == USB_LOCK_VENDOR &&
dev->descriptor.idProduct == USB_LOCK_PRODUCT) {
found_usb_lock(dev);
printf("..Found an Owon device %04x:%04x on bus %s\n", USB_LOCK_VENDOR,
USB_LOCK_PRODUCT, bus->dirname);
// printf("..Resetting device.\n");
dh = usb_open(dev);
usb_reset(dh); // quirky.. device has to be initially reset
return (dev); // return the device
}
}
return (NULL); // No Owon found
}
void writeRawData(char *buf, int count) {
FILE *fp;
if ((fp = fopen(filename, "w")) == NULL) {
printf("..Failed to open file \'%s\'!\n", filename);
return;
} else {
if (fwrite(buf, sizeof(unsigned char), count, fp) != count) {
printf("..Failed to write %d bytes to file %s\n", count, filename);
}
}
fclose(fp);
return;
}
void writeTextData(char *buf, int count) {
FILE *fpout;
unsigned char *ptr[channelcount];
int i, j;
printf("writeTextData: filename = [%s] \n", filename);
if ((fpout = fopen(filename, "w")) == NULL) {
printf("..Failed to open file \'%s\'!\n", filename);
return;
}
printf("..Successfully opened text file \'%s\'!\n", filename);
fprintf(fpout, "# Timebase: %g us Samples: %ld\n",
(double)headers[0].timeBase / 1000, headers[0].samplecount1);
ptr[0] = buf; // initialise first pointer to start of char *buf
ptr[0] +=
VECTORGRAM_FILE_HEADER_LENGTH; // +10 bytes to jump over the file header
ptr[0] += VECTORGRAM_BLOCK_HEADER_LENGTH; // +51 bytes to jump over the
// channel header
// fprintf(stderr, "ptr[%d] = 0x%p (offset %d) \n", 0, ptr[0], (int)
//(ptr[0]-ptr[0]));
// print the channel names as column headers
for (i = 1; i < channelcount; i++) {
ptr[i] = ptr[i - 1] + (int)headers[i - 1].blocklength + 3;
// fprintf(stderr, "ptr[%d] = 0x%p (offset %d) \n", i, ptr[i],
//(int) (ptr[i]-ptr[i-1]));
}
fprintf(fpout, "# ");
for (i = 0; i < channelcount; i++)
fprintf(fpout, "%s\t", headers[i].channelname);
fprintf(fpout, "\n");
for (j = 0; j < (int)headers[0].samplecount1; j++) {
// fprintf(fpout, "%d", j+1);
for (i = 0; i < channelcount; i++) {
if (j > (int)headers[i].samplecount1) // no sample available for this
// timeslot on channel i
fprintf(fpout, "\t\t -");
else {
int s = (short)(ptr[i][0] | (ptr[i][1] << 8));
fprintf(fpout, "%5.1f\t", s * headers[i].vertSensitivity * 0.04);
}
ptr[i] += 2;
}
fprintf(fpout, "\n");
}
// printf("..Successfully written trace data to \'%s\'!\n", txtfilename);
if (!fclose(fpout))
printf("..Successfully closed text file \'%s\'!\n", filename);
}
void readOwonMemory(struct usb_device *dev) {
usb_dev_handle *devHandle = 0;
signed int ret = 0; // set to < 0 to indicate USB errors
int bmp_flg = 0;
int vec_flg = 0;
int i = 0, j = 0;
unsigned int owonDataBufferSize = 0;
char owonCmdBuffer[0x0c];
char owonDescriptorBuffer[0x12];
char *owonDataBuffer; // malloc-ed at runtime
char *headerptr; // used to reference the start of the header
if (dev->descriptor.idVendor != USB_LOCK_VENDOR ||
dev->descriptor.idProduct != USB_LOCK_PRODUCT) {
printf("..Failed device lock attempt: not passed a USB device handle!\n");
return;
}
// printf("..Attempting USB lock on device %04x:%04x\n",
// dev->descriptor.idVendor, dev->descriptor.idProduct);
devHandle = usb_open(dev);
if (devHandle > 0) {
// printf("..Trying to claim interface 0 of %04x:%04x \n",
// dev->descriptor.idVendor, dev->descriptor.idProduct);
ret = usb_set_configuration(devHandle, DEFAULT_CONFIGURATION);
ret = usb_claim_interface(devHandle, DEFAULT_INTERFACE); // interface 0
ret = usb_clear_halt(devHandle, BULK_READ_ENDPOINT);
// ret = usb_set_altinterface(devHandle, DEFAULT_INTERFACE);
if (ret) {
printf("..Failed to claim interface %d: %d : \'%s\'\n", DEFAULT_INTERFACE,
ret, strerror(-ret));
goto bail;
}
} else {
printf("..Failed to open device..\'%s\'", strerror(-ret));
goto bail;
}
// printf("..Successfully claimed interface 0 to %04x:%04x \n",
// dev->descriptor.idVendor, dev->descriptor.idProduct);
// printf("..Attempting to get the Device Descriptor\n");
ret = usb_get_descriptor(devHandle, USB_DT_DEVICE, 0x00, owonDescriptorBuffer,
0x12);
if (ret < 0) {
printf("..Failed to get device descriptor %04x '%s'\n", ret,
strerror(-ret));
goto bail;
} else
// printf("..Successfully obtained device descriptor!\n");
/*
printf("..Attempting to set device to default configuration\n");
ret = usb_set_configuration(devHandle,
DEFAULT_CONFIGURATION); if(ret) { printf("..Failed to set default
configuration %d '%s'\n", ret, strerror(-ret)); goto bail;
}
*/
// clear any halt status on the bulk OUT endpoint
ret = usb_clear_halt(devHandle, BULK_WRITE_ENDPOINT);
// printf("..Attempting to bulk write START command to device...\n");
ret = usb_bulk_write(devHandle, BULK_WRITE_ENDPOINT, OWON_START_DATA_CMD,
strlen(OWON_START_DATA_CMD), DEFAULT_TIMEOUT);
if (ret < 0) {
printf("..Failed to bulk write %04x '%s'\n", ret, strerror(-ret));
goto bail;
}
// printf("..Successful bulk write of %04x bytes!\n", (unsigned int)
// strlen(OWON_START_DATA_CMD));
// clear any halt status on the bulk IN endpoint
ret = usb_clear_halt(devHandle, BULK_READ_ENDPOINT);
// printf("..Attempting to bulk read %04x (%d) bytes from
// device...\n",(unsigned int) sizeof(owonCmdBuffer), (unsigned int)
// sizeof(owonCmdBuffer));
ret = usb_bulk_read(devHandle, BULK_READ_ENDPOINT, owonCmdBuffer,
sizeof(owonCmdBuffer), DEFAULT_TIMEOUT);
if (ret < 0) {
usb_resetep(devHandle, BULK_READ_ENDPOINT);
printf("..Failed to bulk read: %04x (%d) bytes: '%s'\n",
(unsigned int)sizeof(owonCmdBuffer),
(unsigned int)sizeof(owonCmdBuffer), strerror(-ret));
goto bail;
}
// else
// printf("..Successful bulk read of %04x (%d) bytes! :\n", ret, ret);
if (debug) {
// display the contents of the BULK IN Owon Command Buffer
printf("\t%08x: ", 0);
for (i = 0; i < ret; i++)
printf("%02x ", (unsigned char)owonCmdBuffer[i]);
printf("\n");
}
// retrieve the bulk read byte count from the Owon command buffer
// the count is held in little endian format in the first 4 bytes of that
// buffer
memcpy(&owonDataBufferSize, owonCmdBuffer,
4); // owonCmdBuffer, the source for memcpy, is little endian
// printf("dataBufSize = %d\n", owonDataBufferSize);
// printf("..Attempting to malloc read buffer space of %08xh (%d) bytes\n",
// owonDataBufferSize, owonDataBufferSize);
owonDataBuffer = malloc(owonDataBufferSize);
if (!owonDataBuffer) {
printf("..Failed to malloc(%08xh)!\n", owonDataBufferSize);
goto bail;
}
// else
// printf("..Successful malloc!\n");
// printf("..Owon ready to bulk transfer %08xh (%d) bytes\n",
// owonDataBufferSize, owonDataBufferSize);
// printf("..Attempting to bulk read %08xh (%d) bytes from device...\n",
// owonDataBufferSize, owonDataBufferSize);
ret = usb_bulk_read(devHandle, BULK_READ_ENDPOINT, owonDataBuffer,
owonDataBufferSize, DEFAULT_BITMAP_READ_TIMEOUT);
if (ret < 0) {
printf("..Failed to bulk read: %xh (%d) bytes: %d - '%s'\n",
owonDataBufferSize, owonDataBufferSize, ret, strerror(-ret));
usb_reset(devHandle);
goto bail;
}
// else
// printf("..Successful bulk read of %08xh (%d) bytes! : \n", ret, ret);
if (debug) {
// hexdump the first 0x40 bytes of the BULK IN Owon Data Buffer
printf("..Hexdump of first 0x40 bytes of the BULK IN Owon Data Buffer :\n");
for (i = 0; i <= 0x03; i++) {
printf("\t%08x: ", i);
for (j = 0; j < 0x10; j++)
printf("%02x ", (unsigned char)owonDataBuffer[(i * 0x10) + j]);
printf("\n");
}
printf("\n");
}
// determine from the header whether this is bitmap data or vectorgram
// is it a 'BM' (bitmap) ?
if (*owonDataBuffer == 'B' && *(owonDataBuffer + 1) == 'M') {
printf("640x480 bitmap of %04xh (%d) bytes\n", (int)*(owonDataBuffer + 2),
*(owonDataBuffer + 2));
strcat(filename, ".bmp");
bmp_flg = 1;
}
// is it a vectorgram ('SPB') ? If so, we decode the contents..
else if (*owonDataBuffer == 'S' && *(owonDataBuffer + 1) == 'P' &&
*(owonDataBuffer + 2) == 'B') {
strcat(filename, ".txt");
vec_flg = 1;
switch (*(owonDataBuffer + 3)) {
case 'V':
printf("..Found data from Owon PDS5022S\n");
break;
case 'W':
printf("..Found data from Owon PDS6060S\n");
break;
default:
printf("..Found data from Owon unknown model\n");
}
printf("..Found vector gram data\n");
// initialise the header pointer to the first header in the data
headerptr =
owonDataBuffer +
VECTORGRAM_FILE_HEADER_LENGTH; // jump over the "SPB...." file header
while ((owonDataBuffer + owonDataBufferSize) > headerptr) {
if (debug) {
// hexdump the first 0x40 bytes of channel header
printf("..Hexdump of channel header :\n");
for (i = 0; i <= 0x04; i++) {
printf("\t%08x: ", i);
for (j = 0; j < 0x10; j++)
printf("%02x ", (unsigned char)*(headerptr + (i * 0x10) + j));
printf("\n");
}
}
headers[channelcount] = decodeVectorgramBufferHeader(headerptr);
headerptr += (int)headers[channelcount].blocklength;
headerptr += 3; // and jump over the channel name itself
channelcount++;
}
}
// is it neither a BM (bitmap) nor a SPB (vectorgram) ?
else {
printf("..Failed to determine data type.\n");
printf("%c %c %c %c\n", *owonDataBuffer, *(owonDataBuffer + 1),
*(owonDataBuffer + 2), *(owonDataBuffer + 3));
}
if (vec_flg) {
writeTextData(owonDataBuffer, owonDataBufferSize);
}
if (bmp_flg) {
writeRawData(owonDataBuffer, owonDataBufferSize);
}
free(
owonDataBuffer); // a buffer of vectorgrams is just a few KB in size
// but for bitmaps this buffer could be very large (~1MB)
ret = usb_release_interface(devHandle, DEFAULT_INTERFACE);
if (ret) {
printf("Failed to release interface %d: '%s'\n", DEFAULT_INTERFACE,
strerror(-ret));
goto bail;
}
bail:
usb_reset(devHandle);
usb_close(devHandle);
return;
}
int main(int argc, char *argv[]) {
if (argc > 1) {
strcpy(filename, argv[1]);
} else {
strcpy(filename, FNAME_DEFAULT);
}
usb_init();
if (!devfindOwon()) {
printf("..No Owon device %04x:%04x found\n", USB_LOCK_VENDOR,
USB_LOCK_PRODUCT);
return 0;
} else {
readOwonMemory(usb_locks[0]);
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment