#!/usr/bin/env python3 """ MIPI TEST APPLICATION - MIPI_TEST.PY - ENTRY POINT OF APPLICATION VERSION: 0.1 AUTHOR: D. RICE 25/03/2026 © 2026 ARRIVE """ import vxi11 import time import sys import requests import threading import os # --- Configuration --- URL = "http://192.168.45.8:5000/display" SCOPE_IP = "192.168.45.4" PSU_IP = "192.168.45.3" test_running = False # Global flag to control the background thread # --- Instrument Connection --- try: psu = vxi11.Instrument("192.168.45.3") scope = vxi11.Instrument("192.168.45.4") # Increase timeout for large image data transfers scope.timeout = 5 except Exception as e: print(f"ERROR: CANNOT CONNECT TO INSTRUMENTS: {e}") sys.exit(1) def setup_scope(): """Initializes DSO80204B for 210MHz MIPI Signals""" print("Configuring Scope...") scope.write("*RST") time.sleep(1) # Setup Channels (1&2=Clock, 3&4=Data) for ch in range(1, 5): scope.write(f":CHANnel{ch}:DISPlay ON") scope.write(f":CHANnel{ch}:PROBe 19.2") # 910R/50R Divider scope.write(f":CHANnel{ch}:INPut DC50") scope.write(f":CHANnel{ch}:SCALe 0.1") # 100mV/div (200mV HS swing) scope.write(f":CHANnel{ch}:OFFSet 0.0") # Timebase & Trigger scope.write(":TIMebase:SCALe 10e-6") # 10us/div scope.write(":TRIGger:EDGE:SOURce CHANnel3") scope.write(":TRIGger:EDGE:SLOPe FALLing") # Catch LP-11 to HS transition scope.write(":TRIGger:LEVel CHANnel3, 0.15") # 150mV scope.write(":TRIGger:SWEep SINGle") print("Scope Configured.") def save_screenshot(iteration): try: scope.clear() # Use a very short, simple path. No subdirectories if possible. # Ensure C:\TEMP exists! filename = f"C:\\TEMP\\cap{iteration}.png" print(f"ATTEMPTING SIMPLEST XP SYNTAX: {filename}") # Try ONLY the filename and the format. # Many XP versions hate the SCR,COL extra parameters via SCPI. cmd = f':DISK:SAVE:IMAGe "{filename}",PNG' scope.write(cmd) # DO NOT use scope.ask() or any queries yet. # Just sleep and check the folder manually. time.sleep(5.0) print("COMMAND SENT. CHECK C:\\TEMP") except Exception as e: print(f"XP COMM ERROR: {e}") def test_worker(on_time): """ Background loop: - Sets display ON for user-defined duration. - Sets display OFF for exactly 1 second. """ global test_running count = 1 while test_running: # Arm scope scope.write(":SINGle") # Start display requests.put(URL, json={"state": "on"}, timeout=2) # Wait for the scope to trigger and fill memory # We wait slightly longer than the 'on_time' to ensure capture is done time.sleep(on_time + 0.5) # Pull the image save_screenshot(count) count += 1 # Set Display OFF requests.put(URL, json={"state": "off"}, timeout=2) # Fixed 1s OFF period time.sleep(1.0) def main_menu(): global test_running """Main Application Loop.""" while True: print("\n===== MIPI TEST CONTROL =====") print("1. RUN IDN CHECK (PSU & SCOPE)") print("2. SETUP SCOPE (RUN FIRST)") print("3. CONFIGURE PSU (DEFAULT 24V/1.5A)") print("4. PSU OUTPUT ON/OFF (CH1)") print("5. START TEST & CAPTURE") print("6. STOP TEST") print("7. EXIT") choice = input("\nSELECT OPTION (1-7): ") if choice == '1': print(f"PSU: {psu.ask('*IDN?')}") print(f"SCOPE: {scope.ask('*IDN?')}") elif choice == '2': setup_scope() elif choice == '3': psu.write('CH1:VOLT 24.0') psu.write('CH1:CURR 1.5') print("PSU CONFIGURED...") elif choice == '4': state = input("TYPE 'ON' OR 'OFF': ").upper() psu.write(f'OUTP CH1,{state}') elif choice == '5': if not test_running: try: sec = float(input("ON DURATION (sec): ")) test_running = True t = threading.Thread(target=test_worker, args=(sec,), daemon=True) t.start() except ValueError: print("INVALID TIME ENTERED.") else: print("TEST IS ALREADY RUNNING!") elif choice == '6': print("STOPPING TEST...") test_running = False elif choice == '7': test_running = False # Ensure thread stops psu.close() scope.close() break else: print("INVALID ENTRY. PLEASE CHOOSE 1-6") if __name__ == "__main__": # Optional: Run auto-config on startup main_menu()