├── udev ├── 02-owon-sds.rules └── README ├── .gitignore ├── README.md ├── CMakeLists.txt ├── parse.h ├── owon.h ├── owon-parse.c ├── usb.h ├── owon-dump.c ├── usb.c └── parse.c /udev/02-owon-sds.rules: -------------------------------------------------------------------------------- 1 | SUBSYSTEM=="usb", ENV{ID_VENDOR_ID}=="5345", ENV{ID_MODEL_ID}=="1234", GROUP:="uucp", MODE:="0660" 2 | SUBSYSTEM=="usb", MODE:="0664", GROUP:="uucp" 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | test_files 3 | libowon-sds7102* 4 | owon-parse 5 | owon-dump 6 | CMakeFiles 7 | CMakeCache.txt 8 | cmake_install.cmake 9 | Makefile 10 | output.csv 11 | -------------------------------------------------------------------------------- /udev/README: -------------------------------------------------------------------------------- 1 | Add this rule to the "/etc/udev/rules.d" directory then add your user to the uucp group. 2 | 3 | Unplug and re-plug the device and enjoy your rootless owon-dump experience 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # owon-sds7102-protocol 2 | This is a collection of tools to manage Owon oscilloscopes under 3 | GNU/Linux (wonder if udev can be used with other OS). 4 | 5 | Tested with: 6 | Owon SDS7103 7 | Owon SDS5032 (Thanks Tony R.) 8 | 9 | If you need any assistance or have suggestions contact bjonnh-owon at bjonnh.net 10 | 11 | ## Compile 12 | $ cmake . 13 | $ make 14 | 15 | ## Run a dump 16 | $ owon-dump -h 17 | 18 | ## Parse a bin file 19 | $ owon-parse 20 | 21 | It will output a csv file with the first line describing the data 22 | 23 | ## Compile with debug mode activated (useful if you have a different model and need help) 24 | $ cmake -DCMAKE_BUILD_TYPE=Debug . 25 | $ make 26 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 2.6) 2 | project (OWON-SDS7102) 3 | 4 | include(FindPkgConfig) 5 | pkg_check_modules(LIBUSB REQUIRED libusb-1.0) 6 | include_directories(${LIBUSB_INCLUDE_DIRS}) 7 | link_directories(${LIBUSB_LIBRARY_DIRS}) 8 | 9 | add_library (owon-sds7102 SHARED usb.c parse.c) 10 | set_property(TARGET owon-sds7102 PROPERTY VERSION 0.1.0) 11 | 12 | add_executable (owon-dump owon-dump.c) 13 | target_link_libraries(owon-dump owon-sds7102 ${LIBUSB_LIBRARIES}) 14 | 15 | add_executable (owon-parse owon-parse.c) 16 | target_link_libraries(owon-parse owon-sds7102 ${LIBUSB_LIBRARIES}) 17 | 18 | install(TARGETS owon-sds7102 DESTINATION lib) 19 | install(TARGETS owon-dump DESTINATION bin) 20 | 21 | set(CMAKE_C_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wall -g -DDEBUG_KNOWN -DDEBUG_UNKNOWN") 22 | 23 | #Messages 24 | message("") 25 | message("-- Summary:") 26 | message("* Compilation type: CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}") 27 | message("* Install directory: CMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}") 28 | message("") 29 | -------------------------------------------------------------------------------- /parse.h: -------------------------------------------------------------------------------- 1 | #ifndef _PARSE_H_ 2 | #define _PARSE_H_ 3 | 4 | #include 5 | 6 | typedef struct { 7 | const unsigned char *data; 8 | const unsigned char *data_p; 9 | size_t len; 10 | } DATA_st; 11 | 12 | typedef struct { 13 | unsigned char name[4]; 14 | int32_t unknownint; 15 | int32_t datatype; 16 | unsigned char unknown4[4]; 17 | uint32_t samples_count; 18 | uint32_t samples_file; 19 | uint32_t samples3; 20 | double timediv; 21 | int32_t offsety; 22 | float voltsdiv; 23 | uint32_t attenuation; 24 | float time_mul; 25 | float frequency; 26 | float period; 27 | float volts_mul; 28 | double *data; 29 | } CHANNEL_st; 30 | 31 | typedef struct { 32 | uint32_t length; 33 | int32_t unknown1; 34 | int32_t type; 35 | char model[7]; 36 | int32_t intsize; 37 | char serial[30]; 38 | unsigned char triggerstatus; 39 | unsigned char unknownstatus; 40 | uint32_t unknownvalue1; 41 | unsigned char unknownvalue2; 42 | unsigned char unknown3[8]; 43 | size_t channels_count; 44 | CHANNEL_st **channels; 45 | } HEADER_st; 46 | 47 | 48 | int owon_parse(const char * const buf, size_t len, HEADER_st *header); 49 | int owon_output_csv(HEADER_st *header, FILE *file); 50 | void owon_free_header(HEADER_st *header); 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /owon.h: -------------------------------------------------------------------------------- 1 | /* 2 | * owon-utils - a set of programs to use with OWON Oscilloscopes 3 | * Copyright (c) 2012 Levi Larsen 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see 17 | */ 18 | 19 | #ifndef __OWON_H__ 20 | #define __OWON_H__ 21 | 22 | #define OWON_SUCCESS (0) 23 | #define OWON_ERROR (-1) 24 | #define OWON_ERROR_UNSUPPORTED (-2) 25 | #define OWON_ERROR_MEMORY (-3) 26 | #define OWON_ERROR_READ (-4) 27 | #define OWON_ERROR_HEADER (-5) 28 | #define OWON_ERROR_USB (-6) 29 | #define OWON_ERROR_USB_NOT_FOUND (-7) 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /owon-parse.c: -------------------------------------------------------------------------------- 1 | /* 2 | * owon-parse - a program to parse binary files 3 | * Copyright (c) 2013 Jonathan BISSON >bjonnh on bjonnh.net< 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include "parse.h" 25 | 26 | int main(int argc, char **argv) { 27 | FILE *fp,*fp2; 28 | int fd,fd2; 29 | 30 | int i; 31 | 32 | HEADER_st file_header; 33 | 34 | struct stat stbuf; 35 | 36 | char *buffer; 37 | 38 | if (argc<2) { 39 | printf("Give me the food !\n"); 40 | return 1; 41 | } 42 | 43 | fd=open(argv[1],O_RDONLY); 44 | if (fd==-1) { 45 | printf("Error: can't open file %s\n",argv[1]); 46 | return(128); 47 | } 48 | fp=fdopen(fd,"rb"); 49 | if (fp==NULL) { 50 | printf("Error: can't open file %s\n",argv[1]); 51 | return(128); 52 | } 53 | 54 | if (fstat(fd, &stbuf) == -1) { 55 | printf("Error: %s may not be a regular file\n",argv[1]); 56 | return(127); 57 | } 58 | 59 | buffer = calloc(stbuf.st_size,sizeof(char)); 60 | if (buffer==NULL) { 61 | 62 | printf("Can't allocate %d bytes of memory.\n",stbuf.st_size); 63 | return(126); 64 | } 65 | 66 | 67 | if (fread((void *)buffer,sizeof(char),stbuf.st_size,fp) != stbuf.st_size) { 68 | printf("Error: can't read file %s\n",argv[1]); 69 | return(125); 70 | } 71 | 72 | 73 | owon_parse(buffer,stbuf.st_size,&file_header); 74 | 75 | fp2=fopen("output.csv","w+"); 76 | 77 | owon_output_csv(&file_header,fp2); 78 | fclose(fp2); 79 | free((char *)buffer); 80 | owon_free_header(&file_header); 81 | return(0); 82 | } 83 | -------------------------------------------------------------------------------- /usb.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The usb part of owon-dump 3 | * Copyright (c) 2012, 2013, 2014 Jonathan Bisson 4 | * Martin Peres <> 5 | * 6 | * Based on: 7 | * owon-utils - a set of programs to use with OWON Oscilloscopes 8 | * Copyright (c) 2012 Levi Larsen 9 | * 10 | * This program is free software: you can redistribute it and/or modify 11 | * it under the terms of the GNU General Public License as published by 12 | * the Free Software Foundation, either version 3 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * This program is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU General Public License 21 | * along with this program. If not, see 22 | */ 23 | 24 | #ifndef __OWON__USB_H__ 25 | #define __OWON__USB_H__ 26 | 27 | #include 28 | 29 | #ifndef USB_DEBUG 30 | #define USB_DEBUG 3 31 | #endif 32 | 33 | #define OWON_USB_VENDOR_ID 0x5345 34 | #define OWON_USB_PRODUCT_ID 0x1234 35 | 36 | #define OWON_USB_INTERFACE 0x0 37 | #define OWON_USB_CONFIGURATION 0x1 38 | 39 | #define OWON_USB_ENDPOINT_IN 0x81 40 | #define OWON_USB_ENDPOINT_OUT 0x03 41 | 42 | #define OWON_USB_READ_SIZE 0x1000 43 | #define OWON_USB_REALLOC_INCREMENT (OWON_USB_READ_SIZE) 44 | 45 | // Transfer timout in milliseconds 46 | #define OWON_USB_TRANSFER_TIMEOUT 1000 47 | 48 | enum owon_start_command_type { 49 | DUMP_BMP = 0, 50 | DUMP_BIN, 51 | DUMP_MEMDEPTH, 52 | DUMP_DEBUGTXT, 53 | DUMP_COUNT 54 | }; 55 | 56 | enum owon_output_type { 57 | DUMP_OUTPUT_RAW = 0, 58 | DUMP_OUTPUT_CSV, 59 | DUMP_OUTPUT_COUNT 60 | }; 61 | 62 | void owon_usb_init(void); 63 | struct libusb_device_handle *owon_usb_get_device(int dnum); 64 | size_t owon_usb_get_device_count(); 65 | struct libusb_device_handle *owon_usb_easy_open(int dnum); 66 | struct libusb_device_handle *owon_usb_open(struct libusb_device_handle *dev); 67 | int owon_usb_read(struct libusb_device_handle *dev_handle, unsigned char **buffer, enum owon_start_command_type type); 68 | void owon_usb_close(struct libusb_device_handle *dev_handle); 69 | int owon_usb_is_managed(void *device); 70 | #endif // __OWON__USB_H__ 71 | -------------------------------------------------------------------------------- /owon-dump.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Owon-dump, dumping data from Owon oscilloscopes 3 | * Copyright (c) 2012, 2013, 2014 Jonathan Bisson 4 | * Martin Peres 5 | * 6 | * Based on: 7 | * owon-utils - a set of programs to use with OWON Oscilloscopes 8 | * Copyright (c) 2012 Levi Larsen 9 | * 10 | * This program is free software: you can redistribute it and/or modify 11 | * it under the terms of the GNU General Public License as published by 12 | * the Free Software Foundation, either version 3 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * This program is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU General Public License 21 | * along with this program. If not, see 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include "usb.h" 31 | #include "parse.h" 32 | 33 | struct owon_dump_params { 34 | uint8_t dnum; 35 | enum owon_start_command_type mode; 36 | enum owon_output_type output; 37 | char *filename; 38 | }; 39 | 40 | void usage(int argc, char **argv) 41 | { 42 | printf("usage: %s [-m (bmp|bin|memdepth|debugtxt)] [-o (raw|csv)] [-f output_file]", argv[0]); 43 | exit(EXIT_FAILURE); 44 | } 45 | 46 | /*void list_devices() 47 | { 48 | size_t count, i; 49 | 50 | owon_usb_init(); 51 | count = owon_usb_get_device_count(); 52 | 53 | printf("Owon-dump: %u devices\n", count); 54 | 55 | exit(EXIT_SUCCESS); 56 | }*/ 57 | 58 | int parse_cli(int argc, char **argv, struct owon_dump_params *params) 59 | { 60 | char c; 61 | 62 | params->dnum = 0; 63 | params->mode = DUMP_BIN; 64 | params->output = DUMP_OUTPUT_RAW; 65 | params->filename = NULL; 66 | 67 | while ((c = getopt (argc, argv, "m:o:f:")) != -1) { 68 | switch (c) { 69 | /* case 'd': 70 | sscanf(optarg, "%d", ¶ms->dnum); 71 | break;*/ 72 | case 'm': 73 | if (strcasecmp(optarg, "bmp") == 0) 74 | params->mode = DUMP_BMP; 75 | else if (strcasecmp(optarg, "bin") == 0) 76 | params->mode = DUMP_BIN; 77 | else if (strcasecmp(optarg, "memdepth") == 0) 78 | params->mode = DUMP_MEMDEPTH; 79 | else if (strcasecmp(optarg, "debugtxt") == 0) 80 | params->mode = DUMP_DEBUGTXT; 81 | else 82 | return 1; 83 | break; 84 | case 'o': 85 | if (strcasecmp(optarg, "raw") == 0) 86 | params->output = DUMP_OUTPUT_RAW; 87 | else if (strcasecmp(optarg, "csv") == 0) 88 | params->output = DUMP_OUTPUT_CSV; 89 | else 90 | return 1; 91 | break; 92 | case 'f': 93 | params->filename = strdup(optarg); 94 | break; 95 | /* case 'l': 96 | list_devices(); 97 | break;*/ 98 | default: 99 | usage(argc, argv); 100 | break; 101 | } 102 | } 103 | 104 | return 0; 105 | } 106 | 107 | int output_raw(FILE *fp, const char *buffer, long length) 108 | { 109 | // Write data out 110 | fwrite(buffer, sizeof(char), length, fp); 111 | } 112 | 113 | int output_csv(FILE *fp, const char *buffer, long length) 114 | { 115 | HEADER_st header; 116 | 117 | int ret = owon_parse(buffer, length, &header); 118 | if (ret < 0) 119 | return ret; 120 | 121 | owon_output_csv(&header, fp); 122 | } 123 | 124 | int main (int argc, char **argv) 125 | { 126 | struct owon_dump_params params; 127 | 128 | if (parse_cli(argc, argv, ¶ms)) 129 | usage(argc, argv); 130 | 131 | 132 | unsigned char *buffer; 133 | long length = -1; 134 | 135 | struct libusb_device_handle *dev_handle = owon_usb_easy_open(params.dnum); 136 | if (!dev_handle) { 137 | fprintf(stderr,"USB: Impossible to connect to device.\n"); 138 | return 2; 139 | } 140 | 141 | length = owon_usb_read(dev_handle, &buffer, params.mode); 142 | owon_usb_close(dev_handle); 143 | 144 | if (0 >= length) { 145 | libusb_clear_halt(dev_handle,OWON_USB_ENDPOINT_IN); 146 | libusb_clear_halt(dev_handle,OWON_USB_ENDPOINT_OUT); 147 | libusb_reset_device(dev_handle); 148 | fprintf(stderr, "Error reading from device: %li\n", length); 149 | exit(EXIT_FAILURE); 150 | } 151 | fprintf(stderr,"Writing file of length %d\n",length); 152 | // Get file pointer to file or stdout. 153 | FILE *fp; 154 | if (NULL == params.filename) { 155 | fp = stdout; 156 | } else { 157 | fp = fopen(params.filename, "wb"); 158 | if (NULL == fp) { 159 | fprintf(stderr, "Unable to open %s\n", params.filename); 160 | exit(EXIT_FAILURE); 161 | } 162 | } 163 | 164 | switch (params.output) { 165 | case DUMP_OUTPUT_RAW: 166 | output_raw(fp, buffer, length); 167 | break; 168 | case DUMP_OUTPUT_CSV: 169 | output_csv(fp, buffer, length); 170 | break; 171 | } 172 | 173 | free(buffer); 174 | 175 | // Only close fp if it's an actually file (don't close stdout). 176 | if (NULL != params.filename) { 177 | fclose(fp); 178 | } 179 | 180 | return 0; 181 | } 182 | -------------------------------------------------------------------------------- /usb.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The usb part of owon-dump 3 | * Copyright (c) 2012, 2013, 2014 Jonathan Bisson 4 | * Martin Peres <> 5 | * 6 | * Based on: 7 | * owon-utils - a set of programs to use with OWON Oscilloscopes 8 | * Copyright (c) 2012 Levi Larsen 9 | * 10 | * This program is free software: you can redistribute it and/or modify 11 | * it under the terms of the GNU General Public License as published by 12 | * the Free Software Foundation, either version 3 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * This program is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU General Public License 21 | * along with this program. If not, see 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include "usb.h" 31 | #include "owon.h" 32 | 33 | struct owon_start_command { 34 | char *start; 35 | uint8_t response_length; 36 | } commands[] = { 37 | { "STARTBMP", 12 }, 38 | { "STARTBIN", 12 }, 39 | { "STARTMEMDEPTH", 12 }, 40 | { "STARTDEBUGTXT", 4 } 41 | }; 42 | 43 | struct owon_start_response { 44 | unsigned int length; 45 | unsigned int unknown; 46 | unsigned int flag; // 0 for waveform, 1 for bitmap, 128 if multipart 47 | }; 48 | 49 | struct libusb_context *ctx = NULL; 50 | 51 | void owon_usb_init() { 52 | libusb_init(&ctx); 53 | libusb_set_debug(ctx, USB_DEBUG); 54 | } 55 | 56 | size_t owon_usb_get_device_count() { 57 | size_t count = 0; 58 | 59 | struct libusb_device **list; 60 | struct libusb_device *found = NULL; 61 | ssize_t cnt = libusb_get_device_list(NULL, &list); 62 | ssize_t i = 0; 63 | int err = 0; 64 | if (cnt < 0) 65 | fprintf(stderr,"Error getting the device count\n"); 66 | return -1; 67 | 68 | for (i = 0; i < cnt; i++) { 69 | struct libusb_device *device = list[i]; 70 | err = owon_usb_is_managed(device); 71 | if (err>0) 72 | count++; 73 | if (err<0) { 74 | fprint(stderr,"Failed to list devices CODE=%d\n",err); 75 | libusb_free_device_list(list, 1); 76 | return -1; 77 | } 78 | 79 | } 80 | libusb_free_device_list(list, 1); 81 | return count; 82 | } 83 | 84 | 85 | // Returns 1 if the device is managed 86 | // TODO: Add other Product_ID (testers needed) 87 | int owon_usb_is_managed(void *device) { 88 | struct libusb_device_descriptor desc; 89 | struct libusb_device_handle *handle = NULL; 90 | 91 | int ret=0,err=0; 92 | 93 | ret = libusb_get_device_descriptor(device, &desc); 94 | if (ret < 0) 95 | return -1; 96 | err = libusb_open(device,&handle); 97 | if (err < 0) { 98 | libusb_close(handle); 99 | return -2; 100 | } 101 | if (desc.idVendor == OWON_USB_VENDOR_ID && desc.idProduct == OWON_USB_PRODUCT_ID) { 102 | libusb_close(handle); 103 | return 1; 104 | } 105 | libusb_close(handle); 106 | return 0; 107 | } 108 | 109 | struct libusb_device_handle *owon_usb_get_device(int dnum) { 110 | size_t count = 0; 111 | 112 | struct libusb_device **list; 113 | struct libusb_device *found = NULL; 114 | struct libusb_device_handle *dev_handle = NULL; 115 | ssize_t cnt = libusb_get_device_list(NULL, &list); 116 | ssize_t i = 0; 117 | int err = 0; 118 | if (cnt < 0) { 119 | fprintf(stderr,"Error getting the device count\n"); 120 | return 0; 121 | } 122 | 123 | for (i = 0; i < cnt; i++) { 124 | struct libusb_device *device = list[i]; 125 | if (owon_usb_is_managed(device)) // TODO: Manage multiple oscilloscopes on the same computer 126 | found = device; 127 | } 128 | libusb_free_device_list(list, 1); 129 | 130 | if (NULL != found) { 131 | err = libusb_open(found,&dev_handle); 132 | if (err != 0) { 133 | return NULL; 134 | } 135 | } else { 136 | fprintf(stderr, "Device not found\n"); 137 | return NULL; 138 | } 139 | return dev_handle; 140 | } 141 | 142 | struct libusb_device_handle *owon_usb_open(struct libusb_device_handle *dev_handle) { 143 | int ret=0; 144 | int cfg; 145 | ret = libusb_get_configuration(dev_handle, &cfg); 146 | if (cfg != OWON_USB_CONFIGURATION) 147 | ret = libusb_set_configuration(dev_handle, OWON_USB_CONFIGURATION); 148 | ret |= libusb_claim_interface(dev_handle, OWON_USB_INTERFACE); 149 | 150 | if (0 > ret) { 151 | fprintf(stderr,"USB_Configuration error\n"); 152 | return NULL; 153 | } 154 | 155 | return dev_handle; 156 | } 157 | 158 | struct libusb_device_handle *owon_usb_easy_open(int dnum) { 159 | owon_usb_init(); 160 | 161 | struct libusb_device_handle *dev_handle = owon_usb_get_device(dnum); 162 | 163 | if (NULL == dev_handle) { 164 | fprintf(stderr, "Unable to open device\n"); 165 | return NULL; 166 | } 167 | dev_handle = owon_usb_open(dev_handle); 168 | 169 | return dev_handle; 170 | } 171 | 172 | int owon_get_response(struct owon_start_command *cmd, struct libusb_device_handle *dev_handle, struct owon_start_response *start_response) 173 | { 174 | int ret=-255; 175 | uint32_t transferred = 0; 176 | uint8_t tries=3; 177 | char start_response2[0x0c]; 178 | do { 179 | 180 | ret = libusb_bulk_transfer(dev_handle, 181 | OWON_USB_ENDPOINT_IN, 182 | (char *) start_response2, 183 | sizeof(start_response2), &transferred, 184 | OWON_USB_TRANSFER_TIMEOUT); 185 | fprintf(stderr,"Try %d ret=%d\n",3-tries,ret); 186 | } while (tries-->0 && ret<0); 187 | fprintf(stderr,"Get_response code=%d transferred=%d size=%d\n",ret,transferred,sizeof(start_response2)); 188 | 189 | if (ret<0) 190 | return -1; 191 | 192 | if (cmd->response_length != transferred && transferred!=0) { 193 | fprintf(stderr, "error usb: ret = %i resplength=%d, transferred=%d\n", ret,cmd->response_length,transferred); 194 | return -1; 195 | } 196 | memcpy(start_response,&start_response2,sizeof(start_response2)); 197 | return 0; 198 | } 199 | 200 | int owon_usb_read(struct libusb_device_handle *dev_handle, unsigned char **buffer, 201 | enum owon_start_command_type type) { 202 | struct owon_start_command *cmd; 203 | struct owon_start_response start_response; 204 | int multipart = 0; 205 | uint32_t allocated = 0, downloaded = 0; 206 | uint32_t transferred = 0; 207 | if (type >= DUMP_COUNT) 208 | return -1; 209 | 210 | cmd = &commands[type]; 211 | 212 | // Send the START command. 213 | int ret; 214 | 215 | ret = libusb_bulk_transfer(dev_handle,OWON_USB_ENDPOINT_OUT,cmd->start,strlen(cmd->start),&transferred,OWON_USB_TRANSFER_TIMEOUT); 216 | if (strlen(cmd->start) != transferred || ret!=0) { 217 | fprintf(stderr,"Command send error ret=%d\n",ret); 218 | return OWON_ERROR_USB; 219 | } 220 | 221 | // Get the response back. 222 | 223 | do { 224 | ret = owon_get_response(cmd, dev_handle, &start_response); 225 | 226 | fprintf(stderr,"resp: ret=%d",ret); 227 | if (ret>=0) 228 | fprintf(stderr," %d %d %d ", start_response.length,start_response.unknown,start_response.flag); 229 | fprintf(stderr,"\n"); 230 | if (ret == -1) { 231 | if (multipart==1 && start_response.length==0) { 232 | return downloaded; 233 | } 234 | if (allocated==downloaded) { 235 | return downloaded; 236 | } 237 | return -1; 238 | } 239 | if (start_response.flag > 128) { 240 | fprintf(stderr,"Multipart\n"); 241 | multipart=1; 242 | } else { 243 | multipart=0; 244 | } 245 | 246 | // Allocate enough memory to hold the data from the ocilloscope. 247 | if (allocated == 0) { 248 | fprintf(stderr,"Allocating %d\n",start_response.length); 249 | *buffer = malloc(start_response.length); 250 | if (NULL == *buffer) { 251 | fprintf(stderr,"Error allocating %d\n",start_response.length); 252 | return OWON_ERROR_MEMORY; 253 | } 254 | allocated = start_response.length; 255 | } else { 256 | fprintf(stderr,"Reallocating %d\n",start_response.length+allocated); 257 | *buffer = realloc(*buffer, start_response.length + allocated); 258 | if (*buffer == NULL) { 259 | fprintf(stderr,"Error reallocating %d\n",start_response.length+allocated); 260 | return OWON_ERROR_MEMORY; 261 | } 262 | allocated += start_response.length; 263 | } 264 | 265 | // Read data from the ocilloscope. 266 | int forloop; 267 | forloop=start_response.length; 268 | do { 269 | int tries=3; 270 | ret=-255; 271 | do { 272 | ret = libusb_bulk_transfer(dev_handle, OWON_USB_ENDPOINT_IN, *buffer + downloaded, 273 | 131072,&transferred, 50000); 274 | if (ret<0) { 275 | fprintf(stderr,"Try %d ret=%d transf=%d\n",3-tries,ret,transferred); 276 | usleep(100); 277 | } 278 | } while (tries-->0 && ret<0); 279 | 280 | forloop -= transferred; 281 | downloaded += transferred; 282 | 283 | if (ret==-1) 284 | return -1; 285 | fprintf(stderr,"%d/%d %d %% (rest=%d) ret=%d transferred=%d\n",downloaded,allocated,100*downloaded/allocated,forloop,ret,transferred); 286 | } while (allocated > downloaded); 287 | // fprintf(stderr,"%d/%d %d %% (rest=%d) ret=%d\n",downloaded,allocated,100*downloaded/allocated,forloop,ret); 288 | } while (multipart != 0); 289 | fprintf(stderr,"Downloaded: %d\n",downloaded); 290 | return downloaded; 291 | } 292 | 293 | void owon_usb_close(struct libusb_device_handle *dev_handle) { 294 | libusb_release_interface(dev_handle, OWON_USB_INTERFACE); 295 | libusb_close(dev_handle); 296 | libusb_exit(ctx); 297 | } 298 | 299 | -------------------------------------------------------------------------------- /parse.c: -------------------------------------------------------------------------------- 1 | /* 2 | * parse - functions for parsing owon binary files 3 | * Copyright (c) 2013 Jonathan BISSON >bjonnh on bjonnh.net< 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include "parse.h" 25 | 26 | #define ARRAY_LENGTH(x) (sizeof(x)/sizeof(*(x))) 27 | 28 | // _attenuation_table is from the Levi Larsen app 29 | static float _attenuation_table[] = { 1.0e0, 1.0e1, 1.0e2, 1.0e3 }; // We are only sure for these 30 | static float _volt_table[] = { 31 | 2.0e-2, 5.0e-2, // 10 mV 32 | 1.0e-1, 2.0e-1, 5.0e-1, // 100 mV 33 | 1.0e+0, 2.0e+0, 5.0e+0, // 1 V 34 | 1.0e+1, 2.0e+1, 5.0e+1, // 10 V 35 | 1.0e+2 // 100 V 36 | }; 37 | 38 | // Only for SDS7102 need to allow different models 39 | static double _timescale_table[] = { 40 | 2.0e-9, 5.0e-9, // 2 ns 41 | 1.0e-8, 2.0e-8, 5.0e-8, // 10 ns 42 | 1.0e-7, 2.0e-7, 5.0e-7, // 100 ns 43 | 1.0e-6, 2.0e-6, 5.0e-6, // 1 us 44 | 1.0e-5, 2.0e-5, 5.0e-5, // 10 us 45 | 1.0e-4, 2.0e-4, 5.0e-4, // 100 us 46 | 1.0e-3, 2.0e-3, 5.0e-3, // 1 ms 47 | 1.0e-2, 2.0e-2, 5.0e-2, // 10 ms 48 | 1.0e-1, 2.0e-1, 5.0e-1, // 100 ms 49 | 1.0e+0, 2.0e+0, 5.0e+0, // 1 s 50 | 1.0e+1, 2.0e+1, 5.0e+1, // 10 s 51 | 1.0e+2 // 100 s 52 | }; 53 | 54 | static double get_real_timescale(uint32_t timescale) { 55 | if (timescale >= ARRAY_LENGTH(_timescale_table)) { 56 | return(_timescale_table[ARRAY_LENGTH(_timescale_table) - 1]); 57 | } else { 58 | return(_timescale_table[timescale]); 59 | } 60 | } 61 | 62 | static float get_real_attenuation(uint32_t attenuation) { 63 | if (attenuation >= ARRAY_LENGTH(_attenuation_table)) { 64 | return(_attenuation_table[ARRAY_LENGTH(_attenuation_table) - 1]); 65 | } else { 66 | return(_attenuation_table[attenuation]); 67 | } 68 | } 69 | 70 | static float get_real_voltscale(uint32_t volt) { 71 | if (volt >= ARRAY_LENGTH(_volt_table)) { 72 | return(_volt_table[ARRAY_LENGTH(_volt_table) - 1]); 73 | } else { 74 | return(_volt_table[volt]); 75 | } 76 | } 77 | 78 | static void show_hex_uchar(unsigned char *data,unsigned int length) { 79 | int i; 80 | for(i=0;ilength); 129 | #ifdef DEBUG_UNKNOWN 130 | printf("Unknown1: %d\n",file_header->unknown1); 131 | #endif 132 | #ifdef DEBUG_KNOWN 133 | printf("Type: %d\n",file_header->type); 134 | printf("Model: %s\n",file_header->model); 135 | #endif 136 | #ifdef DEBUG_UNKNOWN 137 | printf("Intsize: %d\n",file_header->intsize); 138 | 139 | #endif 140 | #ifdef DEBUG_KNOWN 141 | printf("Serial: %s\n",file_header->serial); 142 | #endif 143 | #ifdef DEBUG_UNKNOWN 144 | printf("TRIGGERSTATUS: %d\n",file_header->triggerstatus); 145 | printf("UNKNOWNSTATUS: %d\n",file_header->unknownstatus); 146 | printf("UnknownValue1: %u\n",file_header->unknownvalue1); 147 | printf("UnknownValue2: %c ",file_header->unknownvalue2); 148 | switch(file_header->unknownvalue2) { 149 | case 'M': 150 | printf("Slow-Scan\n"); 151 | break; 152 | case 'F': 153 | printf("Unknown\n"); 154 | break; 155 | case 'G': 156 | printf("Unknown\n"); 157 | break; 158 | default: 159 | printf("Never seen\n"); 160 | } 161 | 162 | printf("Unknown3\n"); 163 | show_hex_uchar(file_header->unknown3,8); 164 | #endif 165 | } 166 | 167 | // Reading an unsigned 32 and increment the data_p position accordingly 168 | 169 | static uint32_t read_u32(DATA_st *data) { 170 | uint32_t temp; 171 | temp = (uint32_t)(data->data_p[3]) << 24 | 172 | (uint32_t)(data->data_p[2]) << 16 | 173 | (uint32_t)(data->data_p[1]) << 8 | 174 | (uint32_t)(data->data_p[0]); 175 | data->data_p += sizeof(uint32_t); 176 | return temp; 177 | } 178 | 179 | // Reading a signed 32 and increment the data_p position accordingly 180 | 181 | static int32_t read_32(DATA_st *data) { 182 | int32_t temp; 183 | temp = (int32_t)(data->data_p[3]) << 24 | 184 | (int32_t)(data->data_p[2]) << 16 | 185 | (int32_t)(data->data_p[1]) << 8 | 186 | (int32_t)(data->data_p[0]); 187 | data->data_p += sizeof(int32_t); 188 | return temp; 189 | } 190 | 191 | // Reading a signed 16 and increment the data_p position accordingly 192 | 193 | static int16_t read_16(DATA_st *data) { 194 | int16_t temp; 195 | temp = (int16_t)(data->data_p[1]) << 8 | 196 | (int16_t)(data->data_p[0]); 197 | data->data_p += sizeof(uint16_t); 198 | return temp; 199 | } 200 | 201 | // Reading a float and increment the data_p position accordingly 202 | 203 | static float read_f(DATA_st *data) { 204 | float temp; 205 | 206 | memcpy(&temp,&(*data->data_p),4); 207 | 208 | data->data_p += sizeof(float); 209 | return temp; 210 | } 211 | 212 | // Reading a char and increment the data_p position accordingly 213 | 214 | static unsigned char read_char(DATA_st *data) { 215 | unsigned char temp; 216 | temp = (unsigned char)(*data->data_p); 217 | data->data_p += sizeof(unsigned char); 218 | return temp; 219 | } 220 | 221 | // Reading a string from *data_p of length len-1, and copying in the memory area at destination. 222 | // add a \0 at the end of destination and increment data_p position 223 | void read_string_nullify(DATA_st *data, char *destination, size_t len) { 224 | memcpy(destination,data->data_p,len-1); 225 | destination[len]=0; 226 | data->data_p += len-1; 227 | } 228 | 229 | // Reading a string from *data_p of length len, and copying in the memory area at destination. 230 | // then increment data_p position 231 | 232 | static void read_string(DATA_st *data, char *destination, size_t len) { 233 | memcpy(destination,data->data_p,len); 234 | data->data_p += len; 235 | } 236 | 237 | // Parse a channel from data, len will be used to check if there is enough data 238 | 239 | static int parse_channel(DATA_st *data_s, CHANNEL_st *channel) 240 | { 241 | size_t i; 242 | 243 | read_string_nullify(data_s,channel->name,4); 244 | channel->unknownint = read_32(data_s); 245 | channel->datatype = read_32(data_s); 246 | read_string(data_s,channel->unknown4,4); 247 | channel->samples_count = read_u32(data_s); 248 | channel->samples_file = read_u32(data_s); 249 | channel->samples3 = read_u32(data_s); 250 | channel->timediv = get_real_timescale(read_u32(data_s)); 251 | channel->offsety = read_32(data_s); 252 | channel->voltsdiv = get_real_voltscale(read_u32(data_s)); 253 | channel->attenuation = get_real_attenuation(read_u32(data_s)); 254 | channel->time_mul = read_f(data_s); 255 | channel->frequency = read_f(data_s); 256 | channel->period = read_f(data_s); 257 | channel->volts_mul = read_f(data_s); 258 | channel->data = (double *) calloc(channel->samples_file,sizeof(double)); 259 | if (channel->data == NULL) { 260 | printf("Error: Can't allocate %d bytes of memory.\n",channel->samples_file*sizeof(int16_t)); 261 | return 2; 262 | } 263 | for (i=0;isamples_file-1;i++) { 264 | 265 | if (channel->datatype == 2) { 266 | channel->data[i] = read_16(data_s); 267 | } else { 268 | channel->data[i] = read_char(data_s); 269 | } 270 | } 271 | 272 | debug_channel(channel); 273 | } 274 | 275 | int owon_parse(const char * const buf, size_t len, HEADER_st *header) 276 | { 277 | unsigned int i; 278 | DATA_st data; 279 | DATA_st *data_s = &data; 280 | CHANNEL_st **channel_p; 281 | 282 | data_s->data = buf; 283 | data_s->data_p = buf; 284 | data_s->len = len; 285 | 286 | memset(header,0,sizeof(HEADER_st)); // Putting NULL in the structure for fields not present 287 | // This is used only with data coming over usb, we jump the first 12 bytes. 288 | if (strncmp((data_s->data_p)+12,"SPB",3) == 0) { 289 | header->length = read_32(data_s); // This is the length without the LAN header 290 | header->unknown1 = read_32(data_s); 291 | header->type = read_32(data_s); // This seems to be related to the number of parts in the file 292 | #ifdef DEBUG_KNOWN 293 | printf("Debug Known: found length %d\n", header->length); 294 | printf("Debug Known: found unknown1 %d\n", header->unknown1); 295 | printf("Debug Known: found type %d\n", header->type); 296 | #endif 297 | 298 | } 299 | 300 | read_string_nullify(data_s,header->model,sizeof(header->model)); 301 | #ifdef DEBUG_KNOWN 302 | printf("Debug Known, model: %s\n", header->model); 303 | #endif 304 | 305 | header->intsize=read_32(data_s); // Not really the size ? It was in some models, maybe… 306 | #ifdef DEBUG_KNOWN 307 | printf("Debug Known, size: %u\n", header->intsize); 308 | #endif 309 | if (header->intsize != 0xFFFFFF && header->intsize !=0 ) { // For short files coming from official app 310 | read_string_nullify(data_s,header->serial,sizeof(header->serial)); 311 | #ifdef DEBUG_KNOWN 312 | printf("Debug Known, serial: %s\n", header->serial); 313 | #endif 314 | 315 | header->triggerstatus = read_char(data_s); 316 | 317 | header->unknownstatus = read_char(data_s); 318 | header->unknownvalue1 = read_u32(data_s); 319 | header->unknownvalue2 = read_char(data_s); 320 | read_string(data_s,header->unknown3,sizeof(header->unknown3)); 321 | 322 | // debug_file(header); 323 | 324 | } 325 | 326 | header->channels_count = 0; 327 | 328 | while ((void *)data_s->data_p < (void *) data_s->data + data_s->len) { 329 | if (strncmp(data_s->data_p,"CH",2) == 0) { 330 | 331 | header->channels_count++; 332 | 333 | channel_p = realloc(header->channels,header->channels_count*sizeof(CHANNEL_st*)); 334 | if (channel_p==NULL) { 335 | printf("Can't allocate %d bytes of memory for channel data.\n",sizeof(CHANNEL_st*) * header->channels_count); 336 | return(126); 337 | } 338 | 339 | header->channels = channel_p; 340 | header->channels[header->channels_count-1] = malloc(sizeof(CHANNEL_st)); 341 | memset(header->channels[header->channels_count-1],0,sizeof(CHANNEL_st)); // Putting NULL in the structure for fields not present 342 | parse_channel(data_s,header->channels[header->channels_count-1]); 343 | } else if (strncmp(data_s->data_p,"INFO",4) == 0) { 344 | 345 | } 346 | data_s->data_p++; 347 | } 348 | return(0); 349 | } 350 | 351 | static float sample_id_to_time(const HEADER_st *header, uint32_t sample) 352 | { 353 | if (!header->channels_count) 354 | return -1.0; 355 | 356 | return header->channels[0]->timediv * 10.0 * sample / header->channels[0]->samples_count; 357 | } 358 | 359 | static float sample_to_volt(HEADER_st *header, uint8_t channel, uint32_t sample) 360 | { 361 | int8_t val; 362 | 363 | if (channel > header->channels_count - 1) 364 | return -1.0; 365 | 366 | val = header->channels[channel]->data[sample]; 367 | return val * 2.0 * header->channels[channel]->voltsdiv / 5.0; 368 | } 369 | 370 | static void volt_scale_to_string(float volt_scale, uint32_t *val, const char **unit) 371 | { 372 | if (volt_scale < 1) { 373 | *val = volt_scale * 1000; 374 | *unit = "mV"; 375 | } else if (volt_scale < 1000) { 376 | *val = volt_scale; 377 | *unit = "V"; 378 | } else { 379 | *val = volt_scale / 1000; 380 | *unit = "kV"; 381 | } 382 | } 383 | 384 | static void time_scale_to_string(double time_scale, uint32_t *val, const char **unit) 385 | { 386 | if (time_scale < 0.000001) { 387 | *val = time_scale * 1.0e9; 388 | *unit = "ns"; 389 | } else if (time_scale < 0.001) { 390 | *val = time_scale * 1000000; 391 | *unit = "us"; 392 | } else if (time_scale < 1) { 393 | *val = time_scale * 1000; 394 | *unit = "ms"; 395 | } else { 396 | *val = time_scale; 397 | *unit = "s"; 398 | } 399 | } 400 | 401 | int owon_output_csv(HEADER_st *header, FILE *file ) { 402 | size_t sample, channel, channels_count, samples_count; 403 | 404 | #ifdef DEBUG_UNKNOWN 405 | printf("Debug Unknown activated\n"); 406 | #endif 407 | #ifdef DEBUG_KNOWN 408 | printf("Debug Known activated\n"); 409 | #endif 410 | 411 | 412 | channels_count = header->channels_count; 413 | if (channels_count < 1) { 414 | #ifdef DEBUG_UNKNOWN || DEBUG_KNOWN 415 | printf("No channels found\n"); 416 | #endif 417 | 418 | return 1; 419 | } 420 | samples_count = header->channels[0]->samples_file; 421 | 422 | 423 | /* add the header to the CSV */ 424 | fprintf(file, "time"); 425 | for (channel = 0; channel < channels_count; channel++) { 426 | const char *time_unit, *volt_unit; 427 | uint32_t time_val, volt_val; 428 | 429 | time_scale_to_string(header->channels[channel]->timediv, &time_val, &time_unit); 430 | volt_scale_to_string(header->channels[channel]->voltsdiv, &volt_val, &volt_unit); 431 | fprintf(file, ",channel %i (Att %u, %u %s/div, %u %s/div)", 432 | channel + 1, header->channels[channel]->attenuation, 433 | volt_val, volt_unit, time_val, time_unit); 434 | } 435 | fprintf(file, "\n"); 436 | 437 | /* add the actual data */ 438 | for (sample = 0; sample < samples_count; sample++) { 439 | fprintf(file, "%f", sample_id_to_time(header, sample)); 440 | 441 | for(channel=0; channelchannels_count;i++) { 451 | free(header->channels[i]->data); 452 | free(header->channels[i]); 453 | } 454 | free(header->channels); 455 | } 456 | --------------------------------------------------------------------------------