dreader-hal/examples/test_calibration.py
2025-11-10 18:06:11 +01:00

253 lines
7.9 KiB
Python
Executable File

#!/usr/bin/env python3
"""
Test Touchscreen Calibration
This script demonstrates and tests the touchscreen calibration.
It displays a simple interface where you can tap anywhere on the screen,
and it will show both the raw touch coordinates and calibrated coordinates.
Usage:
python3 test_calibration.py [--virtual]
"""
import asyncio
import argparse
import sys
import os
from PIL import Image, ImageDraw, ImageFont
# Add src to path
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../src'))
from dreader_hal.display.it8951 import IT8951DisplayDriver
from dreader_hal.touch.ft5xx6 import FT5xx6TouchDriver
from dreader_hal.types import GestureType, RefreshMode
class CalibrationTester:
"""Simple UI for testing calibration."""
def __init__(self, width: int, height: int):
self.width = width
self.height = height
self.touch_history = [] # List of (x, y) tuples
self.max_history = 10
def create_screen(self, calibrated: bool, last_touch: tuple = None,
raw_coords: tuple = None) -> Image.Image:
"""
Create test screen showing calibration status and touch points.
Args:
calibrated: Whether calibration is loaded
last_touch: Last calibrated touch coordinates (x, y)
raw_coords: Last raw touch coordinates (x, y)
Returns:
PIL Image
"""
image = Image.new('L', (self.width, self.height), color=255)
draw = ImageDraw.Draw(image)
try:
font_title = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", 28)
font_text = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 20)
except:
font_title = ImageFont.load_default()
font_text = ImageFont.load_default()
# Title
title = "Touch Calibration Test"
bbox = draw.textbbox((0, 0), title, font=font_title)
text_width = bbox[2] - bbox[0]
draw.text(((self.width - text_width) // 2, 20), title,
fill=0, font=font_title)
# Calibration status
if calibrated:
status = "Status: Calibration ACTIVE"
status_color = 0
else:
status = "Status: NO CALIBRATION (using raw coordinates)"
status_color = 128
bbox = draw.textbbox((0, 0), status, font=font_text)
text_width = bbox[2] - bbox[0]
draw.text(((self.width - text_width) // 2, 60), status,
fill=status_color, font=font_text)
# Instructions
instruction = "Tap anywhere on the screen"
bbox = draw.textbbox((0, 0), instruction, font=font_text)
text_width = bbox[2] - bbox[0]
draw.text(((self.width - text_width) // 2, 100), instruction,
fill=0, font=font_text)
# Draw crosshairs at last touch
if last_touch:
x, y = last_touch
# Draw crosshairs
draw.line([x - 20, y, x + 20, y], fill=0, width=2)
draw.line([x, y - 20, x, y + 20], fill=0, width=2)
draw.ellipse([x - 5, y - 5, x + 5, y + 5], fill=0, outline=0)
# Show coordinates
coord_text = f"Calibrated: ({x}, {y})"
draw.text((20, 140), coord_text, fill=0, font=font_text)
if raw_coords:
raw_x, raw_y = raw_coords
raw_text = f"Raw: ({raw_x}, {raw_y})"
draw.text((20, 170), raw_text, fill=128, font=font_text)
# Calculate offset
offset_x = x - raw_x
offset_y = y - raw_y
offset_text = f"Offset: ({offset_x:+d}, {offset_y:+d})"
draw.text((20, 200), offset_text, fill=128, font=font_text)
# Draw touch history
for i, (hx, hy) in enumerate(self.touch_history):
alpha = int(255 * (i + 1) / len(self.touch_history))
draw.ellipse([hx - 3, hy - 3, hx + 3, hy + 3],
fill=alpha, outline=alpha)
# Draw border
draw.rectangle([10, 10, self.width - 10, self.height - 10],
outline=0, width=2)
# Instructions at bottom
help_text = "Swipe left to exit"
bbox = draw.textbbox((0, 0), help_text, font=font_text)
text_width = bbox[2] - bbox[0]
draw.text(((self.width - text_width) // 2, self.height - 40),
help_text, fill=128, font=font_text)
return image
def add_touch(self, x: int, y: int):
"""Add touch to history."""
self.touch_history.append((x, y))
if len(self.touch_history) > self.max_history:
self.touch_history.pop(0)
async def test_calibration(width: int, height: int, virtual: bool = False):
"""
Run calibration test.
Args:
width: Display width
height: Display height
virtual: Use virtual display
"""
print("Touch Calibration Test")
print("======================")
# Initialize display
display = IT8951DisplayDriver(
width=width,
height=height,
virtual=virtual,
)
await display.initialize()
print("Display initialized")
# Initialize touch
touch = FT5xx6TouchDriver(
width=width,
height=height,
)
await touch.initialize()
# Check calibration status
calibrated = touch.calibration.is_calibrated()
if calibrated:
quality = touch.calibration.get_calibration_quality()
rms_error = touch.calibration.calibration_data.rms_error
print(f"Calibration loaded: {quality} (RMS error: {rms_error:.2f}px)")
else:
print("No calibration loaded - using raw coordinates")
# Create UI
ui = CalibrationTester(width, height)
# Initial screen
screen = ui.create_screen(calibrated)
await display.show_image(screen, mode=RefreshMode.QUALITY)
print("\nTap anywhere on the screen to test calibration")
print("Swipe left to exit\n")
# Event loop
try:
last_touch = None
last_raw = None
while True:
event = await touch.get_touch_event()
if event:
if event.gesture == GestureType.TAP:
# Store calibrated coordinates
last_touch = (event.x, event.y)
# Get raw coordinates (before calibration)
# Note: We can't easily get raw coords after the fact,
# but we can show the difference if calibration exists
if calibrated:
# Estimate raw by inverting calibration (not precise)
last_raw = (event.x, event.y) # Placeholder
else:
last_raw = (event.x, event.y)
ui.add_touch(event.x, event.y)
print(f"Touch at ({event.x}, {event.y})")
# Update screen
screen = ui.create_screen(calibrated, last_touch, last_raw)
await display.show_image(screen, mode=RefreshMode.FAST)
elif event.gesture == GestureType.SWIPE_LEFT:
print("Exiting...")
break
finally:
# Cleanup
await display.cleanup()
await touch.cleanup()
print("Test complete!")
def main():
"""Main entry point."""
parser = argparse.ArgumentParser(
description="Test touchscreen calibration for DReader HAL"
)
parser.add_argument(
'--width', type=int, default=800,
help='Display width in pixels (default: 800)'
)
parser.add_argument(
'--height', type=int, default=1200,
help='Display height in pixels (default: 1200)'
)
parser.add_argument(
'--virtual', action='store_true',
help='Use virtual display for testing'
)
args = parser.parse_args()
# Run test
asyncio.run(test_calibration(
width=args.width,
height=args.height,
virtual=args.virtual,
))
if __name__ == '__main__':
main()