fitreader 0.2.4 → 0.3.0

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.
@@ -0,0 +1,82 @@
1
+ 0: :file_id
2
+ 1: :capabilities
3
+ 2: :device_settings
4
+ 3: :user_profile
5
+ 4: :hrm_profile
6
+ 5: :sdm_profile
7
+ 6: :bike_profile
8
+ 7: :zones_target
9
+ 8: :hr_zone
10
+ 9: :power_zone
11
+ 10: :met_zone
12
+ 12: :sport
13
+ 15: :goal
14
+ 18: :session
15
+ 19: :lap
16
+ 20: :record
17
+ 21: :event
18
+ 22: :source
19
+ 23: :device_info
20
+ 26: :workout
21
+ 27: :workout_step
22
+ 28: :schedule
23
+ 29: :location
24
+ 30: :weight_scale
25
+ 31: :course
26
+ 32: :course_point
27
+ 33: :totals
28
+ 34: :activity
29
+ 35: :software
30
+ 37: :file_capabilities
31
+ 38: :mesg_capabilities
32
+ 39: :field_capabilities
33
+ 49: :file_creator
34
+ 51: :blood_pressure
35
+ 53: :speed_zone
36
+ 55: :monitoring
37
+ 72: :training_file
38
+ 78: :hrv
39
+ 80: :ant_rx
40
+ 81: :ant_tx
41
+ 82: :ant_channel_id
42
+ 101: :length
43
+ 103: :monitoring_info
44
+ 104: :battery_info
45
+ 105: :pad
46
+ 106: :slave_device
47
+ 127: :connectivity
48
+ 128: :weather_conditions
49
+ 129: :weather_alert
50
+ 131: :cadence_zone
51
+ 132: :hr
52
+ 142: :segment_lap
53
+ 145: :memo_glob
54
+ 147: :sensor_info
55
+ 148: :segment_id
56
+ 149: :segment_leaderboard_entry
57
+ 150: :segment_point
58
+ 151: :segment_file
59
+ 159: :watchface_settings
60
+ 160: :gps_metadata
61
+ 161: :camera_event
62
+ 162: :timestamp_correlation
63
+ 164: :gyroscope_data
64
+ 165: :accelerometer_data
65
+ 167: :three_d_sensor_calibration
66
+ 169: :video_frame
67
+ 174: :obdii_data
68
+ 177: :nmea_sentence
69
+ 178: :aviation_attitude
70
+ 184: :video
71
+ 185: :video_title
72
+ 186: :video_description
73
+ 187: :video_clip
74
+ 188: :ohr_settings
75
+ 200: :exd_screen_configuration
76
+ 201: :exd_data_field_configuration
77
+ 202: :exd_data_concept_configuration
78
+ 206: :field_description
79
+ 207: :developer_data_id
80
+ 208: :magnetometer_data
81
+ 0xFF00: :mfg_range_min
82
+ 0xFFFE: :mfg_range_max
@@ -0,0 +1,23 @@
1
+ require 'yaml'
2
+
3
+ class Sdk
4
+ @enums ||= YAML.load_file(File.join(File.dirname(__FILE__), 'enums.yml'))
5
+ @fields ||= YAML.load_file(File.join(File.dirname(__FILE__), 'fields.yml'))
6
+ @messages ||= YAML.load_file(File.join(File.dirname(__FILE__), 'messages.yml'))
7
+
8
+ def self.enum(name)
9
+ @enums[name]
10
+ end
11
+
12
+ def self.field(msg, field)
13
+ @fields[msg][field]
14
+ end
15
+
16
+ def self.fields(msg)
17
+ @fields[msg]
18
+ end
19
+
20
+ def self.message(num)
21
+ @messages[num]
22
+ end
23
+ end
@@ -0,0 +1,41 @@
1
+ module Unpack
2
+ def readbytes(io, char, len)
3
+ io.read(len).unpack(char).first
4
+ end
5
+
6
+ def read_multiple(io, char, len, size)
7
+ if char == 'Z*'
8
+ readbytes(io, char, len)
9
+ else
10
+ multiples = len / size
11
+ res = io.read(len).unpack(char * multiples)
12
+ if res.length == 1
13
+ res.first
14
+ else
15
+ res
16
+ end
17
+ end
18
+ end
19
+
20
+ def read_bit(byte, bit)
21
+ (byte & MASKS[bit]) >> bit
22
+ end
23
+
24
+ def read_bits(byte, range)
25
+ mask = range.first
26
+ .downto(range.last)
27
+ .inject(0) { |sum, i| sum + MASKS[i] }
28
+ (byte & mask) >> range.last
29
+ end
30
+
31
+ MASKS = {
32
+ 7 => 0b10000000,
33
+ 6 => 0b01000000,
34
+ 5 => 0b00100000,
35
+ 4 => 0b00010000,
36
+ 3 => 0b00001000,
37
+ 2 => 0b00000100,
38
+ 1 => 0b00000010,
39
+ 0 => 0b00000001
40
+ }.freeze
41
+ end
@@ -1,3 +1,3 @@
1
1
  module Fitreader
