Class: Cosmos::HousekeepingDataObject

Inherits:
LinegraphDataObject show all
Defined in:
lib/cosmos/tools/tlm_grapher/data_objects/housekeeping_data_object.rb

Overview

Represents a data object on a line graph for a housekeeping telemetry item

Constant Summary collapse

VALUE_TYPES =

Value Types

[:RAW, :CONVERTED]
ANALYSIS_TYPES =

Analysis Types

[:NONE, :DIFFERENCE, :WINDOWED_MEAN, :WINDOWED_MEAN_REMOVED, :STD_DEV, :ALLAN_DEV, :MAXIMUM, :MINIMUM, :PEAK_TO_PEAK]

Constants inherited from LinegraphDataObject

LinegraphDataObject::Y_AXIS_CHOICES

Constants inherited from DataObject

DataObject::COLOR_LIST, DataObject::DEFAULT_ARRAY_SIZE, DataObject::PRUNE_HYSTERISIS_PERCENTAGE

Instance Attribute Summary collapse

Attributes inherited from LinegraphDataObject

#formatted_x_values, #horizontal_lines, #x_states, #x_values, #y_axis, #y_offset, #y_states, #y_values

Attributes inherited from DataObject

#assigned_color, #color, #data_object_type, #error, #first_x_value, #max_points_saved, #plot

Instance Method Summary collapse

Methods inherited from LinegraphDataObject

#export

Methods inherited from DataObject

#export, #handle_process_exception, #popup_modifier

Constructor Details

#initializeHousekeepingDataObject

Create a new data object



56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/cosmos/tools/tlm_grapher/data_objects/housekeeping_data_object.rb', line 56

def initialize
  super()
  @target_name = nil
  @packet_name = nil
  @item_name = nil
  @time_item_name = nil
  @formatted_time_item_name = nil
  @value_type = :CONVERTED
  @analysis = :NONE
  @analysis_samples = 3
  @show_limits_lines = false
  @limits_lines = []
  @num_samples_ahead = 1
  @num_samples_behind = 1
  @unprocessed_x_values = LowFragmentationArray.new(DEFAULT_ARRAY_SIZE)
  @unprocessed_y_values = LowFragmentationArray.new(DEFAULT_ARRAY_SIZE)
  @differenced_y_values = LowFragmentationArray.new(DEFAULT_ARRAY_SIZE)
end

Instance Attribute Details

#analysisObject

The analysis to perform



44
45
46
# File 'lib/cosmos/tools/tlm_grapher/data_objects/housekeeping_data_object.rb', line 44

def analysis
  @analysis
end

#analysis_samplesObject

The number of samples to use in the analysis (integer)



47
48
49
# File 'lib/cosmos/tools/tlm_grapher/data_objects/housekeeping_data_object.rb', line 47

def analysis_samples
  @analysis_samples
end

#formatted_time_item_nameObject

The housekeeping telemetry item’s formatted time item name (string)



38
39
40
# File 'lib/cosmos/tools/tlm_grapher/data_objects/housekeeping_data_object.rb', line 38

def formatted_time_item_name
  @formatted_time_item_name
end

#item_nameObject (readonly)

The housekeeping telemetry item’s item name (string)



32
33
34
# File 'lib/cosmos/tools/tlm_grapher/data_objects/housekeeping_data_object.rb', line 32

def item_name
  @item_name
end

#limits_linesObject

Limits lines for telemetry item (array)



53
54
55
# File 'lib/cosmos/tools/tlm_grapher/data_objects/housekeeping_data_object.rb', line 53

def limits_lines
  @limits_lines
end

#packet_nameObject (readonly)

The housekeeping telemetry item’s packet name (string)



29
30
31
# File 'lib/cosmos/tools/tlm_grapher/data_objects/housekeeping_data_object.rb', line 29

def packet_name
  @packet_name
end

#show_limits_linesObject

Automatically show limits line for telemetry item (true or false)



50
51
52
# File 'lib/cosmos/tools/tlm_grapher/data_objects/housekeeping_data_object.rb', line 50

def show_limits_lines
  @show_limits_lines
end

#target_nameObject (readonly)

The housekeeping telemetry item’s target name (string)



