Class: Argos::Diag

Inherits:
Array
  • Object
show all
Includes:
Ascii
Defined in:
lib/argos/diag.rb

Overview

Argos DIAG file parser

Usage

diag = Argos::Diag.new
puts diag.parse(filename).to_json

Author:

  • Espen Egeland

  • Conrad Helgeland

Constant Summary collapse

LOCATION_CLASS =
["3", "2", "1", "0", "A", "B", "Z"]
START_REGEX =
/^(\s*Prog\s\d{4,}|\s*\d{5,6}\s+Date : \d{2}.\d{2}.\d{2} \d{2}:\d{2}:\d{2})/

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Ascii

argos?, factory, #latitudes, #longitudes, #platforms, source, type

Constructor Details

#initializeDiag

Returns a new instance of Diag.



53
54
55
56
57
# File 'lib/argos/diag.rb', line 53

def initialize
  @errors = []
  @programs = []
  @log = Logger.new(STDERR)
end

Instance Attribute Details

#bundleObject

Returns the value of attribute bundle.



17
18
19
# File 'lib/argos/diag.rb', line 17

def bundle
  @bundle
end

#errorsObject (readonly)

Returns the value of attribute errors.



19
20
21
# File 'lib/argos/diag.rb', line 19

def errors
  @errors
end

#filenameObject

Returns the value of attribute filename.



17
18
19
# File 'lib/argos/diag.rb', line 17

def filename
  @filename
end

#filesizeObject (readonly)

Returns the value of attribute filesize.



19
20
21
# File 'lib/argos/diag.rb', line 19

def filesize
  @filesize
end

#filterObject

Returns the value of attribute filter.



19
20
21
# File 'lib/argos/diag.rb', line 19

def filter
  @filter
end

#filternameObject (readonly)

Returns the value of attribute filtername.



19
20
21
# File 'lib/argos/diag.rb', line 19

def filtername
  @filtername
end

#logObject

Returns the value of attribute log.



17
18
19
# File 'lib/argos/diag.rb', line 17

def log
  @log
end

#multiplicatesObject (readonly)

Returns the value of attribute multiplicates.



19
20
21
# File 'lib/argos/diag.rb', line 19

def multiplicates
  @multiplicates
end

#programsObject

Returns the value of attribute programs.



17
18
19
# File 'lib/argos/diag.rb', line 17

def programs
  @programs
end

#sha1Object (readonly)

Returns the value of attribute sha1.



19
20
21
# File 'lib/argos/diag.rb', line 19

def sha1
  @sha1
end

#updatedObject (readonly)

Returns the value of attribute updated.



19
20
21
# File 'lib/argos/diag.rb', line 19

def updated
  @updated
end

#validObject (readonly)

Returns the value of attribute valid.



19
20
21
# File 'lib/argos/diag.rb', line 19

def valid
  @valid
end

Instance Method Details

#check_format(contact, line_num) ⇒ Object



158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/argos/diag.rb', line 158

def check_format(contact, line_num)

  if contact =~ /#{$FORMAT_1}/ or contact =~ /#{$FORMAT_2}/ or contact =~ /#{$FORMAT_2}/
    self << create_diag_hash(contact)
    true
  elsif contact =~ /Prog\s\d{5,}$/
    true
  else
    error = "#{filename}:#{line_num} sha1:#{@sha1} Invalid format:\n"  + contact
    @errors << error
    log.error error
    false
  end

end

#convert_date(date) ⇒ Object



268
269
270
271
272
# File 'lib/argos/diag.rb', line 268

def convert_date(date)
  timestamp = DateTime.strptime(date, '%d.%m.%y %H:%M:%S').iso8601.to_s
  timestamp['+00:00'] = "Z"
  timestamp
end

#coordinate_conv(co) ⇒ Object



256
257
258
259
260
261
262
263
264
265
# File 'lib/argos/diag.rb', line 256

def coordinate_conv (co)
  if co =~ /\?+/
    co = nil
  elsif co =~/N$/ || co =~/E$/
    co=co.chop.to_f
  elsif co =~/S$/ || co =~/W$/
    co = co.chop.to_f
    co = co * -1
  end
end

#create_diag_hash(contact = "") ⇒ Object

www.argos-system.org/files/pmedia/public/r363_9_argos_users_manual-v1.5.pdf p. 48 Nb mes : 025 Number of messages received Nb mes>-120 dB: 015 Number of messages received by the satellite at a signal strength greater than -120 decibels Best level : -113 dB Best signal strength, units are dB Pass duration : 900s Time elapsed between the first and last message received by the satellite NOPC = 4 Number Of Plausibility Checks successful (from 0-4) Calcul Freq : 401 650000.3 Calculated frequency Altitude : 213 m Altitude used for location calculation



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
# File 'lib/argos/diag.rb', line 183

