fit4ruby 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/COPYING +280 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +21 -0
- data/README.md +16 -0
- data/Rakefile +19 -0
- data/fit4ruby.gemspec +30 -0
- data/lib/fit4ruby.rb +26 -0
- data/lib/fit4ruby/Activity.rb +119 -0
- data/lib/fit4ruby/Converters.rb +58 -0
- data/lib/fit4ruby/FitDataRecord.rb +79 -0
- data/lib/fit4ruby/FitDefinition.rb +54 -0
- data/lib/fit4ruby/FitDefinitionField.rb +137 -0
- data/lib/fit4ruby/FitFile.rb +154 -0
- data/lib/fit4ruby/FitFileId.rb +31 -0
- data/lib/fit4ruby/FitFilter.rb +26 -0
- data/lib/fit4ruby/FitHeader.rb +55 -0
- data/lib/fit4ruby/FitMessageIdMapper.rb +53 -0
- data/lib/fit4ruby/FitMessageRecord.rb +77 -0
- data/lib/fit4ruby/FitRecord.rb +80 -0
- data/lib/fit4ruby/FitRecordHeader.rb +39 -0
- data/lib/fit4ruby/GlobalFitDictList.rb +68 -0
- data/lib/fit4ruby/GlobalFitDictionaries.rb +302 -0
- data/lib/fit4ruby/GlobalFitMessage.rb +189 -0
- data/lib/fit4ruby/GlobalFitMessages.rb +235 -0
- data/lib/fit4ruby/Lap.rb +44 -0
- data/lib/fit4ruby/Log.rb +45 -0
- data/lib/fit4ruby/Record.rb +56 -0
- data/lib/fit4ruby/Session.rb +110 -0
- data/lib/fit4ruby/version.rb +3 -0
- data/tasks/changelog.rake +169 -0
- data/tasks/gem.rake +52 -0
- data/tasks/rdoc.rake +14 -0
- data/tasks/test.rake +7 -0
- data/test/FitFile_spec.rb +31 -0
- metadata +137 -0
data/lib/fit4ruby/Log.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
#!/usr/bin/env ruby -w
|
2
|
+
# encoding: UTF-8
|
3
|
+
#
|
4
|
+
# = Log.rb -- Fit4Ruby - FIT file processing library for Ruby
|
5
|
+
#
|
6
|
+
# Copyright (c) 2014 by Chris Schlaeger <cs@taskjuggler.org>
|
7
|
+
#
|
8
|
+
# This program is free software; you can redistribute it and/or modify
|
9
|
+
# it under the terms of version 2 of the GNU General Public License as
|
10
|
+
# published by the Free Software Foundation.
|
11
|
+
#
|
12
|
+
|
13
|
+
require 'logger'
|
14
|
+
|
15
|
+
module Fit4Ruby
|
16
|
+
|
17
|
+
# This is the Exception type that will be thrown for all unrecoverable
|
18
|
+
# errors.
|
19
|
+
class Error < StandardError ; end
|
20
|
+
|
21
|
+
class ILogger < Logger
|
22
|
+
|
23
|
+
def fatal(msg)
|
24
|
+
super
|
25
|
+
exit 1
|
26
|
+
end
|
27
|
+
|
28
|
+
def critical(msg, exception = nil)
|
29
|
+
if exception
|
30
|
+
raise Error, "#{msg}: #{exception.message}", exception.backtrace
|
31
|
+
else
|
32
|
+
raise Error, msg
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
Log = ILogger.new(STDOUT)
|
39
|
+
Log.level = Logger::WARN
|
40
|
+
Log.formatter = proc do |severity, time, progname, msg|
|
41
|
+
msg + "\n"
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
@@ -0,0 +1,56 @@
|
|
1
|
+
#!/usr/bin/env ruby -w
|
2
|
+
# encoding: UTF-8
|
3
|
+
#
|
4
|
+
# = Record.rb -- Fit4Ruby - FIT file processing library for Ruby
|
5
|
+
#
|
6
|
+
# Copyright (c) 2014 by Chris Schlaeger <cs@taskjuggler.org>
|
7
|
+
#
|
8
|
+
# This program is free software; you can redistribute it and/or modify
|
9
|
+
# it under the terms of version 2 of the GNU General Public License as
|
10
|
+
# published by the Free Software Foundation.
|
11
|
+
#
|
12
|
+
|
13
|
+
module Fit4Ruby
|
14
|
+
|
15
|
+
class Record
|
16
|
+
|
17
|
+
attr_reader :timestamp, :latitude, :longitude, :altitude, :distance,
|
18
|
+
:speed, :vertical_oscillation, :cadence, :stance_time
|
19
|
+
|
20
|
+
def initialize
|
21
|
+
end
|
22
|
+
|
23
|
+
def set(field, value)
|
24
|
+
case field
|
25
|
+
when 'timestamp'
|
26
|
+
@timestamp = value
|
27
|
+
when 'position_lat'
|
28
|
+
@latitude = value
|
29
|
+
when 'position_long'
|
30
|
+
@longitude = value
|
31
|
+
when 'altitude'
|
32
|
+
@altitude = value
|
33
|
+
when 'distance'
|
34
|
+
@distance = value
|
35
|
+
when 'speed'
|
36
|
+
@speed = value
|
37
|
+
when 'vertical_oscillation'
|
38
|
+
@vertical_oscillation = value
|
39
|
+
when 'cadence'
|
40
|
+
@cadence = 2 * value
|
41
|
+
when 'fractional_cadence'
|
42
|
+
@cadence += 2 * value if @cadence
|
43
|
+
when 'stance_time'
|
44
|
+
@stance_time = value
|
45
|
+
else
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def pace
|
50
|
+
1000.0 / (@speed * 60.0)
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
@@ -0,0 +1,110 @@
|
|
1
|
+
#!/usr/bin/env ruby -w
|
2
|
+
# encoding: UTF-8
|
3
|
+
#
|
4
|
+
# = Session.rb -- Fit4Ruby - FIT file processing library for Ruby
|
5
|
+
#
|
6
|
+
# Copyright (c) 2014 by Chris Schlaeger <cs@taskjuggler.org>
|
7
|
+
#
|
8
|
+
# This program is free software; you can redistribute it and/or modify
|
9
|
+
# it under the terms of version 2 of the GNU General Public License as
|
10
|
+
# published by the Free Software Foundation.
|
11
|
+
#
|
12
|
+
|
13
|
+
require 'fit4ruby/Converters'
|
14
|
+
|
15
|
+
module Fit4Ruby
|
16
|
+
|
17
|
+
class Session
|
18
|
+
|
19
|
+
include Converters
|
20
|
+
|
21
|
+
attr_reader :start_time, :duration, :distance, :strides, :ascend,
|
22
|
+
:descent, :calories, :avg_speed, :avg_heart_rate,
|
23
|
+
:max_heart_rate, :avg_vertical_oscillation, :avg_stance_time,
|
24
|
+
:avg_running_cadence, :avg_running_cadence, :training_effect,
|
25
|
+
:first_lap_index, :num_laps
|
26
|
+
|
27
|
+
def initialize
|
28
|
+
@laps = []
|
29
|
+
end
|
30
|
+
|
31
|
+
def check(activity)
|
32
|
+
@first_lap_index.upto(@first_lap_index - @num_laps) do |i|
|
33
|
+
if (lap = activity.lap[i])
|
34
|
+
@laps << lap
|
35
|
+
else
|
36
|
+
Log.error "Session references lap #{i} which is not contained in "
|
37
|
+
"the FIT file."
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def set(field, value)
|
43
|
+
return unless value
|
44
|
+
|
45
|
+
case field
|
46
|
+
when 'start_time'
|
47
|
+
@start_time = value
|
48
|
+
when 'total_timer_time'
|
49
|
+
@duration = value
|
50
|
+
when 'total_distance'
|
51
|
+
@distance = value
|
52
|
+
when 'total_strides'
|
53
|
+
@strides = value
|
54
|
+
when 'total_ascent'
|
55
|
+
@ascend = value
|
56
|
+
when 'total_descent'
|
57
|
+
@descent = value
|
58
|
+
when 'total_calories'
|
59
|
+
@calories = value
|
60
|
+
when 'avg_speed'
|
61
|
+
@avg_speed = value
|
62
|
+
when 'avg_heart_rate'
|
63
|
+
@avg_heart_rate = value
|
64
|
+
when 'max_heart_rate'
|
65
|
+
@max_heart_rate = value
|
66
|
+
when 'avg_vertical_oscillation'
|
67
|
+
@avg_vertical_oscillation = value
|
68
|
+
when 'avg_stance_time'
|
69
|
+
@avg_stance_time = value
|
70
|
+
when 'avg_running_cadence'
|
71
|
+
@avg_running_cadence = 2 * value
|
72
|
+
when 'avg_fraction_cadence'
|
73
|
+
@avg_running_cadence += 2 * value
|
74
|
+
when 'total_training_effect'
|
75
|
+
@training_effect = value
|
76
|
+
when 'first_lap_index'
|
77
|
+
@first_lap_index = value
|
78
|
+
when 'num_laps'
|
79
|
+
@num_laps = value
|
80
|
+
else
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def avg_stride_length
|
85
|
+
@distance / @strides
|
86
|
+
end
|
87
|
+
|
88
|
+
def to_s
|
89
|
+
<<"EOT"
|
90
|
+
Date: #{@start_time}
|
91
|
+
Distance: #{'%.2f' % (@distance / 1000.0)} km
|
92
|
+
Time: #{secsToHMS(@duration)}
|
93
|
+
Avg Pace: #{speedToPace(@avg_speed)} min/km
|
94
|
+
Total Ascend: #{@ascend} m
|
95
|
+
Total Descend: #{@descent} m
|
96
|
+
Calories: #{@calories} kCal
|
97
|
+
Avg HR: #{@avg_heart_rate} bpm
|
98
|
+
Max HR: #{@max_heart_rate} bpm
|
99
|
+
Training Effect: #{@training_effect}
|
100
|
+
Avg Run Cadence: #{@avg_running_cadence.round} spm
|
101
|
+
Avg Vertical Oscillation: #{'%.1f' % (@avg_vertical_oscillation / 10)} cm
|
102
|
+
Avg Ground Contact Time: #{@avg_stance_time.round} ms
|
103
|
+
Avg Stride Length: #{'%.2f' % (avg_stride_length / 2)} m
|
104
|
+
EOT
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
|
@@ -0,0 +1,169 @@
|
|
1
|
+
require 'time'
|
2
|
+
|
3
|
+
desc 'Generate the CHANGELOG file'
|
4
|
+
task :changelog do
|
5
|
+
|
6
|
+
class Entry
|
7
|
+
|
8
|
+
attr_reader :type
|
9
|
+
|
10
|
+
def initialize(ref, author, time, message)
|
11
|
+
@ref = ref
|
12
|
+
@author = author
|
13
|
+
@time = time
|
14
|
+
@message = message
|
15
|
+
if (m = /New: (.*)/.match(@message))
|
16
|
+
@type = :feature
|
17
|
+
@message = m[1]
|
18
|
+
elsif (m = /Fix: (.*)/.match(@message))
|
19
|
+
@type = :bugfix
|
20
|
+
@message = m[1]
|
21
|
+
else
|
22
|
+
@type = :other
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_s
|
27
|
+
" * #{@message}\n"
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
class Release
|
33
|
+
|
34
|
+
attr_reader :date, :version, :tag
|
35
|
+
|
36
|
+
def initialize(tag, predecessor)
|
37
|
+
@tag = tag
|
38
|
+
# We only support release tags in the form X.X.X
|
39
|
+
@version = /\d+\.\d+\.\d+/.match(tag)
|
40
|
+
|
41
|
+
# Construct a Git range.
|
42
|
+
interval = predecessor ? "#{predecessor.tag}..#{@tag}" : @tag
|
43
|
+
|
44
|
+
# Get the date of the release
|
45
|
+
date = Time.parse(/Date: (.*)/.match(`git show #{tag}`)[1]).utc
|
46
|
+
@date = date.strftime("%Y-%m-%d")
|
47
|
+
|
48
|
+
@entries = []
|
49
|
+
# Use -z option for git-log to get 0 bytes as separators.
|
50
|
+
`git log -z #{interval}`.split("\0").each do |commit|
|
51
|
+
# We ignore merges.
|
52
|
+
next if commit =~ /^Merge: \d*/
|
53
|
+
|
54
|
+
ref, author, time, _, message = commit.split("\n", 5)
|
55
|
+
ref = ref[/commit ([0-9a-f]+)/, 1]
|
56
|
+
author = author[/Author: (.*)/, 1].strip
|
57
|
+
time = Time.parse(time[/Date: (.*)/, 1]).utc
|
58
|
+
# Eleminate git-svn-id: lines
|
59
|
+
message.gsub!(/git-svn-id: .*\n/, '')
|
60
|
+
# Eliminate Signed-off-by: lines
|
61
|
+
message.gsub!(/Signed-off-by: .*\n/, '')
|
62
|
+
message.strip!
|
63
|
+
@entries << Entry.new(ref, author, time, message)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def empty?
|
68
|
+
@entries.empty?
|
69
|
+
end
|
70
|
+
|
71
|
+
def to_s
|
72
|
+
s = ''
|
73
|
+
if hasFeatures? || hasFixes?
|
74
|
+
if hasFeatures?
|
75
|
+
s << "== New Features\n\n"
|
76
|
+
@entries.each do |entry|
|
77
|
+
s << entry.to_s if entry.type == :feature
|
78
|
+
end
|
79
|
+
s << "\n"
|
80
|
+
end
|
81
|
+
if hasFixes?
|
82
|
+
s << "== Bug Fixes\n\n"
|
83
|
+
@entries.each do |entry|
|
84
|
+
s << entry.to_s if entry.type == :bugfix
|
85
|
+
end
|
86
|
+
s << "\n"
|
87
|
+
end
|
88
|
+
else
|
89
|
+
@entries.each do |entry|
|
90
|
+
s << entry.to_s
|
91
|
+
end
|
92
|
+
end
|
93
|
+
s
|
94
|
+
end
|
95
|
+
|
96
|
+
private
|
97
|
+
|
98
|
+
def hasFeatures?
|
99
|
+
@entries.each do |entry|
|
100
|
+
return true if entry.type == :feature
|
101
|
+
end
|
102
|
+
false
|
103
|
+
end
|
104
|
+
|
105
|
+
def hasFixes?
|
106
|
+
@entries.each do |entry|
|
107
|
+
return true if entry.type == :bugfix
|
108
|
+
end
|
109
|
+
false
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
|
114
|
+
class ChangeLog
|
115
|
+
|
116
|
+
def initialize
|
117
|
+
@releases = []
|
118
|
+
predecessor = nil
|
119
|
+
getReleaseVersions.each do |version|
|
120
|
+
@releases << (predecessor = Release.new(version, predecessor))
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def to_s
|
125
|
+
s = ''
|
126
|
+
@releases.reverse.each do |release|
|
127
|
+
next if release.empty?
|
128
|
+
|
129
|
+
# We use RDOC markup syntax to generate a title
|
130
|
+
if release.version
|
131
|
+
s << "= Release #{release.version} (#{release.date})\n\n"
|
132
|
+
else
|
133
|
+
s << "= Next Release (Some Day)\n\n"
|
134
|
+
end
|
135
|
+
s << release.to_s + "\n"
|
136
|
+
end
|
137
|
+
s
|
138
|
+
end
|
139
|
+
|
140
|
+
private
|
141
|
+
|
142
|
+
# 'git tag' is not sorted numerically. This function implements a
|
143
|
+
# numerical comparison for tag versions of the format 'release-X.X.X'. X
|
144
|
+
# can be a multi-digit number.
|
145
|
+
def compareTags(a, b)
|
146
|
+
|
147
|
+
def versionToComparable(v)
|
148
|
+
/\d+\.\d+\.\d+/.match(v)[0].split('.').map{ |l| sprintf("%03d", l.to_i)}.
|
149
|
+
join('.')
|
150
|
+
end
|
151
|
+
|
152
|
+
versionToComparable(a) <=> versionToComparable(b)
|
153
|
+
end
|
154
|
+
|
155
|
+
def getReleaseVersions
|
156
|
+
# Get list of release tags from Git repository
|
157
|
+
releaseVersions = `git tag`.split("\n").map { |r| r.chomp }.
|
158
|
+
delete_if { |r| ! (/release-\d+\.\d+\.\d+/ =~ r) }.
|
159
|
+
sort{ |a, b| compareTags(a, b) }
|
160
|
+
releaseVersions << 'HEAD'
|
161
|
+
end
|
162
|
+
|
163
|
+
end
|
164
|
+
|
165
|
+
File.open('CHANGELOG', 'w+') do |changelog|
|
166
|
+
changelog.puts ChangeLog.new.to_s
|
167
|
+
end
|
168
|
+
|
169
|
+
end
|
data/tasks/gem.rake
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
# GEM TASK
|
2
|
+
require 'find'
|
3
|
+
require 'rubygems'
|
4
|
+
require 'rubygems/package'
|
5
|
+
|
6
|
+
# Unfortunately Rake::GemPackageTest cannot deal with files that are generated
|
7
|
+
# by Rake targets. So we have to write our own packaging task.
|
8
|
+
desc 'Build the gem package'
|
9
|
+
task :gem => [:clobber] do
|
10
|
+
Rake::Task[:changelog].invoke
|
11
|
+
Rake::Task[:permissions].invoke
|
12
|
+
|
13
|
+
load 'fit4ruby.gemspec';
|
14
|
+
|
15
|
+
# Build the gem file according to the loaded spec.
|
16
|
+
if RUBY_VERSION >= "2.0.0"
|
17
|
+
Gem::Package.build(GEM_SPEC)
|
18
|
+
else
|
19
|
+
Gem::Builder.new(GEM_SPEC).build
|
20
|
+
end
|
21
|
+
pkgBase = "#{GEM_SPEC.name}-#{GEM_SPEC.version}"
|
22
|
+
# Create a pkg directory if it doesn't exist already.
|
23
|
+
FileUtils.mkdir_p('pkg')
|
24
|
+
# Move the gem file into the pkg directory.
|
25
|
+
verbose(true) { FileUtils.mv("#{pkgBase}.gem", "pkg/#{pkgBase}.gem")}
|
26
|
+
end
|
27
|
+
|
28
|
+
desc 'Make sure all files and directories are readable'
|
29
|
+
task :permissions do
|
30
|
+
# Find the bin and test directories relative to this file.
|
31
|
+
baseDir = File.expand_path('..', File.dirname(__FILE__))
|
32
|
+
|
33
|
+
execs = Dir.glob("#{baseDir}/bin/*") +
|
34
|
+
Dir.glob("#{baseDir}/test/**/genrefs")
|
35
|
+
|
36
|
+
Find.find(baseDir) do |f|
|
37
|
+
# Ignore the whoke pkg directory as it may contain links to the other
|
38
|
+
# directories.
|
39
|
+
next if Regexp.new("#{baseDir}/pkg/*").match(f)
|
40
|
+
|
41
|
+
FileUtils.chmod_R((FileTest.directory?(f) ||
|
42
|
+
execs.include?(f) ? 0755 : 0644), f)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
desc 'Run all tests and build scripts and create the gem package'
|
47
|
+
task :release do
|
48
|
+
Rake::Task[:test].invoke
|
49
|
+
Rake::Task[:yard].invoke
|
50
|
+
Rake::Task[:gem].invoke
|
51
|
+
end
|
52
|
+
|
data/tasks/rdoc.rake
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
if RUBY_VERSION < '1.9.3'
|
2
|
+
require 'rake/rdoctask'
|
3
|
+
else
|
4
|
+
require 'rdoc/task'
|
5
|
+
end
|
6
|
+
|
7
|
+
# RDOC TASK
|
8
|
+
Rake::RDocTask.new(:rdoc) do |t|
|
9
|
+
t.rdoc_files = %w( README.rdoc COPYING CHANGELOG ) +
|
10
|
+
`git ls-files -- lib`.split("\n")
|
11
|
+
t.title = "TaskJuggler API documentation"
|
12
|
+
t.main = 'README.rdoc'
|
13
|
+
t.rdoc_dir = 'doc'
|
14
|
+
end
|