Class: Jeti::Log::File

Inherits:
Object
  • Object
show all
Defined in:
lib/jeti/log/file.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(uri) ⇒ File

Returns a new instance of File.



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/jeti/log/file.rb', line 20

def initialize(uri)

  open(uri, 'rb') do |file|
    lines = file.readlines.map(&:strip).group_by do |line|
      line.start_with?('#') ? :comments : :rows
    end
    @name = /^#(.*)/.match(lines.fetch(:comments, ['# Unknown']).first)[1].strip

    unknown_index = 0
    @headers = []
    @entries = []
    lines[:rows].each_with_object(';').map(&:split).each do |line|
      if '000000000' == line.first
        if line.length == 3
          unknown_index+=1
          line << "?-#{unknown_index}"
          line << ''
        elsif line.length == 4
          line << ''
        elsif line.length == 5
          # do nothing
        else
          raise RuntimeError, "Unexpected header length (#{line.length})"
        end
        @headers << Header.new(line[0], line[1], line[2..4])
      else
        @entries << Entry.new(line[0], line[1], line[2..-1])
      end
    end

    raise RuntimeError, 'No headers found in log file' if @headers.empty?
    raise RuntimeError, 'No entries found in log file' if @entries.empty?
  end

rescue => e
  raise ArgumentError, "File does not appear to be a Jeti log (#{e})"
end

Instance Attribute Details

#nameObject (readonly)

Returns the value of attribute name.



10
11
12
# File 'lib/jeti/log/file.rb', line 10

def name
  @name
end

Class Method Details

.jeti?(uri) ⇒ Jeti::Log::File

Determines if the file at the given URI is a Jeti telemetry log file.

Parameters:

  • uri

    URI to file to read

Returns:

  • (Jeti::Log::File)

    loaded file if the file is a Jeti log file, nil otherwise



16
17
18
# File 'lib/jeti/log/file.rb', line 16

def self.jeti?(uri)
  File.new(uri) rescue nil
end

Instance Method Details

#device_present?(device) ⇒ Boolean

Returns:

  • (Boolean)


191
192
193
194
# File 'lib/jeti/log/file.rb', line 191

def device_present?(device)
  @headers.any? { |h| device =~ h.name }
  # XXX improve, make sure there are entries
end

#durationFloat

Gets the duration of the session, in seconds.

Returns:

  • (Float)

    duration of the session, in seconds



61
62
63
# File 'lib/jeti/log/file.rb', line 61

def duration
  (@entries.last.time - @entries.first.time) / 1000.0
end

#headers(id = nil) ⇒ Object



186
187
188
189
# File 'lib/jeti/log/file.rb', line 186

def headers(id = nil)
  return @headers if id.nil?
  return @headers.select { |h| h.id == id }
end

#mezon_dataObject



77
78
79
# File 'lib/jeti/log/file.rb', line 77

def mezon_data
  @mezon_data ||= Data::MezonDataBuilder.build(self)
end

#mezon_data?Boolean

Returns:

  • (Boolean)


73
74
75
# File 'lib/jeti/log/file.rb', line 73

def mezon_data?
  device_present?(/Mezon/i)
end

#mgps_dataObject



69
70
71
# File 'lib/jeti/log/file.rb', line 69

def mgps_data
  @mgps_data ||= Data::MGPSDataBuilder.build(self)
end

#mgps_data?Boolean

Returns:

  • (Boolean)


65
66
67
# File 'lib/jeti/log/file.rb', line 65

def mgps_data?
  device_present?(/MGPS/)
end

#mui_dataObject



85
86
87
# File 'lib/jeti/log/file.rb', line 85

def mui_data
  @mui_data ||= Data::MuiDataBuilder.build(self)
end

#mui_data?Boolean

Returns:

  • (Boolean)


81
82
83
# File 'lib/jeti/log/file.rb', line 81

def mui_data?
  device_present?(/MUI/)
end

#rx_dataObject



93
94
95
# File 'lib/jeti/log/file.rb', line 93

def rx_data
  @rx_data ||= Data::RxDataBuilder.build(self)
end

#rx_data?Boolean

Returns:

  • (Boolean)


89
90
91
# File 'lib/jeti/log/file.rb', line 89

def rx_data?
  device_present?(/Rx/)
end

#sensor_present?(device, sensor) ⇒ Boolean

Returns:

  • (Boolean)


196
197
198
199
200
201
# File 'lib/jeti/log/file.rb', line 196

