/* * 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 */ #include #include #include #include #include #include #include // for htonl() macro #include "owondump.h" int debug = 1; // set to 1 for channel data hex dumps struct usb_device *usb_locks[MAX_USB_LOCKS]; int locksFound = 0; char *filename = "output.bin"; // default output filename 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){ printf("Timebase: %lld\n", timebase); 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 printf(".\n"); memcpy(&header.channelname,hdrBuf,3); printf(".\n"); header.channelname[3]='\0'; printf(".\n"); memcpy(&header.blocklength, hdrBuf+3,4); printf(".\n"); memcpy(&header.samplecount1, hdrBuf+7,4); printf(".\n"); memcpy(&header.samplecount2, hdrBuf+11,4); printf(".\n"); memcpy(&header.unknown3, hdrBuf+15,4); printf(".\n"); memcpy(&header.timebasecode, hdrBuf+19,4); printf(".\n"); memcpy(&header.unknown4, hdrBuf+23,4); printf(".\n"); memcpy(&header.vertsenscode, hdrBuf+27,4); printf(".\n"); memcpy(&header.unknown5, hdrBuf+31,4); printf(".\n"); memcpy(&header.unknown6, hdrBuf+35,4); printf(".\n"); memcpy(&header.unknown7, hdrBuf+39,4); printf(".\n"); memcpy(&header.unknown8, hdrBuf+43,4); printf(".\n"); memcpy(&header.unknown9, hdrBuf+43,4); printf(".\n"); header.vertSensitivity = decodeVertSensCode(header.vertsenscode); // 5mV through 5000mV (5V) printf(". decode\n"); 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 // printf("..Successfully opened file \'%s\'!\n", filename); if (fwrite(buf,sizeof(unsigned char), count, fp) != count) printf("..Failed to write %d bytes to file %s\n", count, filename); // else // printf("..Successfully written %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; char txtfilename[strlen(filename)+3]; strcpy(txtfilename,filename); strcat(txtfilename,".txt"); if ((fpout = fopen(txtfilename,"w")) == NULL) { printf("..Failed to open file \'%s\'!\n", txtfilename); return; } // printf("..Successfully opened text file \'%s\'!\n", txtfilename); 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", txtfilename); } void readOwonMemory(struct usb_device *dev) { usb_dev_handle *devHandle = 0; signed int ret=0; // set to < 0 to indicate USB errors 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 headerptr (0x%p) = %d\n", owonDataBuffer+owonDataBufferSize , headerptr, owonDataBuffer+owonDataBufferSize > headerptr); */ 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++; } // for(i=0;i1) { filename = malloc(strlen(argv[1])); memcpy(filename, argv[1], strlen(argv[1])); } // printf("..Initialising libUSB\n"); usb_init(); // printf("..Searching USB buses for Owon\n"); if(!devfindOwon()) { printf("..No Owon device %04x:%04x found\n", USB_LOCK_VENDOR, USB_LOCK_PRODUCT); return 0; } else readOwonMemory(usb_locks[0]); // for(i = 0; i < locksFound; i++) // readOwonMemory(usb_locks[i]); return 0; }