Skip to content

Add support for Siglent eCal emulation.#31

Merged
jankae merged 11 commits into
jankae:mainfrom
acceleratedtech:siglent-ecal
Jun 1, 2025
Merged

Add support for Siglent eCal emulation.#31
jankae merged 11 commits into
jankae:mainfrom
acceleratedtech:siglent-ecal

Conversation

@jwise
Copy link
Copy Markdown
Contributor

@jwise jwise commented May 29, 2025

This patch allows a Siglent SVA1032X to calibrate using a LibreCAL. It adds support in the firmware to boot into a Siglent-compatible mode (removes USB MSC, to avoid the instrument's UI trying to write to the disk; adds a very limited USB TMC that knows how to speak the Siglent calibration commands) when Siglent calibration coefficients are present, and the unit has the FUNCTION key held while powering on.

We also add a convert_siglent.py script, which converts the calibration data stored in the LibreCAL's native Touchstone format to the reverse-engineered header and ZIP+CSV format that a Siglent VNA requires. Some fields in the Siglent header are not fully understood, but they don't seem to make a difference at least on SVA1032X.

The calibration appears to provide good results tested against other calibration standards I have lying around, and so the S-parameter conversion for ports 1 and 2 at least are probably correct. Unimplemented is an "ATT" mode, used for the VNA's Confidence Check function; I suspect this may be an attenuator, but without a real Siglent eCal, I can't know for sure, and more to the point, the LibreCAL doesn't have one other than the Through port, so we do not implement it.

Here's a video of this operating: https://www.youtube.com/watch?v=H64bFWH6v2k

jankae and others added 2 commits May 13, 2025 18:12
This patch allows a Siglent SVA1032X to calibrate using a LibreCAL.  It
adds support in the firmware to boot into a Siglent-compatible mode
(removes USB MSC, to avoid the instrument's UI trying to write to the
disk; adds a very limited USB TMC that knows how to speak the Siglent
calibration commands) when Siglent calibration coefficients are present,
and the unit has the FUNCTION key held while powering on.

We also add a `convert_siglent.py` script, which converts the
calibration data stored in the LibreCAL's native Touchstone format to
the reverse-engineered header and ZIP+CSV format that a Siglent VNA
requires.  Some fields in the Siglent header are not fully understood,
but they don't seem to make a difference at least on SVA1032X.

The calibration appears to provide good results tested against other
calibration standards I have lying around, and so the S-parameter
conversion for ports 1 and 2 at least are probably correct.
Unimplemented is an "ATT" mode, used for the VNA's Confidence Check
function; I suspect this may be an attenuator, but without a real
Siglent eCal, I can't know for sure, and more to the point, the LibreCAL
doesn't have one other than the Through port, so we do not implement it.

Signed-off-by: Joshua Wise <joshua@joshuawise.com>
@jankae
Copy link
Copy Markdown
Owner

jankae commented May 29, 2025

Wow, thank you very much! Implementing something like this has crossed my mind, but so far I have not found reliable enough information about the protocol and calibration data of other eCals (and do not own any myself). Maybe I just didn't check thoroughly enough. I am actually very surprised how little changes you had to make to the code (although I am sure that it took a lot of work to get there).

I do have an SNA5014A (4 port Siglent VNA), so I can definitely check whether this works on all 4 ports.

Looking forward to testing this and I will definitely merge this once I have confirmed that it works.

@jwise
Copy link
Copy Markdown
Contributor Author

jwise commented May 29, 2025

Yeah, the documentation is definitely not out there ... definitely the easy part was implementing it and the hard part was doing the static analysis. This was the better part of my evenings this week and most of last weekend probably totaling to like 40 or 50 hours of Ghidra time :)

If someone actually has one of the real eCals I'd be really interested to see what the actual data format from those things is and see how closely it matches my results.

