| Products > Test Equipment |
| Download speed from Rigol DS1054Z or similar oscilloscope to a PC |
| << < (7/8) > >> |
| lundmar:
RoGeorge, FYI - liblxi also supports connecting to instruments using raw sockets. Simply use 'RAW' instead of 'VXI11' in the lxi_connect() call. RAW is faster because it does not suffer the overhead of the quite heavy VXI11 (SUN OneRPC) protocol. Soon liblxi will also support HiSLIP which basically brings the same speed as RAW but with the benefit of some instrument relevant protocol features. I plan to release the HiSLIP feature start next year. |
| RoGeorge:
Tried liblxi with TCP RAW, and I don't know how to handle the transfers. - it puts consecutive lxi_send() strings in the same data packet, which the oscilloscope won't understand. I don't know how to flush the transmit buffer after each lxi_send(), so as a workaround I'm adding an *OPC? at the end of each command. - even so, when the oscilloscope sends the first 250 000 data bytes, the lxi_receive() returns only the payload of the first TCP packet, which is 1460 bytes, while the oscilloscope continues to send more packets. I was expecting for the lxi_receive() to concatenate the payload of all the incoming TCP packets, and to stop only when the whole 250000 requested bytes are received (or if a timeout occurs), but lxi_receive() returns after the first TCP packet. Not sure if this is a bug or it's the NO_BLOCKING setting of a TCP RAW connection. I don't know how to send TCP_BLOCKING (or alike) to the socket, or if such a feature exists in a liblxi TCP RAW connection. --- Code: ---// liblxi git at https://github.com/lxi-tools/liblxi // to install it from the ubuntu repository (lxilib-dev is in most of the Linux distros repos and in FreeBSD): // sudo apt install liblxi-dev // headers in /usr/include/lxi.h // .so lib in /usr/lib/x86_64-linux-gnu/liblxi.so // display all installed files with 'dpkg -L liblxi-dev' or // sudo apt install apt-file // sudo apt-file update // (sudo???) apt-file list liblxi-dev // no need to append LIBRRY_PATH or LD_LIBRARY_PATH // man lxi_<TAB><TAB> // lxi_connect lxi_discover lxi_init lxi_send lxi_disconnect lxi_discover_if lxi_receive // compile and run this 'demo.c' from a Linux terminal (!!! -l options _must_ be positioned after the .c sources, not before) // cd /home/muuu/wd8TB/2019/Z/_hobby/__Pnnn/221103___VNA_DG4102_DS1054Z_python/sw/C // gcc -Werror -Wfatal-errors ./dl_24M_ADC_samples_ds1054z.c -llxi -ltirpc && time ./a.out #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #include <lxi.h> int main() { printf("liblxi w DS1054Z using TCP mode RAW"); // 12 bytes preamble + max of 24 mil bytes of ADC samples + 1 byte for the end of string terminator #define CHARS_BUFFER_MAX 24000000L // in specs max Rx in a single chunk is 250k chars for Rigol DS1054Z char *rx24MB = NULL; // declare a pointer to char, and initialize it with NULL rx24MB = malloc(CHARS_BUFFER_MAX * sizeof *rx24MB); // allocated memory on the heap for rx24MB[CHARS_BUFFER_MAX] if (!rx24MB) { // !!! always check the memory was allocated successfully !!! fputs ("ERROR: allocation memory failed for the 'rx24MB' buffer, exiting.", stderr); exit (EXIT_FAILURE); } int device, length, timeout = 2000; // timeout is in ms char response[1000]; // allocated on the stack, segmentation fault if too large, Linux max stack is 8MiB //char *command = "*IDN?"; // with or without \n, the osc responded in a VXI11 connection char *command = "*IDN?\n"; lxi_init(); // Initialize LXI library //device = lxi_connect("192.168.1.3", 0, "inst0", timeout, VXI11); // instument responded to either *IDN? or *IDN?\n // device = lxi_connect("192.168.1.3", 0, NULL, timeout, VXI11); // Connect to LXI device device = lxi_connect("192.168.1.3", 5555, "inst0", timeout, RAW); // must end in \n, or else *IDN? timeout as incomplete // device = lxi_connect("192.168.1.3", 5555, "TCP", timeout, RAW); // *IDN? Timeout err for timeout 2000 // device = lxi_connect("192.168.1.3", 0, "inst0", timeout, RAW); // NOPE // device = lxi_connect("192.168.1.3", 5555, "inst0", timeout, HISLIP); // *IDN? garbled chars for timeout 2000 lxi_send(device, command, strlen(command), timeout); // Send SCPI command ("*IDN?") lxi_receive(device, response, sizeof(response), timeout); // Rx until "\n", or sizeoff(response) chars, or timeout printf("%s\n", response); // exit if no DS1054Z // exit(0); // enable only ch1 command = ":CHAN1:DISP 1;*OPC?\n"; lxi_send(device, command, strlen(command), timeout); lxi_receive(device, response, sizeof(response), timeout); // Rx until "\n"?, or sizeoff(response) chars, or timeout command = ":CHAN2:DISP 0;*OPC?\n"; lxi_send(device, command, strlen(command), timeout); lxi_receive(device, response, sizeof(response), timeout); command = ":CHAN3:DISP 0;*OPC?\n"; lxi_send(device, command, strlen(command), timeout); lxi_receive(device, response, sizeof(response), timeout); command = ":CHAN4:DISP 0;*OPC?\n"; lxi_send(device, command, strlen(command), timeout); lxi_receive(device, response, sizeof(response), timeout); // ??? here to insert ":RUN"" // set acquisition mode to max 24_000_000 points memory depth // "auto", 12K, 120K, 1.2M, 12M, 24M" long mdep = 24000000L; command = ":RUN;*OPC?\n"; lxi_send(device, command, strlen(command), timeout); lxi_receive(device, response, sizeof(response), timeout); // sprintf(command, ":ACQ:MDEP %li", mdep); // sprintf(command, ":ACQ:MDEP %li%s", mdep, "\0"); command = ":ACQ:MDEP 24000000;*OPC?\n"; lxi_send(device, command, strlen(command), timeout); lxi_receive(device, response, sizeof(response), timeout); // wait for triggered do { command = ":TRIGger:STATus?\n"; lxi_send(device, command, strlen(command), timeout); lxi_receive(device, response, sizeof(response), timeout); } while (response[0] != 'T'); // then wait for ADC to aquire enough data clock_t stop_time; printf("wait %ld\n", clock()); stop_time = clock() + 30000; //30 seconds printf("beep %ld\n", stop_time); while (clock() < stop_time) ; printf("go %ld\n", clock()); // go to stop mode and prepare to dl 24MSa command = ":STOP;*OPC?\n"; lxi_send(device, command, strlen(command), timeout); lxi_receive(device, response, sizeof(response), timeout); command = ":WAV:SOUR CHAN1;*OPC?\n"; lxi_send(device, command, strlen(command), timeout); lxi_receive(device, response, sizeof(response), timeout); command = ":WAV:MODE RAW;*OPC?\n"; lxi_send(device, command, strlen(command), timeout); lxi_receive(device, response, sizeof(response), timeout); command = ":WAV:FORM BYTE;*OPC?\n"; lxi_send(device, command, strlen(command), timeout); lxi_receive(device, response, sizeof(response), timeout); // char c2[80] = {0}; char c2[80]; // loop to dl all samples in chunks of (max) 250_000 samples // set start-stop indexes for next chunk long rx_len, chunk_len; // long chunk_start, chunk_stop, chunk_size = 125000L; //dl 24Msa/134s long chunk_start, chunk_stop, chunk_size = 250000L; //dl 24Msa/86s max from specs // long chunk_start, chunk_stop, chunk_size = 750000L; //dl 24Msa/65s // long chunk_start, chunk_stop, chunk_size = 1000000L; //dl 24Msa/56s preferred ??? // long chunk_start, chunk_stop, chunk_size = 1175000L; //dl 24Msa/54s long samples_to_dl = 24000000L; if (samples_to_dl > mdep) { samples_to_dl = mdep; } for(chunk_start = 1; chunk_start < samples_to_dl; chunk_start += chunk_size) { sprintf(c2, ":WAV:STAR %li;*OPC?\n", chunk_start); puts(c2); rx_len = lxi_send(device, c2, strlen(c2), timeout); printf("ack_len=%ld\n", rx_len); lxi_receive(device, response, sizeof(response), timeout); //at the last chunk, chunk_len might be shorter than chunk_size if (chunk_start + chunk_size > samples_to_dl) { chunk_len = samples_to_dl - chunk_start + 1; chunk_stop = samples_to_dl; } else { chunk_len = chunk_size; chunk_stop = chunk_start + chunk_size - 1; } sprintf(c2, ":WAV:STOP %li;*OPC?\n", chunk_stop); puts(c2); rx_len = lxi_send(device, c2, strlen(c2), timeout); printf("ack_len=%ld\n", rx_len); lxi_receive(device, response, sizeof(response), timeout); command = ":WAV:DATA?\n"; puts(command); rx_len = lxi_send(device, command, strlen(command), timeout); printf("ack_len=%ld\n", rx_len); rx_len = lxi_receive(device, (char *)(&rx24MB[chunk_start-1]), chunk_len, timeout); printf("rx_data_bytes=%ld\n", rx_len); int err = 0; if(rx_len != chunk_len) { printf("ERROR - received too few bytes: "); err = 1; } printf("chunk_len=%li rx_len=%li\n", chunk_len, rx_len); printf("\n"); if(err) exit(err); } // Disconnect lxi_disconnect(device); } --- End code --- will output is this: --- Code: ---liblxi w DS1054Z using TCP mode RAWRIGOL TECHNOLOGIES,DS1104Z,DS1ZA164658712,00.04.05.SP2 wait 15947 beep 45970 go 45970 :WAV:STAR 1;*OPC? ack_len=18 :WAV:STOP 250000;*OPC? ack_len=23 :WAV:DATA? ack_len=11 rx_data_bytes=1460 ERROR - received too few bytes: chunk_len=250000 rx_len=1460 --- End code --- |
| RoGeorge:
To learn what SOCKET parameters are optimal for a connection with the instrument, I had to read about TCP sockets, and realized that for LXI instruments it is trivial to send SCPI commands (and read the response) directly from Python, using its included 'socket' module. ;D --- Code: ---import socket socket.setdefaulttimeout(5000) with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.connect(('192.168.1.3', 5555)) s.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) s.sendall(b'*IDN?\n') ans = s.recv(1024) print(ans) --- End code --- Will print --- Quote ---b'RIGOL TECHNOLOGIES,DS1104Z,DS1ZA123456789,00.04.05.SP2\n' --- End quote --- Where: #192.168.1.3 # is the fixed IP of the instrument (a Rigol DS1054Z oscilloscope) # 5555 # PORT number (fixed by manufacturer) 5555 for Rigol, 5025 for Agilent, etc. # 5000 # the timeout, in ms # 1024 # the size of the receive buffer I only need to connect by LAN, using fixed IP, don't need any VXI11 or PyVISA or Telnet or any other dependency. :-// |
| switchabl:
One thing to be aware of when using plain TCP sockets is that TCP is a stream-oriented protocol. Oh, of course it operates on packets at the implementation level. But it tries to hide all of that away and provide you with a pipe where you can put in bytes at one end (send) and take some bytes out again at the other end (recv). There is no concept of a "message" and one send on the scope might not correspond to a single recv on your side. In other words, your call to recv is allowed to return --- Code: ---b'RIGOL TECHNOLOGIES,DS1' --- End code --- or even just --- Code: ---b'R' --- End code --- The result will depend on the buffering strategies (and socket options) on either end as well as things like the underlying network packet size. So your example might not work on a different PC and it might not even work the next time you try it. You really need to keep calling recv until you have the expected number of bytes (or the termination character or hit a timeout). In order to minimize latency, the other socket option you might want to try is TCP_QUICKACK (in addition to or instead of TCP_NODELAY). |
| lundmar:
As it has been hinted, TCP is a stream oriented protocol so talking packets etc. does not make much sense. When you use RAW mode with liblxi the interface behaves a bit more low level and you basically have to do two things when sending commands and potentially receiving large amounts of response data. 1. Send command: Append newline ('\n') to command string so that receiving instrument knows a command has been received and proceeds to process it 2. Receive response: Keep calling lxi_receive() until it returns error (timeout) which will be the only indication that the instrument has no more response data to send. That is the downside of using TCP/RAW - one must rely on and wait for timeout because there is no protocol telling the client how much data to expect from the server. This is exactly what protocols like VXI11 and HiSLIP takes care of. VXI11 does it in a bad way because of its heavy protocol overhead but HiSLIP solves that. Now, I could move some of that complexity from the example to the liblxi implementation but I decided against it to give more control to the user so they can decide on buffering strategy etc. I've added some example code here which demonstrates how to send a SCPI command to request screenshot image data from an instrument and receive all the image data: https://github.com/lxi-tools/liblxi/blob/master/test/receive-image-data.c |
| Navigation |
| Message Index |
| Next page |
| Previous page |