2
- VERSION = '0.2.4'
2
+ VERSION = '0.3.0'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fitreader
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.4
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Richard Brodie
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-01-28 00:00:00.000000000 Z
11
+ date: 2017-06-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -56,16 +56,16 @@ dependencies:
56
56
  name: pry
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - ">="
59
+ - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '0'
61
+ version: 0.10.4
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - ">="
66
+ - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '0'
68
+ version: 0.10.4
69
69
  description:
70
70
  email:
71
71
  - richard@samsari.org
@@ -76,6 +76,7 @@ files:
76
76
  - ".gitignore"
77
77
  - ".travis.yml"
78
78
  - Gemfile
79
+ - Gemfile.lock
79
80
  - LICENSE
80
81
  - LICENSE.txt
81
82
  - README.md
@@ -84,26 +85,23 @@ files:
84
85
  - bin/setup
85
86
  - fitreader.gemspec
86
87
  - lib/fitreader.rb
87
- - lib/fitreader/definition.rb
88
- - lib/fitreader/degraded_record.rb
89
- - lib/fitreader/errors.rb
90
- - lib/fitreader/field_data.rb
88
+ - lib/fitreader/data_field.rb
89
+ - lib/fitreader/data_record.rb
90
+ - lib/fitreader/definition_record.rb
91
91
  - lib/fitreader/field_definition.rb
92
92
  - lib/fitreader/file_header.rb
93
- - lib/fitreader/fitfile.rb
94
- - lib/fitreader/message_type.rb
95
- - lib/fitreader/record.rb
93
+ - lib/fitreader/fit.rb
94
+ - lib/fitreader/fit_object.rb
95
+ - lib/fitreader/message.rb
96
96
  - lib/fitreader/record_header.rb
97
- - lib/fitreader/sdk/constants.yml
98
- - lib/fitreader/sdk/types.yml
99
- - lib/fitreader/static.rb
97
+ - lib/fitreader/sdk/enums.yml
98
+ - lib/fitreader/sdk/fields.yml
99
+ - lib/fitreader/sdk/messages.yml
100
+ - lib/fitreader/sdk/sdk.rb
101
+ - lib/fitreader/unpack.rb
100
102
  - lib/fitreader/version.rb
101
- - schema
102
103
  - spec/1471568492.fit
103
104
  - spec/2016-04-09-13-19-18.fit
104
- - spec/fitreader_interfaces_spec.rb
105
- - spec/fitreader_readfile_spec.rb
106
- - spec/fitreader_static_spec.rb
107
105
  - test/fitreader_test.rb
108
106
  - test/test_helper.rb
109
107
  homepage: https://github.com/samsari/fitreader
@@ -126,15 +124,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
126
124
  version: '0'
127
125
  requirements: []
128
126
  rubyforge_project:
129
- rubygems_version: 2.6.8
127
+ rubygems_version: 2.6.11
130
128
  signing_key:
131
129
  specification_version: 4
132
130
  summary: Library for reading FIT files generated by Garmin devices.
133
131
  test_files:
134
132
  - spec/1471568492.fit
135
133
  - spec/2016-04-09-13-19-18.fit
136
- - spec/fitreader_interfaces_spec.rb
137
- - spec/fitreader_readfile_spec.rb
138
- - spec/fitreader_static_spec.rb
139
134
  - test/fitreader_test.rb
140
135
  - test/test_helper.rb
