teich-hrmparser 0.2.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.
- data/History.txt +9 -0
- data/Manifest.txt +20 -0
- data/README.rdoc +42 -0
- data/Rakefile +27 -0
- data/lib/hrmparser/arraymath.rb +25 -0
- data/lib/hrmparser/importer/garmin.rb +66 -0
- data/lib/hrmparser/importer/polar.rb +105 -0
- data/lib/hrmparser/importer.rb +20 -0
- data/lib/hrmparser/trackpoint.rb +15 -0
- data/lib/hrmparser/version.rb +13 -0
- data/lib/hrmparser/workout.rb +55 -0
- data/lib/hrmparser.rb +13 -0
- data/script/console +10 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/spec/arraymath_spec.rb +26 -0
- data/spec/hrmparser_spec.rb +124 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +9 -0
- data/tasks/rspec.rake +21 -0
- metadata +95 -0
data/History.txt
ADDED
data/Manifest.txt
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
History.txt
|
|
2
|
+
Manifest.txt
|
|
3
|
+
README.rdoc
|
|
4
|
+
Rakefile
|
|
5
|
+
lib/hrmparser.rb
|
|
6
|
+
lib/hrmparser/arraymath.rb
|
|
7
|
+
lib/hrmparser/workout.rb
|
|
8
|
+
lib/hrmparser/version.rb
|
|
9
|
+
lib/hrmparser/trackpoint.rb
|
|
10
|
+
lib/hrmparser/importer.rb
|
|
11
|
+
lib/hrmparser/importer/garmin.rb
|
|
12
|
+
lib/hrmparser/importer/polar.rb
|
|
13
|
+
script/console
|
|
14
|
+
script/destroy
|
|
15
|
+
script/generate
|
|
16
|
+
spec/hrmparser_spec.rb
|
|
17
|
+
spec/arraymath_spec.rb
|
|
18
|
+
spec/spec.opts
|
|
19
|
+
spec/spec_helper.rb
|
|
20
|
+
tasks/rspec.rake
|
data/README.rdoc
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
= hrmparser
|
|
2
|
+
|
|
3
|
+
* http://github.com/teich/hrmparser/tree/master
|
|
4
|
+
|
|
5
|
+
== DESCRIPTION:
|
|
6
|
+
|
|
7
|
+
A ruby parser for polar and garmin hrm
|
|
8
|
+
|
|
9
|
+
== FEATURES/PROBLEMS:
|
|
10
|
+
|
|
11
|
+
* Imports garmin 405. Generates a workout object with some averages calculated.
|
|
12
|
+
* Not the fastest right now.
|
|
13
|
+
* Very very early.
|
|
14
|
+
|
|
15
|
+
== REQUIREMENTS:
|
|
16
|
+
|
|
17
|
+
* hpricot
|
|
18
|
+
|
|
19
|
+
== LICENSE:
|
|
20
|
+
|
|
21
|
+
(The MIT License)
|
|
22
|
+
|
|
23
|
+
Copyright (c) 2009 Oren Teich
|
|
24
|
+
|
|
25
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
26
|
+
a copy of this software and associated documentation files (the
|
|
27
|
+
'Software'), to deal in the Software without restriction, including
|
|
28
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
29
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
30
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
31
|
+
the following conditions:
|
|
32
|
+
|
|
33
|
+
The above copyright notice and this permission notice shall be
|
|
34
|
+
included in all copies or substantial portions of the Software.
|
|
35
|
+
|
|
36
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
|
37
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
38
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
39
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
40
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
41
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
42
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
%w[rubygems rake rake/clean fileutils newgem rubigen].each { |f| require f }
|
|
2
|
+
require File.dirname(__FILE__) + '/lib/hrmparser'
|
|
3
|
+
|
|
4
|
+
# Generate all the Rake tasks
|
|
5
|
+
# Run 'rake -T' to see list of generated tasks (from gem root directory)
|
|
6
|
+
$hoe = Hoe.new('hrmparser', HRMParser::VERSION::STRING) do |p|
|
|
7
|
+
p.developer('Oren Teich', 'oren@teich.net')
|
|
8
|
+
p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
|
|
9
|
+
p.rubyforge_name = p.name # TODO this is default value
|
|
10
|
+
# p.extra_deps = [
|
|
11
|
+
# ['activesupport','>= 2.0.2'],
|
|
12
|
+
# ]
|
|
13
|
+
p.extra_dev_deps = [
|
|
14
|
+
['newgem', ">= #{::Newgem::VERSION}"]
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
p.clean_globs |= %w[**/.DS_Store tmp *.log]
|
|
18
|
+
path = (p.rubyforge_name == p.name) ? p.rubyforge_name : "\#{p.rubyforge_name}/\#{p.name}"
|
|
19
|
+
p.remote_rdoc_dir = File.join(path.gsub(/^#{p.rubyforge_name}\/?/,''), 'rdoc')
|
|
20
|
+
p.rsync_args = '-av --delete --ignore-errors'
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
require 'newgem/tasks' # load /tasks/*.rake
|
|
24
|
+
Dir['tasks/**/*.rake'].each { |t| load t }
|
|
25
|
+
|
|
26
|
+
# TODO - want other tests/tasks run by default? Add them to the list
|
|
27
|
+
task :default => [:spec, :features]
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
require 'enumerator'
|
|
2
|
+
|
|
3
|
+
# Set of basic array math functions.
|
|
4
|
+
module ArrayMath
|
|
5
|
+
|
|
6
|
+
def aaverage
|
|
7
|
+
accum = self.asum
|
|
8
|
+
return nil if accum.nil? || self.size == 0
|
|
9
|
+
accum.to_f / self.size
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def asum
|
|
13
|
+
self.map {|i| return nil if i.is_a?(String)}
|
|
14
|
+
inject(0){ |sum,item| sum + item }
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
## Retun array FACTOR smaller. Average values to get smaller
|
|
18
|
+
def smoothed(factor)
|
|
19
|
+
self.enum_for(:each_slice, factor).map { |snipit| snipit.compact.aaverage }
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
class Array
|
|
24
|
+
include ArrayMath
|
|
25
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
module Importer
|
|
2
|
+
class Garmin
|
|
3
|
+
def initialize(opts = {:data => nil})
|
|
4
|
+
@data = opts[:data]
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def restore
|
|
8
|
+
workout = HRMParser::Workout.new(:duration => 0)
|
|
9
|
+
#data = Importer.read_in_file(@file_name)
|
|
10
|
+
|
|
11
|
+
@xml = Hpricot::XML(@data)
|
|
12
|
+
workout.time = Time.parse((@xml/:Id).innerHTML)
|
|
13
|
+
|
|
14
|
+
(@xml/:Lap).each do |lap|
|
|
15
|
+
f_time = (lap/:TotalTimeSeconds).innerHTML
|
|
16
|
+
workout.duration += Float f_time
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
found = false
|
|
20
|
+
trackpoints = Array.new
|
|
21
|
+
distance_one = nil
|
|
22
|
+
time_one = nil
|
|
23
|
+
|
|
24
|
+
(@xml/:Trackpoint).each do |t|
|
|
25
|
+
found = true
|
|
26
|
+
trackpoint = HRMParser::TrackPoint.new
|
|
27
|
+
next if ((t/:HeartRateBpm/:Value).innerHTML == "")
|
|
28
|
+
trackpoint.hr = (t/:HeartRateBpm/:Value).innerHTML.to_i
|
|
29
|
+
trackpoint.lat = (t/:Position/:LatitudeDegrees).innerHTML.to_f
|
|
30
|
+
trackpoint.lng = (t/:Position/:LongitudeDegrees).innerHTML.to_f
|
|
31
|
+
trackpoint.time = Time.parse((t/:Time).innerHTML)
|
|
32
|
+
trackpoint.altitude = (t/:AltitudeMeters).innerHTML.to_f
|
|
33
|
+
trackpoint.distance = (t/:DistanceMeters).innerHTML.to_f
|
|
34
|
+
trackpoints << trackpoint
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
if distance_one.nil?
|
|
38
|
+
distance_one = trackpoint.distance
|
|
39
|
+
time_one = trackpoint.time
|
|
40
|
+
else
|
|
41
|
+
distance_two = trackpoint.distance
|
|
42
|
+
time_two = trackpoint.time
|
|
43
|
+
time_delta = time_two - time_one
|
|
44
|
+
distance_delta = distance_two - distance_one
|
|
45
|
+
if (distance_delta > 0 && time_delta > 0)
|
|
46
|
+
trackpoint.speed = distance_delta / time_delta
|
|
47
|
+
distance_one = distance_two
|
|
48
|
+
time_one = time_two
|
|
49
|
+
else trackpoint.speed = nil
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
if found
|
|
55
|
+
workout.trackpoints = trackpoints
|
|
56
|
+
workout.distance = trackpoints.last.distance if !trackpoints.last.distance.nil?
|
|
57
|
+
workout.calc_average_speed!
|
|
58
|
+
workout.calc_altitude_gain!
|
|
59
|
+
workout.calc_average_hr!
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
return workout
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
module Importer
|
|
2
|
+
class Polar
|
|
3
|
+
|
|
4
|
+
attr_reader :time_zone
|
|
5
|
+
|
|
6
|
+
def initialize(opts = {:data => nil, :time_zone => "UTC"})
|
|
7
|
+
@data = opts[:data]
|
|
8
|
+
@time_zone = opts[:time_zone]
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def restore
|
|
12
|
+
workout = HRMParser::Workout.new(:duration => 0)
|
|
13
|
+
#filehandle = Importer.read_in_file(@file_name)
|
|
14
|
+
#@data = @file.read
|
|
15
|
+
|
|
16
|
+
params = parse_params
|
|
17
|
+
|
|
18
|
+
date = params["Date"] + " " + params["StartTime"] + " " + @time_zone
|
|
19
|
+
|
|
20
|
+
length_array = params["Length"].split(/:/)
|
|
21
|
+
workout.duration = (length_array[0].to_f * 3600) + (length_array[1].to_f * 60) + (length_array[2].to_f)
|
|
22
|
+
workout.time = Time.parse(date)
|
|
23
|
+
|
|
24
|
+
workout.trackpoints = get_trackpoints(workout.time, params["Interval"].to_i)
|
|
25
|
+
|
|
26
|
+
workout.calc_average_hr!
|
|
27
|
+
|
|
28
|
+
return workout
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
# def parse_tabbed_blocks
|
|
36
|
+
# # This is the list of tabbed blocks
|
|
37
|
+
# tabbed_blocks = %w[IntTimes ExtraData Sumary-123 Summary-TH]
|
|
38
|
+
# tabbed_blocks.each do |block_name|
|
|
39
|
+
# @polarHash[block_name] = []
|
|
40
|
+
# block_text = find_block(block_name)
|
|
41
|
+
# block_text.each do |block_line|
|
|
42
|
+
# @polarHash[block_name] << block_line.split(/\t/)
|
|
43
|
+
# end
|
|
44
|
+
# end
|
|
45
|
+
# end
|
|
46
|
+
|
|
47
|
+
# Params is the only "ini" style block
|
|
48
|
+
def parse_params
|
|
49
|
+
hash = {}
|
|
50
|
+
param_block = find_block("Params")
|
|
51
|
+
param_block.each do |param|
|
|
52
|
+
# /=/ in case that doesn't work
|
|
53
|
+
key, value = param.split("=", 2)
|
|
54
|
+
key = key.strip unless key.nil?
|
|
55
|
+
value = value.strip unless value.nil?
|
|
56
|
+
hash[key] = value unless key.nil?
|
|
57
|
+
end
|
|
58
|
+
return hash
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Polar file has [Foo] blocks. Return the data in the block
|
|
62
|
+
def find_block(header)
|
|
63
|
+
found = false
|
|
64
|
+
block = []
|
|
65
|
+
@data.each do |line|
|
|
66
|
+
line.chomp!
|
|
67
|
+
found = false if line =~ /^\[.*\]$/
|
|
68
|
+
block << line if found
|
|
69
|
+
found = true if line =~ /\[#{header}\]/
|
|
70
|
+
end
|
|
71
|
+
return block
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def parse_hrdata
|
|
75
|
+
hrdata = []
|
|
76
|
+
block_text = find_block("HRData")
|
|
77
|
+
block_text.each do |block_line|
|
|
78
|
+
hrdata << block_line.chomp
|
|
79
|
+
end
|
|
80
|
+
return hrdata
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def get_trackpoints(start_time, interval)
|
|
84
|
+
trackpoints = []
|
|
85
|
+
rrcounter = 0
|
|
86
|
+
|
|
87
|
+
hrdata = parse_hrdata
|
|
88
|
+
hrdata.each do |hrd|
|
|
89
|
+
tp = HRMParser::TrackPoint.new
|
|
90
|
+
|
|
91
|
+
if (interval == 238)
|
|
92
|
+
rrcounter += hrd.to_i
|
|
93
|
+
tp.hr = 60000 / hrd.to_i
|
|
94
|
+
tp.time = start_time + (rrcounter/1000)
|
|
95
|
+
else
|
|
96
|
+
tp.hr = hrd.to_i
|
|
97
|
+
tp.time = start_time + (interval * trackpoints.size)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
trackpoints << tp
|
|
101
|
+
end
|
|
102
|
+
return trackpoints
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
require 'importer/garmin'
|
|
2
|
+
require 'importer/polar'
|
|
3
|
+
|
|
4
|
+
module Importer
|
|
5
|
+
def Importer.file_type(name)
|
|
6
|
+
if name =~ /\.tcx$/i
|
|
7
|
+
"GARMIN_XML"
|
|
8
|
+
elsif name =~ /\.hrm$/i
|
|
9
|
+
"POLAR_HRM"
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def Importer.read_in_file(name)
|
|
14
|
+
if File.readable?(name)
|
|
15
|
+
return open(name, "r")
|
|
16
|
+
else
|
|
17
|
+
puts "FILE ERROR, can't read #{name}"
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module HRMParser
|
|
2
|
+
class TrackPoint
|
|
3
|
+
attr_accessor :lat, :lng, :altitude, :speed, :hr, :distance, :time, :cadence
|
|
4
|
+
def initialize(opts = {:lat => nil, :lng => nil, :altitude => nil, :speed => nil, :hr => nil, :distance => nil, :cadence => nil, :time => Time.now})
|
|
5
|
+
@lat = opts[:lat]
|
|
6
|
+
@lng = opts[:lng]
|
|
7
|
+
@altitude = opts[:altitude]
|
|
8
|
+
@speed = opts[:speed]
|
|
9
|
+
@hr = opts[:hr]
|
|
10
|
+
@distance = opts[:distance]
|
|
11
|
+
@time = opts[:time]
|
|
12
|
+
@cadence = opts[:cadence]
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
module HRMParser
|
|
2
|
+
class Workout
|
|
3
|
+
attr_accessor :duration, :distance, :time, :name, :file_name, :trackpoints
|
|
4
|
+
attr_reader :average_hr, :data, :average_speed, :altitude_gain
|
|
5
|
+
|
|
6
|
+
def initialize(opts = {:duration => nil, :distance => nil, :time => Time.now, :name => nil, :file_name => nil})
|
|
7
|
+
@duration = opts[:duration]
|
|
8
|
+
@name = opts[:name]
|
|
9
|
+
@time = opts[:time]
|
|
10
|
+
@distance = opts[:distance]
|
|
11
|
+
@file_name = opts[:file_name]
|
|
12
|
+
|
|
13
|
+
@data = nil
|
|
14
|
+
@trackpoints = {}
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def calc_average_hr!
|
|
19
|
+
@average_hr = heart_rates.compact.aaverage
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def calc_average_speed!
|
|
23
|
+
@average_speed = speeds.compact.aaverage
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def calc_altitude_gain!
|
|
27
|
+
gain = 0
|
|
28
|
+
smoothed_altitude = altitudes.smoothed(10)
|
|
29
|
+
start = smoothed_altitude.first
|
|
30
|
+
smoothed_altitude.each do |alt|
|
|
31
|
+
diff = alt - start
|
|
32
|
+
if (diff > 0)
|
|
33
|
+
gain += diff
|
|
34
|
+
end
|
|
35
|
+
start = alt
|
|
36
|
+
end
|
|
37
|
+
@altitude_gain = gain
|
|
38
|
+
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
## Some helper functions that return specific files from trackpoint as array
|
|
43
|
+
def heart_rates
|
|
44
|
+
@trackpoints.map {|tp| tp.hr }
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def speeds
|
|
48
|
+
@trackpoints.map {|tp| tp.speed }
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def altitudes
|
|
52
|
+
@trackpoints.map { |tp| tp.altitude }
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
data/lib/hrmparser.rb
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
require 'rubygems'
|
|
2
|
+
require 'hpricot'
|
|
3
|
+
require 'time'
|
|
4
|
+
|
|
5
|
+
module HRMParser; end
|
|
6
|
+
|
|
7
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__) + '/hrmparser')
|
|
8
|
+
|
|
9
|
+
require 'arraymath'
|
|
10
|
+
require 'version'
|
|
11
|
+
require 'trackpoint'
|
|
12
|
+
require 'workout'
|
|
13
|
+
require 'importer'
|
data/script/console
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# File: script/console
|
|
3
|
+
irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
|
|
4
|
+
|
|
5
|
+
libs = " -r irb/completion"
|
|
6
|
+
# Perhaps use a console_lib to store any extra methods I may want available in the cosole
|
|
7
|
+
# libs << " -r #{File.dirname(__FILE__) + '/../lib/console_lib/console_logger.rb'}"
|
|
8
|
+
libs << " -r #{File.dirname(__FILE__) + '/../lib/hrmparser.rb'}"
|
|
9
|
+
puts "Loading hrmparser gem"
|
|
10
|
+
exec "#{irb} #{libs} --simple-prompt"
|
data/script/destroy
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
|
3
|
+
|
|
4
|
+
begin
|
|
5
|
+
require 'rubigen'
|
|
6
|
+
rescue LoadError
|
|
7
|
+
require 'rubygems'
|
|
8
|
+
require 'rubigen'
|
|
9
|
+
end
|
|
10
|
+
require 'rubigen/scripts/destroy'
|
|
11
|
+
|
|
12
|
+
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
|
13
|
+
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
|
|
14
|
+
RubiGen::Scripts::Destroy.new.run(ARGV)
|
data/script/generate
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
|
3
|
+
|
|
4
|
+
begin
|
|
5
|
+
require 'rubigen'
|
|
6
|
+
rescue LoadError
|
|
7
|
+
require 'rubygems'
|
|
8
|
+
require 'rubigen'
|
|
9
|
+
end
|
|
10
|
+
require 'rubigen/scripts/generate'
|
|
11
|
+
|
|
12
|
+
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
|
13
|
+
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
|
|
14
|
+
RubiGen::Scripts::Generate.new.run(ARGV)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
|
2
|
+
|
|
3
|
+
module ArrayMath
|
|
4
|
+
describe "ArrayMath" do
|
|
5
|
+
context "Sum" do
|
|
6
|
+
it "sums an array" do
|
|
7
|
+
array = [1,2,3,4,5]
|
|
8
|
+
array.asum.should == 15
|
|
9
|
+
end
|
|
10
|
+
it "can not sum an array of strings" do
|
|
11
|
+
array = ["hello", "goodbye"]
|
|
12
|
+
array.asum.should == nil
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
context "average" do
|
|
16
|
+
it "averages an array of integers and returns a float" do
|
|
17
|
+
array = [2,3,4,3,2]
|
|
18
|
+
array.aaverage.should == 2.8
|
|
19
|
+
end
|
|
20
|
+
it "averages an array of floats" do
|
|
21
|
+
array = [1.5,2.5,3.5,2.5,1.5]
|
|
22
|
+
array.aaverage.should == 2.3
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
|
2
|
+
|
|
3
|
+
# Time to add your specs!
|
|
4
|
+
# http://rspec.info/
|
|
5
|
+
module HRMParser
|
|
6
|
+
describe "TrackPoint" do
|
|
7
|
+
context "new trackpoint" do
|
|
8
|
+
it "has no variables set" do
|
|
9
|
+
hrm = TrackPoint.new
|
|
10
|
+
hrm.speed == nil
|
|
11
|
+
hrm.distance == nil
|
|
12
|
+
hrm.lat.should == nil
|
|
13
|
+
hrm.lng.should == nil
|
|
14
|
+
hrm.altitude.should == nil
|
|
15
|
+
hrm.hr.should == nil
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
describe "Workout" do
|
|
21
|
+
context "new workout" do
|
|
22
|
+
it "has no variables set" do
|
|
23
|
+
workout = Workout.new
|
|
24
|
+
workout.distance.should == nil
|
|
25
|
+
workout.duration.should == nil
|
|
26
|
+
workout.average_hr.should == nil
|
|
27
|
+
workout.name.should == nil
|
|
28
|
+
workout.file_name.should == nil
|
|
29
|
+
end
|
|
30
|
+
it "set name through initializer" do
|
|
31
|
+
workout = Workout.new(:name => "test workout")
|
|
32
|
+
workout.name.should == "test workout"
|
|
33
|
+
end
|
|
34
|
+
it "can not set average_hr during init" do
|
|
35
|
+
workout = Workout.new(:average_hr => 150)
|
|
36
|
+
workout.average_hr.should == nil
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
context "Identifies files" do
|
|
41
|
+
it "identify file as garmin" do
|
|
42
|
+
type = Importer.file_type("spec/samples/small-garmin.TCX")
|
|
43
|
+
type.should == "GARMIN_XML"
|
|
44
|
+
end
|
|
45
|
+
it "identification returns nil if no file specified" do
|
|
46
|
+
type = Importer.file_type("")
|
|
47
|
+
type.should == nil
|
|
48
|
+
end
|
|
49
|
+
it "identify file as polar" do
|
|
50
|
+
type = Importer.file_type("spec/samples/polarRS200.hrm")
|
|
51
|
+
type.should == "POLAR_HRM"
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
context "Parse garmin file" do
|
|
56
|
+
it "finds workout start time on a short workout" do
|
|
57
|
+
filename = "spec/samples/indoor-garmin-405.TCX"
|
|
58
|
+
data = File.read(filename)
|
|
59
|
+
importer = Importer::Garmin.new(:data => data)
|
|
60
|
+
workout = importer.restore
|
|
61
|
+
workout.time.should == Time.parse("Fri Aug 22 01:04:55 UTC 2008")
|
|
62
|
+
end
|
|
63
|
+
it "finds the duration on a short workout" do
|
|
64
|
+
filename = "spec/samples/indoor-garmin-405.TCX"
|
|
65
|
+
data = File.read(filename)
|
|
66
|
+
importer = Importer::Garmin.new(:data => data)
|
|
67
|
+
workout = importer.restore
|
|
68
|
+
workout.duration.should be_close(755, 1)
|
|
69
|
+
end
|
|
70
|
+
it "indoor workout has no trackpoints" do
|
|
71
|
+
filename = "spec/samples/indoor-garmin-405.TCX"
|
|
72
|
+
data = File.read(filename)
|
|
73
|
+
importer = Importer::Garmin.new(:data => data)
|
|
74
|
+
workout = importer.restore
|
|
75
|
+
workout.distance.should == nil
|
|
76
|
+
workout.average_hr.should == nil
|
|
77
|
+
workout.average_speed.should == nil
|
|
78
|
+
workout.altitude_gain.should == nil
|
|
79
|
+
workout.trackpoints == nil
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
## Parsing the full XML is just slow. Commenting out for now.
|
|
83
|
+
it "gets workout level settings for outdoor workout" do
|
|
84
|
+
filename = "spec/samples/outdoor-garmin-405.TCX"
|
|
85
|
+
data = File.read(filename)
|
|
86
|
+
importer = Importer::Garmin.new(:data => data)
|
|
87
|
+
workout = importer.restore
|
|
88
|
+
workout.distance.should be_close(11740, 5)
|
|
89
|
+
workout.average_hr.should be_close(149.7, 0.5)
|
|
90
|
+
workout.average_speed.should be_close(1.5, 0.2)
|
|
91
|
+
workout.altitude_gain.should be_close(572, 1.0)
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
context "Parse polar RS200 file" do
|
|
96
|
+
it "finds the duration and time" do
|
|
97
|
+
filename ="spec/samples/polarRS200.hrm"
|
|
98
|
+
data = File.read(filename)
|
|
99
|
+
importer = Importer::Polar.new(:data => data, :time_zone => "UTC")
|
|
100
|
+
workout = importer.restore
|
|
101
|
+
workout.duration.should be_close(3569,1)
|
|
102
|
+
workout.time.should == Time.parse("Thu Apr 16 12:01:55 UTC 2009")
|
|
103
|
+
end
|
|
104
|
+
it "calculates the average heartrate" do
|
|
105
|
+
filename ="spec/samples/polarRS200.hrm"
|
|
106
|
+
data = File.read(filename)
|
|
107
|
+
importer = Importer::Polar.new(:data => data, :time_zone => "UTC")
|
|
108
|
+
workout = importer.restore
|
|
109
|
+
workout.average_hr.should be_close(145, 1)
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
context "Parse a Polar RR file" do
|
|
113
|
+
it "calculates the heart rate from RR" do
|
|
114
|
+
filename ="spec/samples/polarRS800-RR.hrm"
|
|
115
|
+
data = File.read(filename)
|
|
116
|
+
importer = Importer::Polar.new(:data => data, :time_zone => "UTC")
|
|
117
|
+
workout = importer.restore
|
|
118
|
+
workout.trackpoints.each {|tp| tp.hr.should < 220 && tp.hr.should > 30}
|
|
119
|
+
workout.average_hr.should be_close(115, 1)
|
|
120
|
+
workout.average_speed.should == nil
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
data/spec/spec.opts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
--colour
|
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
dir = File.dirname(__FILE__)
|
|
2
|
+
lib_path = File.expand_path("#{dir}/../lib")
|
|
3
|
+
$LOAD_PATH.unshift lib_path unless $LOAD_PATH.include?(lib_path)
|
|
4
|
+
$_spec_spec = true # Prevents Kernel.exit in various places
|
|
5
|
+
|
|
6
|
+
require 'spec'
|
|
7
|
+
|
|
8
|
+
$:.unshift(File.dirname(__FILE__) + '/../lib')
|
|
9
|
+
require 'hrmparser'
|
data/tasks/rspec.rake
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
begin
|
|
2
|
+
require 'spec'
|
|
3
|
+
rescue LoadError
|
|
4
|
+
require 'rubygems'
|
|
5
|
+
require 'spec'
|
|
6
|
+
end
|
|
7
|
+
begin
|
|
8
|
+
require 'spec/rake/spectask'
|
|
9
|
+
rescue LoadError
|
|
10
|
+
puts <<-EOS
|
|
11
|
+
To use rspec for testing you must install rspec gem:
|
|
12
|
+
gem install rspec
|
|
13
|
+
EOS
|
|
14
|
+
exit(0)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
desc "Run the specs under spec/models"
|
|
18
|
+
Spec::Rake::SpecTask.new do |t|
|
|
19
|
+
t.spec_opts = ['--options', "spec/spec.opts"]
|
|
20
|
+
t.spec_files = FileList['spec/*_spec.rb']
|
|
21
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: teich-hrmparser
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.2.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Oren Teich
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
|
|
12
|
+
date: 2009-04-29 00:00:00 -07:00
|
|
13
|
+
default_executable:
|
|
14
|
+
dependencies:
|
|
15
|
+
- !ruby/object:Gem::Dependency
|
|
16
|
+
name: newgem
|
|
17
|
+
type: :development
|
|
18
|
+
version_requirement:
|
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
20
|
+
requirements:
|
|
21
|
+
- - ">="
|
|
22
|
+
- !ruby/object:Gem::Version
|
|
23
|
+
version: 1.3.0
|
|
24
|
+
version:
|
|
25
|
+
- !ruby/object:Gem::Dependency
|
|
26
|
+
name: hoe
|
|
27
|
+
type: :development
|
|
28
|
+
version_requirement:
|
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - ">="
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: 1.8.0
|
|
34
|
+
version:
|
|
35
|
+
description: A ruby parser for polar and garmin hrm
|
|
36
|
+
email:
|
|
37
|
+
- oren@teich.net
|
|
38
|
+
executables: []
|
|
39
|
+
|
|
40
|
+
extensions: []
|
|
41
|
+
|
|
42
|
+
extra_rdoc_files:
|
|
43
|
+
- History.txt
|
|
44
|
+
- Manifest.txt
|
|
45
|
+
- README.rdoc
|
|
46
|
+
files:
|
|
47
|
+
- History.txt
|
|
48
|
+
- Manifest.txt
|
|
49
|
+
- README.rdoc
|
|
50
|
+
- Rakefile
|
|
51
|
+
- lib/hrmparser.rb
|
|
52
|
+
- lib/hrmparser/arraymath.rb
|
|
53
|
+
- lib/hrmparser/workout.rb
|
|
54
|
+
- lib/hrmparser/version.rb
|
|
55
|
+
- lib/hrmparser/trackpoint.rb
|
|
56
|
+
- lib/hrmparser/importer.rb
|
|
57
|
+
- lib/hrmparser/importer/garmin.rb
|
|
58
|
+
- lib/hrmparser/importer/polar.rb
|
|
59
|
+
- script/console
|
|
60
|
+
- script/destroy
|
|
61
|
+
- script/generate
|
|
62
|
+
- spec/hrmparser_spec.rb
|
|
63
|
+
- spec/arraymath_spec.rb
|
|
64
|
+
- spec/spec.opts
|
|
65
|
+
- spec/spec_helper.rb
|
|
66
|
+
- tasks/rspec.rake
|
|
67
|
+
has_rdoc: true
|
|
68
|
+
homepage: http://github.com/teich/hrmparser/tree/master
|
|
69
|
+
post_install_message:
|
|
70
|
+
rdoc_options:
|
|
71
|
+
- --main
|
|
72
|
+
- README.rdoc
|
|
73
|
+
require_paths:
|
|
74
|
+
- lib
|
|
75
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
76
|
+
requirements:
|
|
77
|
+
- - ">="
|
|
78
|
+
- !ruby/object:Gem::Version
|
|
79
|
+
version: "0"
|
|
80
|
+
version:
|
|
81
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
82
|
+
requirements:
|
|
83
|
+
- - ">="
|
|
84
|
+
- !ruby/object:Gem::Version
|
|
85
|
+
version: "0"
|
|
86
|
+
version:
|
|
87
|
+
requirements: []
|
|
88
|
+
|
|
89
|
+
rubyforge_project: hrmparser
|
|
90
|
+
rubygems_version: 1.2.0
|
|
91
|
+
signing_key:
|
|
92
|
+
specification_version: 2
|
|
93
|
+
summary: A ruby parser for polar and garmin hrm
|
|
94
|
+
test_files: []
|
|
95
|
+
|