Vibe coded some untrustworthy test scripts
This commit is contained in:
@@ -0,0 +1,263 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import serial
|
||||
import threading
|
||||
import time
|
||||
|
||||
# ==========================
|
||||
# Configuration
|
||||
# ==========================
|
||||
|
||||
PORT = "/dev/ttyACM0"
|
||||
BAUDRATE = 115200
|
||||
|
||||
COMMAND_PREFIX = 0x69
|
||||
|
||||
COMMAND_ACK = 0
|
||||
COMMAND_NACK = 1
|
||||
LED = 2
|
||||
|
||||
DEVICE_ID = 0
|
||||
|
||||
# Test parameters
|
||||
TEST_DURATION = 2.0 # seconds per stage
|
||||
LOSS_THRESHOLD = 1.0 # percent
|
||||
|
||||
# Rates to test (packets/second)
|
||||
RATES = [
|
||||
100,
|
||||
200,
|
||||
500,
|
||||
1000,
|
||||
2000,
|
||||
4000,
|
||||
8000,
|
||||
16000,
|
||||
]
|
||||
|
||||
# ==========================
|
||||
# Statistics
|
||||
# ==========================
|
||||
|
||||
lock = threading.Lock()
|
||||
|
||||
tx_packets = 0
|
||||
tx_bytes = 0
|
||||
|
||||
rx_packets = 0
|
||||
rx_ack = 0
|
||||
rx_nack = 0
|
||||
rx_error = 0
|
||||
|
||||
|
||||
# ==========================
|
||||
# Packet helpers
|
||||
# ==========================
|
||||
|
||||
def calculate_crc(msg: bytes) -> int:
|
||||
s = sum(msg) & 0xFF
|
||||
return (-s) & 0xFF
|
||||
|
||||
|
||||
def make_packet(command: int, data: bytes = b"") -> bytes:
|
||||
pkt = bytearray()
|
||||
|
||||
pkt.append(COMMAND_PREFIX)
|
||||
pkt.append(len(data))
|
||||
pkt.append(DEVICE_ID)
|
||||
pkt.append(command)
|
||||
pkt.append(0)
|
||||
|
||||
pkt.extend(data)
|
||||
|
||||
pkt[4] = calculate_crc(pkt[:4] + pkt[5:])
|
||||
return bytes(pkt)
|
||||
|
||||
|
||||
def verify_crc(packet: bytes) -> bool:
|
||||
crc = packet[4]
|
||||
calc = calculate_crc(packet[:4] + packet[5:])
|
||||
return crc == calc
|
||||
|
||||
|
||||
def packet_size(buf: bytes):
|
||||
if len(buf) < 2:
|
||||
return None
|
||||
return 5 + buf[1]
|
||||
|
||||
|
||||
# ==========================
|
||||
# Receiver
|
||||
# ==========================
|
||||
|
||||
def reader(ser):
|
||||
global rx_packets, rx_ack, rx_nack, rx_error
|
||||
|
||||
rx = bytearray()
|
||||
|
||||
while True:
|
||||
data = ser.read(4096)
|
||||
if not data:
|
||||
continue
|
||||
|
||||
rx.extend(data)
|
||||
|
||||
while True:
|
||||
if not rx:
|
||||
break
|
||||
|
||||
# ASCII log output
|
||||
if rx[0] != COMMAND_PREFIX:
|
||||
nl = rx.find(b"\n")
|
||||
if nl == -1:
|
||||
rx.clear()
|
||||
break
|
||||
|
||||
line = rx[: nl + 1]
|
||||
del rx[: nl + 1]
|
||||
|
||||
try:
|
||||
print("[LOG]", line.decode().rstrip())
|
||||
except UnicodeDecodeError:
|
||||
pass
|
||||
|
||||
continue
|
||||
|
||||
size = packet_size(rx)
|
||||
|
||||
if size is None or len(rx) < size:
|
||||
break
|
||||
|
||||
pkt = bytes(rx[:size])
|
||||
del rx[:size]
|
||||
|
||||
if not verify_crc(pkt):
|
||||
continue
|
||||
|
||||
cmd = pkt[3]
|
||||
|
||||
with lock:
|
||||
rx_packets += 1
|
||||
|
||||
if cmd == COMMAND_ACK:
|
||||
rx_ack += 1
|
||||
elif cmd == COMMAND_NACK:
|
||||
rx_nack += 1
|
||||
elif cmd == COMMAND_ERROR:
|
||||
rx_error += 1
|
||||
|
||||
|
||||
# ==========================
|
||||
# Benchmark
|
||||
# ==========================
|
||||
|
||||
def run_stage(ser, packet, target_pps):
|
||||
global tx_packets, tx_bytes
|
||||
global rx_packets, rx_ack, rx_nack, rx_error
|
||||
|
||||
with lock:
|
||||
tx_packets = 0
|
||||
tx_bytes = 0
|
||||
rx_packets = 0
|
||||
rx_ack = 0
|
||||
rx_nack = 0
|
||||
rx_error = 0
|
||||
|
||||
interval = 1.0 / target_pps
|
||||
|
||||
start = time.perf_counter()
|
||||
end = start + TEST_DURATION
|
||||
next_tx = start
|
||||
|
||||
while True:
|
||||
now = time.perf_counter()
|
||||
|
||||
if now >= end:
|
||||
break
|
||||
|
||||
if now >= next_tx:
|
||||
ser.write(packet)
|
||||
|
||||
with lock:
|
||||
tx_packets += 1
|
||||
tx_bytes += len(packet)
|
||||
|
||||
next_tx += interval
|
||||
else:
|
||||
sleep = next_tx - now
|
||||
if sleep > 0:
|
||||
time.sleep(min(sleep, 0.0005))
|
||||
|
||||
# Allow final ACKs to arrive
|
||||
time.sleep(0.25)
|
||||
|
||||
elapsed = time.perf_counter() - start
|
||||
|
||||
with lock:
|
||||
tx = tx_packets
|
||||
ack = rx_ack
|
||||
nack = rx_nack
|
||||
err = rx_error
|
||||
bytes_sent = tx_bytes
|
||||
|
||||
loss = 0.0
|
||||
if tx:
|
||||
loss = (tx - ack) / tx * 100.0
|
||||
|
||||
print()
|
||||
print("=" * 60)
|
||||
print(f"Target Rate : {target_pps:>7} pkt/s")
|
||||
print(f"Elapsed : {elapsed:.3f} s")
|
||||
print(f"Sent : {tx}")
|
||||
print(f"ACK : {ack}")
|
||||
print(f"NACK : {nack}")
|
||||
print(f"ERROR : {err}")
|
||||
print(f"Loss : {loss:.2f}%")
|
||||
print(f"Actual TX : {tx / elapsed:.0f} pkt/s")
|
||||
print(f"Throughput : {bytes_sent / elapsed / 1024:.2f} KiB/s")
|
||||
|
||||
return loss
|
||||
|
||||
|
||||
# ==========================
|
||||
# Main
|
||||
# ==========================
|
||||
|
||||
def main():
|
||||
packet = make_packet(LED)
|
||||
|
||||
print(f"Opening {PORT} @ {BAUDRATE} baud...")
|
||||
ser = serial.Serial(PORT, BAUDRATE, timeout=0.01)
|
||||
|
||||
threading.Thread(target=reader, args=(ser,), daemon=True).start()
|
||||
|
||||
print("\nStarting communication benchmark...\n")
|
||||
|
||||
previous_rate = None
|
||||
|
||||
for rate in RATES:
|
||||
loss = run_stage(ser, packet, rate)
|
||||
|
||||
if loss > LOSS_THRESHOLD:
|
||||
print()
|
||||
print(
|
||||
f"Link became unreliable (> {LOSS_THRESHOLD:.1f}% loss)."
|
||||
)
|
||||
|
||||
if previous_rate is not None:
|
||||
print(f"Maximum reliable rate ≈ {previous_rate} pkt/s")
|
||||
|
||||
break
|
||||
|
||||
previous_rate = rate
|
||||
|
||||
else:
|
||||
print()
|
||||
print("Completed all test stages.")
|
||||
print(f"Reliable up to at least {RATES[-1]} pkt/s.")
|
||||
|
||||
ser.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user