#!/usr/bin/env python3 """ Test script for FastFrame waveform acquisition. Tests acquiring 1000 FastFrame records from a 1MHz square wave on CH1. Signal: 1MHz square wave, +250mV to -250mV Records: 1000 frames, 2500 points each """ import time from tektronix_base import TektronixOscilloscopeBase def main(): """Test FastFrame acquisition with 1MHz square wave on CH1""" scope_ip = "192.168.10.105" print(f"Connecting to oscilloscope at {scope_ip}...") scope = TektronixOscilloscopeBase(resource_name=scope_ip, timeout=20.0) try: # Connect to scope scope.connect() print(f"Connected to {scope.identify()}\n") print("=== Configuring Oscilloscope ===\n") # Configure CH1 for the signal print("Configuring CH1...") print(" Setting vertical scale to 100mV/div...") scope.set_channel_scale(1, 0.1) # 100mV/div for ±250mV signal print(" Setting vertical offset to 0V...") scope.set_channel_offset(1, 0.0) print(" Setting coupling to DC...") scope.set_channel_coupling(1, 'DC') print(" Setting termination to 50 ohms...") scope.set_channel_termination(1, 50) # Verify settings actual_scale = scope.get_channel_scale(1) actual_offset = scope.get_channel_offset(1) actual_coupling = scope.get_channel_coupling(1) actual_term = scope.get_channel_termination(1) print(f" Verified: {actual_scale}V/div, {actual_offset}V offset, {actual_coupling} coupling, {actual_term}Ω\n") # Configure horizontal timebase # For 1MHz square wave (1µs period), let's capture ~4 cycles (4µs) # 4µs over 10 divisions = 400ns/div print("Configuring horizontal timebase...") print(" Setting time scale to 400ns/div (4µs total for ~4 cycles of 1MHz)...") scope.set_time_scale(400e-9) # 400ns/div print(" Setting record length to 2500 points...") scope.set_record_length(2500) # Verify settings actual_time_scale = scope.get_time_scale() actual_record_length = scope.get_record_length() actual_sample_rate = scope.get_sample_rate() print(f" Verified: {actual_time_scale*1e9:.0f}ns/div, {actual_record_length} points") print(f" Sample rate: {actual_sample_rate/1e6:.1f} MS/s\n") # Configure trigger print("Configuring trigger...") print(" Setting trigger source to CH1...") scope.set_trigger_source('CH1') print(" Setting trigger level to +100mV...") scope.set_trigger_level(1, 0.1) # +100mV print(" Setting trigger slope to rising...") scope.set_trigger_slope('RISE') print(" Setting trigger mode to NORMAL...") scope.set_trigger_mode('NORMAL') print(" Setting trigger coupling to DC...") scope.set_trigger_coupling('DC') # Verify trigger settings actual_trigger_source = scope.get_trigger_source() actual_trigger_level = scope.get_trigger_level(1) actual_trigger_slope = scope.get_trigger_slope() actual_trigger_mode = scope.get_trigger_mode() print(f" Verified: {actual_trigger_source}, {actual_trigger_level}V, {actual_trigger_slope}, {actual_trigger_mode}\n") # Configure FastFrame print("Configuring FastFrame...") print(" Enabling FastFrame...") scope.set_fastframe_state(True) print(" Setting frame count to 1000...") scope.set_fastframe_count(1000) # Verify FastFrame settings ff_state = scope.get_fastframe_state() ff_count = scope.get_fastframe_count() print(f" Verified: FastFrame {'enabled' if ff_state else 'disabled'}, {ff_count} frames\n") # Configure waveform transfer print("Configuring waveform transfer...") scope.set_data_encoding('RIBinary') scope.set_wfmoutpre_encoding('BINary') scope.set_wfmoutpre_byte_count(1) scope.set_wfmoutpre_byte_order('MSB') scope.set_data_source('CH1') # Verify configuration actual_encoding = scope.get_data_encoding() actual_source = scope.get_data_source() print(f" Verified: {actual_encoding} encoding, source {actual_source}") print(" Data transfer configured for 8-bit signed binary\n") print("=== Acquiring FastFrame Records ===\n") # Wait for FastFrame settings to take effect print("Waiting for FastFrame configuration to settle...") time.sleep(0.5) # Start acquisition - this arms the scope and begins acquiring triggered frames print("Starting acquisition (arming scope)...") scope.write("ACQuire:STATE RUN") print(" Scope is now armed and acquiring triggered events") # Wait and poll for acquisition to complete print(f" Waiting for {ff_count} triggers to be acquired...") print(" (Polling acquisition state...)") # Poll for up to 10 seconds for i in range(100): time.sleep(0.1) state = scope.query("ACQuire:STATE?") if i % 10 == 0: # Print every second print(f" Polling... state: {state.strip()}") # Check if we've acquired enough frames if i > 20: # After 2 seconds minimum break # Stop acquisition print(" Stopping acquisition...") scope.write("ACQuire:STATE STOP") # Verify we have frames time.sleep(0.2) print("✓ Acquisition stopped, scope should now have all 1000 frames\n") # Clear any leftover data in the receive buffer print("Clearing receive buffer...") scope.socket.setblocking(False) try: while True: junk = scope.socket.recv(4096) if not junk: break print(f" Cleared {len(junk)} bytes of junk data") except: pass scope.socket.setblocking(True) print(" Buffer cleared\n") # Test transferring a single frame first print("Testing single frame transfer first...") try: scope.set_fastframe_selected(1) print(" Selected frame 1") test_curve = scope.transfer_curve() print(f" ✓ Successfully transferred {len(test_curve)} bytes") test_waveform = scope.parse_curve_data(test_curve, byte_count=1, signed=True, byte_order='MSB') print(f" ✓ Parsed {len(test_waveform)} samples") print(f" First 10 values: {test_waveform[:10]}\n") except Exception as e: print(f" ✗ Single frame test failed: {e}") print(" Cannot proceed with bulk transfer\n") raise # Now transfer all 1000 frames print(f"Transferring {ff_count} frames from scope...") print("This may take a while...\n") start_time = time.time() all_waveforms = [] for frame_num in range(1, ff_count + 1): # Show progress every 100 frames if frame_num % 100 == 0 or frame_num == 1: elapsed = time.time() - start_time if frame_num > 1: rate = frame_num / elapsed eta = (ff_count - frame_num) / rate print(f" Frame {frame_num}/{ff_count} - Elapsed: {elapsed:.1f}s - Rate: {rate:.1f} frames/s - ETA: {eta:.1f}s") else: print(f" Frame {frame_num}/{ff_count}...") # Select this frame scope.set_fastframe_selected(frame_num) # Verify frame selection for first frame if frame_num == 1: actual_frame = scope.get_fastframe_selected() if actual_frame != frame_num: print(f" Warning: Frame mismatch - requested {frame_num}, got {actual_frame}") # Transfer curve data directly with longer timeout old_timeout = scope.socket.gettimeout() scope.socket.settimeout(30.0) try: curve_bytes = scope.transfer_curve() waveform = scope.parse_curve_data(curve_bytes, byte_count=1, signed=True, byte_order='MSB') all_waveforms.append(waveform) finally: scope.socket.settimeout(old_timeout) end_time = time.time() total_time = end_time - start_time print(f"\n✓ Acquired all {ff_count} frames in {total_time:.2f} seconds") print(f" Average rate: {ff_count / total_time:.2f} frames/second") print(f" Average time per frame: {total_time / ff_count * 1000:.2f} ms\n") # Analyze the acquired data print("=== Waveform Statistics ===\n") # Check that all frames have the expected length frame_lengths = [len(wf) for wf in all_waveforms] print(f"Frame lengths: {min(frame_lengths)} to {max(frame_lengths)} points") # Calculate statistics for first frame first_frame = all_waveforms[0] print(f"\nFirst frame (frame 1):") print(f" Samples: {len(first_frame)}") print(f" Min ADC value: {min(first_frame)}") print(f" Max ADC value: {max(first_frame)}") print(f" Average ADC value: {sum(first_frame) / len(first_frame):.2f}") print(f" First 20 values: {first_frame[:20]}") # Calculate statistics for middle frame mid_frame_idx = ff_count // 2 mid_frame = all_waveforms[mid_frame_idx] print(f"\nMiddle frame (frame {mid_frame_idx + 1}):") print(f" Samples: {len(mid_frame)}") print(f" Min ADC value: {min(mid_frame)}") print(f" Max ADC value: {max(mid_frame)}") print(f" Average ADC value: {sum(mid_frame) / len(mid_frame):.2f}") # Calculate statistics for last frame last_frame = all_waveforms[-1] print(f"\nLast frame (frame {ff_count}):") print(f" Samples: {len(last_frame)}") print(f" Min ADC value: {min(last_frame)}") print(f" Max ADC value: {max(last_frame)}") print(f" Average ADC value: {sum(last_frame) / len(last_frame):.2f}") # Calculate overall statistics all_values = [val for wf in all_waveforms for val in wf] print(f"\nOverall statistics (all {ff_count} frames, {len(all_values)} total samples):") print(f" Min ADC value: {min(all_values)}") print(f" Max ADC value: {max(all_values)}") print(f" Average ADC value: {sum(all_values) / len(all_values):.2f}") print(f" Total data transferred: {len(all_values)} bytes") print(f" Transfer rate: {len(all_values) / total_time / 1024 / 1024:.2f} MB/s") # Check for square wave characteristics print(f"\nSquare wave detection:") # A square wave should have values clustered around two levels positive_samples = sum(1 for v in first_frame if v > 0) negative_samples = sum(1 for v in first_frame if v < 0) zero_samples = sum(1 for v in first_frame if v == 0) print(f" Frame 1: {positive_samples} positive, {negative_samples} negative, {zero_samples} zero samples") # Estimate duty cycle from first frame if len(first_frame) > 0: duty_cycle = (positive_samples / len(first_frame)) * 100 print(f" Estimated duty cycle: {duty_cycle:.1f}%") print("\n=== FastFrame Acquisition Test Completed Successfully! ===") except Exception as e: print(f"\nError: {type(e).__name__}: {e}") import traceback traceback.print_exc() finally: if scope.is_connected: # Try to disable FastFrame before disconnecting try: print("\nDisabling FastFrame...") scope.set_fastframe_state(False) except Exception: pass scope.disconnect() print("Disconnected from oscilloscope") if __name__ == "__main__": main()