teich-hrmparser 0.4.0 → 0.4.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/CHANGELOG.txt CHANGED
@@ -1,3 +1,8 @@
1
+ 0.4.1 - May 9th, 2009
2
+ Added GPX support.
3
+ NOTE: all workouts do NOT account for stopped time.
4
+ duration is simply last time - first time. Will be fixed in future version.
5
+
1
6
  0.4.0 - May 8th, 2009
2
7
  Added initial support for Suunto T6. Just grabs HRs for now.
3
8
 
data/VERSION.yml CHANGED
@@ -1,4 +1,4 @@
1
1
  ---
2
- :patch: 0
2
+ :patch: 1
3
3
  :major: 0
4
4
  :minor: 4
@@ -0,0 +1,42 @@
1
+ module Importer
2
+ class GPX
3
+ def initialize(opts = {:data => nil, :time_zone => "UTC"})
4
+ @data = opts[:data]
5
+ end
6
+
7
+ def restore
8
+ workout = HRMParser::Workout.new(:duration => 0)
9
+ @xml = Hpricot::XML(@data)
10
+ ttime = (@xml/:time).first.innerHTML
11
+ workout.time = Time.parse(ttime)
12
+
13
+ trackpoints = []
14
+ distance = 0
15
+ (@xml/:trk).each do |trk|
16
+ (trk/:trkpt).each do |trkpt|
17
+ trackpoint = HRMParser::TrackPoint.new
18
+ trackpoint.altitude = (trkpt/:ele).innerHTML.to_f
19
+ trackpoint.time = Time.parse((trkpt/:time).innerHTML)
20
+
21
+ trackpoint.lat = (trkpt.attributes)["lat"].to_f
22
+ trackpoint.lng = (trkpt.attributes)["lon"].to_f
23
+
24
+ distance += trackpoint.calc_distance(trackpoints.last, trackpoint)
25
+ trackpoint.distance = distance
26
+
27
+ trackpoint.speed = trackpoint.calc_speed(trackpoints.last, trackpoint)
28
+
29
+ trackpoints << trackpoint
30
+ end
31
+ end
32
+
33
+ workout.duration = trackpoints.last.time - trackpoints.first.time
34
+ workout.trackpoints = trackpoints
35
+ workout.calc_average_speed!
36
+ workout.calc_altitude_gain!
37
+ workout.distance = trackpoints.last.distance
38
+ return workout
39
+ end
40
+ end
41
+
42
+ end
@@ -1,6 +1,7 @@
1
1
  require 'importer/garmin'
2
2
  require 'importer/polar'
3
3
  require 'importer/suunto'
4
+ require 'importer/gpx'
4
5
 
5
6
  module Importer
6
7
  def Importer.file_type(name)
@@ -11,6 +12,8 @@ module Importer
11
12
  return "POLAR_HRM"
12
13
  when /\.sdf$/i
13
14
  return "SUUNTO"
15
+ when /\.gpx$/i
16
+ return "GPX"
14
17
  end
15
18
  end
16
19
 
@@ -1,15 +1,48 @@
1
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
2
+ class TrackPoint
3
+
4
+ RAD_PER_DEG = 0.017453293 # PI/180
5
+
6
+ attr_accessor :lat, :lng, :altitude, :speed, :hr, :distance, :time, :cadence
7
+ def initialize(opts = {:lat => nil, :lng => nil, :altitude => nil, :speed => nil, :hr => nil, :distance => nil, :cadence => nil, :time => Time.now})
8
+ @lat = opts[:lat]
9
+ @lng = opts[:lng]
10
+ @altitude = opts[:altitude]
11
+ @speed = opts[:speed]
12
+ @hr = opts[:hr]
13
+ @distance = opts[:distance]
14
+ @time = opts[:time]
15
+ @cadence = opts[:cadence]
16
+ end
17
+
18
+ def calc_distance(pointA, pointB)
19
+ return 0 if pointA.nil? || pointA.lat.nil?
20
+
21
+ dlng = pointB.lng - pointA.lng
22
+ dlat = pointB.lat - pointA.lat
23
+
24
+ dlat_rad = dlat * RAD_PER_DEG
25
+ dlng_rad = dlng * RAD_PER_DEG
26
+
27
+ lat1_rad = pointA.lat * RAD_PER_DEG
28
+ lng1_rad = pointA.lng * RAD_PER_DEG
29
+
30
+ lat2_rad = pointB.lat * RAD_PER_DEG
31
+ lng2_rad = pointB.lng * RAD_PER_DEG
32
+
33
+ a = (Math.sin(dlat_rad/2))**2 + Math.cos(lat1_rad) * Math.cos(lat2_rad) * (Math.sin(dlng_rad/2))**2
34
+ c = 2 * Math.atan2( Math.sqrt(a), Math.sqrt(1-a))
35
+
36
+ return 6371000 * c
37
+ end
38
+
39
+ def calc_speed(pointA, pointB)
40
+ return 0 if pointA.nil? || pointA.lat.nil?
41
+ time_delta = pointB.time - pointA.time
42
+ distance_delta = pointB.distance - pointA.distance
43
+ return distance_delta / time_delta
44
+ end
45
+ end
46
+ end
47
+
48
+
@@ -52,112 +52,133 @@ module HRMParser
52
52
  end