Future work (that is worth enumerating but which I don't really have any intention of doing):

  • I think someone else (@miek?) tried emulating an Agilent one, too. Could be nice to have EEPROMs of those, too.
  • What does ATT actually do? Can it be implemented with the same as THROUGH, and be a slightly-less-confidence check?
  • It would be nice to store these in the read-only partition (since, after all, these are factory calibrations), and even to automatically convert and compress it in firmware on boot. (But maybe librecal-gui could do that when updating firmware, so the ZIP engine doesn't have to be implemented in firmware after all?)
  • It would be nice to have some kind of LED indication that you've booted into Siglent mode. Or to be able to set Siglent mode as a default, for those of us who probably will never use their units with anything else.
  • It would be nice to be able to convert secondary calibrations, too.

@miek
Copy link
Copy Markdown

miek commented May 29, 2025

I think someone else (@miek?) tried emulating an Agilent one, too. Could be nice to have EEPROMs of those, too.

Yep, I did a write-up of some of the initial reverse-engineering & emulation here: https://greatscottgadgets.com/2024/11-13-reverse-engineering-a-vna-ecal-interface-with-cynthion/ and the code & EEPROM dumps are here: https://github.com/miek/ecal-reversing
I would be keen to do some more work on it and integrate support in the LibreCAL firmware if there's interest in that.

For now though, I've been using this script to run the process remotely: https://github.com/miek/vnautils/blob/main/src/vnautils/pna_librecal.py

What does ATT actually do? Can it be implemented with the same as THROUGH, and be a slightly-less-confidence check?

I suspect it selects a second through path with some attenuation (then after the cal it can then be measured and compared with the stored data as a rough verification). It probably could be the same as THROUGH, but wouldn't really give any extra confidence at all.

@jankae
Copy link
Copy Markdown
Owner

jankae commented May 29, 2025

No success so far. When I plug in the LibreCAL I do get a message that an eCal has been detected from the SNA5014A:
ScreenImg

But when I then try to start the electronic calibration it claims that no eCal module could be found:
ScreenImg(1)

Unfortunately I do not have an SVA1032X to try it with that.

@jankae
Copy link
Copy Markdown
Owner

jankae commented May 29, 2025

A little bit of progress: My SNA5014A sends *idn? instead of *IDN? (lower case). Your patch does not reply to that, so it claims that the eCal is not valid. By always converting the received command on the USB TMC to upper case I managed to fix that and now it does get detected!

Calibration however does not work yet. The VNA loads the data from the eCal and opens the calibration menu but it can not figure out the connection to the eCal. If I set the orientation to "auto" I get an "Unable to orient ECal module" error. If I set it to "manual", I am prompted to select which VNA port is connected to which LibreCAL port but that window looks as if some GUI elements are missing:
ScreenImg

Here you can see how that dialog is supposed to look like: https://www.youtube.com/watch?v=6vHX0moZUFg&t=1300s

Maybe the VNA expects some additional information from the LibreCAL to populate that dialog? I'll keep investigating that further and would love to hear your thoughts on that.

@jwise
Copy link
Copy Markdown
Contributor Author

jwise commented May 29, 2025

Interesting. Not a surprise that auto orientation doesn't work if it uses the same mechanism as 'confidence check'. Does it send SL ATT,x,y commands?

As it turns out SVA1032X doesn't send *IDN? at all and I just implemented that at the last minute to make sure I could test it from my host! So that's lucky that you noticed that. How are you getting debug data about commands -- do you have a serial port on your LibreCAL? If so, it would be good to log communications between the SNA5014A and the LibreCAL.

Sometimes in Siglent land, they hardcode things for model numbers. So you might want to try having your *IDN? reply with a 4-port SIglent model number. Also consider trying different values for the 0x4E and 0x4F bytes in the info.dat (you may have to reboot the VNA to get it to reload the info file from the ecal -- at least, I do on SVA1032X!) -- I have no idea what they do, only that they are copied into the ecal_model_t struct specially when it is loaded on SVA1032X.

If that doesn't do it, I guess the next steps would either be to pop the main binary from the SNA5000 into a disassembler, or wait until we can get access to a real eCal to dump the header from that.

@jankae
Copy link
Copy Markdown
Owner

jankae commented May 29, 2025

Does it send SL ATT,x,y commands?

No, it does not, there are actually no SL commands at all. There are however obvious other commands to set the port status. Here is the sequence of commands I get when trying to perform a 2-port SOLT calibration (with auto orientation enabled because I still can not select anything on manual mode):

*idn?
FL:DATA:READ:STARt
FL:DATA:READ?
SET:PORT OPEN,A
SET:PORT SHORT,A
SET:PORT LOAD,A
SET:PORT OPEN,B
SET:PORT SHORT,B
SET:PORT LOAD,B
SET:PORT ATT,A
SET:PORT OPEN,A
SET:PORT SHORT,A
SET:PORT LOAD,A
SET:PORT OPEN,D
SET:PORT SHORT,D
SET:PORT LOAD,D
SET:PORT THRU,A,D

I have implemented support for these commands (with ATT setting the standard on that port to Switch::Standard::None because I had no better idea). Port mapping is currently A->1, B->2, C->3, D->4.

The VNA does complete orientation detection and calibrate but the calibration data is all over the place. I have also no idea why it cycles through OPEN, SHORT, LOAD in the order of port A, B, A, D and then finally sets a THRU between A and D. I have ports 1 and 2 of the LibreCAL connected, so I expected it to use A and B only but I guess the auto orientation decided to use A and D (unfortunately it does not show you how it thinks the ports are connected).

How are you getting debug data about commands -- do you have a serial port on your LibreCAL?

That is what I usually do on most projects. But the LibreCAL already uses all pins of the RP2040 and I dropped any UART functionality. I'll definitely need to figure out a better way, but at the moment I have a JLink connected, set breakpoints and inspect variables. A UART output would be really helpful right now.

Sometimes in Siglent land, they hardcode things for model numbers. So you might want to try having your *IDN? reply with a 4-port SIglent model number.

That was my thought as well. I'll try to come up with some model numbers once I have improved on the debugging situation.

If that doesn't do it, I guess the next steps would either be to pop the main binary from the SNA5000 into a disassembler, or wait until we can get access to a real eCal to dump the header from that.

With a bit of luck I still have the binary from my SNA. Can't find it immediately but I definitely dumped that at some point. I won't be much help on the reverse engineering though (by far not enough experience with Ghidra).

@jwise
Copy link
Copy Markdown
Contributor Author

jwise commented May 29, 2025

I assume at some point in the past you had a relatively lengthy 'loading data...' period, and it skipped it this time because it still has the correct md5 of zip file cached.

Definitely try *IDN? replying with model numbers (as well as the header data generated by the python script), and also try experimenting with bytes 0x4E and 0x4F.

I think I might have someone who will be able to get a real eCal's data in the next week or so, as well.

@jankae
Copy link
Copy Markdown
Owner

jankae commented May 29, 2025

I got UART debug output using one of the LEDs now. Still no luck with manual port selection. I tried a replying to *IDN? as I would expect an SEM5014A to do ("Siglent Technologies,SEM5014A,<serial>,<firmware>") but that is apparently not enough.

I assume at some point in the past you had a relatively lengthy 'loading data...' period, and it skipped it this time because it still has the correct md5 of zip file cached.

I am not sure. As far as I remember, the time for loading data was always rather short. Sometimes it is instant, sometimes I see a loading bar for a short time (maybe 1-2 seconds?). It also doesn't seem to load the actual calibration data. With the debug output I have now, I always only see these two commands:

FL:DATA:READ:STARt
FL:DATA:READ? 1024

Never anything like FL:DATA:INDEX 0 which I would expect based on your code. Maybe it did that when it connected for the first time (didn't have debug output then) but I don't remember any long loading time.

I tried changing the md5 and the serial number in info.dat and rebooting the VNA but it still does not ask for the zip file.

@jwise
Copy link
Copy Markdown
Contributor Author

jwise commented May 29, 2025

Changing the md5 (make sure to change both Desc: and Data:? I don't know what Desc does; I thought I saw it entangled with the MD5 logic at some point) should trigger it to reload the zip file, so it is at least a little suspicious that it does not.

Is there an "ECal Info" box that correctly identifies the serial number, at least? On my SVA1032X, it looks like this:

image

I believe that "Port {A,B,C,D} Connector" are supposed to be populated. How they get their data, though, I don't know...

@jankae
Copy link
Copy Markdown
Owner

jankae commented May 29, 2025

My mistake, I only changed the DESC: md5. When I change the DATA: md5 it does indeed reload the zip file.

My eCal info box looks a bit different but it is there. The serial number and name are taken from the info.dat file, the response to *IDN? does not seem to matter:
ScreenImg(2)

I guess it believes the eCal to only have 2 ports in my case? This sort of tracks with the auto orientation detection. When I try to do a 1 port SOL calibration, the LibreCAL is only prompted to cycle its standards on port 1 and 2 during the auto detection, port 3 and 4 are never changed.

SOL with VNA port 1 connected to LibreCAL port 1:

555450 [USB TM,DBG]: USB TMC: SET:PORT OPEN,A
555671 [USB TM,DBG]: USB TMC: SET:PORT SHORT,A
555892 [USB TM,DBG]: USB TMC: SET:PORT LOAD,A
556111 [USB TM,DBG]: USB TMC: SET:PORT OPEN,B
556327 [USB TM,DBG]: USB TMC: SET:PORT SHORT,B
556543 [USB TM,DBG]: USB TMC: SET:PORT LOAD,B
556759 [USB TM,DBG]: USB TMC: SET:PORT ATT,A,B
556981 [USB TM,DBG]: USB TMC: SET:PORT OPEN,A
557511 [USB TM,DBG]: USB TMC: SET:PORT SHORT,A
558040 [USB TM,DBG]: USB TMC: SET:PORT LOAD,A

Makes sense to me: quickly cycling through the available standards on all ports, then setting all ports to ATT (whatever this means) and then slightly slower cycling through the standards on port 1 (A) during the actual calibration measurement.

SOL with VNA port 1 connected to LibreCAL port 2:

535067 [USB TM,DBG]: USB TMC: SET:PORT OPEN,A
535287 [USB TM,DBG]: USB TMC: SET:PORT SHORT,A
535508 [USB TM,DBG]: USB TMC: SET:PORT LOAD,A
535729 [USB TM,DBG]: USB TMC: SET:PORT OPEN,B
535949 [USB TM,DBG]: USB TMC: SET:PORT SHORT,B
536165 [USB TM,DBG]: USB TMC: SET:PORT LOAD,B
536381 [USB TM,DBG]: USB TMC: SET:PORT ATT,A,B
536602 [USB TM,DBG]: USB TMC: SET:PORT OPEN,D
537132 [USB TM,DBG]: USB TMC: SET:PORT SHORT,D
537662 [USB TM,DBG]: USB TMC: SET:PORT LOAD,D

This seems strange to me: the beginning is the same but for the actual calibration measurement port 4 (D) is used now?

I'll continue tomorrow :)

@jwise
Copy link
Copy Markdown
Contributor Author

jwise commented May 29, 2025

One thing that you might consider is mapping ATT to THROUGH, modifying outf.write("#HZ,A,B,C,D,T_AB,T_AC,T_AD,T_BC,T_BD,T_CD\n") to be outf.write("#HZ,A,B,C,D,T_AB,T_AC,T_AD,T_BC,T_BD,T_CD,CF_AB,CF_AC,CF_AD,CF_BC,CF_BD,CF_CD\n"), and then repeating the two-port coefficients in the data file (i.e., for port in ["12", "13", "14", "23", "24", "34"] * 2:).

@jankae
Copy link
Copy Markdown
Owner

jankae commented May 30, 2025

try experimenting with bytes 0x4E and 0x4F.

These bytes seem to be the number of ports on the eCal (uint16, little endian). Depending on what I put in there, I get the following results:

0 (0x4E = 0, 0x4F = 0):

  • Manual port selection dialog is empty
  • eCal info shows 2 ports

4 (0x4E = 4, 0x4F = 0):

  • Manual port selection dialog is populated (can select between A, B, C and D on the eCal)
  • eCal info shows 4 ports

All other values I have tried:

  • Manual port selection dialog is populated (can select between A and B on the eCal)
  • eCal info shows 2 ports

I guess 2 ports is the fallback in case the VNA can not interpret that value. Not sure though why the manual selection dialog is empty when set to 0.

With that figured out, I can now manually assign the ports:
ScreenImg(4)

I had some other bugs in my command parsing but after that I can calibrate! Here is the result of a 2 port SOLT, port 1 is terminated into a short and port 2 into a load:
ScreenImg(6)

My LibreCAL is open, so the measured coefficients are probably not exactly correct. Using other ports on the eCal works as well, I tested this with port 1 (A) and 3 (C). On this particular LibreCAL port 4 (D) is broken but I assume that would otherwise also work.

Automatic orientation detection is unreliable so far. Sometimes it figures out the ports correctly but other times it picks completely wrong ports. I don't think this has anything to do with the ATT command. I do receive that after the auto orientation but I do not believe that the VNA takes any measurements after sending that command. My best interpretation is that the ATT state is the default setting of Siglents eCals.

jankae added 2 commits May 30, 2025 13:53
- handle *IDN? response and SET:PORT commands send by some VNAs
- default to emulation mode if files are present (press function to force default mode)
- Adjusted script to set number of ports and handle early LibreCALs with partially missing factory data
- Add optional logging to LibreCAL firmware
- indicate emulation mode by blinking wait/ready LEDs
@jankae
Copy link
Copy Markdown
Owner

jankae commented May 30, 2025

Merged into the new siglent branch. See fa75511 for the changes/improvements I made.

This seems to work quite well with my SNA5014A now (as long as I manually assign ports) but I will hold off on merging this into main until I have done more tests (full 4 port calibration and testing whether everything still works with the LibreVNA).

Could you please check whether I broke anything for your SVA1032X? If you don't want to compile it yourself you can also use the artifact here.

jwise added 4 commits May 30, 2025 16:35
Signed-off-by: Joshua Wise <joshua@joshuawise.com>
Signed-off-by: Joshua Wise <joshua@joshuawise.com>
Signed-off-by: Joshua Wise <joshua@joshuawise.com>
…o kick the device into bootloader mode without pressing buttons

Signed-off-by: Joshua Wise <joshua@joshuawise.com>
@jwise
Copy link
Copy Markdown
Contributor Author

jwise commented May 30, 2025

I added support for ATT (as a clone of THRU, since we don't have an attenuator to mux in) and it seems to make the confidence check thing work. I wonder if that'll make your auto-assign work?

@jankae
Copy link
Copy Markdown
Owner

jankae commented May 31, 2025

Thanks for the review and for cleaning up after me ;)

I'll run some more tests later today and report back

@jankae
Copy link
Copy Markdown
Owner

jankae commented May 31, 2025

Merged into the siglent branch again (together with unrelated bug fixes).

After some more testing I think this works absolutely flawlessly:

  • Auto orientation detection works
  • Full 4-port calibration on the Siglent works
  • Calibration with the LibreVNA still works (when in default USB mode)

Well, there is one flaw but that is more on Siglent: My LibreCAL is missing P34_THRU coefficients above 7.9 GHz (this was an early bug in the factory calibration procedure). This means that all other Siglent coefficients above 7.9 GHz are also missing. But the SNA5014A happily calibrates all the way up to 8.5 GHz without any complaints. Calibrated data above 7.9 GHz is obviously bad since it has no coefficients for that. The ECal information screen correctly shows the upper frequency limit of 7.9 GHz.

Unless there are any concerns from your side I will merge this into main and create the 0.3.0 release from it (I think this warrants an increase in the minor version instead of just incrementing to 0.2.4).

@jwise
Copy link
Copy Markdown
Contributor Author

jwise commented May 31, 2025 via email

@jankae jankae merged commit 0427bdb into jankae:main Jun 1, 2025
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants