tektronix / tm_devices

Test & Measurement Device Management
https://tm-devices.readthedocs.io
Apache License 2.0
48 stars 21 forks source link

Tek MDO3104 Screenshot Saving #348

Open driftregion opened 4 days ago

driftregion commented 4 days ago

Hello,

I'd like to use tm_devices to save and transfer a screenshot via SCPI on the Tek MDO3104. I am using this example code: https://github.com/tektronix/tm_devices/blob/main/examples/scopes/tekscope/save_screenshot.py However, I run into the following issue.

2024-11-11 12:29:52.791078-07 - Response from '*IDN?' >>  'TEKTRONIX,MDO3104,C024043,CF:91.1CT FV:v1.24'
1.24
builtins.AttributeError: 'MDO3K' object has no attribute 'save_screenshot'

Question: what is the recommended way of saving a screenshot on MDO3104?

Here is my current approach, based on pyVISA:

    def _wait_until_ready(self, timeout=20):
        start_time = time.time()
        while time.time() - start_time < timeout:
            try:
                busy = int(self.inst.query('BUSY?'))
                if busy == 0:
                    break
            except pyvisa.errors.VisaIOError:
                continue
            time.sleep(0.1)

    def _download_file(self, filename) -> bytes:
        """
        :param filename: the name of the file on the scope 
        :return: the contents of the file
        """
        # Knowing the exact size of the file makes the
        # read much faster, so we query it here
        contents = self.inst.query("FILESystem:LDIR?")

        try:
            sizes = {
                name: int(size) for name, _type, size, _date, _time in [
                    line.replace('"', '').split(";") for line in contents.strip().split(",")
                ]
            }
        except ValueError:
            raise ValueError(f"Could not parse file sizes from {contents}")
        size = sizes[filename.split("/")[-1]]
        self.inst.write(f'FILESystem:READFile "{filename}"')

        data = self.inst.read_binary_values(datatype=f"B", header_fmt="empty", data_points=size)
        return bytes(data)

    def get_screenshot_png(self) -> bytes:
        """
        :return: the PNG image of the oscilloscope screen as bytes

        :example:
        with open("foo.png", "wb") as f:
            f.write(scope.get_screenshot_png())
        """
        self.inst.write('SAVE:IMAGe "E:/Temp.png"')
        self._wait_until_ready()

        imgData = self._download_file("E:/Temp.png")

        self.inst.write('FILESystem:DELEte "E:/Temp.png"')
        return imgData

It is ugly and requires that a USB flash drive be plugged in to the front of the scope. I have also used this code on 4-Series scope with success. The 4-series has an internal C:/ drive that may be used instead of the USB flash drive.

amccann-Tek commented 1 day ago

Hi @driftregion,

Thanks for posting about this. You actually stumbled across an example that is in Main but is not yet in a released version. We are just holding off the release until after the holidays. We hope to provide save_screenshot() support to the legacy scope families as well.

In the interim I think what you might be looking for is what Tek used to refer to as "hardcopy" See the below example in our dedicated Example repo: Hardcopy Example

That at least should eliminate the need for the thumb drive. But the rest of your code seems fine.

Let me know if you have any issues or questions -Andrew