def create_diag_hash(contact="")
  platform = contact[/^(\d{5,})/,1]
  location_data = contact[/Date : (\d{2}.\d{2}.\d{2} \d{2}:\d{2}:\d{2})/,1]
  contact_time = convert_date(location_data) unless location_data ==nil
  lc = contact[/LC : (3|2|1|0|A|B|Z)/,1]
  li = contact[/LI : *(\-?\d+)/,1]
  iq= contact[/IQ : *(\d{2})/,1]
  lat1= contact[/Lat1 : +(\d+\.\d{3}[NS]|\?+)/,1]
  lat1 = coordinate_conv(lat1)
  lon1= contact[/Lon1 : +(\d+\.\d{3}[EW]|\?+)/,1]
  lon1 = coordinate_conv(lon1)
  lat2= contact[/Lat2 : +(\d+\.\d{3}[NS]|\?+)/,1]
  lat2 = coordinate_conv(lat2)
  lon2= contact[/Lon2 : +(\d+\.\d{3}[EW]|\?+)/,1]
  lon2 = coordinate_conv(lon2)
  nb= contact[/Nb mes : (\d{3})/,1]
  nb120= contact[/Nb mes>-120(Db|dB) : (\d{3})/,2]
  best_level= contact[/Best level : (-\d{3})/,1]
  pass_duration= contact[/Pass duration : +(\d+|\?)/,1]
  dist_track= contact[/Dist track : +(\d+)/,1]
  nopc= contact[/NOPC : +([0-4]|\?)/,1]
  nopc = nopc =~/\?+/ ? nil : nopc
  nopc.to_i unless nopc == nil
  frequency = contact[/Calcul freq : +(\d{3} \d+\.\d+)/,1]
  if frequency =~ /[ ]/
    frequency = frequency.split(" ").join("").to_f
  end
  altitude= contact[/Altitude : +(\d+)? m/,1]
  altitude = altitude.to_i unless altitude == nil
  
  data_start = contact.index(" m ")
  sensor_data = contact[data_start+2,contact.length].split(" ") unless data_start == nil
 
  diag = { platform: platform.to_i,
    measured: contact_time,
    lc: lc,
    iq: iq,
    li: li,
    latitude: lat1,
    longitude: lon1,
    latitude2: lat2,
    longitude2: lon2,
    messages: nb.to_i,
    messages_120dB: nb120.to_i,
    best_level: best_level.to_i,
    pass_duration: pass_duration.to_i,
    dist_track: dist_track,
    nopc: nopc.to_i,
    frequency: frequency,
    altitude: altitude,
    sensor_data: sensor_data,
    technology: "argos",
    type: type,
    file: "file://"+filename,
    source: "#{sha1}",
  }

  idbase = diag.clone
  idbase.delete :file
  id = Digest::SHA1.hexdigest(idbase.to_json)

  diag[:parser] = Argos.library_version
  diag[:id] = id
  diag[:bundle] = bundle
  if @program
    diag[:program] = @program
  end
  
  
  diag
end

#filter?Boolean

Returns:

  • (Boolean)


59
60
61
# File 'lib/argos/diag.rb', line 59

def filter?
  not @filter.nil?
end

#messagesObject



278
279
280
# File 'lib/argos/diag.rb', line 278

def messages
  self
end

#parse(filename = nil) ⇒ Object

Returns [].

Returns:



73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
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
143
144
145
146
147
148
149
150
151
152
153
154
155
# File 'lib/argos/diag.rb', line 73

def parse(filename=nil)
  if filename.nil?
    filename = @filename
  end
  
  self.clear # Needed if you parse multiple times
  @total = linecount = 0
  @valid = false

  filename = File.realpath(filename)
  @filename = filename
  if filename.nil? or not File.exists? filename
    raise ArgumentError, "Missing ARGOS DS file: \"#{filename}\""
  end
  @sha1 = Digest::SHA1.file(filename).hexdigest 


  valid_file = false

  contact = []
  file = File.open(filename)
  @filesize = file.size
  @updated = file.mtime.utc

  log.debug "Parsing Argos DIAG file #{filename} sha1:#{sha1} (#{filesize} bytes)"
  if filter?
    log.info "Using filter: #{filter}"
  end


  linecount = 0
  match =0
  startline = 0
  contact =""
  
  File.open(filename, :encoding => "iso-8859-1").each do |line|
    line = line.to_s.strip
    linecount+=1
    
    if line =~ /Prog\s(\d{4,})$/
      @program = line.split(" ").last
      log.info "Program: #{@program}"
      @programs << @program
    end
    
    if line =~ START_REGEX
      match+=1
      if contact !=""
        check_format(contact.strip, startline)
      end
      contact = line
      startline = linecount
    else
      contact = contact + " " +line
    end
  end
  
  check_format(contact.strip, startline)
  
  @programs = @programs.uniq
  if filter?
    total = self.size
    filtered = self.select{|diag| filter.call(diag)}    
    log.info "Selected #{filtered.size}/#{total} Argos DIAG segments sha1:#{sha1} #{filename}"
    self.clear
    # programs may be wrong now!
    filtered.each do |filtered|
      self << filtered
    end
  else
    log.info "Parsed #{self.size} Argos DIAG segments sha1:#{sha1} #{filename}"
  end
  
  @multiplicates = group_by { |e| e }.select { |k, v| v.size > 1 }.map(&:first)
  if multiplicates.any?
    #log.warn "#{multiplicates.size} multiplicates in source sha1 #{sha1} #{filename}): #{multiplicates.map {|a|a[:id]} }"
    self.uniq!
    log.info "Unique DIAG messages: #{self.size} sha1: #{sha1} #{filename}"
  end
  self.sort_by! {|diag| diag[:measured]}

  self
end

#sourceObject



295
296
297
# File 'lib/argos/diag.rb', line 295

def source
  sha1
end

#startObject



286
287
288
# File 'lib/argos/diag.rb', line 286

def start
  first[:measured]
end

#stopObject



290
291
292
# File 'lib/argos/diag.rb', line 290

def stop
  last[:measured]
end

#typeObject



274
275
276
# File 'lib/argos/diag.rb', line 274

def type
  "diag"
end