openc3-cosmos-geosim 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE.txt +729 -0
- data/README.md +15 -0
- data/Rakefile +21 -0
- data/microservices/GEOSIM/geosat_target.rb +140 -0
- data/microservices/GEOSIM/targets/GEOSAT/cmd_tlm/cmds.txt +165 -0
- data/microservices/GEOSIM/targets/GEOSAT/cmd_tlm/tlm.txt +273 -0
- data/microservices/GEOSIM/targets/GEOSAT/data/attitude.bin +0 -0
- data/microservices/GEOSIM/targets/GEOSAT/data/position.bin +0 -0
- data/microservices/GEOSIM/targets/GEOSAT/lib/sim_sat.rb +624 -0
- data/microservices/GEOSIM/targets/GEOSAT/target.txt +30 -0
- data/plugin.txt +9 -0
- metadata +57 -0
@@ -0,0 +1,624 @@
|
|
1
|
+
# encoding: ascii-8bit
|
2
|
+
|
3
|
+
# Copyright 2022 OpenC3, Inc.
|
4
|
+
# All Rights Reserved.
|
5
|
+
#
|
6
|
+
# This program is free software; you can modify and/or redistribute it
|
7
|
+
# under the terms of the GNU Affero General Public License
|
8
|
+
# as published by the Free Software Foundation; version 3 with
|
9
|
+
# attribution addendums as found in the LICENSE.txt
|
10
|
+
#
|
11
|
+
# This program is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
# GNU Affero General Public License for more details.
|
15
|
+
|
16
|
+
# Simulates the fake satellite used in COSMOS User Training
|
17
|
+
|
18
|
+
require 'openc3'
|
19
|
+
|
20
|
+
module OpenC3
|
21
|
+
MAX_PWR_WATT_SECONDS = 100000
|
22
|
+
INIT_PWR_WATT_SECONDS = 25000
|
23
|
+
HYSTERESIS = 2.0
|
24
|
+
|
25
|
+
# Simulated satellite for the training. Populates several packets and cycles
|
26
|
+
# the telemetry to simulate a real satellite.
|
27
|
+
class SimSat < SimulatedTarget
|
28
|
+
def initialize(target_name)
|
29
|
+
super(target_name)
|
30
|
+
|
31
|
+
@target = System.targets[target_name]
|
32
|
+
position_filename = File.join(@target.dir, 'data', 'position.bin')
|
33
|
+
attitude_filename = File.join(@target.dir, 'data', 'attitude.bin')
|
34
|
+
@position_file = File.open(position_filename, 'rb')
|
35
|
+
@attitude_file = File.open(attitude_filename, 'rb')
|
36
|
+
@position_file_size = File.size(position_filename)
|
37
|
+
@attitude_file_size = File.size(attitude_filename)
|
38
|
+
@position_file_bytes_read = 0
|
39
|
+
@attitude_file_bytes_read = 0
|
40
|
+
|
41
|
+
@pos_packet = Structure.new(:BIG_ENDIAN)
|
42
|
+
@pos_packet.append_item('DAY', 16, :UINT)
|
43
|
+
@pos_packet.append_item('MSOD', 32, :UINT)
|
44
|
+
@pos_packet.append_item('USOMS', 16, :UINT)
|
45
|
+
@pos_packet.append_item('POSX', 32, :FLOAT)
|
46
|
+
@pos_packet.append_item('POSY', 32, :FLOAT)
|
47
|
+
@pos_packet.append_item('POSZ', 32, :FLOAT)
|
48
|
+
@pos_packet.append_item('SPARE1', 16, :UINT)
|
49
|
+
@pos_packet.append_item('SPARE2', 32, :UINT)
|
50
|
+
@pos_packet.append_item('SPARE3', 16, :UINT)
|
51
|
+
@pos_packet.append_item('VELX', 32, :FLOAT)
|
52
|
+
@pos_packet.append_item('VELY', 32, :FLOAT)
|
53
|
+
@pos_packet.append_item('VELZ', 32, :FLOAT)
|
54
|
+
@pos_packet.append_item('SPARE4', 32, :UINT)
|
55
|
+
@pos_packet.enable_method_missing
|
56
|
+
|
57
|
+
@att_packet = Structure.new(:BIG_ENDIAN)
|
58
|
+
@att_packet.append_item('DAY', 16, :UINT)
|
59
|
+
@att_packet.append_item('MSOD', 32, :UINT)
|
60
|
+
@att_packet.append_item('USOMS', 16, :UINT)
|
61
|
+
@att_packet.append_item('Q1', 32, :FLOAT)
|
62
|
+
@att_packet.append_item('Q2', 32, :FLOAT)
|
63
|
+
@att_packet.append_item('Q3', 32, :FLOAT)
|
64
|
+
@att_packet.append_item('Q4', 32, :FLOAT)
|
65
|
+
@att_packet.append_item('BIASX', 32, :FLOAT)
|
66
|
+
@att_packet.append_item('BIASY', 32, :FLOAT)
|
67
|
+
@att_packet.append_item('BIASZ', 32, :FLOAT)
|
68
|
+
@att_packet.append_item('SPARE', 32, :FLOAT)
|
69
|
+
@att_packet.enable_method_missing
|
70
|
+
|
71
|
+
# Initialize fixed parts of packets
|
72
|
+
packet = @tlm_packets['HEALTH_STATUS']
|
73
|
+
packet.enable_method_missing
|
74
|
+
packet.CcsdsSeqFlags = 'NOGROUP'
|
75
|
+
packet.CcsdsLength = packet.buffer.length - 7
|
76
|
+
|
77
|
+
packet = @tlm_packets['THERMAL']
|
78
|
+
packet.enable_method_missing
|
79
|
+
packet.CcsdsSeqFlags = 'NOGROUP'
|
80
|
+
packet.CcsdsLength = packet.buffer.length - 7
|
81
|
+
|
82
|
+
packet = @tlm_packets['EVENT']
|
83
|
+
packet.enable_method_missing
|
84
|
+
packet.CcsdsSeqFlags = 'NOGROUP'
|
85
|
+
packet.CcsdsLength = packet.buffer.length - 7
|
86
|
+
|
87
|
+
packet = @tlm_packets['ADCS']
|
88
|
+
packet.enable_method_missing
|
89
|
+
packet.CcsdsSeqFlags = 'NOGROUP'
|
90
|
+
packet.CcsdsLength = packet.buffer.length - 7
|
91
|
+
|
92
|
+
packet = @tlm_packets['IMAGE']
|
93
|
+
packet.enable_method_missing
|
94
|
+
packet.CcsdsSeqFlags = 'NOGROUP'
|
95
|
+
packet.image = ("\x05" * 10000) + "The Secret is Astral Body"
|
96
|
+
packet.CcsdsLength = packet.buffer.length - 7
|
97
|
+
|
98
|
+
packet = @tlm_packets['MECH']
|
99
|
+
packet.enable_method_missing
|
100
|
+
packet.CcsdsSeqFlags = 'NOGROUP'
|
101
|
+
packet.CcsdsLength = packet.buffer.length - 7
|
102
|
+
|
103
|
+
packet = @tlm_packets['IMAGER']
|
104
|
+
packet.enable_method_missing
|
105
|
+
packet.CcsdsSeqFlags = 'NOGROUP'
|
106
|
+
packet.CcsdsLength = packet.buffer.length - 7
|
107
|
+
|
108
|
+
@get_count = 0
|
109
|
+
@queue = Queue.new
|
110
|
+
|
111
|
+
# ADCS
|
112
|
+
@trackStars = Array.new
|
113
|
+
@trackStars[0] = 1237
|
114
|
+
@trackStars[1] = 1329
|
115
|
+
@trackStars[2] = 1333
|
116
|
+
@trackStars[3] = 1139
|
117
|
+
@trackStars[4] = 1161
|
118
|
+
@trackStars[5] = 682
|
119
|
+
@trackStars[6] = 717
|
120
|
+
@trackStars[7] = 814
|
121
|
+
@trackStars[8] = 583
|
122
|
+
@trackStars[9] = 622
|
123
|
+
@adcs_ctrl = 'OFF'
|
124
|
+
@sr_ang_to_sun = 0
|
125
|
+
|
126
|
+
# HEALTH_STATUS
|
127
|
+
@cmd_acpt_cnt = 0
|
128
|
+
@cmd_rjct_cnt = 0
|
129
|
+
@mode = 'SAFE'
|
130
|
+
@cpu_pwr = 100
|
131
|
+
@table_data = "\x00" * 10
|
132
|
+
|
133
|
+
# THERMAL
|
134
|
+
@temp1 = 0
|
135
|
+
@temp2 = 0
|
136
|
+
@heater1_ctrl = 'OFF'
|
137
|
+
@heater1_state = 'OFF'
|
138
|
+
@heater1_setpt = 0.0
|
139
|
+
@heater1_pwr = 0.0
|
140
|
+
@heater2_ctrl = 'OFF'
|
141
|
+
@heater2_state = 'OFF'
|
142
|
+
@heater2_setpt = 0.0
|
143
|
+
@heater2_pwr = 0.0
|
144
|
+
|
145
|
+
# MECH
|
146
|
+
@slrpnl1_ang = 180.0
|
147
|
+
@slrpnl2_ang = 180.0
|
148
|
+
@slrpnl1_state = 'STOWED'
|
149
|
+
@slrpnl2_state = 'STOWED'
|
150
|
+
@slrpnl1_pwr = 0.0
|
151
|
+
@slrpnl2_pwr = 0.0
|
152
|
+
@pwr_watt_seconds = INIT_PWR_WATT_SECONDS
|
153
|
+
@battery = (@pwr_watt_seconds.to_f / MAX_PWR_WATT_SECONDS.to_f) * 100.0
|
154
|
+
|
155
|
+
# IMAGER
|
156
|
+
@collects = 0
|
157
|
+
@duration = 10
|
158
|
+
@collect_type = 'NORMAL'
|
159
|
+
@imager_state = 'OFF'
|
160
|
+
@imager_pwr = 0.0
|
161
|
+
@collect_end_time = nil
|
162
|
+
end
|
163
|
+
|
164
|
+
def set_rates
|
165
|
+
set_rate('ADCS', 10)
|
166
|
+
set_rate('HEALTH_STATUS', 100)
|
167
|
+
set_rate('THERMAL', 100)
|
168
|
+
set_rate('MECH', 100)
|
169
|
+
set_rate('IMAGER', 100)
|
170
|
+
end
|
171
|
+
|
172
|
+
def accept_cmd(message = nil)
|
173
|
+
if message
|
174
|
+
event_packet = @tlm_packets['EVENT']
|
175
|
+
event_packet.message = message
|
176
|
+
time = Time.now
|
177
|
+
event_packet.timesec = time.tv_sec
|
178
|
+
event_packet.timeus = time.tv_usec
|
179
|
+
event_packet.ccsdsseqcnt += 1
|
180
|
+
@queue << event_packet.dup
|
181
|
+
end
|
182
|
+
@cmd_acpt_cnt += 1
|
183
|
+
end
|
184
|
+
|
185
|
+
def reject_cmd(message)
|
186
|
+
event_packet = @tlm_packets['EVENT']
|
187
|
+
event_packet.message = message
|
188
|
+
time = Time.now
|
189
|
+
event_packet.timesec = time.tv_sec
|
190
|
+
event_packet.timeus = time.tv_usec
|
191
|
+
event_packet.ccsdsseqcnt += 1
|
192
|
+
@queue << event_packet.dup
|
193
|
+
@cmd_rjct_cnt += 1
|
194
|
+
end
|
195
|
+
|
196
|
+
def write(packet)
|
197
|
+
name = packet.packet_name.upcase
|
198
|
+
|
199
|
+
case name
|
200
|
+
when 'COLLECT'
|
201
|
+
if @mode == 'OPERATE'
|
202
|
+
@collects += 1
|
203
|
+
@duration = packet.read('duration')
|
204
|
+
@collect_type = packet.read("type")
|
205
|
+
@collect_end_time = Time.now + @duration
|
206
|
+
accept_cmd()
|
207
|
+
else
|
208
|
+
reject_cmd("Mode must be OPERATE to collect images")
|
209
|
+
end
|
210
|
+
|
211
|
+
when 'ABORT'
|
212
|
+
@collect_end_time = nil
|
213
|
+
accept_cmd()
|
214
|
+
|
215
|
+
when 'CLEAR'
|
216
|
+
accept_cmd()
|
217
|
+
@collects = 0
|
218
|
+
@cmd_acpt_cnt = 0
|
219
|
+
@cmd_rjct_cnt = 0
|
220
|
+
|
221
|
+
when 'SET_MODE'
|
222
|
+
mode = packet.read('mode')
|
223
|
+
case mode
|
224
|
+
when 'SAFE'
|
225
|
+
@mode = mode
|
226
|
+
accept_cmd()
|
227
|
+
when 'CHECKOUT'
|
228
|
+
if @battery >= 50.0
|
229
|
+
@mode = mode
|
230
|
+
accept_cmd()
|
231
|
+
else
|
232
|
+
reject_cmd("Cannot enter checkout if battery < 50.0%")
|
233
|
+
end
|
234
|
+
when 'OPERATE'
|
235
|
+
if @temp1 < 35.0 and @temp1 > 25.0 and @temp2 < 35.0 and @temp2 > 25.0
|
236
|
+
@mode = mode
|
237
|
+
accept_cmd()
|
238
|
+
else
|
239
|
+
reject_cmd("Cannot enter OPERATE unless temperatures are stable near 30.0")
|
240
|
+
end
|
241
|
+
else
|
242
|
+
reject_cmd("Invalid Mode: #{mode}")
|
243
|
+
end
|
244
|
+
|
245
|
+
when 'SLRPNLDEPLOY'
|
246
|
+
num = packet.read('NUM')
|
247
|
+
case num
|
248
|
+
when 1
|
249
|
+
@slrpnl1_state = 'DEPLOYED'
|
250
|
+
accept_cmd()
|
251
|
+
when 2
|
252
|
+
@slrpnl2_state = 'DEPLOYED'
|
253
|
+
accept_cmd()
|
254
|
+
else
|
255
|
+
reject_cmd("Invalid Solar Array Number: #{num}")
|
256
|
+
end
|
257
|
+
|
258
|
+
when 'SLRPNLSTOW'
|
259
|
+
num = packet.read('NUM')
|
260
|
+
case num
|
261
|
+
when 1
|
262
|
+
@slrpnl1_state = 'STOWED'
|
263
|
+
accept_cmd()
|
264
|
+
when 2
|
265
|
+
@slrpnl2_state = 'STOWED'
|
266
|
+
accept_cmd()
|
267
|
+
else
|
268
|
+
reject_cmd("Invalid Solar Array Number: #{num}")
|
269
|
+
end
|
270
|
+
|
271
|
+
when 'SLRPNLANG'
|
272
|
+
num = packet.read('NUM')
|
273
|
+
ang = packet.read('ANG')
|
274
|
+
case num
|
275
|
+
when 1
|
276
|
+
case ang
|
277
|
+
when 0..360
|
278
|
+
@slrpnl1_ang = ang
|
279
|
+
accept_cmd()
|
280
|
+
else
|
281
|
+
reject_cmd("Invalid Solar Array Angle: #{setpt}")
|
282
|
+
end
|
283
|
+
when 2
|
284
|
+
case ang
|
285
|
+
when 0..360
|
286
|
+
@slrpnl2_ang = ang
|
287
|
+
accept_cmd()
|
288
|
+
else
|
289
|
+
reject_cmd("Invalid Solar Array Angle: #{setpt}")
|
290
|
+
end
|
291
|
+
else
|
292
|
+
reject_cmd("Invalid Solar Array Number: #{num}")
|
293
|
+
end
|
294
|
+
|
295
|
+
when 'TABLE_LOAD'
|
296
|
+
@table_data = packet.read('DATA')
|
297
|
+
|
298
|
+
when 'HTR_CTRL'
|
299
|
+
num = packet.read('NUM')
|
300
|
+
state = packet.read('STATE')
|
301
|
+
case num
|
302
|
+
when 1
|
303
|
+
case state
|
304
|
+
when 'ON', 'OFF'
|
305
|
+
@heater1_ctrl = state
|
306
|
+
accept_cmd()
|
307
|
+
else
|
308
|
+
reject_cmd("Invalid Heater Control: #{state}")
|
309
|
+
end
|
310
|
+
when 2
|
311
|
+
case state
|
312
|
+
when 'ON', 'OFF'
|
313
|
+
@heater2_ctrl = state
|
314
|
+
accept_cmd()
|
315
|
+
else
|
316
|
+
reject_cmd("Invalid Heater Control: #{state}")
|
317
|
+
end
|
318
|
+
else
|
319
|
+
reject_cmd("Invalid Heater Number: #{num}")
|
320
|
+
end
|
321
|
+
|
322
|
+
when 'HTR_STATE'
|
323
|
+
num = packet.read('NUM')
|
324
|
+
state = packet.read('STATE')
|
325
|
+
case num
|
326
|
+
when 1
|
327
|
+
case state
|
328
|
+
when 'ON', 'OFF'
|
329
|
+
@heater1_state = state
|
330
|
+
accept_cmd()
|
331
|
+
else
|
332
|
+
reject_cmd("Invalid Heater State: #{state}")
|
333
|
+
end
|
334
|
+
when 2
|
335
|
+
case state
|
336
|
+
when 'ON', 'OFF'
|
337
|
+
@heater2_state = state
|
338
|
+
accept_cmd()
|
339
|
+
else
|
340
|
+
reject_cmd("Invalid Heater State: #{state}")
|
341
|
+
end
|
342
|
+
else
|
343
|
+
reject_cmd("Invalid Heater Number: #{num}")
|
344
|
+
end
|
345
|
+
|
346
|
+
when 'HTR_SETPT'
|
347
|
+
num = packet.read('NUM')
|
348
|
+
setpt = packet.read('SETPT')
|
349
|
+
case num
|
350
|
+
when 1
|
351
|
+
case setpt
|
352
|
+
when -100..100
|
353
|
+
@heater1_setpt = setpt
|
354
|
+
accept_cmd()
|
355
|
+
else
|
356
|
+
reject_cmd("Invalid Heater Setpoint: #{setpt}")
|
357
|
+
end
|
358
|
+
when 2
|
359
|
+
case setpt
|
360
|
+
when -100..100
|
361
|
+
@heater2_setpt = setpt
|
362
|
+
accept_cmd()
|
363
|
+
else
|
364
|
+
reject_cmd("Invalid Heater Setpoint: #{setpt}")
|
365
|
+
end
|
366
|
+
else
|
367
|
+
reject_cmd("Invalid Heater Number: #{num}")
|
368
|
+
end
|
369
|
+
|
370
|
+
when 'ADCS_CTRL'
|
371
|
+
state = packet.read('STATE')
|
372
|
+
case state
|
373
|
+
when 'ON', 'OFF'
|
374
|
+
@adcs_ctrl = state
|
375
|
+
accept_cmd()
|
376
|
+
else
|
377
|
+
reject_cmd("Invalid ADCS Control: #{state}")
|
378
|
+
end
|
379
|
+
end
|
380
|
+
end
|
381
|
+
|
382
|
+
def graceful_kill
|
383
|
+
end
|
384
|
+
|
385
|
+
def get_pending_packets(count_100hz)
|
386
|
+
pending_packets = super(count_100hz)
|
387
|
+
while @queue.length > 0
|
388
|
+
pending_packets << @queue.pop
|
389
|
+
end
|
390
|
+
pending_packets
|
391
|
+
end
|
392
|
+
|
393
|
+
def read(count_100hz, time)
|
394
|
+
pending_packets = get_pending_packets(count_100hz)
|
395
|
+
|
396
|
+
pending_packets.each do |packet|
|
397
|
+
case packet.packet_name
|
398
|
+
when 'ADCS'
|
399
|
+
# Read 44 Bytes for Position Data
|
400
|
+
pos_data = nil
|
401
|
+
begin
|
402
|
+
pos_data = @position_file.read(44)
|
403
|
+
@position_file_bytes_read += 44
|
404
|
+
rescue
|
405
|
+
# Do Nothing
|
406
|
+
end
|
407
|
+
|
408
|
+
if pos_data.nil? or pos_data.length == 0
|
409
|
+
# Assume end of file - close and reopen
|
410
|
+
@position_file.close
|
411
|
+
@position_file = File.open(File.join(@target.dir, 'data', 'position.bin'), 'rb')
|
412
|
+
pos_data = @position_file.read(44)
|
413
|
+
@position_file_bytes_read = 44
|
414
|
+
end
|
415
|
+
|
416
|
+
@pos_packet.buffer = pos_data
|
417
|
+
packet.posx = @pos_packet.posx
|
418
|
+
packet.posy = @pos_packet.posy
|
419
|
+
packet.posz = @pos_packet.posz
|
420
|
+
packet.velx = @pos_packet.velx
|
421
|
+
packet.vely = @pos_packet.vely
|
422
|
+
packet.velz = @pos_packet.velz
|
423
|
+
|
424
|
+
# Read 40 Bytes for Attitude Data
|
425
|
+
att_data = nil
|
426
|
+
begin
|
427
|
+
att_data = @attitude_file.read(40)
|
428
|
+
@attitude_file_bytes_read += 40
|
429
|
+
rescue
|
430
|
+
# Do Nothing
|
431
|
+
end
|
432
|
+
|
433
|
+
if att_data.nil? or att_data.length == 0
|
434
|
+
@attitude_file.close
|
435
|
+
@attitude_file = File.open(File.join(@target.dir, 'data', 'attitude.bin'), 'rb')
|
436
|
+
att_data = @attitude_file.read(40)
|
437
|
+
@attitude_file_bytes_read = 40
|
438
|
+
end
|
439
|
+
|
440
|
+
@att_packet.buffer = att_data
|
441
|
+
packet.q1 = @att_packet.q1
|
442
|
+
packet.q2 = @att_packet.q2
|
443
|
+
packet.q3 = @att_packet.q3
|
444
|
+
packet.q4 = @att_packet.q4
|
445
|
+
packet.biasx = @att_packet.biasx
|
446
|
+
packet.biasy = @att_packet.biasy
|
447
|
+
packet.biasy = @att_packet.biasz
|
448
|
+
|
449
|
+
packet.star1id = @trackStars[((@get_count / 100) + 0) % 10]
|
450
|
+
packet.star2id = @trackStars[((@get_count / 100) + 1) % 10]
|
451
|
+
packet.star3id = @trackStars[((@get_count / 100) + 2) % 10]
|
452
|
+
packet.star4id = @trackStars[((@get_count / 100) + 3) % 10]
|
453
|
+
packet.star5id = @trackStars[((@get_count / 100) + 4) % 10]
|
454
|
+
|
455
|
+
packet.posprogress = (@position_file_bytes_read.to_f / @position_file_size.to_f) * 100.0
|
456
|
+
packet.attprogress = (@attitude_file_bytes_read.to_f / @attitude_file_size.to_f) * 100.0
|
457
|
+
@sr_ang_to_sun = packet.posprogress * 3.6
|
458
|
+
packet.sr_ang_to_sun = @sr_ang_to_sun
|
459
|
+
packet.adcs_ctrl = @adcs_ctrl
|
460
|
+
|
461
|
+
packet.timesec = time.tv_sec
|
462
|
+
packet.timeus = time.tv_usec
|
463
|
+
packet.ccsdsseqcnt += 1
|
464
|
+
|
465
|
+
when 'HEALTH_STATUS'
|
466
|
+
packet.timesec = time.tv_sec
|
467
|
+
packet.timeus = time.tv_usec
|
468
|
+
packet.ccsdsseqcnt += 1
|
469
|
+
|
470
|
+
packet.cmd_acpt_cnt = @cmd_acpt_cnt
|
471
|
+
packet.cmd_rjct_cnt = @cmd_rjct_cnt
|
472
|
+
packet.mode = @mode
|
473
|
+
packet.cpu_pwr = @cpu_pwr
|
474
|
+
packet.table_data = @table_data
|
475
|
+
|
476
|
+
when 'THERMAL'
|
477
|
+
packet.timesec = time.tv_sec
|
478
|
+
packet.timeus = time.tv_usec
|
479
|
+
packet.ccsdsseqcnt += 1
|
480
|
+
|
481
|
+
if @heater1_ctrl == 'ON'
|
482
|
+
if @temp1 < (@heater1_setpt - HYSTERESIS)
|
483
|
+
@heater1_state = 'ON'
|
484
|
+
elsif @temp1 > (@heater1_setpt + HYSTERESIS)
|
485
|
+
@heater1_state = 'OFF'
|
486
|
+
end
|
487
|
+
end
|
488
|
+
|
489
|
+
if @heater2_ctrl == 'ON'
|
490
|
+
if @temp2 < (@heater2_setpt - HYSTERESIS)
|
491
|
+
@heater2_state = 'ON'
|
492
|
+
elsif @temp2 > (@heater2_setpt + HYSTERESIS)
|
493
|
+
@heater2_state = 'OFF'
|
494
|
+
end
|
495
|
+
end
|
496
|
+
|
497
|
+
if @heater1_state == 'ON'
|
498
|
+
@heater1_pwr = 300
|
499
|
+
@temp1 += 0.5
|
500
|
+
if @temp1 > 50.0
|
501
|
+
@temp1 = 50.0
|
502
|
+
end
|
503
|
+
else
|
504
|
+
@heater1_pwr = 0
|
505
|
+
@temp1 -= 0.1
|
506
|
+
if @temp1 < -20.0
|
507
|
+
@temp1 = -20.0
|
508
|
+
end
|
509
|
+
end
|
510
|
+
|
511
|
+
if @heater2_state == 'ON'
|
512
|
+
@heater2_pwr = 300
|
513
|
+
@temp2 += 0.5
|
514
|
+
if @temp2 > 100.0
|
515
|
+
@temp2 = 100.0
|
516
|
+
end
|
517
|
+
else
|
518
|
+
@heater2_pwr = 0
|
519
|
+
@temp2 -= 0.1
|
520
|
+
if @temp2 < -20.0
|
521
|
+
@temp2 = -20.0
|
522
|
+
end
|
523
|
+
end
|
524
|
+
|
525
|
+
packet.heater1_ctrl = @heater1_ctrl
|
526
|
+
packet.heater1_state = @heater1_state
|
527
|
+
packet.heater1_setpt = @heater1_setpt
|
528
|
+
packet.heater1_pwr = @heater1_pwr
|
529
|
+
packet.heater2_ctrl = @heater2_ctrl
|
530
|
+
packet.heater2_state = @heater2_state
|
531
|
+
packet.heater2_setpt = @heater2_setpt
|
532
|
+
packet.heater2_pwr = @heater2_pwr
|
533
|
+
packet.temp1 = @temp1
|
534
|
+
packet.temp2 = @temp2
|
535
|
+
|
536
|
+
when 'MECH'
|
537
|
+
if @adcs_ctrl == 'ON'
|
538
|
+
@slrpnl1_ang = @sr_ang_to_sun
|
539
|
+
@slrpnl2_ang = @sr_ang_to_sun
|
540
|
+
end
|
541
|
+
|
542
|
+
delta_ang = (@sr_ang_to_sun - @slrpnl1_ang).abs
|
543
|
+
if delta_ang > 180.0
|
544
|
+
delta_ang = 360 - delta_ang
|
545
|
+
end
|
546
|
+
if @slrpnl1_state == 'DEPLOYED'
|
547
|
+
@slrpnl1_pwr = 500 * (1 - (delta_ang / 180.0))
|
548
|
+
else
|
549
|
+
@slrpnl1_pwr = 0
|
550
|
+
end
|
551
|
+
|
552
|
+
delta_ang = (@sr_ang_to_sun - @slrpnl2_ang).abs
|
553
|
+
if delta_ang > 180.0
|
554
|
+
delta_ang = 360 - delta_ang
|
555
|
+
end
|
556
|
+
if @slrpnl2_state == 'DEPLOYED'
|
557
|
+
@slrpnl2_pwr = 500 * (1 - (delta_ang / 180.0))
|
558
|
+
else
|
559
|
+
@slrpnl2_pwr = 0
|
560
|
+
end
|
561
|
+
|
562
|
+
incoming_pwr = @slrpnl1_pwr + @slrpnl2_pwr # Upto 1000 per second
|
563
|
+
|
564
|
+
used_pwr = @cpu_pwr + @imager_pwr + @heater1_pwr + @heater2_pwr # Up to 900 per second
|
565
|
+
delta_pwr = incoming_pwr - used_pwr
|
566
|
+
@pwr_watt_seconds += delta_pwr
|
567
|
+
if @pwr_watt_seconds < 0
|
568
|
+
@pwr_watt_seconds = 100
|
569
|
+
elsif @pwr_watt_seconds > MAX_PWR_WATT_SECONDS
|
570
|
+
@pwr_watt_seconds = MAX_PWR_WATT_SECONDS
|
571
|
+
end
|
572
|
+
@battery = (@pwr_watt_seconds.to_f / MAX_PWR_WATT_SECONDS.to_f) * 100.0
|
573
|
+
if @battery < 50.0
|
574
|
+
@mode = 'SAFE'
|
575
|
+
end
|
576
|
+
|
577
|
+
packet.timesec = time.tv_sec
|
578
|
+
packet.timeus = time.tv_usec
|
579
|
+
packet.ccsdsseqcnt += 1
|
580
|
+
packet.slrpnl1_ang = @slrpnl1_ang
|
581
|
+
packet.slrpnl2_ang = @slrpnl2_ang
|
582
|
+
packet.slrpnl1_state = @slrpnl1_state
|
583
|
+
packet.slrpnl2_state = @slrpnl2_state
|
584
|
+
packet.slrpnl1_pwr = @slrpnl1_pwr
|
585
|
+
packet.slrpnl2_pwr = @slrpnl2_pwr
|
586
|
+
packet.battery = @battery
|
587
|
+
|
588
|
+
when 'IMAGER'
|
589
|
+
if @collect_end_time
|
590
|
+
if @collect_end_time < Time.now
|
591
|
+
@imager_state = 'OFF'
|
592
|
+
@collect_end_time = nil
|
593
|
+
@imager_pwr = 0
|
594
|
+
image_packet = @tlm_packets['IMAGE']
|
595
|
+
time = Time.now
|
596
|
+
image_packet.timesec = time.tv_sec
|
597
|
+
image_packet.timeus = time.tv_usec
|
598
|
+
image_packet.ccsdsseqcnt += 1
|
599
|
+
@queue << image_packet.dup
|
600
|
+
else
|
601
|
+
@imager_state = 'ON'
|
602
|
+
@imager_pwr = 200
|
603
|
+
end
|
604
|
+
else
|
605
|
+
@imager_pwr = 0
|
606
|
+
end
|
607
|
+
|
608
|
+
packet.timesec = time.tv_sec
|
609
|
+
packet.timeus = time.tv_usec
|
610
|
+
packet.ccsdsseqcnt += 1
|
611
|
+
packet.collects = @collects
|
612
|
+
packet.duration = @duration
|
613
|
+
packet.collect_type = @collect_type
|
614
|
+
packet.imager_state = @imager_state
|
615
|
+
packet.imager_pwr = @imager_pwr
|
616
|
+
|
617
|
+
end
|
618
|
+
end
|
619
|
+
|
620
|
+
@get_count += 1
|
621
|
+
pending_packets
|
622
|
+
end
|
623
|
+
end
|
624
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# Ignored Parameters
|
2
|
+
IGNORE_PARAMETER CCSDSVER
|
3
|
+
IGNORE_PARAMETER CCSDSTYPE
|
4
|
+
IGNORE_PARAMETER CCSDSSHF
|
5
|
+
IGNORE_PARAMETER CCSDSAPID
|
6
|
+
IGNORE_PARAMETER CCSDSSEQFLAGS
|
7
|
+
IGNORE_PARAMETER CCSDSSEQCNT
|
8
|
+
IGNORE_PARAMETER CCSDSLENGTH
|
9
|
+
IGNORE_PARAMETER PKTID
|
10
|
+
|
11
|
+
# Ignored Items
|
12
|
+
IGNORE_ITEM CCSDSVER
|
13
|
+
IGNORE_ITEM CCSDSTYPE
|
14
|
+
IGNORE_ITEM CCSDSSHF
|
15
|
+
IGNORE_ITEM CCSDSAPID
|
16
|
+
IGNORE_ITEM CCSDSSEQFLAGS
|
17
|
+
IGNORE_ITEM CCSDSSEQCNT
|
18
|
+
IGNORE_ITEM CCSDSLENGTH
|
19
|
+
IGNORE_ITEM PACKET_TIME
|
20
|
+
IGNORE_ITEM PACKET_TIMESECONDS
|
21
|
+
IGNORE_ITEM RECEIVED_TIMESECONDS
|
22
|
+
IGNORE_ITEM TIMESEC
|
23
|
+
IGNORE_ITEM TIMEUS
|
24
|
+
IGNORE_ITEM PKTID
|
25
|
+
|
26
|
+
# Explicitly define command and telemetry files
|
27
|
+
# If not given then all the files in cmd/tlm will be processed in
|
28
|
+
# alphabetical order
|
29
|
+
COMMANDS cmds.txt
|
30
|
+
TELEMETRY tlm.txt
|
data/plugin.txt
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
# Set VARIABLEs here to allow variation in your plugin
|
2
|
+
# See https://openc3.com/docs/v5/plugins for more information
|
3
|
+
VARIABLE geosim_port 7272
|
4
|
+
VARIABLE geosim_microservice_name GEOSIM
|
5
|
+
|
6
|
+
MICROSERVICE GEOSIM <%= geosim_microservice_name %>
|
7
|
+
WORK_DIR .
|
8
|
+
PORT <%= geosim_port %>
|
9
|
+
CMD ruby geosat_target.rb <%= geosim_port %>
|