Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 27 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# Dwarf II TCP server

**NOTE: This is a work in progress. It's been cloudy, so I haven't tested this code yet.**

This software lets you send goto commands to the Dwarf II using Stellarium's Telescope Control.

Requires:
Expand All @@ -22,15 +20,28 @@ This software setups up a TCP server. When you select an object Stellarium, and

3. Install libraries

On Linux, Mac or WSL for Windows :

```
pip install -r requirements.txt
```

On Windows :

you can install Python 3.11 (search on Microsoft Store)
open a command prompt on the directory and do :

```
python3 -m pip install -r requirements.txt
```

4. Copy `config.sample.py`, and rename it to `config.py`.

The `HOST` and `PORT` should be the same as those used in Stellarium Telescope Plugin.

Fill in your `LATITUDE` and `LONGITUDE`.
Fill in your `LATITUDE` and `LONGITUDE` (LONGITUDE is negative west of Greenwich)

Add also your `TIMEZONE`, it's need when you are using the Stellarium Mobile App

If you are using the Dwarf wifi, the `DWARF_IP` is 192.168.88.1. If you are using Dwarf II in STA mode, then get the IP for your Dwarf II.

Expand All @@ -43,4 +54,16 @@ If you are using the Dwarf wifi, the `DWARF_IP` is 192.168.88.1. If you are usin
python server.py
```

6. Select an object in Stellarium, and issue a slew command. The Dwarf II should move to that object.
On Windows :

```
python3 server.py
```

6. Start the dwarf II and use the mobile app and go to Astro Mode and do the calibration

7. Select an object in Stellarium, and issue a slew command. (shortcut for Windows is Alt + number, See Stellarium documentation, Command + number for Mac). The Dwarf II should move to that object.

8. You can also use the Stellarium Plus Mobile App with the remote Telescopte function, Select an object in Stellarium, and issue a goto command with the remote control. The Dwarf II should move to that object.

**NOTE: If the server disconnects from Stellarium, You can try reconnect on the cmd window by typing Y otherwise the program will stop **
Binary file added TUTORIAL PLUGIN STELLARIUM MOBILE.pdf
Binary file not shown.
3 changes: 3 additions & 0 deletions config.sample.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,7 @@
LATITUDE = 0
LONGITUDE = 0
DWARF_IP = "192.168.88.1"
TIME_ZONE="Europe/Paris"
VERSION_API = 2 # 1 or 2
CLIENT_ID = "0000DAF2-0000-1000-8000-00805F9B3500"
DEBUG = True
12 changes: 7 additions & 5 deletions lib/dwarfII_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,21 @@ def ws_uri(dwarf_ip):
return f"ws://{dwarf_ip}:9900"


def now():
return str(datetime.datetime.now()).split(".")[0]
def nowUTC():
return str(datetime.datetime.utcnow()).split(".")[0]


def nowLocalFileName():
return datetime.datetime.today().strftime('%Y%m%d%H%M%S')

def goto_target(latitude, longitude, rightAscension, declination, planet=None):
options = {
"interface": startGotoCmd,
"camId": telephotoCamera,
"lon": longitude,
"lat": latitude,
"date": now(),
"path": "DWARF_GOTO_timestamp",
"date": nowUTC(),
"path": "DWARF_GOTO_" + nowLocalFileName(),
}

if planet is not None:
Expand All @@ -31,7 +34,6 @@ def goto_target(latitude, longitude, rightAscension, declination, planet=None):

return options


def cameraWorkingState():
return {
"interface": statusWorkingStateTelephotoCmd,
Expand Down
127 changes: 119 additions & 8 deletions lib/dwarf_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,138 @@
import lib.dwarfII_api as d2
import config
import lib.my_logger as log
import proto.astro_pb2 as astro
import proto.system_pb2 as system
import time
import math

def perform_gotoV1(ra, dec, result_queue):
# Inverse LONGITUDE for DwarfII !!!!!!!
payload = d2.goto_target(config.LATITUDE, - config.LONGITUDE, ra, dec)

def perform_goto(ra, dec):
payload = d2.goto_target(config.LATITUDE, config.LONGITUDE, ra, dec)
response = connect_socketV1(payload)

response = connect_socket(payload)
if response:

if response["interface"] == payload["interface"]:
if response["interface"] == payload["interface"]:
if response["code"] == 0:
log.debug("Goto success")
result_queue.put("ok")
return "ok"
elif response["code"] == -45:
log.error("Target below horizon")
elif response["code"] == -46:
log.error("Goto or correction bump limit")
else:
log.error("Error:", response)
else:
else:
log.error("Dwarf API:", response)
else:
log.error("Dwarf API:", "Dwarf II not connected")

result_queue.put(False)

# Add an number to target Stellarium to have a different target name each time
global id_target
id_target = 0

global do_time
do_time = False

global do_timezone
do_timezone = False


def perform_goto(ra, dec, result_queue):

global do_time
global do_timezone

if (not do_time):
perform_time()
do_time = True

if (not do_time):
perform_timezone()
do_timezone = True

# Add an number to target Stellarium to have a different target name each time
global id_target
id_target += 1

if (id_target >= 10):
id_target = 1

# GOTO
module_id = 3 # MODULE_ASTRO
type_id = 0; #REQUEST

ReqGotoDSO_message = astro.ReqGotoDSO()
ReqGotoDSO_message.ra = ra
ReqGotoDSO_message.dec = dec
ReqGotoDSO_message.target_name = f"Stellarium_{id_target}"

command = 11002 #CMD_ASTRO_START_GOTO_DSO
response = connect_socket(ReqGotoDSO_message, command, type_id, module_id)

if response is not False:

if response == "ok":
log.debug("Goto success")
result_queue.put("ok")
return "ok"
else:
log.error("Error:", response)
else:
log.error("Dwarf API:", "Dwarf II not connected")

result_queue.put(False)

def perform_time():

# SET TIME
module_id = 4 # MODULE_SYSTEM
type_id = 0; #REQUEST

ReqSetTime_message = system.ReqSetTime()
ReqSetTime_message.timestamp = math.floor(time.time())

command = 13000 #CMD_SYSTEM_SET_TIME
response = connect_socket(ReqSetTime_message, command, type_id, module_id)

if response is not False:

if response == 0:
log.debug("Set Time success")
return True
else:
log.error("Error:", response)
else:
log.error("Dwarf API:", "Dwarf II not connected")

return False

def perform_timezone():

# SET TIMEZONE
module_id = 4 # MODULE_SYSTEM
type_id = 0; #REQUEST

ReqSetTimezone_message = system.ReqSetTimezone()
ReqSetTimezone_message.timezone = read_timezone()

command = 13001 #CMD_SYSTEM_SET_TIME_ZONE
response = connect_socket(ReqSetTimezone_message, command, type_id, module_id)

if response is not False:

if response == 0:
log.debug("Set TimeZone success")
return True
else:
log.error("Error:", response)
else:
log.error("Dwarf API:", "Dwarf II not connected")

return False

def perform_camera_status():
payload = d2.cameraWorkingState()
connect_socket(payload)
53 changes: 53 additions & 0 deletions lib/ftp_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import paramiko

def download_file_via_ssh(ssh_host, ssh_port, ssh_username, ssh_password, remote_file_path, local_file_path):
# Connect to the SSH server
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(ssh_host, port=ssh_port, username=ssh_username, password=ssh_password)

# Use SFTP to download the file
sftp = ssh.open_sftp()
sftp.get(remote_file_path, local_file_path)
sftp.close()
ssh.close()

def extract_last_matching_line(file_path, search_string):
last_matching_line = ""
# Open the local file to read its content
with open(file_path, 'r', encoding='utf-8', errors='replace') as file:
# Iterate through each line in the file
for line in file:
# If the search string is found in the line, update the last matching line
if search_string in line:
last_matching_line = line.strip()
return last_matching_line

def extract_desired_value(line, search_string):
# Find the starting index of the desired value in the line
start_index = line.find(search_string) + len(search_string)
# Return the extracted value after the search string
return line[start_index:].strip()

def test_ssh_connection(ssh_host, ssh_port, ssh_username, ssh_password):
try:
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(ssh_host, port=ssh_port, username=ssh_username, password=ssh_password)
ssh.close()
return True
except Exception as e:
print(f"Failed to connect to SSH server: {e}")
return False

def update_config_file(file_path, new_client_id):
with open(file_path, 'r') as file:
lines = file.readlines()

with open(file_path, 'w') as file:
for line in lines:
if line.startswith('CLIENT_ID'):
file.write(f'CLIENT_ID = "{new_client_id}"\n')
else:
file.write(line)

3 changes: 3 additions & 0 deletions lib/my_logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ def debug(*messages):
for message in messages:
print(message)

def info(*messages):
for message in messages:
print(message)

def error(*messages):
for message in messages:
Expand Down
54 changes: 41 additions & 13 deletions lib/stellarium_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

import lib.my_logger as my_logger


def process_ra_dec_int(ra_int, dec_int):
h = ra_int
d = math.floor(0.5 + dec_int * (360 * 3600 * 1000 / 4294967296.0))
Expand Down Expand Up @@ -45,6 +44,21 @@ def process_ra_dec_int(ra_int, dec_int):
},
}

def process_ra_dec(ra_int, dec_int):
data = process_ra_dec_int(ra_int, dec_int)
ra = data["ra"]
dec = data["dec"]
ra_number = ra['hour'] + ra['minute'] / 60 + ra['second'] / 3600
if dec_int >= 0:
dec_number = dec['degree'] + dec['minute'] / 60 + dec['second'] / 3600
else:
dec_number = -(dec['degree'] + dec['minute'] / 60 + dec['second'] / 3600)

return {
"ra_number": ra_number,
"dec_number": dec_number
}


def format_ra_dec(ra_int, dec_int):
data = process_ra_dec_int(ra_int, dec_int)
Expand All @@ -59,20 +73,34 @@ def format_ra_dec(ra_int, dec_int):


def process_stellarium_data(raw_data):
data = struct.unpack("3iIi", raw_data)
my_logger.debug("data from Stellarium >>", data)
my_logger.debug("data from Stellarium >>", raw_data)
try:
data = struct.unpack("3iIi", raw_data)
my_logger.debug("data from Stellarium >>", data)

ra_int = data[3]
dec_int = data[4]
formatted_data = format_ra_dec(ra_int, dec_int)
my_logger.debug("ra: " + formatted_data["ra"] + ", dec: " + formatted_data["dec"])
ra_int = data[3]
dec_int = data[4]
formatted_data = format_ra_dec(ra_int, dec_int)
my_logger.debug("ra: " + formatted_data["ra"] + ", dec: " + formatted_data["dec"])

return {
"ra_int": ra_int,
"ra": formatted_data["ra"],
"dec_int": dec_int,
"dec": formatted_data["dec"],
}
data_number = process_ra_dec(ra_int, dec_int)
ra_number = data_number["ra_number"]
dec_number = data_number["dec_number"]
my_logger.debug("ra: " + f"{ra_number}" + ", dec: " + f"{dec_number}")

return {
"ra_int": ra_int,
"ra": formatted_data["ra"],
"ra_number": ra_number,
"dec_int": dec_int,
"dec": formatted_data["dec"],
"dec_number": dec_number,
}

except struct.error:
# If a struct.error occurs, the data is not in the expected structure format
my_logger.debug("data from Stellarium Mobile >>", raw_data)
return False


def update_stellarium(ra_int, dec_int, connection):
Expand Down
Loading