From 784a61ed03cb0eae2c0d9c4118da8c7c8045dbf0 Mon Sep 17 00:00:00 2001 From: Kees Bakker Date: Mon, 15 Jun 2026 22:16:52 +0200 Subject: [PATCH 1/2] fix: full_field_name needs a developer_data_ids argument This is a bugfix of a change made in commit 0b0a72f9d5 --- lib/fit4ruby/FitMessageRecord.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/fit4ruby/FitMessageRecord.rb b/lib/fit4ruby/FitMessageRecord.rb index cdc3601..ef8cf77 100644 --- a/lib/fit4ruby/FitMessageRecord.rb +++ b/lib/fit4ruby/FitMessageRecord.rb @@ -111,7 +111,7 @@ def read(io, entity, filter = nil, fields_dump = nil, fit_entity) next end - field_name = field_description.full_field_name(fit_entity) + field_name = field_description.full_field_name(fit_entity.top_level_record.developer_data_ids) units = field_description.units type = field.type native_message_number = field_description.native_mesg_num @@ -205,4 +205,3 @@ def produce(definition) end end - From 2b6d9bcd6d43e4a42952cce8e59cb7a60ec9d8f4 Mon Sep 17 00:00:00 2001 From: Kees Bakker Date: Mon, 15 Jun 2026 22:21:39 +0200 Subject: [PATCH 2/2] fix: Wahoo ELMNT FIT does not have application_id The function always full_field_name tried to use .application_id, but some devices (e.g. Wahoo ELMNT) does not have it. --- Gemfile | 1 + lib/fit4ruby/FieldDescription.rb | 27 ++++++++++++++++++-------- lib/fit4ruby/GlobalFitMessages.rb | 32 ++++++++++++++++++++++++++++++- lib/fit4ruby/Log.rb | 1 - 4 files changed, 51 insertions(+), 10 deletions(-) diff --git a/Gemfile b/Gemfile index 7f4f5e9..6b25705 100644 --- a/Gemfile +++ b/Gemfile @@ -3,3 +3,4 @@ source 'https://rubygems.org' gemspec +gem "debug", ">= 1.0.0" diff --git a/lib/fit4ruby/FieldDescription.rb b/lib/fit4ruby/FieldDescription.rb index fe946e3..decc95c 100644 --- a/lib/fit4ruby/FieldDescription.rb +++ b/lib/fit4ruby/FieldDescription.rb @@ -38,18 +38,30 @@ def full_field_name(developer_data_ids) return end - app_id = developer_data_ids[@developer_data_index].application_id - # Convert the byte array with the app ID into a 16 character hex string. - app_id_str = app_id.map { |i| '%02X' % i }.join('') - @full_field_name = - "#{@field_name.gsub(/[^A-Za-z0-9_]/, '_')}_#{app_id_str}" + field_name = @field_name.gsub(/[^A-Za-z0-9_]/, '_') + if (app_id = developer_data_ids[@developer_data_index].application_id) + # Convert the byte array with the app ID into a 16 character hex string. + id_str = app_id.map { |i| '%02X' % i }.join('') + @full_field_name = "#{field_name}_#{id_str}" + else + @full_field_name = field_name + end end def create_global_definition(fit_entity) messages = fit_entity.developer_fit_messages + field_id = "'#{@field_name}' (developer data index " + + "#{@developer_data_index}, field #{@field_definition_number})" + # The native message number is optional. Without it, the field is not + # bound to a global message. + if @native_mesg_num.nil? + Log.debug { "Developer field description #{field_id} has no " + + "native message number" } + return + end unless (gfm = GlobalFitMessages[@native_mesg_num]) - Log.error "Developer field description references unknown global " + - "message number #{@native_mesg_num}" + Log.error "Developer field description #{field_id} references " + + "unknown global message number #{@native_mesg_num}" return end @@ -78,4 +90,3 @@ def create_global_definition(fit_entity) end end - diff --git a/lib/fit4ruby/GlobalFitMessages.rb b/lib/fit4ruby/GlobalFitMessages.rb index 0fe6f06..5d90d60 100644 --- a/lib/fit4ruby/GlobalFitMessages.rb +++ b/lib/fit4ruby/GlobalFitMessages.rb @@ -27,6 +27,7 @@ module Fit4Ruby field 5, 'uint16', 'number' field 6, 'uint16', 'undocumented_field_6' field 7, 'uint32', 'undocumented_field_7' + field 8, 'string', 'product_name' message 2, 'device_settings' field 0, 'uint8', 'active_time_zone' @@ -243,6 +244,16 @@ module Fit4Ruby field 16, 'uint32', 'undocumented_field_16' field 254, 'uint16', 'message_index' + message 8, 'hr_zone' + field 1, 'uint8', 'high_bpm', :unit => 'bpm' + field 2, 'string', 'name' + field 254, 'uint16', 'message_index' + + message 9, 'power_zone' + field 1, 'uint16', 'high_value', :unit => 'watts' + field 2, 'string', 'name' + field 254, 'uint16', 'message_index' + message 12, 'sport' field 0, 'enum', 'sport', :dict => 'sport' field 1, 'enum', 'sub_sport', :dict => 'sub_sport' @@ -399,10 +410,16 @@ module Fit4Ruby field 46, 'enum', 'pool_length_unit', :dict => 'display_measure' field 47, 'uint16', 'num_active_lengths', :unit => 'lengths' field 48, 'uint32', 'total_work', :unit => 'J' + field 49, 'uint16', 'avg_altitude', :scale => 5, :offset => 500, :unit => 'm' + field 50, 'uint16', 'max_altitude', :scale => 5, :offset => 500, :unit => 'm' + field 52, 'sint16', 'avg_grade', :scale => 100, :unit => '%' + field 55, 'sint16', 'max_pos_grade', :scale => 100, :unit => '%' + field 56, 'sint16', 'max_neg_grade', :scale => 100, :unit => '%' field 57, 'sint8', 'avg_temperature', :unit => 'C' field 58, 'sint8', 'max_temperature', :unit => 'C' field 65, 'uint32', 'time_in_hr_zone', :array => true, :scale => 1000, :unit => 's' field 68, 'uint32', 'time_in_power_zone', :array => true, :scale => 1000, :unit => 's' + field 71, 'uint16', 'min_altitude', :scale => 5, :offset => 500, :unit => 'm' field 78, 'uint32', 'undocumented_field_78' field 79, 'uint16', 'undocumented_field_79' field 80, 'uint16', 'undocumented_field_80' @@ -531,10 +548,16 @@ module Fit4Ruby field 39, 'enum', 'sub_sport', :dict => 'sub_sport' field 40, 'uint16', 'num_active_lengths', :unit => 'lengths' field 41, 'uint32', 'total_work', :unit => 'J' + field 42, 'uint16', 'avg_altitude', :scale => 5, :offset => 500, :unit => 'm' + field 43, 'uint16', 'max_altitude', :scale => 5, :offset => 500, :unit => 'm' + field 45, 'sint16', 'avg_grade', :scale => 100, :unit => '%' + field 48, 'sint16', 'max_pos_grade', :scale => 100, :unit => '%' + field 49, 'sint16', 'max_neg_grade', :scale => 100, :unit => '%' field 50, 'sint8', 'avg_temperature', :unit => 'C' field 51, 'sint8', 'max_temperature', :unit => 'C' field 57, 'uint32', 'time_in_hr_zone', :array => true, :scale => 1000, :unit => 's' field 60, 'uint32', 'avg_pos_vertical_speed', :scale => 1000, :unit => 'm/s' + field 62, 'uint16', 'min_altitude', :scale => 5, :offset => 500, :unit => 'm' field 70, 'uint32', 'undefined_value_70' field 71, 'uint16', 'wkt_step_index' field 72, 'enum', 'undocumented_field_72' @@ -605,9 +628,13 @@ module Fit4Ruby field 13, 'sint8', 'temperature', :unit => 'C' field 14, 'uint32', 'undefined_field_14' field 15, 'uint32', 'undefined_field_15' - field 18, 'uint8', 'undefined_field_18' + field 17, 'uint8', 'speed_1s', :array => true, :scale => 16, :unit => 'm/s' + field 18, 'uint8', 'cycles', :unit => 'cycles' + field 19, 'uint32', 'total_cycles', :unit => 'cycles' + field 28, 'uint16', 'compressed_accumulated_power', :unit => 'watts' field 29, 'uint32', 'accumulated_power', :unit => 'watts' field 30, 'uint8', 'left_right_balance', :dict => 'left_right_balance' + field 31, 'uint8', 'gps_accuracy', :unit => 'm' field 39, 'uint16', 'vertical_oscillation', :scale => 10, :unit => 'mm' field 40, 'uint16', 'stance_time_percent', :scale => 100, :unit => 'percent' field 41, 'uint16', 'stance_time', :scale => 10, :unit => 'ms' @@ -621,6 +648,7 @@ module Fit4Ruby field 54, 'uint16', 'total_hemoglobin_conc', :scale => 100, :unit => 'g/dL' field 57, 'uint16', 'saturated_hemoglobin_percent', :scale => 10, :unit => '%' field 61, 'uint16', 'undefined_value_61' + field 62, 'uint8', 'device_index' field 63, 'uint16', 'undefined_value_63' field 64, 'uint16', 'undefined_value_64' field 65, 'uint16', 'undefined_value_65' @@ -638,6 +666,7 @@ module Fit4Ruby field 77, 'uint32', 'undefined_value_77' field 79, 'uint32', 'undefined_value_79' field 80, 'uint32', 'undefined_value_80' + field 81, 'uint8', 'battery_soc', :scale => 2, :unit => '%' # lev battery state of charge field 78, 'uint32', 'enhanced_elevation', :scale => 5, :offset => 500, :unit => 'm' field 83, 'uint16', 'vertical_ratio', :scale => 100, :unit => '%' # guessed field 84, 'uint16', 'gct_balance', :scale => 100, :unit => '%' # guessed @@ -740,6 +769,7 @@ module Fit4Ruby field 23, 'uint8', 'undocumented_field_23' field 24, 'uint32z', 'ant_id' # just a guess field 25, 'enum', 'source_type', :dict => 'source_type' + field 27, 'string', 'product_name' field 29, 'uint8', 'undocumented_field_29' field 30, 'uint8', 'undocumented_field_30' field 31, 'uint32', 'undocumented_field_31' diff --git a/lib/fit4ruby/Log.rb b/lib/fit4ruby/Log.rb index 0708244..0bf6bff 100644 --- a/lib/fit4ruby/Log.rb +++ b/lib/fit4ruby/Log.rb @@ -79,4 +79,3 @@ def fatal(msg, &block) end end -