53
53
  end
54
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 be_nil
76
- workout.average_hr.should be_nil
77
- workout.average_speed.should be_nil
78
- workout.altitude_gain.should be_nil
79
- workout.trackpoints.should == {}
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"
55
+ context "Parse a GPS file" do
56
+ it "finds the duration, time" do
57
+ filename = "spec/samples/gps-with-suunto.gpx"
85
58
  data = File.read(filename)
86
- importer = Importer::Garmin.new(:data => data)
59
+ importer = Importer::GPX.new(:data => data)
87
60
  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(583, 1.0)
61
+ workout.time.should == Time.parse("Fri May 08 00:58:35 UTC 2009")
62
+
63
+ # Duration is actualy less, but we don't account for stopped time right now
64
+ workout.duration.should be_close(6382,1)
92
65
  end
93
-
94
- it "gets workout level settings for weird distance workout" do
95
- filename = "spec/samples/garmin-405-dies-distance.TCX"
66
+ it "calculates the distance and speed" do
67
+ filename = "spec/samples/gps-with-suunto.gpx"
96
68
  data = File.read(filename)
97
- importer = Importer::Garmin.new(:data => data)
69
+ importer = Importer::GPX.new(:data => data)
98
70
  workout = importer.restore
99
- workout.distance.should be_close(9426, 1)
100
- workout.average_hr.should == nil
101
71
  workout.average_speed.should be_close(6.7, 0.2)
102
- workout.altitude_gain.should be_close(40, 1.0)
103
- end
104
-
105
- it "doesn't have any 0 in latitude" do
106
- filename = "spec/samples/garmin-405-with-0-0.TCX"
107
- data = File.read(filename)
108
- importer = Importer::Garmin.new(:data => data)
109
- workout = importer.restore
110
- workout.trackpoints.map {|tp| tp.lat.should_not == 0.0}
111
- workout.trackpoints.map {|tp| tp.lat.should_not == "undefined"}
112
- end
113
- end
114
-
115
- context "Parse polar RS200 file" do
116
- it "finds the duration and time" do
117
- filename ="spec/samples/polarRS200.hrm"
118
- data = File.read(filename)
119
- importer = Importer::Polar.new(:data => data, :time_zone => "UTC")
120
- workout = importer.restore
121
- workout.duration.should be_close(3569,1)
122
- workout.time.should == Time.parse("Thu Apr 16 12:01:55 UTC 2009")
123
- end
124
- it "calculates the average heartrate" do
125
- filename ="spec/samples/polarRS200.hrm"
126
- data = File.read(filename)
127
- importer = Importer::Polar.new(:data => data, :time_zone => "UTC")
128
- workout = importer.restore
129
- workout.average_hr.should be_close(145, 1)
130
- end
131
- end
132
- context "Parse a Polar RR file" do
133
- it "calculates the heart rate from RR" do
134
- filename ="spec/samples/polarRS800-RR.hrm"
135
- data = File.read(filename)
136
- importer = Importer::Polar.new(:data => data, :time_zone => "UTC")
137
- workout = importer.restore
138
- workout.trackpoints.each {|tp| tp.hr.should < 220 && tp.hr.should > 30}
139
- workout.average_hr.should be_close(115, 1)
140
- workout.average_speed.should == nil
72
+ workout.distance.should be_close(26427, 1)
141
73
  end
142
74
  end
143
75
 
