summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorChris Schlaeger <chris@linux.com>2015-04-25 07:56:10 +0200
committerChris Schlaeger <chris@linux.com>2015-04-25 07:56:10 +0200
commit0f074edb6d211d8df2c7b40fd9895effd0ecd38f (patch)
tree803264e6d6d4ee1c3bb92fed6831bd29589575c0 /lib
parent6c81603a295667eee155bf379927b0de718c789c (diff)
downloadpostrunner-0f074edb6d211d8df2c7b40fd9895effd0ecd38f.zip
New: Download GPS cache data from Garmin server and store it on device
Diffstat (limited to 'lib')
-rw-r--r--lib/postrunner/EPO_Downloader.rb95
-rw-r--r--lib/postrunner/Main.rb80
2 files changed, 153 insertions, 22 deletions
diff --git a/lib/postrunner/EPO_Downloader.rb b/lib/postrunner/EPO_Downloader.rb
new file mode 100644
index 0000000..6121d20
--- /dev/null
+++ b/lib/postrunner/EPO_Downloader.rb
@@ -0,0 +1,95 @@
+#!/usr/bin/env ruby -w
+# encoding: UTF-8
+#
+# = EPO_Downloader.rb -- PostRunner - Manage the data from your Garmin sport devices.
+#
+# Copyright (c) 2015 by Chris Schlaeger <cs@taskjuggler.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of version 2 of the GNU General Public License as
+# published by the Free Software Foundation.
+#
+
+require 'uri'
+require 'net/http'
+
+module PostRunner
+
+ # This class can download the current set of ephemeris data for GPS
+ # satellites and store them in the EPO.BIN file format. Some Garmin devices
+ # pick up this file under GARMIN/GARMIN/REMOTESW/EPO.BIN.
+ class EPO_Downloader
+
+ @@URI = URI('http://omt.garmin.com/Rce/ProtobufApi/EphemerisService/GetEphemerisData')
+ # This is the payload of the POST request. It was taken from
+ # http://www.kluenter.de/garmin-ephemeris-files-and-linux/. It may contain
+ # a product ID or serial number.
+ @@POST_DATA = "\n-\n\aexpress\u0012\u0005de_DE\u001A\aWindows\"\u0012601 Service Pack 1\u0012\n\b\x8C\xB4\x93\xB8\u000E\u0012\u0000\u0018\u0000\u0018\u001C\"\u0000"
+ @@HEADER = {
+ 'Garmin-Client-Name' => 'CoreService',
+ 'Content-Type' => 'application/octet-stream',
+ 'Content-Length' => '63'
+ }
+
+ # Create an EPO_Downloader object.
+ def initialize
+ @http = Net::HTTP.new(@@URI.host, @@URI.port)
+ @request = Net::HTTP::Post.new(@@URI.path, initheader = @@HEADER)
+ @request.body = @@POST_DATA
+ end
+
+ # Download the current ephemeris data from the Garmin server and write it
+ # into the specified output file.
+ # @param output_file [String] The name of the output file. Usually this is
+ # 'EPO.BIN'.
+ def download(output_file)
+ return false unless (epo = get_epo_from_server)
+ return false unless (epo = fix(epo))
+ write_file(output_file, epo)
+ Log.info "GPS caching data has been downloaded from Garmin site."
+
+ true
+ end
+
+ private
+
+ def get_epo_from_server
+ res = @http.request(@request)
+ if res.code.to_i != 200
+ Log.error "GPS data download failed: #{res}"
+ return nil
+ end
+ res.body
+ end
+
+ # The downloaded data contains ephemeris data for 6 hour windows for 7
+ # days. Each window set is 2307 bytes long, but the first 3 bytes must
+ # be removed for the FR620 to understand it.
+ # https://forums.garmin.com/showthread.php?79555-when-will-garmin-express-mac-be-able-to-sync-GPS-EPO-bin-file-on-fenix-2&p=277398#post277398
+ def fix(epo)
+ unless epo.length == 28 * 2307
+ Log.error "GPS data has unexpected length of #{epo.length} bytes"
+ return nil
+ end
+
+ epo_fixed = ''
+ 0.upto(27) do |i|
+ offset = i * 2307
+ epo_fixed += epo[offset + 3, 2304]
+ end
+
+ epo_fixed
+ end
+
+ def write_file(output_file, data)
+ begin
+ File.write(output_file, data)
+ rescue IOError
+ Log.fatal "Cannot write EPO file '#{output_file}': #{$!}"
+ end
+ end
+
+ end
+
+end
+
diff --git a/lib/postrunner/Main.rb b/lib/postrunner/Main.rb
index 0dffde2..ddec18e 100644
--- a/lib/postrunner/Main.rb
+++ b/lib/postrunner/Main.rb
@@ -17,6 +17,7 @@ require 'fit4ruby'
require 'postrunner/version'
require 'postrunner/RuntimeConfig'
require 'postrunner/ActivitiesDB'
+require 'postrunner/EPO_Downloader'
module PostRunner
@@ -115,51 +116,54 @@ EOT
Commands:
check [ <fit file> | <ref> ... ]
- Check the provided FIT file(s) for structural errors. If no file or
- reference is provided, the complete archive is checked.
+ Check the provided FIT file(s) for structural errors. If no file or
+ reference is provided, the complete archive is checked.
dump <fit file> | <ref>
- Dump the content of the FIT file.
+ Dump the content of the FIT file.
import [ <fit file> | <directory> ]
- Import the provided FIT file(s) into the postrunner database. If no
- file or directory is provided, the directory that was used for the
- previous import is being used.
+ Import the provided FIT file(s) into the postrunner database. If no
+ file or directory is provided, the directory that was used for the
+ previous import is being used.
delete <ref>
- Delete the activity from the archive.
+ Delete the activity from the archive.
list
- List all FIT files stored in the data base.
+ List all FIT files stored in the data base.
records
- List all personal records.
+ List all personal records.
rename <new name> <ref>
- For the specified activities replace current activity name with a
- new name that describes the activity. By default the activity name
- matches the FIT file name.
+ For the specified activities replace current activity name with a
+ new name that describes the activity. By default the activity name
+ matches the FIT file name.
set <attribute> <value> <ref>
- For the specified activies set the attribute to the given value. The
- following attributes are supported:
+ For the specified activies set the attribute to the given value. The
+ following attributes are supported:
- name: The activity name (defaults to FIT file name)
- type: The type of the activity
- subtype: The subtype of the activity
+ name: The activity name (defaults to FIT file name)
+ type: The type of the activity
+ subtype: The subtype of the activity
show [ <ref> ]
- Show the referenced FIT activity in a web browser. If no reference
- is provided show the list of activities in the database.
+ Show the referenced FIT activity in a web browser. If no reference
+ is provided show the list of activities in the database.
summary <ref>
- Display the summary information for the FIT file.
+ Display the summary information for the FIT file.
units <metric | statute>
- Change the unit system.
+ Change the unit system.
htmldir <directory>
- Change the output directory for the generated HTML files
+ Change the output directory for the generated HTML files
+
+update-gps Download the current set of GPS ephemeris data and store them
+ on the device.
<fit file> An absolute or relative name of a .FIT file.
@@ -239,6 +243,8 @@ EOT
change_unit_system(args)
when 'htmldir'
change_html_dir(args)
+ when 'update-gps'
+ update_gps_data
when nil
Log.fatal("No command provided. " +
"See 'postrunner -h' for more information.")
@@ -352,6 +358,36 @@ EOT
end
end
+ def update_gps_data
+ epo_dir = File.join(@db_dir, 'epo')
+ @cfg.create_directory(epo_dir, 'GPS Data Cache')
+ epo_file = File.join(epo_dir, 'EPO.BIN')
+
+ if !File.exists?(epo_file) ||
+ (File.mtime(epo_file) < Time.now - (24 * 60 * 60))
+ if EPO_Downloader.new.download(epo_file)
+ unless (remotesw_dir = @cfg[:import_dir])
+ Log.error "No device directory set. Please import an activity " +
+ "from your device first."
+ return
+ end
+ remotesw_dir = File.join(remotesw_dir, '..', 'REMOTESW')
+ unless Dir.exists?(remotesw_dir)
+ Log.error "Cannot find '#{remotesw_dir}'. Please connect and " +
+ "mount your Garmin device."
+ return
+ end
+ begin
+ FileUtils.cp(epo_file, remotesw_dir)
+ rescue
+ Log.error "Cannot copy EPO.BIN file to your device at " +
+ "'#{remotesw_dir}'."
+ return
+ end
+ end
+ end
+ end
+
def handle_version_update
if @cfg.get_option(:version) != VERSION
Log.warn "PostRunner version upgrade detected."