- Cleaned up argparse setup to remove duplicate --frequency and --frames arguments - Ensures script runs correctly with all options Related to #46
138 lines
4.4 KiB
Python
138 lines
4.4 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Simple Oscillator Sensor Demo
|
|
|
|
This script demonstrates the oscillator sensor by:
|
|
1. Creating an oscillator sensor with various waveforms
|
|
2. Printing the waveform data in real-time
|
|
|
|
Usage:
|
|
uv run python scripts/demo_oscillator_simple.py --waveform sine --frequency 1.0
|
|
uv run python scripts/demo_oscillator_simple.py --waveform square --frequency 2.0
|
|
"""
|
|
|
|
import argparse
|
|
import math
|
|
import time
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
# Add mainline to path
|
|
sys.path.insert(0, str(Path(__file__).parent.parent))
|
|
|
|
from engine.sensors.oscillator import OscillatorSensor, register_oscillator_sensor
|
|
|
|
|
|
def render_waveform(width: int, height: int, osc: OscillatorSensor, frame: int) -> str:
|
|
"""Render a waveform visualization."""
|
|
# Get current reading
|
|
current_reading = osc.read()
|
|
current_value = current_reading.value if current_reading else 0.0
|
|
|
|
# Generate waveform data - sample the waveform function directly
|
|
# This shows what the waveform looks like, not the live reading
|
|
samples = []
|
|
waveform_fn = osc.WAVEFORMS[osc._waveform]
|
|
|
|
for i in range(width):
|
|
# Sample across one complete cycle (0 to 1)
|
|
phase = i / width
|
|
value = waveform_fn(phase)
|
|
samples.append(value)
|
|
|
|
# Build visualization
|
|
lines = []
|
|
|
|
# Header with sensor info
|
|
header = (
|
|
f"Oscillator: {osc.name} | Waveform: {osc.waveform} | Freq: {osc.frequency}Hz"
|
|
)
|
|
lines.append(header)
|
|
lines.append("─" * width)
|
|
|
|
# Waveform plot (scaled to fit height)
|
|
num_rows = height - 3 # Header, separator, footer
|
|
for row in range(num_rows):
|
|
# Calculate the sample value that corresponds to this row
|
|
# 0.0 is bottom, 1.0 is top
|
|
row_value = 1.0 - (row / (num_rows - 1)) if num_rows > 1 else 0.5
|
|
|
|
line_chars = []
|
|
for x, sample in enumerate(samples):
|
|
# Determine if this sample should be drawn in this row
|
|
# Map sample (0.0-1.0) to row (0 to num_rows-1)
|
|
# 0.0 -> row 0 (bottom), 1.0 -> row num_rows-1 (top)
|
|
sample_row = int(sample * (num_rows - 1))
|
|
if sample_row == row:
|
|
# Use different characters for waveform vs current position marker
|
|
# Check if this is the current reading position
|
|
if abs(x / width - (osc._phase % 1.0)) < 0.02:
|
|
line_chars.append("◎") # Current position marker
|
|
else:
|
|
line_chars.append("█")
|
|
else:
|
|
line_chars.append(" ")
|
|
lines.append("".join(line_chars))
|
|
|
|
# Footer with current value and phase info
|
|
footer = f"Value: {current_value:.3f} | Frame: {frame} | Phase: {osc._phase:.2f}"
|
|
lines.append(footer)
|
|
|
|
return "\n".join(lines)
|
|
|
|
|
|
def demo_oscillator(waveform: str = "sine", frequency: float = 1.0, frames: int = 0):
|
|
"""Run oscillator demo."""
|
|
print(f"Starting oscillator demo: {waveform} wave at {frequency}Hz")
|
|
if frames > 0:
|
|
print(f"Running for {frames} frames")
|
|
else:
|
|
print("Press Ctrl+C to stop")
|
|
print()
|
|
|
|
# Create oscillator sensor
|
|
register_oscillator_sensor(name="demo_osc", waveform=waveform, frequency=frequency)
|
|
osc = OscillatorSensor(name="demo_osc", waveform=waveform, frequency=frequency)
|
|
osc.start()
|
|
|
|
# Run demo loop
|
|
try:
|
|
frame = 0
|
|
while frames == 0 or frame < frames:
|
|
# Render waveform
|
|
visualization = render_waveform(80, 20, osc, frame)
|
|
|
|
# Print with ANSI escape codes to clear screen and move cursor
|
|
print("\033[H\033[J" + visualization)
|
|
|
|
time.sleep(0.05) # 20 FPS
|
|
frame += 1
|
|
|
|
except KeyboardInterrupt:
|
|
print("\n\nDemo stopped by user")
|
|
|
|
finally:
|
|
osc.stop()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
parser = argparse.ArgumentParser(description="Oscillator sensor demo")
|
|
parser.add_argument(
|
|
"--waveform",
|
|
choices=["sine", "square", "sawtooth", "triangle", "noise"],
|
|
default="sine",
|
|
help="Waveform type",
|
|
)
|
|
parser.add_argument(
|
|
"--frequency", type=float, default=1.0, help="Oscillator frequency in Hz"
|
|
)
|
|
parser.add_argument(
|
|
"--frames",
|
|
type=int,
|
|
default=0,
|
|
help="Number of frames to render (0 = infinite until Ctrl+C)",
|
|
)
|
|
|
|
args = parser.parse_args()
|
|
demo_oscillator(args.waveform, args.frequency, args.frames)
|