Nur geaenderte Zellen schreiben -- gegen das Flackern im Sekundentakt
Seit der Einzelpositionierung blitzten bei jeder Aktualisierung willkuerliche Pixel auf, in beiden Zeilen. Ursache ist die Telegrammlaenge: Eine volle Zeile kostet mit einzeln gesetzten Zeichen rund 100 Bytes statt 20, bei 19200 Baud und 8E1 (11 Bit je Byte) dauerte ein kompletter Bildwechsel damit rund 300 ms. Die Anzeige stellt schon waehrend des Empfangs dar, also war der Aufbau zu sehen -- im Sekundentakt ein Drittel der Zeit. Von einer Sekunde zur naechsten aendert sich aber fast nichts: "18h36m56s" -> "18h36m57s" ist eine einzige Ziffer, die Deklination steht meist still. show_lines vergleicht deshalb gegen den zuletzt dargestellten Inhalt und schreibt nur die betroffenen Zellen; eine unveraenderte Zeile erzeugt gar kein Telegramm mehr. Am Geraet gemessen: 306 ms -> 66 ms je Bildwechsel. Dabei am Geraet aufgefallen und mitbehoben: Dem "s" hinter den Sekunden fehlten die linken Pixel, dem "m" hinter den Minuten ebenso -- beides Zeichen direkt rechts von einer Stelle, die sich gerade geaendert hatte. Die Anzeige malt je Zeichen 7 px breit (daher auch der 7-px-Auto-Vorschub), gesetzt wird aber auf 6-px-Raster: Ein neu geschriebenes Zeichen loescht die erste Pixelspalte seines rechten Nachbarn. Beim vollstaendigen Neuaufbau fiel das nie auf, weil der Nachbar gleich danach ohnehin neu gemalt wurde. _zelle_faellig zieht ihn jetzt mit. Weitere Details: - clear() verwirft den gemerkten Inhalt, sonst haelt der Vergleich Zellen faelschlich fuer vorhanden und die Anzeige bliebe teilweise leer. - Schlaegt ein Telegramm fehl, wird der gemerkte Inhalt der Zeile verworfen -- was angekommen ist, ist dann ungewiss. - Der Zeichensatz wird nur noch bei der ersten Ausgabe einer Zeile gesetzt. Das spart nicht nur Bytes: Jeder Wechsel beschaedigt die Home-Zelle. Findet doch einer statt (neues Gradzeichen), wird die oberste Zeile vollstaendig nachgezogen. Zu beiden Befunden gibt es Tests mit Gegenprobe (mit der alten Fassung schlagen sie fehl). 155 Tests gruen, am Geraet bestaetigt: ruhig, keine fehlenden Pixel. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+155
@@ -81,6 +81,161 @@ class TestFesteTexte(unittest.TestCase):
|
||||
self.assertLessEqual(len(getattr(config, name)), len(config.LINE_Y))
|
||||
|
||||
|
||||
class TestNurGeaenderteZellen(unittest.TestCase):
|
||||
"""Der Grund fuer den Vergleich: eine ruhige Anzeige.
|
||||
|
||||
Mit Einzelpositionierung kostet eine volle Zeile rund 100 Bytes; bei 19200
|
||||
Baud dauerte ein kompletter Bildwechsel damit rund 300 ms, und der Aufbau
|
||||
war als Flackern zu sehen (am Geraet gemessen 2026-07-27). Deshalb wird nur
|
||||
geschrieben, was sich geaendert hat.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
self.t = FakeTransport(echo=False)
|
||||
self.d = Display(self.t, response=True)
|
||||
|
||||
def _neu(self):
|
||||
"""Telegramme seit dem letzten Aufruf."""
|
||||
frames = self.t.frames[:]
|
||||
self.t.frames.clear()
|
||||
return frames
|
||||
|
||||
def test_sekundenwechsel_schreibt_nur_die_noetigen_zellen(self):
|
||||
"""Der Alltagsfall: RA tickt, alles andere steht still.
|
||||
|
||||
Geschrieben werden die geaenderte Ziffer *und* ihr rechter Nachbar --
|
||||
siehe test_rechter_nachbar_wird_mitgeschrieben.
|
||||
"""
|
||||
self.d.show_lines("18h36m56s", "+38°47'01\"")
|
||||
self._neu()
|
||||
|
||||
self.d.show_lines("18h36m57s", "+38°47'01\"")
|
||||
frames = self._neu()
|
||||
|
||||
# Die Deklination ist unveraendert -- dafuer darf gar nichts gesendet werden.
|
||||
self.assertEqual(len(frames), 1)
|
||||
x = lambda i: config.LINE_X + i * config.CELL_WIDTH
|
||||
self.assertEqual(zellen(frames), {(x(7), config.LINE_Y[0]): "7",
|
||||
(x(8), config.LINE_Y[0]): "s"})
|
||||
|
||||
def test_rechter_nachbar_wird_mitgeschrieben(self):
|
||||
"""Am Geraet aufgefallen (2026-07-27): dem "s" fehlten die linken Pixel.
|
||||
|
||||
Die Anzeige malt je Zeichen 7 px breit, gesetzt wird auf 6-px-Raster --
|
||||
ein neu geschriebenes Zeichen loescht die erste Pixelspalte seines
|
||||
rechten Nachbarn. Der muss deshalb mit, sonst bleibt er beschaedigt.
|
||||
"""
|
||||
self.d.show_lines("AAAAAAAAAA")
|
||||
self._neu()
|
||||
self.d.show_lines("AAAAXAAAAA") # nur Zelle 4 aendert sich
|
||||
gesetzt = zellen(self._neu())
|
||||
x = lambda i: config.LINE_X + i * config.CELL_WIDTH
|
||||
y = config.LINE_Y[0]
|
||||
self.assertIn((x(4), y), gesetzt) # die geaenderte Zelle
|
||||
self.assertIn((x(5), y), gesetzt) # ihr rechter Nachbar
|
||||
self.assertNotIn((x(3), y), gesetzt) # links davon bleibt unberuehrt
|
||||
self.assertNotIn((x(6), y), gesetzt) # und uebernaechste auch
|
||||
|
||||
def test_letzte_zelle_hat_keinen_nachbarn(self):
|
||||
"""Aendert sich die aeusserste rechte Zelle, gibt es nichts nachzuziehen."""
|
||||
self.d.show_lines("AAAAAAAAAA")
|
||||
self._neu()
|
||||
self.d.show_lines("AAAAAAAAAX")
|
||||
gesetzt = zellen(self._neu())
|
||||
x = lambda i: config.LINE_X + i * config.CELL_WIDTH
|
||||
self.assertEqual(list(gesetzt), [(x(9), config.LINE_Y[0])])
|
||||
|
||||
def test_unveraenderte_zeilen_erzeugen_kein_telegramm(self):
|
||||
self.d.show_lines("18h36m56s", "+38°47'01\"")
|
||||
self._neu()
|
||||
self.d.show_lines("18h36m56s", "+38°47'01\"")
|
||||
self.assertEqual(self._neu(), [])
|
||||
|
||||
def test_telegramm_wird_deutlich_kuerzer(self):
|
||||
"""Vorher-nachher in Bytes -- das ist die eigentliche Wirkung."""
|
||||
self.d.show_lines("18h36m56s", "+38°47'01\"")
|
||||
voll = sum(len(f) for f in self._neu())
|
||||
|
||||
self.d.show_lines("18h36m57s", "+38°47'01\"")
|
||||
diff = sum(len(f) for f in self._neu())
|
||||
|
||||
# Gemessen: 236 Bytes gegen 30 (Faktor knapp 8). Die Schranke liegt
|
||||
# bewusst darunter, damit der Test nicht bei jeder Kleinigkeit kippt --
|
||||
# er soll die Groessenordnung sichern, nicht eine exakte Zahl.
|
||||
self.assertLess(diff * 5, voll,
|
||||
"Der Sekundenwechsel sollte um ein Vielfaches "
|
||||
"kuerzer sein als der Vollaufbau (%d vs %d Bytes)"
|
||||
% (diff, voll))
|
||||
|
||||
def test_clear_erzwingt_vollstaendiges_neuschreiben(self):
|
||||
self.d.show_lines("18h36m56s", "+38°47'01\"")
|
||||
self._neu()
|
||||
self.d.show_lines("18h36m56s", "+38°47'01\"", clear=True)
|
||||
frames = self._neu()
|
||||
# Loeschtelegramm + beide Zeilen vollstaendig
|
||||
self.assertEqual(len(frames), 3)
|
||||
self.assertEqual(len(zeile(frames, 0)), config.CHARS_PER_LINE)
|
||||
self.assertEqual(len(zeile(frames, 1)), config.CHARS_PER_LINE)
|
||||
|
||||
def test_nach_clear_kein_rest_der_alten_anzeige(self):
|
||||
"""clear muss den gemerkten Inhalt verwerfen.
|
||||
|
||||
Sonst haelt der Vergleich Zellen faelschlich fuer schon vorhanden und
|
||||
die Anzeige bliebe nach dem Loeschen teilweise leer.
|
||||
"""
|
||||
self.d.show_lines("ABCDEFGHIJ", "0123456789")
|
||||
self._neu()
|
||||
self.d.clear()
|
||||
self._neu()
|
||||
self.d.show_lines("ABCDEFGHIJ", "0123456789")
|
||||
frames = self._neu()
|
||||
self.assertEqual(zeile(frames, 0), "ABCDEFGHIJ")
|
||||
self.assertEqual(zeile(frames, 1), "0123456789")
|
||||
|
||||
def test_nach_sendefehler_wird_die_zeile_voll_neu_geschrieben(self):
|
||||
"""Bei einem Fehler ist ungewiss, was ankam -- also nichts annehmen."""
|
||||
self.d.show_lines("18h36m56s", "+38°47'01\"")
|
||||
self._neu()
|
||||
|
||||
class Kaputt(Exception):
|
||||
pass
|
||||
|
||||
def platzt(_):
|
||||
raise Kaputt()
|
||||
|
||||
echt, self.d._t.write = self.d._t.write, platzt
|
||||
with self.assertRaises(Kaputt):
|
||||
self.d.show_lines("18h36m57s", "+38°47'01\"")
|
||||
self.d._t.write = echt
|
||||
self._neu()
|
||||
|
||||
# Dieselbe Zeile noch einmal: muss vollstaendig geschrieben werden.
|
||||
self.d.show_lines("18h36m57s", "+38°47'01\"")
|
||||
self.assertEqual(len(zeile(self._neu(), 0)), config.CHARS_PER_LINE)
|
||||
|
||||
def test_gradzeichen_wird_nicht_staendig_neu_gesetzt(self):
|
||||
"""Spart nicht nur Bytes: Jeder Zeichensatzwechsel beschaedigt (0,0)."""
|
||||
self.d.show_lines("18h36m56s", "+38°47'01\"")
|
||||
self._neu()
|
||||
self.d.show_lines("18h36m57s", "+38°47'02\"")
|
||||
gesendet = b"".join(self._neu())
|
||||
self.assertNotIn(b"\x1bz01", gesendet)
|
||||
|
||||
def test_neues_gradzeichen_zieht_die_oberste_zeile_nach(self):
|
||||
"""Wird der Zeichensatz doch gewechselt, ist die Home-Zelle hin.
|
||||
|
||||
Dann muss die oberste Zeile vollstaendig nachgezogen werden, sonst
|
||||
bleibt ihr erstes Zeichen zerstoert.
|
||||
"""
|
||||
self.d.show_lines("18h36m56s", "kein Grad")
|
||||
self._neu()
|
||||
self.d.show_lines("18h36m56s", "+38°47'01\"") # Gradzeichen kommt neu dazu
|
||||
frames = self._neu()
|
||||
self.assertIn(b"\x1bz01", b"".join(frames)) # Wechsel fand statt
|
||||
# Obere Zeile trotz unveraendertem Text komplett neu geschrieben.
|
||||
self.assertEqual(len(zeile(frames, 0)), config.CHARS_PER_LINE)
|
||||
|
||||
|
||||
class TestZeilenaufbau(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
|
||||
Reference in New Issue
Block a user