@@ -1,34 +0,0 @@
1
- require 'fitreader/field_definition'
2
-
3
- module Fitreader
4
- class Definition
5
- attr_accessor :name, :global_num, :local_num, :fit_msg, :field_definitions, :num_fields
6
- def initialize(msg_num, bytes)
7
- @local_num = msg_num
8
- @architecture = bytes[1].unpack('C').first
9
- @global_num = bytes[2..3].unpack('v').first
10
- @fit_msg = Static.message[@global_num]
11
- @name = Static.enums[:enum_mesg_num][@global_num]
12
- @num_fields = bytes[4].unpack('C').first
13
- end
14
-
15
- def field(id)
16
- @field_definitions.find { |x| x.def_num == id }
17
- end
18
-
19
- def add_fields(bytes)
20
- bytes.chars.each_slice(3) do |x|
21
- fd = FieldDefinition.new(@global_num, x)
22
- (@field_definitions ||= []).push(fd)
23
- end
24
- end
25
-
26
- def content_length
27
- @field_definitions.inject(0){|sum,x| sum + x.size}
28
- end
29
-
30
- def message_type
31
- Static.message[@global_num]
32
- end
33
- end
34
- end
@@ -1,37 +0,0 @@
1
- require 'fitreader/field_data'
2
- require 'fitreader/errors'
3
-
4
- module Fitreader
5
- class DegradedRecord
6
- attr_reader :fields
7
- def initialize(definition, bytes)
8
- @fields = {}
9
-
10
- start = 0
11
- definition.field_definitions.each do |f|
12
- raw = bytes[start...start+=f.size]
13
- b = Static.base[f.base_num]
14
- data = unpack_data(f, b, raw)
15
- @fields[f.def_num] = data
16
- end
17
- end
18
-
19
- def unpack_data(f, b, raw)
20
- if !b[:unpack_type].nil?
21
- if b[:size] != f.size && f.base_num != 7
22
- data = []
23
- s = 0
24
- (f.size/b[:size]).times do |_|
25
- data.push(raw[s..s+=b[:size]].unpack(b[:unpack_type]).first)
26
- end
27
- else
28
- data = raw.unpack(b[:unpack_type]).first
29
- end
30
- elsif f.base_num.zero?
31
- data = raw.unpack('C').first
32
- else
33
- data = raw.split(/\0/)[0]
34
- end
35
- end
36
- end
37
- end
@@ -1,18 +0,0 @@
1
- module Fitreader
2
- class UnknownFieldTypeError < RuntimeError
3
- attr :field, :definition, :data, :reason
4
- def initialize(definition, field, data, reason)
5
- @field = field
6
- @definition = definition
7
- @data = data
8
- @reason = reason
9
- end
10
- end
11
-
12
- class UnknownMessageTypeError < RuntimeError
13
- # attr :definition
14
- # def initialize(definition)
15
- # @definition = definition
16
- # end
17
- end
18
- end
@@ -1,58 +0,0 @@
1
- module Fitreader
2
- class FieldData
3
- attr_accessor :id, :raw_value, :value, :name, :valid
4
-
5
- def initialize(id, raw_value, type)
6
- @id = id
7
- @raw_value = raw_value
8
- unless type.nil?
9
- process_type(raw_value, type)
10
- else
11
- puts "nil type #{id} - #{raw_value}"
12
- end
13
- end
14
-
15
- private
16
- def process_type(data, type)
17
- @name = type[:name]
18
- if type[:type][0..3].to_sym == :enum
19
- data = Static.enums[type[:type]][data]
20
- elsif type[:type] == :date_time
21
- data = process_datetime(data)
22
- elsif type[:type] == :local_date_time
23
- data = process_local_datetime(data)
24
- elsif type[:type] == :coordinates
25
- data = process_coord(data)
26
- end
27
-
28
- if type[:scale] != 0
29
- if data.class == Array
30
- data = data.collect{|x| (x*1.0)/type[:scale]}
31
- else
32
- data = (data*1.0)/type[:scale]
33
- end
34
- end
35
-
36
- if type[:offset] != 0
37
- if data.class == Array
38
- data = data.collect{|x| x - type[:offset]}
39
- else
40
- data = data - type[:offset]
41
- end
42
- end
43
- @value = data
44
- end
45
-
46
- def process_local_datetime(data)
47
- t = Time.new(1989, 12, 31, 0, 0, 0, '+02:00').utc.to_i
48
- Time.at(data+t)
49
- end
50
- def process_datetime(data)
51
- t = Time.new(1989, 12, 31, 0, 0, 0, '+00:00').utc.to_i
52
- Time.at(data+t).utc
53
- end
54
- def process_coord(data)
55
- data * ( 180.0 / 2**31 )
56
- end
57
- end
58
- end
@@ -1,63 +0,0 @@
1
- require 'fitreader/file_header'
2
- require 'fitreader/record_header'
3
- require 'fitreader/definition'
4
- require 'fitreader/record'
5
- require 'fitreader/degraded_record'
6
- require 'fitreader/message_type'
7
- require 'fitreader/static'
8
-
9
- module Fitreader
10
- class FitFile
11
- attr_reader :header, :messages, :file
12
-
13
- def initialize(path)
14
- if path.is_a? String
15
- @file = File.open(path, 'rb')
16
- elsif path.is_a? File
17
- @file = path
18
- end
19
- @header = FileHeader.new(@file.read(14))
20
- if valid?
21
- @defs = {}
22
- @messages = {}
23
- process_next_record while @file.pos < @header.num_records
24
- end
25
- end
26
-
27
- private
28
-
29
- def valid?
30
- @header.valid_file
31
- end
32
-
33
- def process_next_record
34
- h = RecordHeader.new(@file.read(1).unpack('C').first)
35
- dr = @defs[h.local_num]
36
- if h.header_type == :definition
37
- dr = Definition.new(h.local_num, @file.read(5))
38
- dr.add_fields(@file.read(dr.num_fields * 3))
39
- @defs[h.local_num] = dr
40
- m = MessageType.new(dr)
41
- (@messages[dr.global_num] ||= []).push m
42
- elsif h.header_type == :data
43
- unless dr.nil?
44
- begin
45
- data = @file.read(dr.content_length)
46
- datr = Record.new(dr, data)
47
- @messages[dr.global_num].last.records.push datr
48
- rescue UnknownMessageTypeError
49
- degraded = DegradedRecord.new(dr, data)
50
- @messages[dr.global_num].last.undefined_records.push degraded
51
- end
52
- else
53
- msg = "no record def found! #{h.local_msg_num}"
54
- raise msg
55
- end
56
- elsif h.header_type == :timestamp
57
- raise "timestamp :: msg_num: #{h.local_msg_num}, offset: #{h.timestamp_offset}"
58
- else
59
- raise 'not a valid record'
60
- end
61
- end
62
- end
63
- end