Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add -cpe flag to download CPE.BIN data #32

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@

# Output files
/EPO.BIN
/CPE.BIN
/raw
12 changes: 8 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,20 @@

## About

Retrieve EPO.BIN GPS data from Garmin's servers on any platform that you
can compile a Go binary for, rather than only those that support Garmin
Retrieve EPO.BIN and CPE.BIN GPS data from Garmin's servers on any platform that
you can compile a Go binary for, rather than only those that support Garmin
Connect. Since Garmin's software doesn't run natively on Linux, it's
useful there.

EPO.BIN is GPS Extended Prediction Orbit (EPO) satellite data valid for
7 days, used to help speed up GPS locking on Garmin devices that support
this.

For Forerunner devices, the file should be copied to
`GARMIN/REMOTESW/EPO.BIN` on the watch.
CPE.BIN is similar to EPO.BIN and used for some Sony GPS chipsets.

For Forerunner and Vivoactive devices, the file should be copied to the following folder on the watch.
- EPO: `GARMIN/REMOTESW/EPO.BIN`
- CPE: `Primary/GARMIN/RemoteSW/CPE.BIN`

## Alternatives

Expand All @@ -30,6 +33,7 @@ This [repository](https://github.com/StevenMaude/epo-bin) hosts a daily
download of `EPO.BIN`.

## Usage
Use the `-cpe` flag to download CPE data instead of EPO

### Download

Expand Down
81 changes: 73 additions & 8 deletions armstrong.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,11 @@ import (
"log"
"net/http"
"time"
"flag"
)

// retrieveData makes a HTTP request to get Garmin EPO data and returns the body as []byte if successful.
func retrieveData() ([]byte, error) {
func retrieveDataEPO() ([]byte, error) {
url := "https://omt.garmin.com/Rce/ProtobufApi/EphemerisService/GetEphemerisData"
// Data from https://www.kluenter.de/garmin-ephemeris-files-and-linux/
data := []byte("\n-\n\aexpress\u0012\u0005de_DE\u001A\aWindows\"" +
Expand All @@ -48,8 +49,8 @@ func retrieveData() ([]byte, error) {
return body, nil
}

// checkDataLength checks the EPO data length; if not as expected, returns an error.
func checkDataLength(data []byte) error {
// checkDataLengthEPO checks the EPO data length; if not as expected, returns an error.
func checkDataLengthEPO(data []byte) error {
dataLength := len(data)
// Each EPO data set is 2307 bytes long, with the first three bytes to be removed.
if dataLength != 28*2307 {
Expand All @@ -69,16 +70,16 @@ func cleanEPO(rawEPOData []byte) []byte {
return outData
}

// main retrieves EPO data, checks it, cleans it and writes it to disk.
func main() {
fmt.Println("Retrieving data from Garmin's servers...")
rawEPOData, err := retrieveData()
// Retrieves EPO data, checks it, cleans it and writes it to disk.
func downloadFileEPO() {
fmt.Println("Retrieving EPO data from Garmin's servers...")
rawEPOData, err := retrieveDataEPO()
if err != nil {
log.Fatal(err)
}

fmt.Println("Processing EPO.BIN...")
err = checkDataLength(rawEPOData)
err = checkDataLengthEPO(rawEPOData)
if err != nil {
log.Fatal(err)
}
Expand All @@ -91,3 +92,67 @@ func main() {
}
fmt.Println("Done! EPO.BIN saved.")
}


// retrieveData makes a HTTP request to get Garmin EPO data and returns the body as []byte if successful.
func retrieveDataCPE() ([]byte, error) {
url := "https://api.gcs.garmin.com/ephemeris/cpe/sony?coverage=WEEKS_1"

c := &http.Client{
Timeout: 20 * time.Second,
}
resp, err := c.Get(url)
if err != nil {
return nil, err
}
defer resp.Body.Close()

body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
return body, nil
}

// checkDataLengthCPE errors out of CPE data is empty
// - CPE internal data format is not yet known
func checkDataLengthCPE(data []byte) error {
dataLength := len(data)
if dataLength == 0 {
return fmt.Errorf("CPE data has unexpected length of zero")
}
return nil
}

// Retrieves CPE data, checks it and writes it to disk.
func downloadFileCPE() {
fmt.Println("Retrieving CPE data from Garmin's servers...\n")
rawCPEData, err := retrieveDataCPE()
if err != nil {
log.Fatal(err)
}

err = checkDataLengthCPE(rawCPEData)
if err != nil {
log.Fatal(err)
}

err = ioutil.WriteFile("CPE.BIN", rawCPEData, 0644)
if err != nil {
log.Fatal(err)
}
fmt.Println("Done! CPE.BIN saved.\n")
}


func main() {
var modeCPE bool
flag.BoolVar(&modeCPE, "cpe", false, "Download CPE format ephemeris data (instead of EPO)")
flag.Parse()

if modeCPE == true {
downloadFileCPE()
} else {
downloadFileEPO()
}
}