diff options
author | Chris Schlaeger <chris@linux.com> | 2015-04-25 07:56:10 +0200 |
---|---|---|
committer | Chris Schlaeger <chris@linux.com> | 2015-04-25 07:56:10 +0200 |
commit | 0f074edb6d211d8df2c7b40fd9895effd0ecd38f (patch) | |
tree | 803264e6d6d4ee1c3bb92fed6831bd29589575c0 | |
parent | 6c81603a295667eee155bf379927b0de718c789c (diff) | |
download | postrunner-0f074edb6d211d8df2c7b40fd9895effd0ecd38f.zip |
New: Download GPS cache data from Garmin server and store it on device
-rw-r--r-- | lib/postrunner/EPO_Downloader.rb | 95 | ||||
-rw-r--r-- | lib/postrunner/Main.rb | 80 |
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." |