26
27
28
# File 'lib/cosmos/tools/tlm_grapher/data_objects/housekeeping_data_object.rb', line 26

def target_name
  @target_name
end

#time_item_nameObject

The housekeeping telemetry item’s time item name (string)



35
36
37
# File 'lib/cosmos/tools/tlm_grapher/data_objects/housekeeping_data_object.rb', line 35

def time_item_name
  @time_item_name
end

#value_typeObject

Type of data to collect - :RAW or :CONVERTED



41
42
43
# File 'lib/cosmos/tools/tlm_grapher/data_objects/housekeeping_data_object.rb', line 41

def value_type
  @value_type
end

Instance Method Details

#configuration_stringObject

Returns the configuration lines used to create this data object



76
77
78
79
80
81
82
83
84
85
86
# File 'lib/cosmos/tools/tlm_grapher/data_objects/housekeeping_data_object.rb', line 76

def configuration_string
  string = super()
  string << "      ITEM #{@target_name} #{@packet_name} #{@item_name}\n" if @target_name and @packet_name and @item_name
  string << "      TIME_ITEM #{@time_item_name}\n" if @time_item_name
  string << "      FORMATTED_TIME_ITEM #{@formatted_time_item_name}\n" if @formatted_time_item_name
  string << "      VALUE_TYPE #{@value_type}\n"
  string << "      ANALYSIS #{@analysis}\n"
  string << "      ANALYSIS_SAMPLES #{@analysis_samples}\n"
  string << "      SHOW_LIMITS_LINES #{@show_limits_lines.to_s.upcase}\n"
  string
end

#copyObject

Creates a copy of the data object with settings but without data



282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
# File 'lib/cosmos/tools/tlm_grapher/data_objects/housekeeping_data_object.rb', line 282

def copy
  data_object = super()
  if @target_name and @packet_name and @item_name
    data_object.set_item(@target_name.clone, @packet_name.clone, @item_name.clone)
  end
  data_object.time_item_name = @time_item_name.clone if @time_item_name
  data_object.formatted_time_item_name = @formatted_time_item_name.clone if @formatted_time_item_name
  data_object.value_type = @value_type
  data_object.analysis = @analysis
  data_object.analysis_samples = @analysis_samples
  data_object.show_limits_lines = @show_limits_lines
  if @target_name
    limits_lines = []
    get_limits_lines()
    @limits_lines.each do |y_value, color|
      limits_lines << [y_value, color.clone]
    end
    data_object.limits_lines = limits_lines
  end
  data_object
end

#edit(editted_data_object) ⇒ Object

Edits the data object - show_limits_lines is the only edit_safe? attribute



305
306
307
308
# File 'lib/cosmos/tools/tlm_grapher/data_objects/housekeeping_data_object.rb', line 305

def edit(editted_data_object)
  self.show_limits_lines = editted_data_object.show_limits_lines
  super(editted_data_object)
end

#edit_safe?(editted_data_object) ⇒ Boolean

Determines if changes can be made to the data object without affecting data

Parameters:

  • edited_data_object (DataObject)

    The data object which was edited

Returns:

  • (Boolean)


313
314
315
316
317
318
319
320
321
322
323
324
325
326
# File 'lib/cosmos/tools/tlm_grapher/data_objects/housekeeping_data_object.rb', line 313

def edit_safe?(editted_data_object)
  if @target_name != editted_data_object.target_name or
     @packet_name != editted_data_object.packet_name or
     @item_name != editted_data_object.item_name or
     @time_item_name != editted_data_object.time_item_name or
     @formatted_time_item_name != editted_data_object.formatted_time_item_name or
     @value_type != editted_data_object.value_type or
     @analysis != editted_data_object.analysis or
     @analysis_samples != editted_data_object.analysis_samples
    false
  else
    super(editted_data_object)
  end
end

#handle_keyword(parser, keyword, parameters) ⇒ Object

Handles data object specific keywords



89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# File 'lib/cosmos/tools/tlm_grapher/data_objects/housekeeping_data_object.rb', line 89

