"""Rektaszension und Deklination einlesen und fuer die Anzeige formatieren. Die Eingabe ist bewusst tolerant, damit man beim Testen nicht auf ein Format festgelegt ist: 12h34m56s | 12:34:56 | "12 34 56" | 12.5824 (Stunden dezimal) Bei der Deklination entsprechend mit Vorzeichen und Grad/Bogenminuten. Ausgegeben wird die breiteste Schreibweise, die noch in die Zeile passt -- die tatsaechliche Zeichenbreite der Anzeige kennen wir erst nach dem Livetest. """ import config _SEPARATORS = "hHmMsSdD°'\"::," def _split(value): """Zahlenfelder aus einer Koordinatenangabe holen, Trennzeichen egal.""" out, cur = [], "" for ch in str(value).strip(): if ch.isdigit() or ch == ".": cur += ch elif ch in "+-" and not cur and not out: cur += ch else: if cur: out.append(cur) cur = "" if cur: out.append(cur) if not out: raise ValueError("keine Zahlen in %r gefunden" % (value,)) return out def _to_sexagesimal(value, unit_max, name): """Eingabe in (Vorzeichen, Ganzteil, Minuten, Sekunden) zerlegen.""" fields = _split(value) negative = fields[0].startswith("-") fields[0] = fields[0].lstrip("+-") if len(fields) == 1: # dezimal, z.B. 12.5824 total = float(fields[0]) * 3600.0 else: parts = [float(f) for f in fields[:3]] + [0.0] * (3 - len(fields[:3])) total = parts[0] * 3600.0 + parts[1] * 60.0 + parts[2] total = int(round(total)) big, rest = divmod(total, 3600) minutes, seconds = divmod(rest, 60) if big >= unit_max: raise ValueError("%s: %s ausserhalb des gueltigen Bereichs" % (name, value)) return negative, big, minutes, seconds def parse_ra(value): """Rektaszension -> (Stunden, Minuten, Sekunden).""" _, h, m, s = _to_sexagesimal(value, 24, "Rektaszension") return h, m, s def parse_dec(value): """Deklination -> (Vorzeichen als '+'/'-', Grad, Bogenminuten, Bogensekunden).""" neg, d, m, s = _to_sexagesimal(value, 91, "Deklination") if d > 90 or (d == 90 and (m or s)): raise ValueError("Deklination %s liegt ausserhalb +/-90 Grad" % (value,)) return ("-" if neg else "+"), d, m, s def _fit(candidates, width): """Erste Schreibweise nehmen, die in die Zeile passt; sonst die kuerzeste.""" for text in candidates: if len(text) <= width: return text return candidates[-1][:width] def _to_tenths(h, m, s): """(h, m, s) in (Stunden, Minuten, Zehntelminuten) umrechnen. Ganzzahlig gerechnet, damit es unter MicroPython keine Float-Ueberraschungen gibt. Der Uebertrag ist wichtig: 18:36:57 sind 36,95 Minuten und werden zu 18:37.0, nicht zu 18:36.10. """ 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 high = config.HIGH_PRECISION if high_precision is None else high_precision h, m, s = parse_ra(value) if high: candidates = [ "%02dh%02dm%02ds" % (h, m, s), "%02d:%02d:%02d" % (h, m, s), "%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): width = config.CHARS_PER_LINE if width is None else width high = config.HIGH_PRECISION if high_precision is None else high_precision sign, d, m, s = parse_dec(value) deg = config.DEGREE_MARKER if high: candidates = [ "%s%02d%s%02d'%02d\"" % (sign, d, deg, m, s), # 10: mit Bogensekundenzeichen "%s%02d%s%02d'%02d" % (sign, d, deg, m, s), # 9: Gradzeichen, ohne " am Ende "%s%02d:%02d:%02d" % (sign, d, m, s), # 9: Doppelpunkte "%s%02d%s%02d'" % (sign, d, deg, m), # 7: ohne Bogensekunden "%s%02d:%02d" % (sign, d, m), # 6 ] else: # Niedrigpraezision: ohne Sekunden (die Montierung liefert nur sDD MM). candidates = [ "%s%02d%s%02d'" % (sign, d, deg, m), # 7: -00°55' "%s%02d:%02d" % (sign, d, m), # 6: -00:55 ] return _fit(candidates, width)