<d1b2>
<codepox.> Sorry if I am in the wrong channel, I am new to ngscopeclient which I discovered recently, I am very impressed by its capabilities so I decide to try it. I have a Siglent SDS5104X which seems to be supported. I can connect to the scope but I cannot receive a waveform and get the errors below. The SDS5104X runs the software version V0.98R2 which is the most recent one. ERROR: ReadWaveformBlock: invalid length format ERROR: ReadWaveformBlock:
<d1b2>
invalid length format ERROR: ReadWaveformBlock: invalid length format ERROR: ReadWaveformBlock for wavedesc 0 failed ERROR: ReadWaveformBlock: invalid length format ERROR: ReadWaveformBlock for wavedesc 1 failed ERROR: ReadWaveformBlock: invalid length format ERROR: ReadWaveformBlock: invalid length format ERROR: ReadWaveformBlock for wavedesc 0 failed ERROR: ReadWaveformBlock: invalid length format
<d1b2>
<azonenberg> @codepox. no, this is the right channel. Can you open a ticket on github (on the ngscopeclient/scopehal repo) with more details? Specifically, re-run the application with --debug --trace SCPISocketTransport (assuming you're connecting via Ethernet) and upload the console output
<d1b2>
<azonenberg> that will log every command and reply sent to/from the scope (but not binary waveform data)
<d1b2>
<azonenberg> That may or may not be sufficient to debug the issue. Another potentially helpful thing would be to install Wireshark or a similar packet sniffer and upload a pcap of the session talking to the scope
<d1b2>
<azonenberg> Another way you can investigate would be to rebuild ngscopeclient in debug mode (cmake -DCMAKE_BUILD_TYPE=DEBUG then the normal build process) and then run under a debugger, setting a breakpoint on the error message (SiglentSCPIOscilloscope.cpp:1457)
<d1b2>
<azonenberg> What appears to be happening is that packetSizeSequence has a format that the driver doesn't know how to interpret as the length of a waveform. This means either a) the communication desynced somehow and the driver is parsing a response to an unrelated command as a waveform or b) the scope is sending a different format than we expect
<d1b2>
<azonenberg> ok yeah so this is showing where the problem happens but this log isn't detailed enough since we don't log binary data reads (this is to avoid spamming the log with raw ADC samples and making it megabytes long)
<d1b2>
<azonenberg> We need to know what the scope sent in reply to that ReadRawData call
<d1b2>
<azonenberg> So either a pcap or a debugger paused at that line I mentioned
<d1b2>
<azonenberg> (or i guess modifying the error message to print the length field it doesn't like)
veegee has quit [Ping timeout: 240 seconds]
<d1b2>
<codepox.> No problem, which debugger do you use? just GDB or do you use something with a graphical interface? Sorry if this is a stupid question, I am just new at this.
<d1b2>
<azonenberg> Up to you, I generally use gdb
veegee has joined #scopehal
<d1b2>
<codepox.> Do you know something with a graphical interface you can recommend?
<d1b2>
<azonenberg> I'm not familiar with GUI debuggers for Linux, sorry. But what I can do, if that's easier, is add some additional debug code that will log the info I need to the console
<d1b2>
<codepox.> I'll give it a try with GDB, it is a good learning exercise for me
<d1b2>
<azonenberg> Ok. Alternatively try to open up SiglentSCPIOscilloscope.cpp and find the ReadWaveformBlock() function
<d1b2>
<azonenberg> (I know there are graphical debuggers for Linux, I just don't use any of them)
<d1b2>
<azonenberg> As you can see from looking at that code, there's several different possible formats for the response here. Your scope probably is sending one we haven't seen before
<d1b2>
<azonenberg> aha ok so it's truncating the reply and only sending 6 bytes not the 9 we expected. Got it
<d1b2>
<azonenberg> Gimme a few minutes to code up a fix
<d1b2>
<codepox.> Wow, you are fast
<d1b2>
<azonenberg> So the SCPI standard format for binary data blocks is a # character, then an ASCII digit describing the length of the block length (in your case 6), then that many ASCII decimal digits containing the actual length value, then the data
<d1b2>
<azonenberg> so #662500 means "six byte length", "62500x bytes of data" where the X is unknown because we didn't read that byte from the scope yet
<d1b2>
<azonenberg> Siglent has, until now, always sent a nine digit length regardless of the actual length (so #9xxxxxxxxx)
<d1b2>
<codepox.> Ah, interesting
<d1b2>
<azonenberg> And our driver code assumed it would always do that
<d1b2>
<azonenberg> not sure if the 5000 series or your specific firmware or what changed that
<d1b2>
<azonenberg> Before I code up the modified version can you zoom in on the scope, as far as it will go, and run again?
<d1b2>
<azonenberg> i want to see what format it sends with minimum memory depth
<d1b2>
<azonenberg> this may get messy because there's actually three different possible framing formats the scope can send
<d1b2>
<azonenberg> DESC,#Nxxxx
<d1b2>
<azonenberg> DAT2,#Nxxxx
<d1b2>
<azonenberg> or just #Nxxxx
<d1b2>
<azonenberg> Right now we read seven bytes which is enough to distinguish. but if it sends <7 bytes of length with shallow memory depths I'll have to complicate this parsing logic a bit
<d1b2>
<codepox.> Oh wait it is different with 2 channels enabled SCPISocketTransport::SendCommand Sending :WAVEFORM:SOURCE C1;:WAVEFORM:DATA? [SCPISocketTransport::ReadRawData] Got 7 bytes ERROR: ReadWaveformBlock: invalid length format "#15 !!" [SCPISocketTransport::ReadRawData] Got 2 bytes SCPISocketTransport::SendCommand Sending :WAVEFORM:SOURCE C2;:WAVEFORM:DATA? [SCPISocketTransport::ReadRawData] Got 7 bytes ERROR: ReadWaveformBlock: invalid length
<d1b2>
format " #15" [SCPISocketTransport::ReadRawData] Got 2 bytes
<d1b2>
<azonenberg> So it can go down to one byte of length
<d1b2>
<azonenberg> ok, this is gonna take a little time to code up. pretty much going to end up being a full rewrite of ReadWaveformBlock
<d1b2>
<azonenberg> (made extra fun by the fact that I don't have a siglent scope in front of me to test with so I'm gonna be writing this entirely black box)
<d1b2>
<codepox.> This is annoying that they change the messages format like that. I would like to help but this might be over my head, I do some programming but in C not C++ and I don't know the message protocol. Does SIGLENT documents their protocol somewhere?
<d1b2>
<azonenberg> There's a programmers reference, yeth. But this particular bit is SCPI standard
<d1b2>
<azonenberg> the original code was written falsely assuming the scope would always send a 9-digit length, which is what almost every other scope sends
<d1b2>
<azonenberg> My plan is just to read one byte at a time until I see the # character, throw away anything before that, then read the length digit, then the actual length
<d1b2>
<azonenberg> just have to do some chores before i have a chance to do that
<d1b2>
<azonenberg> it'll be slightly less efficient than what we have now but given how slow the siglent firmware is i don't expect a measurable change in performance
<d1b2>
<codepox.> Thanks for looking into this but it should not be a priority, the SIGLENT 5104X is my personal scope, I have other scopes at work I can use (Tektronix MSO64, MS056B, MSO44, Pico 6426E) to play with ngscopeclient
<d1b2>
<codepox.> Oh, you already did it
<d1b2>
<codepox.> OK, i'll try
<d1b2>
<azonenberg> Fixing bugs and new-user-experience issues is our big priority leading up to the official v0.1 release some time in early to mid 2024
<d1b2>
<azonenberg> anything that ruins the day of someone trying it for the first time is something we want to fix
<d1b2>
<codepox.> Thanks again, much better now. I was getting a compile error so I changed this line to "uint32_t getLength = atoi(textlen);"
<d1b2>
<azonenberg> oops yeah i renamed the variable and didnt copy that line
<d1b2>
<codepox.> Sorry, I had to leave. Yes, the waveform acquisition is working now. Doing a little bit more testing, the channel scale and offset seems to work fine but there seem to be a problem with the time base. The timeline also seems correct and match what I see on the scope so for example the scope is on 500us/div, the timeline shows 5ms of signal which correct (10 divisions) The timebase shows 10kS/s and 100kS but the scope is on 125MS/s and 625kS
<d1b2>
If I change the sample rate to 1GS/s (still 100kS) the scope goes 500MS/s and 500KS, here is the trace: SCPISocketTransport::SendCommand Sending :ACQUIRE:MDEPTH? SCPISocketTransport::ReadReply Got 625k SCPISocketTransport::SendCommand Sending :TIMEBASE:SCALE 6.25E-05 SCPISocketTransport::SendCommand Sending :TRIGGER:MODE STOP SCPISocketTransport::SendCommand Sending :TRIGGER:MODE SINGLE SCPISocketTransport::SendCommand Sending :TRIGGER:STATUS?
<d1b2>
SCPISocketTransport::ReadReply Got Arm SCPISocketTransport::SendCommand Sending :TRIGGER:STATUS? SCPISocketTransport::ReadReply Got Stop
<d1b2>
<azonenberg> So setting of timebase is failing?
<d1b2>
<codepox.> Yes, there is some kind of factor 2 error on the time base and the number of sample is changing in a way I don't understand
<d1b2>
<azonenberg> But the software correctly displays the actual timebase on the scope?
<d1b2>
<azonenberg> Ok
<d1b2>
<codepox.> The timeline seems correct all the time
<d1b2>
<azonenberg> File a separate ticket for that with as many data points as you can. specifically the starting configuration, what you asked for, what you got
<d1b2>
<azonenberg> (both sample rate and record length)
<d1b2>
<azonenberg> Also if you can sweep through the time/div range and report the list of sample rates / memory depths available, that will help
<d1b2>
<azonenberg> Looking at the driver, it seems like right now most of the code for the 5000x treats it the same as a 2000x+
<d1b2>
<azonenberg> it's possible there is a slight change in timebase configuration that's breaking it
<d1b2>
<codepox.> Will do, also the scope do for example 125MS/s and not 100MS/s as well as go up to 5GS/s which is not a choice in the timebase window
<d1b2>
<azonenberg> I don't actually know if any of our users have tested on a 5000 specifically; the 2000/5000/6000 all use a common SCPI command set but the ADC config varies
<d1b2>
<azonenberg> Yeah. So my guess is that we need to add some code to the GetSampleRates[Non]Interleaved and GetSampleDepths[Non]Interleaved functions for the 5000 specifically
<d1b2>
<azonenberg> As far as 5 Gsps is that what the specs say, or what you can actually configure it for?
<d1b2>
<azonenberg> in particular, the set of enabled channels matters
<d1b2>
<azonenberg> most of these scopes you can use two channels at double the rate as four
<d1b2>
<azonenberg> anyway yeah what's most likely happening (for starters) is that the 5000 doesn't have dedicated code for memory depth and sample rate because nobody ever put that in the driver. So if you give me the list of legal values i can code it up quickly enough
<d1b2>
<codepox.> Yes, looks like the 5000 is different from the 2000. The scope can do 5GS/s with 2 channels but drops to 2.5GS/s with more
<d1b2>
<codepox.> OK, I'll do the list now
<d1b2>
<azonenberg> Sounds good thanks. I'm off to grab lunch and will poke at it after
<d1b2>
<azonenberg> if you want to try yourself, SiglentSCPIOscilloscope::GetSampleDepthsNoninterleaved() and GetSampleRatesNonInterleaved() have the list of legal values for 4 channel mode
<d1b2>
<azonenberg> right now MODEL_SIGLENT_SDS2000XP, MODEL_SIGLENT_SDS2000X_HD, and MODEL_SIGLENT_SDS5000X all share the same code path
<d1b2>
<azonenberg> (the 2 channel values are always double the 4 channel so it's calculated automatically)
<d1b2>
<codepox.> NonInterleaved is the 2 channel mode, no?
<d1b2>
<azonenberg> NonInterleaved is 4 channel
<d1b2>
<codepox.> OK you are right
<d1b2>
<azonenberg> Interleaved is 2 channel, where two ADCs are taking turns sampling the single input for each half of the scope
<d1b2>
<azonenberg> giving you double the rate and memory depth at the cost of turning off the other input
<d1b2>
<azonenberg> Some scopes support more complex multilevel interleaving, e.g. a lot of Rigol scopes have 1/2/4 channel modes
<d1b2>
<codepox.> Does the code knows which channels are interleaved (1,2) and (3,4) so 2 channels 1 and 3 goes up to 5GS/s but 2 channels 1 and 2 can only go 2.5GS/s
<d1b2>
<azonenberg> That's what GetInterleaveConflicts() does
<d1b2>
<azonenberg> it returns a list of channel pairs where if both are enabled, interleaving isn't available
<d1b2>
<azonenberg> right now, for all siglent scopes it returns (0,1) and (2, 3)
<d1b2>
<azonenberg> (internal channel numbers are zero based even though the front panel channels use one based indexing)
<d1b2>
<codepox.> OK, so the 5000 is no different (0,1) and (2,3)
<d1b2>
<azonenberg> Great. so no need to touch that
<d1b2>
<codepox.> I am making progress on the changing the code for the timebase but I see something strange, sending the command "ACQUIRE:MDEPTH" seems to change the maximum sample depth and not the current sample depth
<d1b2>
<azonenberg> In general, a lot of scopes don't provide explicit control over memory depth and sample rate separately
<d1b2>
<azonenberg> they expect you to control time/div and either depth or rate, and the other is a dependent variable
<d1b2>
<azonenberg> it's ugly and annoying and i don't like it, but that's how it's done
<d1b2>
<azonenberg> scopehal's API provides you with direct control over both
<d1b2>
<azonenberg> So the driver has to translate between the two, which can get tricky
<d1b2>
<codepox.> That's annoying, so the only values I should put in the code are the max values?
<d1b2>
<azonenberg> The driver should set time/div so that the full scope view, at the requested sample rate, is the target number of samples long
<d1b2>
<azonenberg> then set the memory depth to provide the requested sample rate
<d1b2>
<azonenberg> basically, any time you change record length OR sample rate in the code you will probably have to tweak both on the scope
<d1b2>
<azonenberg> The driver should already be doing this for you
<d1b2>
<azonenberg> and all you have to change is the list of legal values
<d1b2>
<azonenberg> But i can't promise it's bug free 🙂
<d1b2>
<azonenberg> see SetSampleRate()
<d1b2>
<codepox.> OK, I see now. It is a bit tricky indeed.
<d1b2>
<codepox.> I modified the source code so it has the right sample rate and sample depth values, it works better than using the same values as the 2000 but the algorithm to set the time base does not work very well with this scope. I will need to spend more time on this. I attached the modified file