def handle_keyword(parser, keyword, parameters)
  case keyword
  when 'ITEM'
    # Expect 3 parameters
    parser.verify_num_parameters(3, 3, "ITEM <Target Name> <Packet Name> <Item Name>")
    set_item(parameters[0], parameters[1], parameters[2], true)

  when 'TIME_ITEM'
    # Expect 1 parameter
    parser.verify_num_parameters(1, 1, "TIME_ITEM <Time Item Name>")
    @time_item_name = parameters[0].upcase

  when 'FORMATTED_TIME_ITEM'
    # Expect 1 parameter
    parser.verify_num_parameters(1, 1, "FORMATTED_TIME_ITEM <Formatted Time Item Name>")
    @formatted_time_item_name = parameters[0].upcase

  when 'VALUE_TYPE'
    # Expect 1 parameter
    parser.verify_num_parameters(1, 1, "VALUE_TYPE <RAW or CONVERTED>")
    value_type = parameters[0].upcase.intern
    if VALUE_TYPES.include?(value_type)
      @value_type = value_type
    else
      raise ArgumentError, "Unknown VALUE_TYPE value: #{value_type}"
    end

  when 'ANALYSIS'
    # Expect 1 parameter
    parser.verify_num_parameters(1, 1, "ANALYSIS <Analysis Type>")
    analysis = parameters[0].upcase.intern
    if ANALYSIS_TYPES.include?(analysis)
      @analysis = analysis
    else
      raise ArgumentError, "Unknown ANALYSIS value: #{analysis}"
    end

  when 'ANALYSIS_SAMPLES'
    # Expect 1 parameter
    parser.verify_num_parameters(1, 1, "ANALYSIS_SAMPLES <Number of Samples>")
    self.analysis_samples = parameters[0].to_i

  when 'SHOW_LIMITS_LINES'
    # Expect 1 parameter
    parser.verify_num_parameters(1, 1, "SHOW_LIMITS_LINES <TRUE or FALSE>")
    self.show_limits_lines = ConfigParser.handle_true_false(parameters[0])

  else
    # Unknown keywords are passed to parent data object
    super(parser, keyword, parameters)

  end # case keyword

end

#nameObject

Returns the name of this data object



261
262
263
264
265
266
267
268
269
270
271
# File 'lib/cosmos/tools/tlm_grapher/data_objects/housekeeping_data_object.rb', line 261

def name
  if @target_name and @packet_name and @item_name
    if @analysis != :NONE
      "#{@target_name} #{@packet_name} #{@item_name} (#{@analysis})"
    else
      "#{@target_name} #{@packet_name} #{@item_name}"
    end
  else
    ''
  end
end

#process_packet(packet, count) ⇒ Object

Processes a packet associated with this data object

Parameters:

  • packet (Packet)

    The packet to process

  • count (Integer)

    Count which increments for each packet received by the higher level process



154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
# File 'lib/cosmos/tools/tlm_grapher/data_objects/housekeeping_data_object.rb', line 154

