Die grundsätzliche Arbeitsweise zwischen Linux und dem Teleskop (Steuerung) kann man auch ohne Elektronik (& Teleskop) studieren. Die drei großen Clients (KStart, Skychart und Stellarium) können mit der Indi-Schnittstelle kommunizieren und virtuelle Teleskopmontierungen einbinden. Diese Teleskopsimulatoren sind technisch gesehen INDI-C-Treiber. Ich erzeuge hier z.B. mit Java eine virtuelle serielle Schnittstelle und benutze „täusche“ eine rudimentäre LX200 Montierung vor, die mit Stellarium verwendet werden kann.
Um mit Java auf die serielle Schnittstelle zugreifen zu können, verwenden wir die jSSC (Java Simple Serial Connector) API. Sie besteht aus nur einer jar-Datei und wird auch von der Arduino-IDE selbst verwendet. In Eclipse wird sie über Eigenschaften (Java Build Path - Add External JAR's…) dem Projekt hinzugefügt. https://code.google.com/archive/p/java-simple-serial-connector/ oder serial.zip
Die folgenden beiden befehle erfordern Rootrechte. Außerdem müssen auf diese Weise angelegte Schnittstellen muss häufig neu eingerichtet werden. Nicht vergessen, dieses Projekt soll zum eigenen Experimentieren anregen und lediglich die grundlegende Kommunikation aufzeigen.
Die virtuelle Schnittstelle wird mit dem folgenden Befehl gesetzt
socat -d -d PTY,link=/dev/ttyUSB60 PTY,link=/dev/ttyUSB61
Anschließend werden zum Testen alle Rechte vergeben
# chmod 777 /dev/ttyUSB6*
Überprüfen Sie (Konsole ls-Befehl) ob alles richtig gemacht wurde
Ein erste Test erfolgt mit einer User-Konsole, indem folgender Befehl an das Gerät gesendet wird, zuvor muss das Programm (z.B. über Eclipse gestartet werden).
echo ":GD#" > /dev/ttyUSB61
Auf der Konsole (Eclipse) sollten jetzt die Anzahl der gelesenen Bytes angezeigt werden. Damit funktioniert die Kommunikation über die virtuelle serielle Schnittstelle.
Der zweite Test ist spannender, da wir mit Stellarium kommunizieren.
Das Protokoll kann hier eingesehen werden LX200 Seriell-Protokoll
Das Javaprogramm implementiert nur wenige Kommandos des Protokolls. Nachdem Stellarium sich mit dem „Teleskop“ (Javaprogramm) verbunden hat, sendet es permanent Anfragen um die Rektaszension (#:GR#) und die Deklination (#:GE#) des Telekops zu erhalten.
Wenn mit Stellarium geschwenkt werden soll, d.h. ein bestimmtes Objekt angefahren werden soll, werden drei Kommandos geschickt. Die ersten beiden Kommandos übermitteln die Zielkoordinaten, während das dritte Kommando (:MS#) eine Bestätigung erwartet um das Teleskop zu schwenken. Diese Befehle sind im LX200 Seriell-Protokoll aufgelistet. Damit wir bei Stellarium eine animierte Bewegung sehen, wurde die Methode simulateMove() hinzugefügt, die lediglich die RA-Achse in zehn Schritten von den aktuellen Koordinaten currentRA zu den Zielkoordinaten targetRA bewegt.
/** LX200 Protocol * @author torsten.roehl@fsg-preetz.org * simple demo to communicate with a indi-client! */ import java.util.Timer; import java.util.TimerTask; import jssc.SerialPort; import jssc.SerialPortException; public class SimulatorTeleskop { static String currentRA = "02:31:50#"; static String currentDE = "+89*15#"; static String targetRA = currentRA; static String targetDE = currentDE; static String dev = "/dev/ttyUSB60"; public enum Protocol // some serial lx200 commands { CMD_GR, CMD_GD, CMD_QS, CMD_SD, CMD_MS, CMD_UNKNOWN } public static void main(String[] args) throws InterruptedException { SerialPort serialPort = new SerialPort(dev); try { serialPort.openPort(); serialPort.setParams(9600, 8, 1, 0); System.out.println("... initializing the serial port"); Thread.sleep(4000); // necessary, otherwise serial will fail!!! } catch (SerialPortException e1) { e1.printStackTrace(); } try { while (true) { String msg = decode(serialPort); Protocol cmd = protocol(msg); switch (cmd) { case CMD_GR: writeMessage(serialPort, currentRA); break; case CMD_GD: writeMessage(serialPort, currentDE); break; case CMD_QS: targetRA = msg.substring(msg.indexOf(' ')).trim(); writeMessage(serialPort, "1"); break; case CMD_SD: currentDE = msg.substring(msg.indexOf(' ')).trim(); targetDE = currentDE; writeMessage(serialPort, "1"); break; case CMD_MS: writeMessage(serialPort, "0"); simulateMove(); break; case CMD_UNKNOWN: if (!msg.startsWith("zero")) System.out.println("...received unknown serial command: " + msg); break; } Thread.sleep(100); } } catch (Exception e) { e.printStackTrace(); } try { serialPort.closePort(); } catch (SerialPortException e) { e.printStackTrace(); } } private static Protocol protocol(String msg) { if (msg.startsWith("#:GR#")) return Protocol.CMD_GR; if (msg.startsWith("#:GD#")) return Protocol.CMD_GD; if (msg.startsWith("#:Q#:S")) return Protocol.CMD_QS; if (msg.startsWith(":Sd ")) return Protocol.CMD_SD; if (msg.startsWith(":MS#")) return Protocol.CMD_MS; return Protocol.CMD_UNKNOWN; } static String decode(SerialPort serialPort) { try { // step 1: read bytes from port byte[] buffer = serialPort.readBytes(); // step 2: verify if (buffer == null) return "zero buffer"; int numRead = buffer.length; if (buffer.length <= 0) return "zero length"; // step 3: convert buffer to string String res = ""; for (int i = 0; i < numRead; i++) { byte b = buffer[i]; // fixme: problem with Star * char c = (char) b; if (b == -33) c = '*'; res += c; } return res; } catch (SerialPortException e) { e.printStackTrace(); } return "should not happen"; } static void writeMessage(SerialPort p, String msg) { try { byte[] buffer = msg.getBytes(); p.writeBytes(buffer); } catch (SerialPortException e) { e.printStackTrace(); } } /** * helper method convert a ra-string to a number (for arithmetic use) */ static int string2int(String str) { String tmp = str.substring(0, str.length() - 1); String lsTmp[] = tmp.split(":"); int res = 3600 * Integer.valueOf(lsTmp[0]) + 60 * Integer.valueOf(lsTmp[1]) + Integer.valueOf(lsTmp[2]); return res; } /** * helper method to convert a number into ra-string */ static String number2string(double number) { int h = (int) (number - (number % 3600)) / 3600; int rest = (int) (number - h * 3600); int m = (int) (rest - (rest % 60)) / 60; rest = rest - m * 60; int s = rest; String strH = String.format("%02d", h); String strM = String.format("%02d", m); String strS = String.format("%02d", s); return strH + ":" + strM + ":" + strS + "#"; } /** * dummy method to simulate kind of telescope motion - using ten steps only * in ra-axis */ static void simulateMove() { TimerTask repeatedTask = new TimerTask() { int run = 0; double stepwidth = 0; int iTo = 0; int iFrom = 0; public void run() { System.out.println(run + " ...move simulation "); // step 0: calculate stepwidth once if (run == 0) { iFrom = string2int(currentRA); iTo = string2int(targetRA); stepwidth = (iTo - iFrom) / 10.0; } // step 1: adjust currentRA with stepwidth if (run > 0) { currentRA = number2string(iFrom + run * stepwidth); } // step 2: finish motion run++; if (run > 10) { currentRA = targetRA; cancel(); } } }; Timer timer = new Timer("Timer"); timer.scheduleAtFixedRate(repeatedTask, 0, 250); } }
Das dieses einfache Programm (die nur wenige Kommandos des Protokolls implementiert) funktioniert, verdanken wir den saloppen Umgang von Stellarium (getestet mit 0.18.0) mit dem Protokoll. Eine verfünftige Initialisierung (Handschake) ist beispielsweise nicht erforderlich.