def sensor_present?(device, sensor)
  headers, _entries = headers_and_entries_for_device(device)
  sensor_headers = (headers.select { |h| sensor =~ h.name })

  return sensor_headers.count > 0 && sensor_headers.first.sensor_id
end

#to_kml(file_options = {}, placemark_options = {}) ⇒ String

Converts the session into a KML document containing a placemark.

Parameters:

  • file_options (Hash) (defaults to: {})

    hash containing options for file

  • placemark_options (Hash) (defaults to: {})

    hash containing options for placemark

Returns:

  • (String)

    KML document for the session

Raises:

  • (RuntimeError)

See Also:



119
120
121
122
# File 'lib/jeti/log/file.rb', line 119

def to_kml(file_options = {}, placemark_options = {})
  raise RuntimeError, 'No coordinates available for KML generation' unless to_kml?
  to_kml_file(file_options, placemark_options).render
end

#to_kml?Boolean

Determines if KML methods can be called for this session.

Returns:

  • (Boolean)

    true if KML can be generated for this session, false otherwise



108
109
110
# File 'lib/jeti/log/file.rb', line 108

def to_kml?
  mgps_data?
end

#to_kml_file(file_options = {}, placemark_options = {}) ⇒ KMLFile

Converts the session into a KMLFile containing a placemark.

Parameters:

  • file_options (Hash) (defaults to: {})

    hash containing options for file

  • placemark_options (Hash) (defaults to: {})

    hash containing options for placemark

Options Hash (file_options):

  • :name (String)

    name option of KML::Document

  • :description (String)

    name option of KML::Document

  • :style_id (String)

    id option of KML::Style

Returns:

  • (KMLFile)

    file for the session

Raises:

  • (RuntimeError)

See Also:



133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/jeti/log/file.rb', line 133

def to_kml_file(file_options = {}, placemark_options = {})
  raise RuntimeError, 'No coordinates available for KML generation' unless to_kml?
  options = apply_default_file_options(file_options)

  kml = KMLFile.new
  kml.objects << KML::Document.new(
    name: options[:name],
    description: options[:description],
    styles: [
      KML::Style.new(
        id: options[:style_id],
        line_style: KML::LineStyle.new(color: '7F00FFFF', width: 4),
        poly_style: KML::PolyStyle.new(color: '7F00FF00')
      )
    ],
    features: [ to_kml_placemark(placemark_options) ]
  )
  kml
end

#to_kml_placemark(options = {}) ⇒ KML::Placemark

Converts the session into a KML::Placemark containing GPS coordinates.

Parameters:

  • options (Hash) (defaults to: {})

    hash containing options for placemark

Options Hash (options):

  • :altitude_mode (String)

    altitude_mode option of KML::LineString

  • :extrude (Boolean)

    extrude option of KML::LineString

  • :name (String)

    name option of KML::Placemark

  • :style_url (String)

    style_url option of KML::Placemark

  • :tessellate (Boolean)

    tessellate option of KML::LineString

Returns:

  • (KML::Placemark)

    placemark for the session

Raises:

  • (RuntimeError)


162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
# File 'lib/jeti/log/file.rb', line 162

def to_kml_placemark(options = {})
  raise RuntimeError, 'No coordinates available for KML generation' unless to_kml?
  options = apply_default_placemark_options(options)

  coords = mgps_data.map { |l| [l.longitude, l.latitude, l.altitude] }
  KML::Placemark.new(
    name: options[:name],
    style_url: options[:style_url],
    geometry: KML::LineString.new(
      altitude_mode: options[:altitude_mode],
      extrude: options[:extrude],
      tessellate: options[:tessellate],
      coordinates: coords.map { |c| c.join(',') }.join(' ')
    )
  )
end

#tx_dataObject



101
102
103
# File 'lib/jeti/log/file.rb', line 101

def tx_data
  @tx_data ||= Data::TxDataBuilder.build(self)
end

#tx_data?Boolean

Returns:

  • (Boolean)


97
98
99
# File 'lib/jeti/log/file.rb', line 97

def tx_data?
  device_present?(/Tx/)
end

#value_dataset(device, sensor) ⇒ Object



179
180
181
182
183
184
# File 'lib/jeti/log/file.rb', line 179

def value_dataset(device, sensor)
  headers, entries = headers_and_entries_for_device(device)
  sensor_id = (headers.select { |h| sensor =~ h.name })[0].sensor_id
  entries.reject! { |e| e.detail(sensor_id).nil? }
  entries.map { |e| [e.time, e.value(sensor_id)] }
end