def process_packet(packet, count)
  begin
    # Retrieve x and y values from packet
    x_value = packet.read(@time_item_name)
    if @value_type == :RAW
      y_value = packet.read(@item_name, :RAW)
    else
      y_value = packet.read(@item_name)
    end
    # Bail on the values if they are NaN or nil as we can't graph them
    return if x_value.nil? || y_value.nil? ||
      (x_value.respond_to?(:nan?) && x_value.nan?) ||
      (y_value.respond_to?(:nan?) && y_value.nan?)
    @formatted_x_values << packet.read(@formatted_time_item_name) if @formatted_time_item_name

    upper_index  = nil
    sample_index = nil
    lower_index  = nil
    if @analysis != :NONE
      # Add to unprocessed data
      @unprocessed_x_values << x_value
      @unprocessed_y_values << y_value

      # Calculate indexes into arrays
      upper_index  = @unprocessed_y_values.length - 1
      sample_index = upper_index - @num_samples_ahead
      lower_index  = sample_index - @num_samples_behind
    end

    case @analysis
    when :NONE
      @x_values << x_value
      @y_values << (y_value + @y_offset)
      @plot.redraw_needed = true

    when :DIFFERENCE, :ALLAN_DEV
      if @unprocessed_y_values.length > 1
        differenced_y_value = y_value - @unprocessed_y_values[-2]

        if @analysis == :DIFFERENCE
          @x_values << x_value
          @y_values << (differenced_y_value + @y_offset)
          @plot.redraw_needed = true
        else # @analysis == :ALLAN_DEV
          @differenced_y_values[upper_index] = differenced_y_value

          if lower_index >= 1
            data = @differenced_y_values[lower_index..upper_index]
            @x_values << @unprocessed_x_values[sample_index]
            @y_values << (Math.sqrt(data.squared.sum / (@analysis_samples * 2)) + @y_offset)
            @plot.redraw_needed = true
          end
        end
      end

    when :WINDOWED_MEAN, :WINDOWED_MEAN_REMOVED, :STD_DEV
      if @unprocessed_y_values.length >= @analysis_samples
        # Isolate window of data
        data = @unprocessed_y_values[lower_index..upper_index]

        # Calculate windowed mean
        mean = data.mean

        case @analysis
        when :WINDOWED_MEAN
          @y_values << (mean + @y_offset)
        when :WINDOWED_MEAN_REMOVED
          @y_values << (@unprocessed_y_values[sample_index] - mean + @y_offset)
        when :STD_DEV
          data.map! {|value| value - mean}
          @y_values << (Math.sqrt(data.squared.sum / (@analysis_samples - 1)) + @y_offset)
        end
        @x_values << @unprocessed_x_values[sample_index]
        @plot.redraw_needed = true
      end

    when :MAXIMUM, :MINIMUM, :PEAK_TO_PEAK
      if @unprocessed_y_values.length >= @analysis_samples
        case @analysis
        when :MAXIMUM
          @y_values << (@unprocessed_y_values[lower_index..upper_index].max + @y_offset)
        when :MINIMUM
          @y_values << (@unprocessed_y_values[lower_index..upper_index].min + @y_offset)
        else # :PEAK_TO_PEAK
          @y_values << ((@unprocessed_y_values[lower_index..upper_index].max - @unprocessed_y_values[lower_index..upper_index].min) + @y_offset)
        end
        @x_values << @unprocessed_x_values[sample_index]
        @plot.redraw_needed = true
      end

    end # case @analysis

    # Make sure the data object's first x value is the smallest x value encountered during packet processing.
    # This is the smallest x_value seen since reset() was called, i.e. it doesn't get deleted during pruning.
    # We make the assumption that @x_values is ordered, so just check against its first value.
    if (@x_values.length > 0) and (@x_values[0] < @first_x_value)
      @first_x_value = @x_values[0]
    end

    # Prune Data
    prune_to_max_points_saved()
  rescue Exception => error
    handle_process_exception(error, "#{packet.target_name} #{packet.packet_name} #{@item_name}")
  end
end

#processed_packetsObject

Returns the packet processed by this data object



145
146
147
148
149
150
151
# File 'lib/cosmos/tools/tlm_grapher/data_objects/housekeeping_data_object.rb', line 145

def processed_packets
  if @target_name and @packet_name
    [[@target_name, @packet_name]]
  else
    []
  end
end

#resetObject

Resets the data object



274
275
276
277
278
279
# File 'lib/cosmos/tools/tlm_grapher/data_objects/housekeeping_data_object.rb', line 274

def reset
  super()
  @unprocessed_x_values = LowFragmentationArray.new(DEFAULT_ARRAY_SIZE)
  @unprocessed_y_values = LowFragmentationArray.new(DEFAULT_ARRAY_SIZE)
  @differenced_y_values = LowFragmentationArray.new(DEFAULT_ARRAY_SIZE)
end

#set_item(target_name, packet_name, item_name, during_configuration = false) ⇒ Object

Sets the housekeeping item



329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
# File 'lib/cosmos/tools/tlm_grapher/data_objects/housekeeping_data_object.rb', line 329

def set_item(target_name, packet_name, item_name, during_configuration = false)
  @target_name = target_name
  @packet_name = packet_name
  @item_name = item_name
  _, item = System.telemetry.packet_and_item(@target_name, @packet_name, @item_name)
  @y_states = item.states
  if @y_states
    @value_type = :RAW
  else
    @value_type = :CONVERTED unless during_configuration
  end

  @time_item_name = 'RECEIVED_TIMESECONDS' unless @time_item_name

  # Update limits lines
  self.show_limits_lines = @show_limits_lines
end