RA in Zehntelminuten mit Beschriftung: "RA 18h36.9m" / "DE +38°47'"
Die GM4000 liefert im Auslieferzustand keine Sekunden, sondern Zehntelminuten.
Bisher wurde daraus eine Sekundenanzeige gerechnet -- die sprang in
6-Sekunden-Schritten, weil die Rohdaten sie gar nicht hergeben. Die Zehntelform
ist ehrlicher und kuerzer, und der gewonnene Platz traegt die Beschriftung:
RA 18h36.9m
DE +38°47'
Bei Hochpraezision entfaellt die Beschriftung, dort braucht die Deklination mit
+38°47'01" alle Zellen.
Der Mock konnte das gar nicht nachstellen -- er sendete immer das
Hochpraezisionsformat. Neu: "mount_mock.py --low" bildet den Auslieferzustand ab
(RA als HH:MM.T, DEC ohne Bogensekunden). Vorgabe bleibt hoch, damit die
bestehenden Tests mit ihren festen Erwartungen unveraendert durchlaufen.
Zur Darstellung, alles am Geraet ausprobiert:
- **Schmale Zeichen** (config.CELL_NARROW): Dezimalpunkt und der Abstand
zwischen Beschriftung und Wert bekommen 3 statt 6 px -- in der Sperrschrift
standen sie sonst sehr luftig. Fuer die Leerzeichen am Zeilenende gilt das
nicht, die sollen den Rest der Zeile ueberschreiben.
- Damit entscheidet die **Pixelbreite** ueber das Abschneiden, nicht mehr die
Zeichenzahl: "RA 18h36.9m" sind 11 Zeichen und trotzdem nur 60 px.
- **Linksbuendig ab LINE_X = 0**, der Schalter zum Zentrieren ist entfallen.
Zentriert standen die Zeilen um wenige Pixel gegeneinander versetzt, weil die
RA-Zeile mit ihrem schmalen Punkt kuerzer ist als die DEC-Zeile.
Ein Zwischenschritt, der wieder verworfen wurde und als Warnung im Code steht:
Ein Doppelpunkt als Trenner ("RA:18h36.9m") sah in Zeichensatz 0 zu wuchtig aus;
der aus Zeichensatz 1 ist schlanker, steht aber mitten in der obersten Zeile --
und jeder Zeichensatzwechsel zerstoert die Home-Zelle. Das erste Zeichen danach
noch einmal zu schreiben machte es schlimmer: Das nachgezogene Zeichen bemalt
GLYPH_WIDTH Pixel und frisst die linke Spalte seines rechten Nachbarn. Deshalb
steht in der obersten Zeile jetzt bewusst kein Sonderzeichen.
161 Tests gruen, am Geraet bestaetigt.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -23,6 +23,8 @@ python3 run_display.py --host 10.0.0.42 # andere IP
|
|||||||
python3 run_display.py --mock --move # ohne Teleskop: lokaler Mock, RA läuft
|
python3 run_display.py --mock --move # ohne Teleskop: lokaler Mock, RA läuft
|
||||||
python3 run_display.py --mock --move --ldr 2000 # zusätzlich mit Helligkeitsregelung
|
python3 run_display.py --mock --move --ldr 2000 # zusätzlich mit Helligkeitsregelung
|
||||||
python3 mount_mock.py --move # Mock separat starten
|
python3 mount_mock.py --move # Mock separat starten
|
||||||
|
python3 mount_mock.py --move --low # ... im Auslieferformat der GM4000
|
||||||
|
# (RA als HH:MM.T, DEC ohne Sekunden)
|
||||||
|
|
||||||
# Helligkeit aus einem LDR-Wert setzen:
|
# Helligkeit aus einem LDR-Wert setzen:
|
||||||
python3 brightness.py --ldr 1500 # LDR-Rohwert -> Helligkeit -> senden
|
python3 brightness.py --ldr 1500 # LDR-Rohwert -> Helligkeit -> senden
|
||||||
@@ -310,16 +312,23 @@ die Reihenfolge der Telegramme statt der Reihenfolge innerhalb eines Telegramms.
|
|||||||
|
|
||||||
- Die Deklination passt damit in **voller Form mit abschließendem `"`**
|
- Die Deklination passt damit in **voller Form mit abschließendem `"`**
|
||||||
(`+38°47'01"`, 10 Zeichen). `coords.format_dec` wählt automatisch die breiteste
|
(`+38°47'01"`, 10 Zeichen). `coords.format_dec` wählt automatisch die breiteste
|
||||||
Form, die in `CHARS_PER_LINE` passt — bei Niedrigpräzision entsprechend
|
Form, die in die Zeile passt — bei Niedrigpräzision entsprechend `+38°47'`
|
||||||
`+38°47'` ohne Sekunden.
|
ohne Sekunden.
|
||||||
|
|
||||||
- **Zeilen werden zentriert** (`CENTER_LINES = True`). Das wirkt auf zwei Ebenen:
|
- **Schmale Zeichen bekommen weniger Platz** (`config.CELL_NARROW`). Der
|
||||||
`LINE_X` rückt den Zeichenblock in die Mitte (10·6 = 60 px lassen je 2 px Rand),
|
Dezimalpunkt der Zehntelminuten und der Abstand zwischen Beschriftung und Wert
|
||||||
und kürzere Texte werden beidseitig mit Leerzeichen aufgefüllt. Der Block bleibt
|
belegen statt 6 nur 3 px — in der Sperrschrift standen sie sonst sehr luftig.
|
||||||
dabei immer gleich breit und an derselben Stelle — so überschreibt er bei jedem
|
Möglich ist das nur, weil ohnehin jedes Zeichen einzeln positioniert wird.
|
||||||
Durchlauf denselben Bereich und lässt keine Reste stehen. Ein pixelgenau
|
Für die Leerzeichen am Zeilenende gilt es *nicht*: Die sollen den Rest der
|
||||||
mitwandernder Offset könnte das nicht; dafür ist die Zentrierung nur auf eine
|
Zeile überschreiben und behalten die volle Breite.
|
||||||
halbe Zelle (3 px) genau, wenn die Restbreite ungerade ist.
|
|
||||||
|
Deshalb entscheidet die **Pixelbreite** über das Abschneiden, nicht die
|
||||||
|
Zeichenzahl: `RA 18h36.9m` sind 11 Zeichen und trotzdem nur 60 px, `DE +38°47'`
|
||||||
|
wären als 11 volle Zellen dagegen 66 px und damit zu breit.
|
||||||
|
|
||||||
|
- **Zeilen stehen linksbündig** ab `LINE_X = 0`. Zentriert waren sie
|
||||||
|
zwischenzeitlich, standen dann aber um wenige Pixel gegeneinander versetzt, weil
|
||||||
|
die RA-Zeile mit ihrem schmalen Dezimalpunkt kürzer ist als die DEC-Zeile.
|
||||||
|
|
||||||
- **Es wird nur geschrieben, was sich geändert hat.** `show_lines` vergleicht gegen
|
- **Es wird nur geschrieben, was sich geändert hat.** `show_lines` vergleicht gegen
|
||||||
den zuletzt dargestellten Inhalt und schickt nur die betroffenen Zellen; eine
|
den zuletzt dargestellten Inhalt und schickt nur die betroffenen Zellen; eine
|
||||||
@@ -353,15 +362,23 @@ RA als `HH:MM.T` (Zehntel-Minuten), DEC als `sDD°MM` (ganze Bogenminuten). Das
|
|||||||
Gradzeichen kommt als Byte `0xDF` — auf der Anzeige korrekt als Gradring dargestellt;
|
Gradzeichen kommt als Byte `0xDF` — auf der Anzeige korrekt als Gradring dargestellt;
|
||||||
in der Terminal-Konsole erscheint der **Rohwert** dieses Bytes als `ß` (rein kosmetisch).
|
in der Terminal-Konsole erscheint der **Rohwert** dieses Bytes als `ß` (rein kosmetisch).
|
||||||
|
|
||||||
`config.HIGH_PRECISION` steuert die DEC-Darstellung:
|
`config.HIGH_PRECISION` steuert die ganze Darstellung — sie folgt dem, was die
|
||||||
- `False` (Vorgabe): DEC ohne Sekunden (`-00°55'`).
|
Montierung wirklich liefert:
|
||||||
- `True`: DEC mit Sekunden (`+38°47'01`) — erst sinnvoll, wenn die Montierung per
|
|
||||||
`:U#` auf Hochpräzision gestellt ist (schreibendes Kommando, hier bewusst nicht
|
|
||||||
automatisch gesendet).
|
|
||||||
|
|
||||||
Die **RA** wird immer mit Sekunden gezeigt (`11h36m54s`); bei Niedrigpräzision
|
| | Niedrigpräzision (Vorgabe) | Hochpräzision (nach `:U#`) |
|
||||||
kommen die Sekunden aus den Zehntel-Minuten und springen daher in 6-Sekunden-Schritten
|
|---|---|---|
|
||||||
(kein Dezimalpunkt — der wirkt in der Sperrschrift zu luftig).
|
| Zeile 1 | `RA 18h36.9m` | `18h36m56s` |
|
||||||
|
| Zeile 2 | `DE +38°47'` | `+38°47'01"` |
|
||||||
|
|
||||||
|
Bei Niedrigpräzision zeigt die **RA Zehntelminuten** statt Sekunden — genau die
|
||||||
|
Auflösung der Rohdaten. Bis 2026-07-27 wurde hier in Sekunden umgerechnet
|
||||||
|
(`11h36m54s`); die sprangen aber in 6-Sekunden-Schritten, weil die Montierung sie
|
||||||
|
gar nicht hergibt. Die kürzere Form lässt zugleich Platz für die **Beschriftung**
|
||||||
|
`RA` / `DE` (`config.LABEL_RA`, `LABEL_DEC`).
|
||||||
|
|
||||||
|
Bei Hochpräzision entfällt die Beschriftung: Dort braucht die Deklination mit
|
||||||
|
`+38°47'01"` alle Zellen. `:U#` ist ein schreibendes Kommando und wird hier
|
||||||
|
bewusst nicht automatisch gesendet.
|
||||||
|
|
||||||
`coords.format_ra`/`format_dec` nehmen `high_precision=` als Parameter (Vorgabe aus
|
`coords.format_ra`/`format_dec` nehmen `high_precision=` als Parameter (Vorgabe aus
|
||||||
`config`). Die Montierungs-Rohwerte werden im `coords`-Parser unverändert korrekt
|
`config`). Die Montierungs-Rohwerte werden im `coords`-Parser unverändert korrekt
|
||||||
|
|||||||
@@ -55,33 +55,56 @@ CHARSET = 0
|
|||||||
CHARSET_SPACED = True
|
CHARSET_SPACED = True
|
||||||
LINE_Y = (0, 9)
|
LINE_Y = (0, 9)
|
||||||
|
|
||||||
# 10 Zeichen zu 6 px -- mit "probe.py pitch 6" am Geraet entschieden (2026-07-27).
|
# 6 px pro Zeichenzelle -- mit "probe.py pitch 6" am Geraet gemessen (2026-07-27).
|
||||||
#
|
#
|
||||||
# Die Messung stellt den Auto-Vorschub der Anzeige gegen feste Pixelabstaende und
|
# Die Messung stellt den Auto-Vorschub der Anzeige gegen feste Pixelabstaende und
|
||||||
# hat beides zugleich gezeigt:
|
# hat beides zugleich gezeigt:
|
||||||
# - Auto-Vorschub der Sperrschrift ESC z ist 7 px. Eine Zeile aus einem Stueck
|
# - Auto-Vorschub der Sperrschrift ESC z ist 7 px. Eine Zeile aus einem Stueck
|
||||||
# bricht deshalb nach 9 Zeichen um (9*7 = 63 von 64 px). Daher stand hier
|
# bricht deshalb nach 9 Zeichen um (9*7 = 63 von 64 px). Frueher stand hier
|
||||||
# frueher 9 -- das war der Vorschub, nicht die Zellenbreite.
|
# darum 9 -- das war der Vorschub, nicht die Zellenbreite.
|
||||||
# - Die Zeichenmatrix selbst ist aber nur 6 px breit: zehn Ziffern, einzeln auf
|
# - Die Zeichenmatrix selbst ist nur 6 px breit: zehn Ziffern, einzeln auf
|
||||||
# x = i*6 gesetzt, stehen sauber und getrennt nebeneinander (60 von 64 px).
|
# x = i*6 gesetzt, stehen sauber und getrennt nebeneinander (60 von 64 px).
|
||||||
#
|
#
|
||||||
# Deshalb positioniert display._emit_line jedes Zeichen selbst, statt den
|
# Deshalb positioniert display._emit_line jedes Zeichen selbst, statt den
|
||||||
# Auto-Vorschub laufen zu lassen. So passen 10 Zeichen, und die DEC-Form bekommt
|
# Auto-Vorschub laufen zu lassen.
|
||||||
# ihr abschliessendes " (siehe coords.format_dec).
|
CELL_WIDTH = 6 # Pixel pro Zeichenzelle
|
||||||
CHARS_PER_LINE = 10
|
|
||||||
CELL_WIDTH = 6 # Pixel pro Zeichenzelle bei fester Positionierung
|
|
||||||
DISPLAY_WIDTH = 64 # Pixelbreite der Anzeige
|
DISPLAY_WIDTH = 64 # Pixelbreite der Anzeige
|
||||||
|
|
||||||
# Zeilen mittig setzen statt linksbuendig. Wirkt auf zwei Ebenen:
|
# Obergrenze in *Zeichen*. Die harte Grenze ist DISPLAY_WIDTH in Pixeln: Seit
|
||||||
# - LINE_X rueckt den ganzen Zeichenblock in die Mitte: 10*6 = 60 px belegen
|
# schmale Zeichen weniger Platz brauchen (CELL_NARROW), haengt die Breite einer
|
||||||
# nicht die vollen 64, das laesst je 2 px Rand.
|
# Zeile davon ab, welche Zeichen darin stehen. 11 gehen nur auf, wenn mindestens
|
||||||
# - Kuerzere Texte werden in _emit_line beidseitig mit Leerzeichen aufgefuellt,
|
# eines schmal ist -- "RA:18h51.0m" braucht mit seinem Dezimalpunkt 63 der 64 px,
|
||||||
# z.B. die 7 Zeichen von "+38°47'" bei Niedrigpraezision.
|
# "DE:+38°47'" waere als 11 volle Zellen dagegen 66 px und damit zu breit.
|
||||||
# Der Block bleibt dabei immer gleich breit und an derselben Stelle -- er
|
# display._aufbereiten schneidet deshalb nach Pixeln ab, nicht nach Zeichen.
|
||||||
# ueberschreibt so bei jedem Durchlauf denselben Bereich, ohne Reste der vorigen
|
CHARS_PER_LINE = 11
|
||||||
# Anzeige stehen zu lassen. Ein pixelgenau mitwandernder Offset koennte das nicht.
|
|
||||||
CENTER_LINES = True
|
# Breite, die die Anzeige beim Schreiben eines Zeichens tatsaechlich bemalt --
|
||||||
LINE_X = (DISPLAY_WIDTH - CHARS_PER_LINE * CELL_WIDTH) // 2
|
# mehr als CELL_WIDTH. Daher auch der Auto-Vorschub von 7 px. Wichtig fuer das
|
||||||
|
# Nachfuehren einzelner Zellen: Ein neu geschriebenes Zeichen loescht die erste
|
||||||
|
# Pixelspalte seines rechten Nachbarn, der muss also mitgeschrieben werden
|
||||||
|
# (siehe display._zelle_faellig).
|
||||||
|
GLYPH_WIDTH = 7
|
||||||
|
|
||||||
|
# Zeichen, die weniger Platz brauchen als eine volle Zelle -- in der Sperrschrift
|
||||||
|
# stehen sie sonst sehr luftig zwischen den Ziffern. Je 3 px am Geraet
|
||||||
|
# ausprobiert (2026-07-27); weil jedes Zeichen ohnehin einzeln positioniert wird,
|
||||||
|
# kostet das nichts.
|
||||||
|
# "." Dezimalpunkt der Zehntelminuten (RA 18h37.1m)
|
||||||
|
# " " Abstand zwischen Beschriftung und Wert (RA 18h37.1m)
|
||||||
|
# Achtung: Das gilt auch fuer die Leerzeichen, mit denen eine Zeile aufgefuellt
|
||||||
|
# wird. display._aufbereiten rechnet beim Auffuellen darum bewusst mit der vollen
|
||||||
|
# Zellenbreite -- sonst reichte die letzte Zelle ueber den Rand hinaus.
|
||||||
|
CELL_NARROW = {".": 3, " ": 3}
|
||||||
|
|
||||||
|
# Alle Zeilen beginnen linksbuendig bei LINE_X. Bis 2026-07-27 gab es einen
|
||||||
|
# Schalter zum Zentrieren; er ist entfallen, weil die Zeilen seit den schmalen
|
||||||
|
# Zeichen (CELL_NARROW) unterschiedlich breit sind und zentriert um wenige Pixel
|
||||||
|
# gegeneinander versetzt standen. Mit der Beschriftung "RA:" / "DE:" sollen sie
|
||||||
|
# buendig untereinander beginnen.
|
||||||
|
#
|
||||||
|
# LINE_X = 0, ganz am Rand: Die RA-Zeile braucht mit der Minutenangabe am Ende
|
||||||
|
# ("RA:18h51.0m") 63 der 64 Pixel -- fuer einen Rand ist da kein Platz mehr.
|
||||||
|
LINE_X = 0
|
||||||
|
|
||||||
COLOR = migra.RED # einfarbig rote Anzeige
|
COLOR = migra.RED # einfarbig rote Anzeige
|
||||||
BACKGROUND = migra.BLACK
|
BACKGROUND = migra.BLACK
|
||||||
@@ -112,6 +135,22 @@ POLL_INTERVAL = 1.0 # Sekunden zwischen zwei Koordinatenabfragen
|
|||||||
# True = Hochpraezision (Montierung mit ":U#" umgestellt): RA/DEC mit Sekunden.
|
# True = Hochpraezision (Montierung mit ":U#" umgestellt): RA/DEC mit Sekunden.
|
||||||
HIGH_PRECISION = False
|
HIGH_PRECISION = False
|
||||||
|
|
||||||
|
# Beschriftung der beiden Zeilen:
|
||||||
|
#
|
||||||
|
# RA 18h37.1m
|
||||||
|
# DE +38°47'
|
||||||
|
#
|
||||||
|
# Der Abstand ist ein Leerzeichen, das ueber CELL_NARROW nur 3 px breit gesetzt
|
||||||
|
# wird -- ein Doppelpunkt stand hier zwischenzeitlich, wirkte in Zeichensatz 0
|
||||||
|
# aber zu wuchtig (2026-07-27).
|
||||||
|
#
|
||||||
|
# Die Beschriftung gibt es nur bei Niedrigpraezision: Dort liefert die Montierung
|
||||||
|
# ohnehin nur Zehntelminuten und ganze Bogenminuten, das laesst den Platz. Bei
|
||||||
|
# Hochpraezision braucht die Deklination alle Zellen (+38°47'01") -- dann
|
||||||
|
# entfaellt sie.
|
||||||
|
LABEL_RA = "RA "
|
||||||
|
LABEL_DEC = "DE "
|
||||||
|
|
||||||
# Anzeige, wenn die Montierung (noch) nicht erreichbar ist.
|
# Anzeige, wenn die Montierung (noch) nicht erreichbar ist.
|
||||||
# Achtung: hoechstens CHARS_PER_LINE Zeichen, sonst wird abgeschnitten --
|
# Achtung: hoechstens CHARS_PER_LINE Zeichen, sonst wird abgeschnitten --
|
||||||
# "MONTIERUNG" stand hier zuerst und erschien am Geraet als "MONTIERUN".
|
# "MONTIERUNG" stand hier zuerst und erschien am Geraet als "MONTIERUN".
|
||||||
|
|||||||
@@ -75,22 +75,53 @@ def _fit(candidates, width):
|
|||||||
return candidates[-1][:width]
|
return candidates[-1][:width]
|
||||||
|
|
||||||
|
|
||||||
def format_ra(value, width=None, high_precision=None):
|
def _to_tenths(h, m, s):
|
||||||
"""Rektaszension formatieren, immer mit Sekunden (HHhMMmSSs).
|
"""(h, m, s) in (Stunden, Minuten, Zehntelminuten) umrechnen.
|
||||||
|
|
||||||
Bei Niedrigpraezision liefert die Montierung nur Zehntel-Minuten; die
|
Ganzzahlig gerechnet, damit es unter MicroPython keine Float-Ueberraschungen
|
||||||
Sekunden kommen dann in 6-Sekunden-Schritten. Das Format ist dasselbe wie
|
gibt. Der Uebertrag ist wichtig: 18:36:57 sind 36,95 Minuten und werden zu
|
||||||
bei Hochpraezision -- der Unterschied steckt allein in der Aufloesung der
|
18:37.0, nicht zu 18:36.10.
|
||||||
Rohdaten, nicht in der Darstellung. high_precision wird daher fuer RA nicht
|
"""
|
||||||
ausgewertet (nur bei DEC relevant, siehe format_dec).
|
zehntel = ((h * 3600 + m * 60 + s) * 10 + 30) // 60 # kaufmaennisch runden
|
||||||
|
minuten, t = divmod(zehntel, 10)
|
||||||
|
h, m = divmod(minuten, 60)
|
||||||
|
return h % 24, m, t
|
||||||
|
|
||||||
|
|
||||||
|
def format_ra(value, width=None, high_precision=None):
|
||||||
|
"""Rektaszension formatieren.
|
||||||
|
|
||||||
|
Die Darstellung folgt dem, was die Montierung wirklich liefert:
|
||||||
|
|
||||||
|
Niedrigpraezision (Vorgabe): **HHhMM.T** mit Zehntelminuten -- genau die
|
||||||
|
Aufloesung, die die GM4000 in diesem Modus sendet. Sieben Zeichen, das laesst
|
||||||
|
Platz fuer die Beschriftung "RA:" auf der Anzeige (siehe
|
||||||
|
displayloop.format_coordinates). Das "h" statt eines Doppelpunkts ist
|
||||||
|
Nutzerwunsch -- der Doppelpunkt wirkt in der Sperrschrift zu wuchtig.
|
||||||
|
|
||||||
|
Hochpraezision: HHhMMmSSs mit echten Sekunden.
|
||||||
|
|
||||||
|
Bis 2026-07-27 wurde auch bei Niedrigpraezision mit Sekunden angezeigt. Die
|
||||||
|
sprangen dort in 6-Sekunden-Schritten, weil die Rohdaten sie gar nicht
|
||||||
|
hergeben -- die Zehntelform ist ehrlicher und kuerzer.
|
||||||
"""
|
"""
|
||||||
width = config.CHARS_PER_LINE if width is None else width
|
width = config.CHARS_PER_LINE if width is None else width
|
||||||
|
high = config.HIGH_PRECISION if high_precision is None else high_precision
|
||||||
h, m, s = parse_ra(value)
|
h, m, s = parse_ra(value)
|
||||||
return _fit([
|
if high:
|
||||||
"%02dh%02dm%02ds" % (h, m, s),
|
candidates = [
|
||||||
"%02d:%02d:%02d" % (h, m, s),
|
"%02dh%02dm%02ds" % (h, m, s),
|
||||||
"%02dh%02dm" % (h, m),
|
"%02d:%02d:%02d" % (h, m, s),
|
||||||
], width)
|
"%02dh%02dm" % (h, m),
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
h, m, t = _to_tenths(h, m, s)
|
||||||
|
candidates = [
|
||||||
|
"%02dh%02d.%dm" % (h, m, t), # 8: 18h36.9m
|
||||||
|
"%02dh%02d.%d" % (h, m, t), # 7: ohne Minutenzeichen
|
||||||
|
"%02dh%02dm" % (h, m), # 6: ohne Zehntel
|
||||||
|
]
|
||||||
|
return _fit(candidates, width)
|
||||||
|
|
||||||
|
|
||||||
def format_dec(value, width=None, high_precision=None):
|
def format_dec(value, width=None, high_precision=None):
|
||||||
|
|||||||
+116
-31
@@ -115,13 +115,51 @@ class Display:
|
|||||||
self._gezeigt = {}
|
self._gezeigt = {}
|
||||||
self._zeile_schreiben(index, text)
|
self._zeile_schreiben(index, text)
|
||||||
|
|
||||||
def _aufbereiten(self, text):
|
@staticmethod
|
||||||
"""Auf Zeilenbreite bringen: abschneiden und auffuellen (ggf. mittig)."""
|
def _breiten(text):
|
||||||
|
"""Breite jeder einzelnen Zelle in Pixeln.
|
||||||
|
|
||||||
|
Zeichen aus config.CELL_NARROW brauchen weniger Platz. Fuer Leerzeichen
|
||||||
|
gilt das aber nur *innerhalb* des Textes -- dort trennen sie Beschriftung
|
||||||
|
und Wert ("RA 18h36.9m"). Die Leerzeichen, mit denen die Zeile rechts
|
||||||
|
aufgefuellt wird, behalten die volle Breite: Sie sollen den Rest der
|
||||||
|
Zeile ueberschreiben, und je schmaler sie waeren, desto mehr Zellen
|
||||||
|
muesste man dafuer schreiben.
|
||||||
|
"""
|
||||||
|
ohne_fuellung = len(text.rstrip())
|
||||||
|
return [config.CELL_NARROW.get(ch, config.CELL_WIDTH)
|
||||||
|
if i < ohne_fuellung else config.CELL_WIDTH
|
||||||
|
for i, ch in enumerate(text)]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _breite(text):
|
||||||
|
"""Pixelbreite eines Textes."""
|
||||||
|
return sum(Display._breiten(text))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _aufbereiten(text):
|
||||||
|
"""Auf die Zeilenbreite bringen: abschneiden und auffuellen.
|
||||||
|
|
||||||
|
Massgeblich sind die **Pixel**, nicht die Zeichenzahl: Seit schmale
|
||||||
|
Zeichen weniger Platz brauchen (config.CELL_NARROW), passen mal 10 und
|
||||||
|
mal 11 Zeichen in dieselbe Zeile. Ein Text wird deshalb so lange
|
||||||
|
gekuerzt, bis er in DISPLAY_WIDTH passt, und nur so weit aufgefuellt, wie
|
||||||
|
eine weitere Zelle noch hineingeht. Ohne das ragte die letzte Zelle ueber
|
||||||
|
den Rand und die Anzeige quittierte mit Fehlercode 4.
|
||||||
|
"""
|
||||||
# Ohne str.ljust/center: die kennt MicroPython nicht (am Geraet aufgefallen).
|
# Ohne str.ljust/center: die kennt MicroPython nicht (am Geraet aufgefallen).
|
||||||
|
platz = config.DISPLAY_WIDTH - config.LINE_X
|
||||||
text = text[:config.CHARS_PER_LINE]
|
text = text[:config.CHARS_PER_LINE]
|
||||||
rest = config.CHARS_PER_LINE - len(text)
|
while text and Display._breite(text) > platz:
|
||||||
links = rest // 2 if config.CENTER_LINES else 0
|
text = text[:-1]
|
||||||
return " " * links + text + " " * (rest - links)
|
# Beim Auffuellen bewusst mit der *vollen* Zellenbreite rechnen, obwohl
|
||||||
|
# das Leerzeichen schmal gesetzt wird: Die Anzeige bemalt je Zeichen
|
||||||
|
# GLYPH_WIDTH Pixel, ein Leerzeichen am rechten Rand reichte sonst
|
||||||
|
# darueber hinaus.
|
||||||
|
while (len(text) < config.CHARS_PER_LINE
|
||||||
|
and Display._breite(text) + config.CELL_WIDTH <= platz):
|
||||||
|
text += " "
|
||||||
|
return text
|
||||||
|
|
||||||
def _zeile_schreiben(self, index, text, voll=False):
|
def _zeile_schreiben(self, index, text, voll=False):
|
||||||
"""Eine Zeile nachfuehren. Liefert True, wenn der Zeichensatz gewechselt wurde.
|
"""Eine Zeile nachfuehren. Liefert True, wenn der Zeichensatz gewechselt wurde.
|
||||||
@@ -129,19 +167,27 @@ class Display:
|
|||||||
voll=True schreibt alle Zellen, auch die unveraenderten.
|
voll=True schreibt alle Zellen, auch die unveraenderten.
|
||||||
"""
|
"""
|
||||||
neu = self._aufbereiten(text)
|
neu = self._aufbereiten(text)
|
||||||
alt = None if voll else self._gezeigt.get(index)
|
pos = self._positionen(neu)
|
||||||
if alt == neu:
|
alt, alt_pos = self._gezeigt.get(index, (None, None))
|
||||||
|
if voll:
|
||||||
|
alt = None
|
||||||
|
elif alt_pos != pos:
|
||||||
|
# Die Zeile hat eine andere Form als vorher (z.B. ein Dezimalpunkt
|
||||||
|
# kommt dazu, der weniger Platz braucht). Dann sitzen alle Zellen
|
||||||
|
# woanders und der Vergleich waere sinnlos -- vollstaendig neu.
|
||||||
|
alt = None
|
||||||
|
elif alt == neu:
|
||||||
return False # nichts zu tun, kein Telegramm
|
return False # nichts zu tun, kein Telegramm
|
||||||
|
|
||||||
du = migra.DataUnit()
|
du = migra.DataUnit()
|
||||||
du.attributes(fg=config.COLOR, bg=config.BACKGROUND, blink=migra.STATIC)
|
du.attributes(fg=config.COLOR, bg=config.BACKGROUND, blink=migra.STATIC)
|
||||||
if alt is None:
|
if alt is None:
|
||||||
# Erste Ausgabe dieser Zeile: Zeichensatz sicherheitshalber setzen.
|
# Erste Ausgabe dieser Zeile: Zeichensatz sicherheitshalber setzen.
|
||||||
# Danach behaelt die Anzeige ihn, und _emit_degree schaltet nach
|
# Danach behaelt die Anzeige ihn, und _emit_fremd schaltet nach
|
||||||
# jedem Gradzeichen wieder zurueck -- ein Wechsel je Telegramm waere
|
# jedem Sonderzeichen wieder zurueck -- ein Wechsel je Telegramm
|
||||||
# nur unnoetiger Ballast und wuerde die Home-Zelle beschaedigen.
|
# waere nur Ballast und wuerde die Home-Zelle beschaedigen.
|
||||||
du.charset(config.CHARSET, spaced=config.CHARSET_SPACED)
|
du.charset(config.CHARSET, spaced=config.CHARSET_SPACED)
|
||||||
wechsel = self._emit_line(du, config.LINE_Y[index], neu, alt)
|
wechsel = self._emit_line(du, config.LINE_Y[index], neu, alt, pos)
|
||||||
try:
|
try:
|
||||||
self.send(du)
|
self.send(du)
|
||||||
except Exception:
|
except Exception:
|
||||||
@@ -151,23 +197,43 @@ class Display:
|
|||||||
# faelschlich fuer aktuell hielten.
|
# faelschlich fuer aktuell hielten.
|
||||||
self._gezeigt.pop(index, None)
|
self._gezeigt.pop(index, None)
|
||||||
raise
|
raise
|
||||||
self._gezeigt[index] = neu
|
self._gezeigt[index] = (neu, pos)
|
||||||
return wechsel or alt is None
|
return wechsel or alt is None
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _zelle_faellig(neu, alt, i):
|
def _positionen(text):
|
||||||
|
"""x-Position jeder Zelle einer Zeile.
|
||||||
|
|
||||||
|
Normalerweise ein festes Raster aus CELL_WIDTH. Zeichen aus
|
||||||
|
config.CELL_NARROW bekommen weniger Platz -- der Dezimalpunkt der
|
||||||
|
Zehntelminuten stuende sonst sehr luftig zwischen den Ziffern.
|
||||||
|
"""
|
||||||
|
breiten = Display._breiten(text)
|
||||||
|
x = config.LINE_X
|
||||||
|
aus = []
|
||||||
|
for b in breiten:
|
||||||
|
aus.append(x)
|
||||||
|
x += b
|
||||||
|
return aus
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _zelle_faellig(neu, alt, i, pos):
|
||||||
"""Muss Zelle i neu geschrieben werden?
|
"""Muss Zelle i neu geschrieben werden?
|
||||||
|
|
||||||
Ja, wenn sie sich selbst geaendert hat -- oder wenn ihr **linker**
|
Ja, wenn sie sich selbst geaendert hat -- oder wenn ihr **linker**
|
||||||
Nachbar neu geschrieben wird: Der reicht 1 px in diese Zelle hinein und
|
Nachbar neu geschrieben wird und bis in diese Zelle hineinragt: Die
|
||||||
loescht deren erste Pixelspalte (siehe _emit_line).
|
Anzeige bemalt je Zeichen GLYPH_WIDTH Pixel, mehr als eine Zelle breit
|
||||||
|
ist, und loescht damit die erste Pixelspalte hier (siehe _emit_line).
|
||||||
"""
|
"""
|
||||||
def anders(k):
|
def anders(k):
|
||||||
return k >= len(alt) or alt[k] != neu[k]
|
return k >= len(alt) or alt[k] != neu[k]
|
||||||
|
|
||||||
return anders(i) or (i > 0 and anders(i - 1))
|
if anders(i):
|
||||||
|
return True
|
||||||
|
return (i > 0 and anders(i - 1)
|
||||||
|
and pos[i] < pos[i - 1] + config.GLYPH_WIDTH)
|
||||||
|
|
||||||
def _emit_line(self, du, y, text, alt=None):
|
def _emit_line(self, du, y, text, alt=None, pos=None):
|
||||||
"""Zellen einer Zeile ausgeben. Liefert True bei Zeichensatzwechsel.
|
"""Zellen einer Zeile ausgeben. Liefert True bei Zeichensatzwechsel.
|
||||||
|
|
||||||
Jedes Zeichen wird einzeln auf x = LINE_X + i*CELL_WIDTH gesetzt, statt
|
Jedes Zeichen wird einzeln auf x = LINE_X + i*CELL_WIDTH gesetzt, statt
|
||||||
@@ -189,26 +255,45 @@ class Display:
|
|||||||
Am Geraet aufgefallen (2026-07-27): Beim Sekundenwechsel fehlten dem "s"
|
Am Geraet aufgefallen (2026-07-27): Beim Sekundenwechsel fehlten dem "s"
|
||||||
die linken Pixel, beim Minutenwechsel dem "m".
|
die linken Pixel, beim Minutenwechsel dem "m".
|
||||||
"""
|
"""
|
||||||
|
if pos is None:
|
||||||
|
pos = self._positionen(text)
|
||||||
wechsel = False
|
wechsel = False
|
||||||
for i in range(len(text)):
|
for i in range(len(text)):
|
||||||
if alt is not None and not self._zelle_faellig(text, alt, i):
|
if alt is not None and not self._zelle_faellig(text, alt, i, pos):
|
||||||
continue
|
continue
|
||||||
du.cursor(config.LINE_X + i * config.CELL_WIDTH, y)
|
du.cursor(pos[i], y)
|
||||||
if text[i] == config.DEGREE_MARKER:
|
if self._emit_zeichen(du, text[i]):
|
||||||
self._emit_degree(du)
|
|
||||||
wechsel = True
|
wechsel = True
|
||||||
else:
|
|
||||||
du.text(text[i])
|
|
||||||
return wechsel
|
return wechsel
|
||||||
|
|
||||||
def _emit_degree(self, du):
|
def _emit_zeichen(self, du, ch):
|
||||||
"""Das Gradzeichen einfuegen -- ueber Zeichensatzwechsel oder als Fallback."""
|
"""Ein Zeichen ausgeben. Liefert True, wenn der Zeichensatz wechseln musste.
|
||||||
if config.DEGREE_CHARSET is None:
|
|
||||||
du.text(config.DEGREE_FALLBACK)
|
Nur das Gradzeichen kommt aus einem anderen Zeichensatz -- in
|
||||||
return
|
Zeichensatz 0 gibt es keines.
|
||||||
du.charset(config.DEGREE_CHARSET, spaced=config.CHARSET_SPACED)
|
|
||||||
du.text(config.DEGREE_CHAR)
|
Ein Zeichensatzwechsel beschaedigt die Home-Zelle (0,0), also den Anfang
|
||||||
du.charset(config.CHARSET, spaced=config.CHARSET_SPACED)
|
der obersten Zeile. Solange alle Wechsel in der *unteren* Zeile
|
||||||
|
stattfinden, genuegt die Reihenfolge in show_lines (unten vor oben).
|
||||||
|
|
||||||
|
**Ein Wechsel mitten in der obersten Zeile laesst sich dagegen nicht
|
||||||
|
sauber reparieren.** Das erste Zeichen danach noch einmal zu schreiben,
|
||||||
|
wurde am Geraet ausprobiert (2026-07-27) und machte es schlimmer: Das
|
||||||
|
nachgezogene Zeichen bemalt GLYPH_WIDTH Pixel und frisst dabei die linke
|
||||||
|
Spalte seines rechten Nachbarn. Deshalb steht in der obersten Zeile
|
||||||
|
bewusst kein Sonderzeichen -- die Beschriftung nutzt ein schmales
|
||||||
|
Leerzeichen statt eines Doppelpunkts aus Zeichensatz 1.
|
||||||
|
"""
|
||||||
|
if ch == config.DEGREE_MARKER:
|
||||||
|
if config.DEGREE_CHARSET is None:
|
||||||
|
du.text(config.DEGREE_FALLBACK)
|
||||||
|
return False
|
||||||
|
du.charset(config.DEGREE_CHARSET, spaced=config.CHARSET_SPACED)
|
||||||
|
du.text(config.DEGREE_CHAR)
|
||||||
|
du.charset(config.CHARSET, spaced=config.CHARSET_SPACED)
|
||||||
|
return True
|
||||||
|
du.text(ch)
|
||||||
|
return False
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
self._t.close()
|
self._t.close()
|
||||||
|
|||||||
+23
-2
@@ -21,8 +21,29 @@ class LinkError(Exception):
|
|||||||
|
|
||||||
|
|
||||||
def format_coordinates(ra_raw, dec_raw):
|
def format_coordinates(ra_raw, dec_raw):
|
||||||
"""Rohstrings der Montierung fuer die Anzeige aufbereiten."""
|
"""Rohstrings der Montierung fuer die Anzeige aufbereiten.
|
||||||
return coords.format_ra(ra_raw), coords.format_dec(dec_raw)
|
|
||||||
|
Bei **Niedrigpraezision** bekommen die Zeilen eine Beschriftung:
|
||||||
|
|
||||||
|
RA:18h36.9
|
||||||
|
DE:+38°47'
|
||||||
|
|
||||||
|
Das geht genau auf, weil die Montierung in diesem Modus ohnehin nur
|
||||||
|
Zehntelminuten (7 Zeichen) und ganze Bogenminuten (7 Zeichen) liefert -- in
|
||||||
|
zehn Zellen bleiben also drei fuer "RA:"/"DE:" uebrig. Die Zeilen sind
|
||||||
|
linksbuendig gesetzt (config.CENTER_LINES), damit beide Beschriftungen
|
||||||
|
buendig untereinander beginnen.
|
||||||
|
|
||||||
|
Bei **Hochpraezision** entfaellt die Beschriftung: Dort braucht die
|
||||||
|
Deklination mit +38°47'01" alle zehn Zeichen.
|
||||||
|
"""
|
||||||
|
if config.HIGH_PRECISION:
|
||||||
|
return coords.format_ra(ra_raw), coords.format_dec(dec_raw)
|
||||||
|
|
||||||
|
breite_ra = config.CHARS_PER_LINE - len(config.LABEL_RA)
|
||||||
|
breite_dec = config.CHARS_PER_LINE - len(config.LABEL_DEC)
|
||||||
|
return (config.LABEL_RA + coords.format_ra(ra_raw, width=breite_ra),
|
||||||
|
config.LABEL_DEC + coords.format_dec(dec_raw, width=breite_dec))
|
||||||
|
|
||||||
|
|
||||||
def poll_loop(display, client, interval=None, once=False,
|
def poll_loop(display, client, interval=None, once=False,
|
||||||
|
|||||||
+40
-12
@@ -58,31 +58,54 @@ def _numbers(text):
|
|||||||
return [int(x) for x in out]
|
return [int(x) for x in out]
|
||||||
|
|
||||||
|
|
||||||
def _fmt_ra(total_s):
|
def _fmt_ra(total_s, high=True):
|
||||||
|
"""Rektaszension wie die GM4000.
|
||||||
|
|
||||||
|
high=True -> HH:MM:SS (Hochpraezision, nach ":U#")
|
||||||
|
high=False -> HH:MM.T (Zehntelminuten -- so liefert die Montierung im
|
||||||
|
Auslieferzustand, verifiziert 2026-07-18)
|
||||||
|
"""
|
||||||
total_s %= 86400
|
total_s %= 86400
|
||||||
h, rest = divmod(total_s, 3600)
|
h, rest = divmod(total_s, 3600)
|
||||||
m, s = divmod(rest, 60)
|
m, s = divmod(rest, 60)
|
||||||
return "%02d:%02d:%02d" % (h, m, s)
|
if high:
|
||||||
|
return "%02d:%02d:%02d" % (h, m, s)
|
||||||
|
zehntel = (total_s * 10 + 30) // 60 # in Zehntelminuten, gerundet
|
||||||
|
minuten, t = divmod(zehntel, 10)
|
||||||
|
h, m = divmod(minuten, 60)
|
||||||
|
return "%02d:%02d.%d" % (h % 24, m, t)
|
||||||
|
|
||||||
|
|
||||||
def _fmt_dec(total_arcsec):
|
def _fmt_dec(total_arcsec, high=True):
|
||||||
|
"""Deklination wie die GM4000.
|
||||||
|
|
||||||
|
high=True -> sDD*MM:SS (Hochpraezision)
|
||||||
|
high=False -> sDD*MM (ganze Bogenminuten, Auslieferzustand)
|
||||||
|
|
||||||
|
Gradzeichen als '*', Minuten/Sekunden mit ':' -- der coords-Parser kommt mit
|
||||||
|
beiden Trennzeichen klar. (Die echte Montierung sendet 0xDF als Gradzeichen.)
|
||||||
|
"""
|
||||||
sign = "-" if total_arcsec < 0 else "+"
|
sign = "-" if total_arcsec < 0 else "+"
|
||||||
total_arcsec = abs(total_arcsec)
|
total_arcsec = abs(total_arcsec)
|
||||||
d, rest = divmod(total_arcsec, 3600)
|
d, rest = divmod(total_arcsec, 3600)
|
||||||
m, s = divmod(rest, 60)
|
m, s = divmod(rest, 60)
|
||||||
# Gradzeichen als '*', Minuten/Sekunden mit ':' -- der coords-Parser
|
if high:
|
||||||
# kommt mit beiden Trennzeichen klar.
|
return "%s%02d*%02d:%02d" % (sign, d, m, s)
|
||||||
return "%s%02d*%02d:%02d" % (sign, d, m, s)
|
return "%s%02d*%02d" % (sign, d, m)
|
||||||
|
|
||||||
|
|
||||||
class MountMock:
|
class MountMock:
|
||||||
"""Simuliert die Montierung auf TCP-Ebene."""
|
"""Simuliert die Montierung auf TCP-Ebene."""
|
||||||
|
|
||||||
def __init__(self, host="127.0.0.1", port=3490,
|
def __init__(self, host="127.0.0.1", port=3490,
|
||||||
ra="18:36:56", dec="+38:47:01", move=False):
|
ra="18:36:56", dec="+38:47:01", move=False, high=True):
|
||||||
self._ra0 = _parse_ra(ra)
|
self._ra0 = _parse_ra(ra)
|
||||||
self._dec = _parse_dec(dec)
|
self._dec = _parse_dec(dec)
|
||||||
self._move = move
|
self._move = move
|
||||||
|
# high=False bildet den Auslieferzustand der GM4000 nach: keine Sekunden,
|
||||||
|
# RA als Zehntelminuten. Vorgabe bleibt True, damit bestehende Tests mit
|
||||||
|
# ihren festen Erwartungen unveraendert durchlaufen.
|
||||||
|
self._high = high
|
||||||
self._tick = 0 # zaehlt bei --move pro RA-Abfrage hoch
|
self._tick = 0 # zaehlt bei --move pro RA-Abfrage hoch
|
||||||
self._dectick = 0 # zaehlt bei --move pro DEC-Abfrage hoch
|
self._dectick = 0 # zaehlt bei --move pro DEC-Abfrage hoch
|
||||||
self._lock = threading.Lock()
|
self._lock = threading.Lock()
|
||||||
@@ -116,9 +139,9 @@ class MountMock:
|
|||||||
def _reply_for(self, cmd):
|
def _reply_for(self, cmd):
|
||||||
"""Antwort (inkl. '#') fuer ein Kommando ohne ':' und ohne '#'."""
|
"""Antwort (inkl. '#') fuer ein Kommando ohne ':' und ohne '#'."""
|
||||||
if cmd == "GR":
|
if cmd == "GR":
|
||||||
return _fmt_ra(self._current_ra()) + "#"
|
return _fmt_ra(self._current_ra(), self._high) + "#"
|
||||||
if cmd == "GD":
|
if cmd == "GD":
|
||||||
return _fmt_dec(self._current_dec()) + "#"
|
return _fmt_dec(self._current_dec(), self._high) + "#"
|
||||||
if cmd == "GVP":
|
if cmd == "GVP":
|
||||||
return PRODUCT + "#"
|
return PRODUCT + "#"
|
||||||
if cmd == "GVN":
|
if cmd == "GVN":
|
||||||
@@ -204,13 +227,18 @@ def main(argv=None):
|
|||||||
p.add_argument("--ra", default="18:36:56")
|
p.add_argument("--ra", default="18:36:56")
|
||||||
p.add_argument("--dec", default="+38:47:01")
|
p.add_argument("--dec", default="+38:47:01")
|
||||||
p.add_argument("--move", action="store_true", help="RA laeuft mit der Zeit hoch")
|
p.add_argument("--move", action="store_true", help="RA laeuft mit der Zeit hoch")
|
||||||
|
p.add_argument("--low", action="store_true",
|
||||||
|
help="Niedrigpraezision wie im Auslieferzustand der GM4000: "
|
||||||
|
"RA als HH:MM.T, DEC ohne Bogensekunden")
|
||||||
args = p.parse_args(argv)
|
args = p.parse_args(argv)
|
||||||
|
|
||||||
host = "0.0.0.0" if args.lan else args.host
|
host = "0.0.0.0" if args.lan else args.host
|
||||||
mock = MountMock(host=host, port=args.port, ra=args.ra, dec=args.dec,
|
mock = MountMock(host=host, port=args.port, ra=args.ra, dec=args.dec,
|
||||||
move=args.move)
|
move=args.move, high=not args.low)
|
||||||
print("Mock-Montierung auf %s:%d (RA=%s DEC=%s%s)"
|
print("Mock-Montierung auf %s:%d (RA=%s DEC=%s%s, %s)"
|
||||||
% (mock.host, mock.port, args.ra, args.dec, ", move" if args.move else ""))
|
% (mock.host, mock.port, args.ra, args.dec,
|
||||||
|
", move" if args.move else "",
|
||||||
|
"Niedrigpraezision" if args.low else "Hochpraezision"))
|
||||||
if host == "0.0.0.0":
|
if host == "0.0.0.0":
|
||||||
ip = lan_address()
|
ip = lan_address()
|
||||||
if ip:
|
if ip:
|
||||||
|
|||||||
+110
-33
@@ -38,7 +38,14 @@ def zellen(frames):
|
|||||||
y = int(frame[i + 5:i + 8])
|
y = int(frame[i + 5:i + 8])
|
||||||
j = frame.find(bytes([migra.SEP]), i + 8)
|
j = frame.find(bytes([migra.SEP]), i + 8)
|
||||||
if j >= 0 and j + 1 < len(frame):
|
if j >= 0 and j + 1 < len(frame):
|
||||||
out[(x, y)] = chr(frame[j + 1])
|
ch = chr(frame[j + 1])
|
||||||
|
# Das Gradzeichen entsteht ueber einen Zeichensatzwechsel und
|
||||||
|
# steht als DEGREE_CHAR ("/") im Telegramm. Hier wieder in das
|
||||||
|
# zurueckuebersetzen, was auf der Anzeige zu sehen ist.
|
||||||
|
if (frame[i + 8:j].startswith(b"\x1bz")
|
||||||
|
and ch == config.DEGREE_CHAR):
|
||||||
|
ch = config.DEGREE_MARKER
|
||||||
|
out[(x, y)] = ch
|
||||||
i += 8
|
i += 8
|
||||||
else:
|
else:
|
||||||
i += 1
|
i += 1
|
||||||
@@ -52,12 +59,22 @@ def zeile(frames, index):
|
|||||||
return "".join(ch for _, ch in sorted(treffer))
|
return "".join(ch for _, ch in sorted(treffer))
|
||||||
|
|
||||||
|
|
||||||
def zentriert(text):
|
# So viele *volle* Zellen passen in eine Zeile. Das ist weniger als
|
||||||
"""Erwartungswert: Text so, wie _emit_line ihn auf die Zeile legt."""
|
# config.CHARS_PER_LINE: Dessen 11 Zeichen gehen nur auf, wenn mindestens ein
|
||||||
text = text[:config.CHARS_PER_LINE]
|
# schmales dabei ist (config.CELL_NARROW). Fuer Tests mit normalen Zeichen ist
|
||||||
rest = config.CHARS_PER_LINE - len(text)
|
# das hier die Zeilenbreite.
|
||||||
links = rest // 2 if config.CENTER_LINES else 0
|
VOLLE_ZELLEN = (config.DISPLAY_WIDTH - config.LINE_X) // config.CELL_WIDTH
|
||||||
return " " * links + text + " " * (rest - links)
|
|
||||||
|
|
||||||
|
def aufbereitet(text):
|
||||||
|
"""Erwartungswert: Text so, wie er auf der Zeile landet.
|
||||||
|
|
||||||
|
Nutzt bewusst dieselbe Funktion wie der Treiber. Wie breit eine Zeile ist,
|
||||||
|
haengt seit config.CELL_NARROW davon ab, welche Zeichen darin stehen -- eine
|
||||||
|
zweite Nachbildung hier waere nur eine Fehlerquelle. Was der Wert *bedeutet*,
|
||||||
|
pruefen die Tests darunter mit eigenen Zusicherungen.
|
||||||
|
"""
|
||||||
|
return Display._aufbereiten(text)
|
||||||
|
|
||||||
|
|
||||||
class TestFesteTexte(unittest.TestCase):
|
class TestFesteTexte(unittest.TestCase):
|
||||||
@@ -174,8 +191,8 @@ class TestNurGeaenderteZellen(unittest.TestCase):
|
|||||||
frames = self._neu()
|
frames = self._neu()
|
||||||
# Loeschtelegramm + beide Zeilen vollstaendig
|
# Loeschtelegramm + beide Zeilen vollstaendig
|
||||||
self.assertEqual(len(frames), 3)
|
self.assertEqual(len(frames), 3)
|
||||||
self.assertEqual(len(zeile(frames, 0)), config.CHARS_PER_LINE)
|
self.assertEqual(len(zeile(frames, 0)), VOLLE_ZELLEN)
|
||||||
self.assertEqual(len(zeile(frames, 1)), config.CHARS_PER_LINE)
|
self.assertEqual(len(zeile(frames, 1)), VOLLE_ZELLEN)
|
||||||
|
|
||||||
def test_nach_clear_kein_rest_der_alten_anzeige(self):
|
def test_nach_clear_kein_rest_der_alten_anzeige(self):
|
||||||
"""clear muss den gemerkten Inhalt verwerfen.
|
"""clear muss den gemerkten Inhalt verwerfen.
|
||||||
@@ -211,7 +228,7 @@ class TestNurGeaenderteZellen(unittest.TestCase):
|
|||||||
|
|
||||||
# Dieselbe Zeile noch einmal: muss vollstaendig geschrieben werden.
|
# Dieselbe Zeile noch einmal: muss vollstaendig geschrieben werden.
|
||||||
self.d.show_lines("18h36m57s", "+38°47'01\"")
|
self.d.show_lines("18h36m57s", "+38°47'01\"")
|
||||||
self.assertEqual(len(zeile(self._neu(), 0)), config.CHARS_PER_LINE)
|
self.assertEqual(len(zeile(self._neu(), 0)), VOLLE_ZELLEN)
|
||||||
|
|
||||||
def test_gradzeichen_wird_nicht_staendig_neu_gesetzt(self):
|
def test_gradzeichen_wird_nicht_staendig_neu_gesetzt(self):
|
||||||
"""Spart nicht nur Bytes: Jeder Zeichensatzwechsel beschaedigt (0,0)."""
|
"""Spart nicht nur Bytes: Jeder Zeichensatzwechsel beschaedigt (0,0)."""
|
||||||
@@ -221,6 +238,8 @@ class TestNurGeaenderteZellen(unittest.TestCase):
|
|||||||
gesendet = b"".join(self._neu())
|
gesendet = b"".join(self._neu())
|
||||||
self.assertNotIn(b"\x1bz01", gesendet)
|
self.assertNotIn(b"\x1bz01", gesendet)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def test_neues_gradzeichen_zieht_die_oberste_zeile_nach(self):
|
def test_neues_gradzeichen_zieht_die_oberste_zeile_nach(self):
|
||||||
"""Wird der Zeichensatz doch gewechselt, ist die Home-Zelle hin.
|
"""Wird der Zeichensatz doch gewechselt, ist die Home-Zelle hin.
|
||||||
|
|
||||||
@@ -233,7 +252,66 @@ class TestNurGeaenderteZellen(unittest.TestCase):
|
|||||||
frames = self._neu()
|
frames = self._neu()
|
||||||
self.assertIn(b"\x1bz01", b"".join(frames)) # Wechsel fand statt
|
self.assertIn(b"\x1bz01", b"".join(frames)) # Wechsel fand statt
|
||||||
# Obere Zeile trotz unveraendertem Text komplett neu geschrieben.
|
# Obere Zeile trotz unveraendertem Text komplett neu geschrieben.
|
||||||
self.assertEqual(len(zeile(frames, 0)), config.CHARS_PER_LINE)
|
self.assertEqual(len(zeile(frames, 0)), VOLLE_ZELLEN)
|
||||||
|
|
||||||
|
|
||||||
|
class TestSchmaleZeichen(unittest.TestCase):
|
||||||
|
"""Der Dezimalpunkt der Zehntelminuten braucht weniger als eine volle Zelle.
|
||||||
|
|
||||||
|
In der Sperrschrift stand "RA 18:37.1" sonst sehr luftig -- der Punkt belegt
|
||||||
|
6 px, obwohl er nur ein, zwei Pixel breit ist. 3 px am Geraet ausprobiert
|
||||||
|
(2026-07-27). Moeglich ist das nur, weil jedes Zeichen ohnehin einzeln
|
||||||
|
positioniert wird.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.t = FakeTransport(echo=False)
|
||||||
|
self.d = Display(self.t, response=True)
|
||||||
|
|
||||||
|
def _neu(self):
|
||||||
|
frames = self.t.frames[:]
|
||||||
|
self.t.frames.clear()
|
||||||
|
return frames
|
||||||
|
|
||||||
|
def test_punkt_bekommt_weniger_platz(self):
|
||||||
|
pos = Display._positionen("1.2")
|
||||||
|
self.assertEqual(pos[1] - pos[0], config.CELL_WIDTH) # vor dem Punkt
|
||||||
|
self.assertEqual(pos[2] - pos[1], config.CELL_NARROW["."]) # nach dem Punkt
|
||||||
|
|
||||||
|
def test_zeile_mit_punkt_wird_schmaler_und_bleibt_mittig(self):
|
||||||
|
def breite(positionen):
|
||||||
|
return positionen[-1] + config.CELL_WIDTH - positionen[0]
|
||||||
|
|
||||||
|
ohne = Display._positionen("RA 18:3701") # gleiche Laenge, kein Punkt
|
||||||
|
mit = Display._positionen("RA 18:37.1")
|
||||||
|
self.assertEqual(breite(ohne) - breite(mit),
|
||||||
|
config.CELL_WIDTH - config.CELL_NARROW["."])
|
||||||
|
# Weiterhin vollstaendig innerhalb der Anzeige.
|
||||||
|
self.assertGreaterEqual(mit[0], 0)
|
||||||
|
self.assertLessEqual(mit[-1] + config.CELL_WIDTH, config.DISPLAY_WIDTH)
|
||||||
|
|
||||||
|
def test_zehntel_wechsel_schreibt_weiter_nur_wenige_zellen(self):
|
||||||
|
"""Der Alltagsfall bei Niedrigpraezision -- das Nachfuehren muss greifen."""
|
||||||
|
self.d.show_lines("RA 18:37.1", "DE +38°47'")
|
||||||
|
self._neu()
|
||||||
|
self.d.show_lines("RA 18:37.2", "DE +38°47'")
|
||||||
|
frames = self._neu()
|
||||||
|
self.assertEqual(len(frames), 1) # DEC unveraendert
|
||||||
|
self.assertLessEqual(len(zellen(frames)), 2) # nur die Zehntelziffer
|
||||||
|
|
||||||
|
def test_strukturwechsel_schreibt_die_zeile_vollstaendig_neu(self):
|
||||||
|
"""Kommt ein Punkt dazu, sitzen alle folgenden Zellen woanders.
|
||||||
|
|
||||||
|
Ein Vergleich Zeichen fuer Zeichen waere dann sinnlos -- es muss alles
|
||||||
|
neu geschrieben werden.
|
||||||
|
"""
|
||||||
|
self.d.show_lines("RA 18:3701") # ohne Punkt: 10 volle Zellen
|
||||||
|
self._neu()
|
||||||
|
self.d.show_lines("RA 18:37.1") # mit Punkt: andere Positionen
|
||||||
|
gesetzt = zellen(self._neu())
|
||||||
|
# Alles neu -- und dank des schmalen Punktes passt sogar ein Zeichen mehr.
|
||||||
|
self.assertEqual(len(gesetzt), len(Display._aufbereiten("RA 18:37.1")))
|
||||||
|
self.assertGreater(len(gesetzt), VOLLE_ZELLEN)
|
||||||
|
|
||||||
|
|
||||||
class TestZeilenaufbau(unittest.TestCase):
|
class TestZeilenaufbau(unittest.TestCase):
|
||||||
@@ -252,18 +330,28 @@ class TestZeilenaufbau(unittest.TestCase):
|
|||||||
nicht kennt (am Geraet aufgefallen).
|
nicht kennt (am Geraet aufgefallen).
|
||||||
"""
|
"""
|
||||||
self.d.show_lines("AB", "CD")
|
self.d.show_lines("AB", "CD")
|
||||||
self.assertEqual(zeile(self._alle(), 0), zentriert("AB"))
|
self.assertEqual(zeile(self._alle(), 0), aufbereitet("AB"))
|
||||||
self.assertEqual(zeile(self._alle(), 1), zentriert("CD"))
|
self.assertEqual(zeile(self._alle(), 1), aufbereitet("CD"))
|
||||||
# Es wird immer die volle Breite geschrieben, damit nichts stehen bleibt.
|
# Es wird immer die volle Breite geschrieben, damit nichts stehen bleibt.
|
||||||
self.assertEqual(len(zeile(self._alle(), 0)), config.CHARS_PER_LINE)
|
self.assertEqual(len(zeile(self._alle(), 0)), VOLLE_ZELLEN)
|
||||||
|
|
||||||
def test_kuerzere_zeile_wird_mittig_gesetzt(self):
|
def test_kuerzere_zeile_steht_linksbuendig(self):
|
||||||
"""Zentriert heisst: links und rechts gleich viel Luft (Nutzerwunsch)."""
|
"""Nutzerwunsch 2026-07-27: beide Zeilen buendig untereinander.
|
||||||
|
|
||||||
|
Zentriert standen sie um wenige Pixel gegeneinander versetzt, weil die
|
||||||
|
RA-Zeile mit ihrem schmalen Dezimalpunkt kuerzer ist als die DEC-Zeile.
|
||||||
|
"""
|
||||||
self.d.show_lines("+38°47'") # 7 Zeichen in 10 Zellen
|
self.d.show_lines("+38°47'") # 7 Zeichen in 10 Zellen
|
||||||
text = zeile(self._alle(), 0)
|
text = zeile(self._alle(), 0)
|
||||||
links = len(text) - len(text.lstrip())
|
self.assertEqual(text, "+38°47'" + " " * (VOLLE_ZELLEN - 7))
|
||||||
rechts = len(text) - len(text.rstrip())
|
|
||||||
self.assertLessEqual(abs(links - rechts), 1)
|
def test_beide_zeilen_beginnen_an_derselben_stelle(self):
|
||||||
|
"""Der eigentliche Punkt: RA und DE muessen buendig starten."""
|
||||||
|
self.d.show_lines("RA:18h37.1", "DE:+38°47'")
|
||||||
|
gesetzt = zellen(self._alle())
|
||||||
|
erste_oben = min(x for (x, y) in gesetzt if y == config.LINE_Y[0])
|
||||||
|
erste_unten = min(x for (x, y) in gesetzt if y == config.LINE_Y[1])
|
||||||
|
self.assertEqual(erste_oben, erste_unten)
|
||||||
|
|
||||||
def test_jedes_zeichen_sitzt_auf_seiner_zelle(self):
|
def test_jedes_zeichen_sitzt_auf_seiner_zelle(self):
|
||||||
"""Der Auto-Vorschub der Sperrschrift ist 7 px und liesse nur 9 Zeichen
|
"""Der Auto-Vorschub der Sperrschrift ist 7 px und liesse nur 9 Zeichen
|
||||||
@@ -272,13 +360,13 @@ class TestZeilenaufbau(unittest.TestCase):
|
|||||||
self.d.show_lines("0123456789")
|
self.d.show_lines("0123456789")
|
||||||
gesetzt = zellen(self._alle())
|
gesetzt = zellen(self._alle())
|
||||||
y = config.LINE_Y[0]
|
y = config.LINE_Y[0]
|
||||||
for i in range(config.CHARS_PER_LINE):
|
for i in range(VOLLE_ZELLEN):
|
||||||
self.assertIn((config.LINE_X + i * config.CELL_WIDTH, y), gesetzt)
|
self.assertIn((config.LINE_X + i * config.CELL_WIDTH, y), gesetzt)
|
||||||
|
|
||||||
def test_zeile_passt_in_die_anzeigenbreite(self):
|
def test_zeile_passt_in_die_anzeigenbreite(self):
|
||||||
"""Auch die letzte Zelle darf nicht ueber den rechten Rand hinausragen --
|
"""Auch die letzte Zelle darf nicht ueber den rechten Rand hinausragen --
|
||||||
sonst quittiert die Anzeige mit Fehlercode 4 (ausserhalb)."""
|
sonst quittiert die Anzeige mit Fehlercode 4 (ausserhalb)."""
|
||||||
rechts = config.LINE_X + config.CHARS_PER_LINE * config.CELL_WIDTH
|
rechts = config.LINE_X + VOLLE_ZELLEN * config.CELL_WIDTH
|
||||||
self.assertLessEqual(rechts, config.DISPLAY_WIDTH)
|
self.assertLessEqual(rechts, config.DISPLAY_WIDTH)
|
||||||
self.assertGreaterEqual(config.LINE_X, 0)
|
self.assertGreaterEqual(config.LINE_X, 0)
|
||||||
|
|
||||||
@@ -287,18 +375,7 @@ class TestZeilenaufbau(unittest.TestCase):
|
|||||||
beschaedigt die andere Zeile -- deshalb hart abschneiden."""
|
beschaedigt die andere Zeile -- deshalb hart abschneiden."""
|
||||||
lang = "X" * (config.CHARS_PER_LINE + 5)
|
lang = "X" * (config.CHARS_PER_LINE + 5)
|
||||||
self.d.show_lines(lang)
|
self.d.show_lines(lang)
|
||||||
self.assertEqual(zeile(self._alle(), 0), "X" * config.CHARS_PER_LINE)
|
self.assertEqual(zeile(self._alle(), 0), "X" * VOLLE_ZELLEN)
|
||||||
|
|
||||||
def test_ohne_zentrierung_steht_der_text_links(self):
|
|
||||||
"""CENTER_LINES = False muss wieder linksbuendig setzen."""
|
|
||||||
alt = config.CENTER_LINES
|
|
||||||
try:
|
|
||||||
config.CENTER_LINES = False
|
|
||||||
self.d.show_lines("AB")
|
|
||||||
self.assertEqual(zeile(self._alle(), 0),
|
|
||||||
"AB" + " " * (config.CHARS_PER_LINE - 2))
|
|
||||||
finally:
|
|
||||||
config.CENTER_LINES = alt
|
|
||||||
|
|
||||||
def test_oberste_zeile_wird_zuletzt_gezeichnet(self):
|
def test_oberste_zeile_wird_zuletzt_gezeichnet(self):
|
||||||
"""Schutz der Home-Zelle: ein Zeichensatzwechsel zerstoert (0,0),
|
"""Schutz der Home-Zelle: ein Zeichensatzwechsel zerstoert (0,0),
|
||||||
|
|||||||
+12
-6
@@ -556,13 +556,17 @@ class TestEsp32GegenMock(unittest.TestCase):
|
|||||||
self.assertIn("Selbsttest bestanden", text)
|
self.assertIn("Selbsttest bestanden", text)
|
||||||
|
|
||||||
def test_main_once_zeigt_die_koordinaten(self):
|
def test_main_once_zeigt_die_koordinaten(self):
|
||||||
self._run(self.run_esp32.main, fake_display=True, once=True)
|
"""Der Mock sendet 18:36:56; bei Niedrigpraezision (config-Vorgabe) wird
|
||||||
|
daraus die Zehntelform mit Beschriftung."""
|
||||||
|
self._run(self.run_esp32.main, fake_display=True, once=True,
|
||||||
|
with_mqtt=False)
|
||||||
text = "\n".join(self.log)
|
text = "\n".join(self.log)
|
||||||
self.assertIn("18h36m56s", text) # RA, fuer die Anzeige formatiert
|
self.assertIn("RA 18h36.9m", text)
|
||||||
self.assertIn("+38", text) # DEC
|
self.assertIn("DE +38", text)
|
||||||
|
|
||||||
def test_main_once_baut_ein_gueltiges_telegramm(self):
|
def test_main_once_baut_ein_gueltiges_telegramm(self):
|
||||||
hexdump = self._run(self.run_esp32.main, fake_display=True, once=True)
|
hexdump = self._run(self.run_esp32.main, fake_display=True, once=True,
|
||||||
|
with_mqtt=False)
|
||||||
self.assertIn("TX: 02 81 80 81", hexdump) # STX, DA, SA, FC
|
self.assertIn("TX: 02 81 80 81", hexdump) # STX, DA, SA, FC
|
||||||
self.assertTrue(hexdump.strip().endswith("03")) # ETX
|
self.assertTrue(hexdump.strip().endswith("03")) # ETX
|
||||||
|
|
||||||
@@ -574,7 +578,8 @@ class TestEsp32GegenMock(unittest.TestCase):
|
|||||||
self.mock.stop()
|
self.mock.stop()
|
||||||
with contextlib.redirect_stdout(io.StringIO()) as out:
|
with contextlib.redirect_stdout(io.StringIO()) as out:
|
||||||
self.run_esp32.main(host="127.0.0.1", port=self.mock.port,
|
self.run_esp32.main(host="127.0.0.1", port=self.mock.port,
|
||||||
fake_display=True, once=True, log=self.log.append)
|
fake_display=True, once=True, with_mqtt=False,
|
||||||
|
log=self.log.append)
|
||||||
self.assertTrue(any("Montierung" in line for line in self.log))
|
self.assertTrue(any("Montierung" in line for line in self.log))
|
||||||
# Die Hinweiszeilen stehen wirklich im Telegramm -- ohne den Text hier
|
# Die Hinweiszeilen stehen wirklich im Telegramm -- ohne den Text hier
|
||||||
# zu wiederholen, sonst bricht der Test bei jeder Umformulierung.
|
# zu wiederholen, sonst bricht der Test bei jeder Umformulierung.
|
||||||
@@ -598,7 +603,8 @@ class TestEsp32GegenMock(unittest.TestCase):
|
|||||||
import io
|
import io
|
||||||
with contextlib.redirect_stdout(io.StringIO()):
|
with contextlib.redirect_stdout(io.StringIO()):
|
||||||
self.run_esp32.main(host=self.mock.host, port=self.mock.port,
|
self.run_esp32.main(host=self.mock.host, port=self.mock.port,
|
||||||
fake_display=True, once=True, log=self.log.append)
|
fake_display=True, once=True, with_mqtt=False,
|
||||||
|
log=self.log.append)
|
||||||
finally:
|
finally:
|
||||||
config.WIFI_TIMEOUT = orig
|
config.WIFI_TIMEOUT = orig
|
||||||
text = "\n".join(self.log)
|
text = "\n".join(self.log)
|
||||||
|
|||||||
+31
-8
@@ -136,14 +136,38 @@ class TestKoordinaten(unittest.TestCase):
|
|||||||
self.assertEqual(coords.format_dec("+45d12m30s", width=7, **hp), "+45°12'")
|
self.assertEqual(coords.format_dec("+45d12m30s", width=7, **hp), "+45°12'")
|
||||||
|
|
||||||
def test_formatierung_niedrigpraezision(self):
|
def test_formatierung_niedrigpraezision(self):
|
||||||
|
"""RA in Zehntelminuten -- so, wie die Montierung sie liefert.
|
||||||
|
|
||||||
|
Bis 2026-07-27 wurde hier in Sekunden umgerechnet (11h36m54s). Die
|
||||||
|
sprangen aber in 6-Sekunden-Schritten, weil die Rohdaten sie nicht
|
||||||
|
hergeben. Die Zehntelform ist ehrlicher und laesst mit sieben Zeichen
|
||||||
|
Platz fuer die Beschriftung "RA:".
|
||||||
|
"""
|
||||||
lp = dict(high_precision=False)
|
lp = dict(high_precision=False)
|
||||||
# RA: Zehntel-Minute (0.9 min) kommt als Sekunden (54 s) heraus
|
# Breiteste passende Form: mit Minutenzeichen am Ende, sonst ohne.
|
||||||
self.assertEqual(coords.format_ra("11:36.9", width=9, **lp), "11h36m54s")
|
self.assertEqual(coords.format_ra("11:36.9", width=9, **lp), "11h36.9m")
|
||||||
|
self.assertEqual(coords.format_ra("11:36.9", width=8, **lp), "11h36.9m")
|
||||||
|
self.assertEqual(coords.format_ra("11:36.9", width=7, **lp), "11h36.9")
|
||||||
self.assertEqual(coords.format_ra("11:36.9", width=6, **lp), "11h36m")
|
self.assertEqual(coords.format_ra("11:36.9", width=6, **lp), "11h36m")
|
||||||
# DEC: ohne Sekunden
|
# DEC: ohne Sekunden
|
||||||
self.assertEqual(coords.format_dec("-00°55", width=9, **lp), "-00°55'")
|
self.assertEqual(coords.format_dec("-00°55", width=9, **lp), "-00°55'")
|
||||||
self.assertEqual(coords.format_dec("-00°55", width=6, **lp), "-00:55")
|
self.assertEqual(coords.format_dec("-00°55", width=6, **lp), "-00:55")
|
||||||
|
|
||||||
|
def test_zehntelminuten_mit_uebertrag(self):
|
||||||
|
"""18:36:57 sind 36,95 Minuten -- das muss auf 18:37.0 aufgehen."""
|
||||||
|
lp = dict(high_precision=False, width=7)
|
||||||
|
self.assertEqual(coords.format_ra("18:36:57", **lp), "18h37.0")
|
||||||
|
self.assertEqual(coords.format_ra("18:36:00", **lp), "18h36.0")
|
||||||
|
self.assertEqual(coords.format_ra("18:36:54", **lp), "18h36.9")
|
||||||
|
# Auch ueber die Stunde und ueber Mitternacht hinweg.
|
||||||
|
self.assertEqual(coords.format_ra("18:59:58", **lp), "19h00.0")
|
||||||
|
self.assertEqual(coords.format_ra("23:59:58", **lp), "00h00.0")
|
||||||
|
|
||||||
|
def test_hochpraezision_behaelt_die_sekundenform(self):
|
||||||
|
hp = dict(high_precision=True, width=10)
|
||||||
|
self.assertEqual(coords.format_ra("18:36:56", **hp), "18h36m56s")
|
||||||
|
self.assertEqual(coords.format_dec("+38*47:01", **hp), "+38°47'01\"")
|
||||||
|
|
||||||
|
|
||||||
class TestDisplay(unittest.TestCase):
|
class TestDisplay(unittest.TestCase):
|
||||||
|
|
||||||
@@ -173,20 +197,19 @@ class TestDisplay(unittest.TestCase):
|
|||||||
def zelle(i, y):
|
def zelle(i, y):
|
||||||
return b"\x1bC%03d%03d" % (config.LINE_X + i * config.CELL_WIDTH, y)
|
return b"\x1bC%03d%03d" % (config.LINE_X + i * config.CELL_WIDTH, y)
|
||||||
|
|
||||||
# "12:34:56" hat 8 Zeichen und wird in 10 Zellen mittig gesetzt (ab Zelle 1),
|
# Linksbuendig (config.CENTER_LINES): beide Zeilen beginnen bei Zelle 0.
|
||||||
# "+45:12:30" hat 9 und beginnt daher schon bei Zelle 0.
|
self.assertIn(zelle(0, 0) + b"\x1f1", oben)
|
||||||
self.assertIn(zelle(1, 0) + b"\x1f1", oben)
|
self.assertIn(zelle(1, 0) + b"\x1f2", oben)
|
||||||
self.assertIn(zelle(2, 0) + b"\x1f2", oben)
|
|
||||||
self.assertIn(zelle(0, 9) + b"\x1f+", unten)
|
self.assertIn(zelle(0, 9) + b"\x1f+", unten)
|
||||||
|
|
||||||
def test_ohne_clear_kein_fill_aber_aufgefuellt(self):
|
def test_ohne_clear_kein_fill_aber_aufgefuellt(self):
|
||||||
"""Standard (clear=False): kein Vollbild-Loeschen, Zeilen auf volle Breite."""
|
"""Standard (clear=False): kein Vollbild-Loeschen, Zeilen auf volle Breite."""
|
||||||
from test_display import zeile, zentriert
|
from test_display import zeile, aufbereitet
|
||||||
t = FakeTransport(echo=False)
|
t = FakeTransport(echo=False)
|
||||||
Display(t, response=True).show_lines("12:34:56", clear=False)
|
Display(t, response=True).show_lines("12:34:56", clear=False)
|
||||||
self.assertNotIn(b"\x1bF0", t.frames[0])
|
self.assertNotIn(b"\x1bF0", t.frames[0])
|
||||||
# auf CHARS_PER_LINE mit Leerzeichen aufgefuellt (mittig, siehe CENTER_LINES)
|
# auf CHARS_PER_LINE mit Leerzeichen aufgefuellt (mittig, siehe CENTER_LINES)
|
||||||
self.assertEqual(zeile(t.frames, 0), zentriert("12:34:56"))
|
self.assertEqual(zeile(t.frames, 0), aufbereitet("12:34:56"))
|
||||||
|
|
||||||
def test_gradzeichen_via_zeichensatz(self):
|
def test_gradzeichen_via_zeichensatz(self):
|
||||||
"""Gradzeichen wird durch kurzen Wechsel auf Zeichensatz 1 erzeugt."""
|
"""Gradzeichen wird durch kurzen Wechsel auf Zeichensatz 1 erzeugt."""
|
||||||
|
|||||||
+2
-1
@@ -398,7 +398,8 @@ class TestSchleifenanbindung(MqttTest):
|
|||||||
self.client.fail_check = True
|
self.client.fail_check = True
|
||||||
gezeigt = self._loop_einmal(self.bridge)
|
gezeigt = self._loop_einmal(self.bridge)
|
||||||
self.assertIsNotNone(gezeigt) # Anzeige wurde trotzdem bedient
|
self.assertIsNotNone(gezeigt) # Anzeige wurde trotzdem bedient
|
||||||
self.assertEqual(gezeigt[0], "18h36m56s")
|
# Bei Niedrigpraezision (config-Vorgabe) die beschriftete Zehntelform.
|
||||||
|
self.assertEqual(gezeigt[0], "RA 18h36.9m")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
Reference in New Issue
Block a user