Initial commit: merge nuescan, pymso, pybbd202, and pypewpewhops into scanengine-3
- Merged four separate hardware control projects into unified platform - Created unified requirements.txt with all dependencies - Added comprehensive .gitignore - Added project overview README Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Executable
+292
@@ -0,0 +1,292 @@
|
||||
#!/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()
|
||||
Reference in New Issue
Block a user