Skip to content

Commit

Permalink
Merge pull request #5 from synfinatic/lx200
Browse files Browse the repository at this point in the history
LX200 protocol support
  • Loading branch information
synfinatic authored Dec 30, 2020
2 parents 6ad22d1 + f25d50f commit 598faed
Show file tree
Hide file tree
Showing 6 changed files with 578 additions and 32 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Added:
- Refactor code to make it easier for other projects to use
- Add support for additional Nexstar commands
- More unit tests
- Add LX200 support

Fixed:
- Bug with enum for Alpaca Axis control
Expand Down
38 changes: 28 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,12 @@ https://ascom-standards.org) but that only allows IPC via Windows COM
which doesn't even support talking to programs on other computers (or my iPad).

But then in 2019, ASCOM introduced [Alpaca](
https://ascom-standards.org/Developer/Alpaca.htm) which basically exposes
the COM API via REST. Of course, SkySafari doesn't support this (yet) so
I decided to write a service which emulates a telescope SkySafari supports
and talks to Alpaca. The result is that SkySafari can now control my Celestron
Evolution mount, or any mount that supports the ASCOM standard.
https://ascom-standards.org/Developer/Alpaca.htm) which via
[Alpaca Remote Server](https://github.com/ASCOMInitiative/ASCOMRemote/releases)
exposes the ASCOM API via REST. Of course, SkySafari doesn't support this (yet)
so I decided to write a service which emulates a telescope SkySafari supports
and talks to Alpaca Remote Server. The result is that SkySafari can now control
my Celestron Evolution mount, or any mount that supports the ASCOM standard.

## Usage

Expand All @@ -37,21 +38,36 @@ Celestron Evolution <-> CWPI <-> ASCOM <-> Alpaca Server <-> AlpacaScope <-> Sky

Basically, just download the binary for your system (easist to run on the same Windows
box as ASCOM & the Alpaca Remote Server) and run it. By default it will try to connect
to Alpaca on the local host (127.0.0.0) on port 11111 and listen on port 4030.
to Alpaca Remote Server on the local host (127.0.0.0) on port 11111 and listen on port 4030.

Configure SkySafari or other remote control software to connect to AlpacaScope on port
4030 using the Celestron Nexstar (I use the Nexstar/Advanced GT) protocol.

## FAQ

#### What do I need at minimum?

1. A telescope connected to a Windows machine
2. ASCOM installed an configured for your mount
3. Alpaca Remote Server installed, configured & running
4. AlpacaScope installed and running
5. Some kind astronomy software which talks the LX200 or Celestron Nexstar protocols
(SkySafari, etc)

#### Does this support SkySafari on Mac, iPad, Windows, Android, etc?
Yes, this supports all versions of SkySafari which allow for controlling telescopes.
Typically this is SkySafari Plus and Pro.

#### What about other astronomy software?
Yep, anything that can do Celestron Nexstar protocol over TCP/IP should work.
That said, I haven't yet implimented the complete Nexstar protocol so there
may be issues. Please open a bug report if you find any issues!
Yep, anything that can do Celestron Nexstar or LX200 protocols over TCP/IP
should work.

#### What features work?

* Manual slewing
* Controlling slew speed
* Goto a target
* Align on target (maybe?)

#### Does AlpacaScope support [INDI](https://www.indilib.org)?
No it doesn't. There's probably no reason it can't support INDI, but CWPI
Expand All @@ -63,4 +79,6 @@ to be able to talk to the Alpaca Server running on the same computer as the
ASCOM driver connected to your telescope mount.

#### My software only supports the Meade LX200 protocol. Can I use that?
Not yet. I plan on adding that in a future version.
Yes! Slewing and GoTo works. Be sure to specify `--mode lx200`.
I believe align/sync should work but it doesn't seem to work with my mount/CWPI.
Not sure if it's a bug on my end or a limitation with CWPI?
97 changes: 85 additions & 12 deletions alpaca/telescope.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,20 @@ import (
"time"
)

type AlignmentMode int
type AlignmentMode int32

const (
algAltAz int = 0
algPolar int = 1
algGermanPolar int = 2
AlignmentAltAz AlignmentMode = iota
AlignmentPolar
AlignmentGermanPolar
)

type AxisType int

const (
AxisAzmRa = 0
AxisAltDec = 1
AxisTertiary = 2
AxisAzmRa AxisType = iota
AxisAltDec
AxisTertiary
)

type Telescope struct {
Expand Down Expand Up @@ -54,8 +54,9 @@ func (t *Telescope) GetSupportedActions() ([]string, error) {
return t.alpaca.GetSupportedActions("telescope", t.Id)
}

func (t *Telescope) GetAlignmentMode() (int32, error) {
return t.alpaca.GetInt32("telescope", t.Id, "alignmentmode")
func (t *Telescope) GetAlignmentMode() (AlignmentMode, error) {
mode, err := t.alpaca.GetInt32("telescope", t.Id, "alignmentmode")
return AlignmentMode(mode), err
}

func (t *Telescope) GetAltitude() (float64, error) {
Expand Down Expand Up @@ -111,9 +112,43 @@ func (t *Telescope) GetSiteLongitude() (float64, error) {
return t.alpaca.GetFloat64("telescope", t.Id, "sitelongitude")
}

// Returns the min & max rate (deg/sec) that the given axis can move
func (t *Telescope) GetAxisRates(axis AxisType) ([]uint32, error) {
return t.alpaca.GetListUint32("telescope", t.Id, "axisrates")
func (t *Telescope) GetTargetDeclination() (float64, error) {
return t.alpaca.GetFloat64("telescope", t.Id, "targetdeclination")
}

func (t *Telescope) GetTargetAltitude() (float64, error) {
return t.alpaca.GetFloat64("telescope", t.Id, "targetrightascension")
}

func (t *Telescope) GetUTCDate() (time.Time, error) {
iso8601, err := t.alpaca.GetString("telescope", t.Id, "utcdate")
if err != nil {
return time.Unix(0, 0), err
}
return time.Parse("2006-01-02T15:04:05Z0700", iso8601)
}

type mapAxisRates struct {
Value []map[string]float64 `json:"Value"`
ClientTransactionID int32 `json:"ClientTransactionID"`
ServerTransactionID int32 `json:"ServerTransactionID"`
ErrorNumber int32 `json:"ErrorNumber"`
ErrorMessage string `json:"ErrorMessage"`
}

// Returns the `Maximum` & `Minimum` rate (deg/sec) that the given axis can move
func (t *Telescope) GetAxisRates(axis AxisType) (map[string]float64, error) {
url := t.alpaca.url("telescope", t.Id, "axisrates")
querystr := fmt.Sprintf("Axis=%d&%s", axis, t.alpaca.getQueryString())
resp, err := t.alpaca.client.R().
SetResult(&mapAxisRates{}).
SetQueryString(querystr).
Get(url)
if err != nil {
return map[string]float64{}, err
}
result := (resp.Result().(*mapAxisRates))
return result.Value[0], nil
}

type putMoveAxis struct {
Expand Down Expand Up @@ -187,6 +222,26 @@ func (t *Telescope) PutSiteLongitude(long float64) error {
return err
}

func (t *Telescope) PutTargetRightAscension(long float64) error {
var form map[string]string = map[string]string{
"TargetRightAscension": fmt.Sprintf("%g", long),
"ClientID": fmt.Sprintf("%d", t.alpaca.ClientId),
"ClientTransactionID": fmt.Sprintf("%d", t.alpaca.GetNextTransactionId()),
}
err := t.alpaca.Put("telescope", t.Id, "targetrightascension", form)
return err
}

func (t *Telescope) PutTargetDeclination(long float64) error {
var form map[string]string = map[string]string{
"TargetDeclination": fmt.Sprintf("%g", long),
"ClientID": fmt.Sprintf("%d", t.alpaca.ClientId),
"ClientTransactionID": fmt.Sprintf("%d", t.alpaca.GetNextTransactionId()),
}
err := t.alpaca.Put("telescope", t.Id, "targetdeclination", form)
return err
}

func (t *Telescope) PutUTCDate(date time.Time) error {
var form map[string]string = map[string]string{
"UTCDate": fmt.Sprintf("%s", date.Format(time.RFC3339)),
Expand All @@ -206,6 +261,24 @@ func (t *Telescope) PutAbortSlew() error {
return err
}

func (t *Telescope) PutSlewToTargetAsync() error {
var form map[string]string = map[string]string{
"ClientID": fmt.Sprintf("%d", t.alpaca.ClientId),
"ClientTransactionID": fmt.Sprintf("%d", t.alpaca.GetNextTransactionId()),
}
err := t.alpaca.Put("telescope", t.Id, "slewtotargetasync", form)
return err
}

func (t *Telescope) PutSyncToTarget() error {
var form map[string]string = map[string]string{
"ClientID": fmt.Sprintf("%d", t.alpaca.ClientId),
"ClientTransactionID": fmt.Sprintf("%d", t.alpaca.GetNextTransactionId()),
}
err := t.alpaca.Put("telescope", t.Id, "synctotarget", form)
return err
}

/*
* Helper functions
*/
Expand Down
Loading

0 comments on commit 598faed

Please sign in to comment.