lib/fit4ruby/Activity.rb
def check
      unless @timestamp && @timestamp >= Time.parse('1990-01-01T00:00:00+00:00')
        Log.fatal "Activity has no valid timestamp"
      end
      unless @total_timer_time
        Log.fatal "Activity has no valid total_timer_time"
      end
      unless @device_infos.length > 0
        Log.fatal "Activity must have at least one device_info section"
      end
      @device_infos.each.with_index { |d, index| d.check(index) }
      @sensor_settings.each.with_index { |s, index| s.check(index) }
      unless @num_sessions == @sessions.count
        Log.fatal "Activity record requires #{@num_sessions}, but "
                  "#{@sessions.length} session records were found in the "
                  "FIT file."
      end

      # Records must have consecutively growing timestamps and distances.
      ts = Time.parse('1989-12-31')
      distance = nil
      invalid_records = []
      @records.each_with_index do |r, idx|
        Log.fatal "Record has no timestamp" unless r.timestamp
        if r.timestamp < ts
          Log.fatal "Record has earlier timestamp than previous record"
        end
        if r.distance
          if distance && r.distance < distance
            # Normally this should be a fatal error as the FIT file is clearly
            # broken. Unfortunately, the Skiing/Boarding app in the Fenix3
            # produces such broken FIT files. So we just warn about this
            # problem and discard the earlier records.
            Log.error "Record #{r.timestamp} has smaller distance " +
                      "(#{r.distance}) than an earlier record (#{distance})."
            # Index of the list record to be discarded.
            (idx - 1).downto(0) do |i|
              if (ri = @records[i]).distance > r.distance
                # This is just an approximation. It looks like the app adds
                # records to the FIT file for runs that it meant to discard.
                # Maybe the two successive time start events are a better
                # criteria. But this workaround works for now.
                invalid_records << ri
              else
                # All broken records have been found.
                break
              end
            end
          end
          distance = r.distance
        end
        ts = r.timestamp
      end
      unless invalid_records.empty?
        # Delete all the broken records from the @records Array.
        Log.warn "Discarding #{invalid_records.length} earlier records"
        @records.delete_if { |r| invalid_records.include?(r) }
      end

      # Laps must have a consecutively growing message index.
      @laps.each.with_index do |lap, index|
        lap.check(index)
        # If we have heart rate zone records, there should be one for each
        # lap
        @heart_rate_zones[index].check(index) if @heart_rate_zones[index]
      end
      @sessions.each { |s| s.check(self) }
    end
def check
      unless @timestamp && @timestamp >= Time.parse('1990-01-01T00:00:00+00:00')
        Log.fatal "Activity has no valid timestamp"
      end
      unless @total_timer_time
        Log.fatal "Activity has no valid total_timer_time"
      end
      unless @device_infos.length > 0
        Log.fatal "Activity must have at least one device_info section"
      end
      @device_infos.each.with_index { |d, index| d.check(index) }
      @sensor_settings.each.with_index { |s, index| s.check(index) }
      unless @num_sessions == @sessions.count
        Log.fatal "Activity record requires #{@num_sessions}, but "
                  "#{@sessions.length} session records were found in the "
                  "FIT file."
      end

      # Records must have consecutively growing timestamps and distances.
      ts = Time.parse('1989-12-31')
      distance = nil
      invalid_records = []
      @records.each_with_index do |r, idx|
        Log.fatal "Record has no timestamp" unless r.timestamp
        if r.timestamp < ts
          Log.fatal "Record has earlier timestamp than previous record"
        end
        if r.distance
          if distance && r.distance < distance
            # Normally this should be a fatal error as the FIT file is clearly
            # broken. Unfortunately, the Skiing/Boarding app in the Fenix3
            # produces such broken FIT files. So we just warn about this
            # problem and discard the earlier records.
            Log.error "Record #{r.timestamp} has smaller distance " +
                      "(#{r.distance}) than an earlier record (#{distance})."
            # Index of the list record to be discarded.
            (idx - 1).downto(0) do |i|
              if (ri = @records[i]).distance > r.distance
                # This is just an approximation. It looks like the app adds
                # records to the FIT file for runs that it meant to discard.
                # Maybe the two successive time start events are a better
                # criteria. But this workaround works for now.
                invalid_records << ri
              else
                # All broken records have been found.
                break
              end
            end
          end
          distance = r.distance
        end
        ts = r.timestamp
      end
      unless invalid_records.empty?
        # Delete all the broken records from the @records Array.
        Log.warn "Discarding #{invalid_records.length} earlier records"
        @records.delete_if { |r| invalid_records.include?(r) }
      end

      # Laps must have a consecutively growing message index.
      @laps.each.with_index do |lap, index|
        lap.check(index)
        # If we have heart rate zone records, there should be one for each
        # lap
        @heart_rate_zones[index].check(index) if @heart_rate_zones[index]
      end
      @sessions.each { |s| s.check(self) }
    end
Create a new Activity object.
 @param field_values [Hash] A Hash that provides initial values for
        certain fields of the FitDataRecord.
 Perform some basic logical checks on the object and all references sub
 objects. Any errors will be reported via the Log object.