144
- context "Parse a Suunto T6C RR file" do
145
- it "finds the duration and time" do
146
- filename = "spec/samples/suunto-t6-RR-stops.sdf"
147
- data = File.read(filename)
148
- importer = Importer::Suunto.new(:data => data, :time_zone => "-0700")
149
- workout = importer.restore
150
- workout.duration.should be_close(4781,1)
151
- workout.time.should == Time.parse("Thu May 07 14:16:07 -0700 2009")
152
- end
153
- it "calculates the average HR" do
154
- filename = "spec/samples/suunto-t6-RR-stops.sdf"
155
- data = File.read(filename)
156
- importer = Importer::Suunto.new(:data => data, :time_zone => "-0700")
157
- workout = importer.restore
158
- workout.average_hr.should be_close(152,1)
159
- workout.average_speed.should == nil
76
+ context "Parse garmin file" do
77
+ it "finds workout start time on a short workout" do
78
+ filename = "spec/samples/indoor-garmin-405.TCX"
79
+ data = File.read(filename)
80
+ importer = Importer::Garmin.new(:data => data)
81
+ workout = importer.restore
82
+ workout.time.should == Time.parse("Fri Aug 22 01:04:55 UTC 2008")
83
+ end
84
+ it "finds the duration on a short workout" do
85
+ filename = "spec/samples/indoor-garmin-405.TCX"
86
+ data = File.read(filename)
87
+ importer = Importer::Garmin.new(:data => data)
88
+ workout = importer.restore
89
+ workout.duration.should be_close(755, 1)
90
+ end
91
+ it "indoor workout has no trackpoints" do
92
+ filename = "spec/samples/indoor-garmin-405.TCX"
93
+ data = File.read(filename)
94
+ importer = Importer::Garmin.new(:data => data)
95
+ workout = importer.restore
96
+ workout.distance.should be_nil
97
+ workout.average_hr.should be_nil
98
+ workout.average_speed.should be_nil
99
+ workout.altitude_gain.should be_nil
100
+ workout.trackpoints.should == {}
101
+ end
102
+
103
+ # Parsing the full XML is just slow. Commenting out for now.
104
+ it "gets workout level settings for outdoor workout" do
105
+ filename = "spec/samples/outdoor-garmin-405.TCX"
106
+ data = File.read(filename)
107
+ importer = Importer::Garmin.new(:data => data)
108
+ workout = importer.restore
109
+ workout.distance.should be_close(11740, 5)
110
+ workout.average_hr.should be_close(149.7, 0.5)
111
+ workout.average_speed.should be_close(1.5, 0.2)
112
+ workout.altitude_gain.should be_close(583, 1.0)
113
+ end
114
+
115
+ it "gets workout level settings for weird distance workout" do
116
+ filename = "spec/samples/garmin-405-dies-distance.TCX"
117
+ data = File.read(filename)
118
+ importer = Importer::Garmin.new(:data => data)
119
+ workout = importer.restore
120
+ workout.distance.should be_close(9426, 1)
121
+ workout.average_hr.should == nil
122
+ workout.average_speed.should be_close(6.7, 0.2)
123
+ workout.altitude_gain.should be_close(40, 1.0)
124
+ end
125
+
126
+ it "doesn't have any 0 in latitude" do
127
+ filename = "spec/samples/garmin-405-with-0-0.TCX"
128
+ data = File.read(filename)
129
+ importer = Importer::Garmin.new(:data => data)
130
+ workout = importer.restore
131
+ workout.trackpoints.map {|tp| tp.lat.should_not == 0.0}
132
+ workout.trackpoints.map {|tp| tp.lat.should_not == "undefined"}
133
+ end
134
+ end
135
+
136
+ context "Parse polar RS200 file" do
137
+ it "finds the duration and time" do
138
+ filename ="spec/samples/polarRS200.hrm"
139
+ data = File.read(filename)
140
+ importer = Importer::Polar.new(:data => data, :time_zone => "UTC")
141
+ workout = importer.restore
142
+ workout.duration.should be_close(3569,1)
143
+ workout.time.should == Time.parse("Thu Apr 16 12:01:55 UTC 2009")
144
+ end
145
+ it "calculates the average heartrate" do
146
+ filename ="spec/samples/polarRS200.hrm"
147
+ data = File.read(filename)
148
+ importer = Importer::Polar.new(:data => data, :time_zone => "UTC")
149
+ workout = importer.restore
150
+ workout.average_hr.should be_close(145, 1)
151
+ end
152
+ end
153
+ context "Parse a Polar RR file" do
154
+ it "calculates the heart rate from RR" do
155
+ filename ="spec/samples/polarRS800-RR.hrm"
156
+ data = File.read(filename)
157
+ importer = Importer::Polar.new(:data => data, :time_zone => "UTC")
158
+ workout = importer.restore
159
+ workout.trackpoints.each {|tp| tp.hr.should < 220 && tp.hr.should > 30}
160
+ workout.average_hr.should be_close(115, 1)
161
+ workout.average_speed.should == nil
162
+ end
163
+ end
164
+
165
+ context "Parse a Suunto T6C RR file" do
166
+ it "finds the duration and time" do
167
+ filename = "spec/samples/suunto-t6-RR-stops.sdf"
168
+ data = File.read(filename)
169
+ importer = Importer::Suunto.new(:data => data, :time_zone => "-0700")
170
+ workout = importer.restore
171
+ workout.duration.should be_close(4781,1)
172
+ workout.time.should == Time.parse("Thu May 07 14:16:07 -0700 2009")
173
+ end
174
+ it "calculates the average HR" do
175
+ filename = "spec/samples/suunto-t6-RR-stops.sdf"
176
+ data = File.read(filename)
177
+ importer = Importer::Suunto.new(:data => data, :time_zone => "-0700")
178
+ workout = importer.restore
179
+ workout.average_hr.should be_close(152,1)
180
+ workout.average_speed.should == nil
181
+ end
160
182
  end
161
- end
162
183
  end
163
184
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: teich-hrmparser
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Oren Teich
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-05-08 00:00:00 -07:00
12
+ date: 2009-05-09 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -30,6 +30,7 @@ files:
30
30
  - lib/hrmparser/arraymath.rb
31
31
  - lib/hrmparser/importer.rb
32
32
  - lib/hrmparser/importer/garmin.rb
33
+ - lib/hrmparser/importer/gpx.rb
33
34
  - lib/hrmparser/importer/polar.rb
34
35
  - lib/hrmparser/importer/suunto.rb
35
36
  - lib/hrmparser/trackpoint.rb