def __init__(self): super().__init__() logger.info("Initializing...") self.init_parameters() self.command_queue = Queue() self.downlink_queue = Queue() self.FMQueue = Queue() self.commands_to_execute = [] self.downlinks_to_execute = [] self.telemetry = Telemetry(self) self.burn_queue = Queue() self.reorientation_queue = Queue() self.reorientation_list = [] self.maneuver_queue = Queue() # maneuver queue # self.init_comms() self.command_handler = CommandHandler() self.downlink_handler = DownlinkHandler() self.command_counter = 0 self.downlink_counter = 0 self.command_definitions = CommandDefinitions(self) self.init_sensors() self.last_opnav_run = datetime.now() # Figure out what to set to for first opnav run self.log_dir = LOG_DIR self.logger = get_log() self.attach_sigint_handler() # FIXME if os.path.isdir(self.log_dir): self.flight_mode = RestartMode(self) else: os.makedirs(CISLUNAR_BASE_DIR) os.mkdir(LOG_DIR) self.flight_mode = BootUpMode(self) self.create_session = create_sensor_tables_from_path(DB_FILE)
def test_config2_command(): config2 = ps.eps_config2_t() config2.batt_maxvoltage = 8300 config2.batt_normalvoltage = 7600 config2.batt_safevoltage = 7200 config2.batt_criticalvoltage = 6800 config2_dict = dict_from_eps_config2(config2) COUNTER = 0 ch = CommandHandler() command_bytes = ch.pack_command(COUNTER, FMEnum.Normal.value, NormalCommandEnum.GomConf2Set.value, **config2_dict) mac, counter, mode, command_id, kwargs = ch.unpack_command(command_bytes) assert mac == MAC assert counter == COUNTER assert mode == FMEnum.Normal.value assert command_id == NormalCommandEnum.GomConf2Set.value unpacked_config2 = eps_config2_from_dict(kwargs) assert config2._fields_ == unpacked_config2._fields_ # ps.displayConfig2(config2) # ps.displayConfig2(unpacked_config2) assert config2.batt_maxvoltage == unpacked_config2.batt_maxvoltage assert config2.batt_normalvoltage == unpacked_config2.batt_normalvoltage assert config2.batt_safevoltage == unpacked_config2.batt_safevoltage assert config2.batt_criticalvoltage == unpacked_config2.batt_criticalvoltage
def __init__(self): super().__init__() logger.info("Initializing...") self.init_parameters() self.command_queue = Queue() self.downlink_queue = Queue() self.FMQueue = Queue() self.commands_to_execute = [] self.downlinks_to_execute = [] self.burn_queue = Queue() self.reorientation_queue = Queue() self.reorientation_list = [] self.maneuver_queue = Queue() # maneuver queue self.opnav_queue = Queue() # determine state of opnav success # self.init_comms() logger.info("Initializing commands and downlinks") self.command_handler = CommandHandler() self.downlink_handler = DownlinkHandler() self.command_counter = 0 self.downlink_counter = 0 self.command_definitions = CommandDefinitions(self) self.last_opnav_run = datetime.now() # Figure out what to set to for first opnav run self.log_dir = LOG_DIR self.logger = get_log() self.attach_sigint_handler() # FIXME self.file_block_bank = {} self.need_to_reboot = False self.gom: Gomspace self.gyro: GyroSensor self.adc: ADC self.rtc: RTC self.radio: Radio self.mux: camera.CameraMux self.camera: camera.Camera self.nemo_manager: NemoManager self.init_sensors() # Opnav subprocess variables self.opnav_proc_queue = Queue() self.opnav_process = Process() # define the subprocess if os.path.isdir(self.log_dir): logger.info("We are in Restart Mode") self.flight_mode = RestartMode(self) else: logger.info("We are in Bootup Mode") os.makedirs(CISLUNAR_BASE_DIR, exist_ok=True) os.mkdir(LOG_DIR) self.flight_mode = BootUpMode(self) self.create_session = create_sensor_tables_from_path(DB_FILE) logger.info("Initializing Telemetry") self.telemetry = Telemetry(self) logger.info("Done intializing")
def __init__(self): super().__init__() logger.info("Initializing...") self.init_parameters() self.command_queue = Queue() self.downlink_queue = Queue() self.FMQueue = Queue() self.commands_to_execute = [] self.downlinks_to_execute = [] self.telemetry = Telemetry(self) self.burn_queue = Queue() self.reorientation_queue = Queue() self.reorientation_list = [] self.maneuver_queue = Queue() # maneuver queue self.opnav_queue = Queue() # determine state of opnav success # self.init_comms() self.command_handler = CommandHandler() self.downlink_handler = DownlinkHandler() self.command_counter = 0 self.downlink_counter = 0 self.command_definitions = CommandDefinitions(self) self.last_opnav_run = datetime.now() # Figure out what to set to for first opnav run self.log_dir = LOG_DIR self.logger = get_log() self.attach_sigint_handler() # FIXME self.need_to_reboot = False self.gom = None self.gyro = None self.adc = None self.rtc = None self.radio = None self.mux = None self.camera = None self.init_sensors() # Telemetry self.tlm = Telemetry(self) # Opnav subprocess variables self.opnav_proc_queue = Queue() self.opnav_process = Process() # define the subprocess if os.path.isdir(self.log_dir): self.flight_mode = RestartMode(self) else: os.makedirs(CISLUNAR_BASE_DIR, exist_ok=True) os.mkdir(LOG_DIR) self.flight_mode = BootUpMode(self) self.create_session = create_sensor_tables_from_path(DB_FILE)
def test_command_serialization(self): # Both are doubles arg1 = "arg1" arg2 = "arg2" ch = CommandHandler() print(f"Packers: {ch.packers}") command_args = [arg1, arg2] command_data_tuple = (command_args, 16) application_id = 78 command_dict = {application_id: command_data_tuple} mode_id = 255 kwargs = { arg1: 78.1, arg2: 99.5, } command_counter = 1 ch.register_new_command(mode_id, application_id, **kwargs) command_buffer = ch.pack_command(command_counter, mode_id, application_id, **kwargs) assert len(command_buffer) == ch.get_command_size(mode_id, application_id) unpacked_mac, unpacked_counter, unpacked_mode, unpacked_app, unpacked_args = ch.unpack_command(command_buffer) print(unpacked_args) assert unpacked_mode == mode_id assert unpacked_app == application_id assert unpacked_args[arg1] == kwargs[arg1] assert unpacked_args[arg2] == kwargs[arg2] assert unpacked_counter == command_counter assert unpacked_mac == MAC
def test_register_new_codec(self): arg = "testarg" ch = CommandHandler() ch.register_new_codec(arg, pack_double, unpack_double) assert arg in ch.unpackers and arg in ch.packers with raises(SerializationException): ch.register_new_codec(arg, pack_double, unpack_double)
import communications.groundstation as gs from communications.commands import CommandHandler import board import busio import time from adafruit_bus_device.spi_device import SPIDevice from communications.ax5043_manager.ax5043_driver import Ax5043 from communications.ax5043_manager.ax5043_manager import Manager #Radio setup driver = Ax5043(SPIDevice(busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO))) mgr = Manager(driver) ch = CommandHandler() #Pack command into a bytearray gs.registerCommand(ch,1,2,number1=7, number2=4) command = ch.pack_command(1,2,number1=7, number2=4) #Send the command to the satellite gs.transmitCommand(mgr, command) print('Transmitted') print('Entering Receiving Mode') cycleNumber = 1 #Enter listening mode while True: print('Cycle: ' + str(cycleNumber))
def test_config_command(): config = ps.eps_config_t() config.ppt_mode = randint(1, 2) config.battheater_mode = randint(0, 1) config.battheater_low = randint(0, 5) config.battheater_high = randint(6, 20) config.output_normal_value[0] = randint(0, 1) config.output_normal_value[1] = randint(0, 1) config.output_normal_value[2] = randint(0, 1) config.output_normal_value[3] = randint(0, 1) config.output_normal_value[4] = randint(0, 1) config.output_normal_value[5] = randint(0, 1) config.output_normal_value[6] = randint(0, 1) config.output_normal_value[7] = randint(0, 1) config.output_safe_value[0] = randint(0, 1) config.output_safe_value[1] = randint(0, 1) config.output_safe_value[2] = randint(0, 1) config.output_safe_value[3] = randint(0, 1) config.output_safe_value[4] = randint(0, 1) config.output_safe_value[5] = randint(0, 1) config.output_safe_value[6] = randint(0, 1) config.output_safe_value[7] = randint(0, 1) initial_on = randint(0, 65535) config.output_initial_on_delay[0] = initial_on config.output_initial_on_delay[1] = initial_on config.output_initial_on_delay[2] = initial_on config.output_initial_on_delay[3] = initial_on config.output_initial_on_delay[4] = initial_on config.output_initial_on_delay[5] = initial_on config.output_initial_on_delay[6] = initial_on config.output_initial_on_delay[7] = initial_on initial_off = randint(0, 65535) config.output_initial_off_delay[0] = initial_off config.output_initial_off_delay[1] = initial_off config.output_initial_off_delay[2] = initial_off config.output_initial_off_delay[3] = initial_off config.output_initial_off_delay[4] = initial_off config.output_initial_off_delay[5] = initial_off config.output_initial_off_delay[6] = initial_off config.output_initial_off_delay[7] = initial_off config.vboost[0] = randint(1000, 4000) config.vboost[1] = randint(1000, 4000) config.vboost[2] = randint(1000, 4000) config_dict = dict_from_eps_config(config) COUNTER = 0 ch = CommandHandler() command_bytes = ch.pack_command(COUNTER, FMEnum.Normal.value, NormalCommandEnum.GomConf1Set.value, **config_dict) mac, counter, mode, command_id, kwargs = ch.unpack_command(command_bytes) assert mac == MAC assert counter == COUNTER assert mode == FMEnum.Normal.value assert command_id == NormalCommandEnum.GomConf1Set.value unpacked_config = eps_config_from_dict(**kwargs) # ps.displayConfig(config) # ps.displayConfig(unpacked_config) assert config.ppt_mode == unpacked_config.ppt_mode assert config.battheater_mode == unpacked_config.battheater_mode assert config.battheater_low == unpacked_config.battheater_low assert config.battheater_high == unpacked_config.battheater_high assert config.output_normal_value[ 0] == unpacked_config.output_normal_value[0] assert config.output_normal_value[ 1] == unpacked_config.output_normal_value[1] assert config.output_normal_value[ 2] == unpacked_config.output_normal_value[2] assert config.output_normal_value[ 3] == unpacked_config.output_normal_value[3] assert config.output_normal_value[ 4] == unpacked_config.output_normal_value[4] assert config.output_normal_value[ 5] == unpacked_config.output_normal_value[5] assert config.output_normal_value[ 6] == unpacked_config.output_normal_value[6] assert config.output_normal_value[ 7] == unpacked_config.output_normal_value[7] assert config.output_safe_value[0] == unpacked_config.output_safe_value[0] assert config.output_safe_value[1] == unpacked_config.output_safe_value[1] assert config.output_safe_value[2] == unpacked_config.output_safe_value[2] assert config.output_safe_value[3] == unpacked_config.output_safe_value[3] assert config.output_safe_value[4] == unpacked_config.output_safe_value[4] assert config.output_safe_value[5] == unpacked_config.output_safe_value[5] assert config.output_safe_value[6] == unpacked_config.output_safe_value[6] assert config.output_safe_value[7] == unpacked_config.output_safe_value[7] assert config.output_initial_on_delay[ 0] == unpacked_config.output_initial_on_delay[0] assert config.output_initial_off_delay[ 0] == unpacked_config.output_initial_off_delay[0] assert config.vboost[0] == unpacked_config.vboost[0] assert config.vboost[1] == unpacked_config.vboost[1] assert config.vboost[2] == unpacked_config.vboost[2]
from communications.satellite_radio import Radio from communications.commands import CommandHandler from communications.downlink import DownlinkHandler import time # Setup ch = CommandHandler() dh = DownlinkHandler() groundstation = Radio() # Send command to get gyro/mag/acc data gmaCommand = ch.pack_command(1, 8, 7) groundstation.transmit(gmaCommand) print('GMA Command Transmitted') # Listen for response print('Receiving...') cycle = 1 while True: print('Receiving Cycle: ' + str(cycle)) downlink = groundstation.receiveSignal() if downlink is not None: print('Downlink Received:') data = dh.unpack_downlink(downlink)[-1] print('Gyro: ' + str(data['gyro1']) + ', ' + str(data['gyro2']) + ', ' + str(data['gyro3'])) break
class MainSatelliteThread(Thread): def __init__(self): super().__init__() logger.info("Initializing...") self.init_parameters() self.command_queue = Queue() self.downlink_queue = Queue() self.FMQueue = Queue() self.commands_to_execute = [] self.downlinks_to_execute = [] self.telemetry = Telemetry(self) self.burn_queue = Queue() self.reorientation_queue = Queue() self.reorientation_list = [] self.maneuver_queue = Queue() # maneuver queue self.opnav_queue = Queue() # determine state of opnav success # self.init_comms() self.command_handler = CommandHandler() self.downlink_handler = DownlinkHandler() self.command_counter = 0 self.downlink_counter = 0 self.command_definitions = CommandDefinitions(self) self.last_opnav_run = datetime.now() # Figure out what to set to for first opnav run self.log_dir = LOG_DIR self.logger = get_log() self.attach_sigint_handler() # FIXME self.need_to_reboot = False self.gom = None self.gyro = None self.adc = None self.rtc = None self.radio = None self.mux = None self.camera = None self.init_sensors() # Telemetry self.tlm = Telemetry(self) # Opnav subprocess variables self.opnav_proc_queue = Queue() self.opnav_process = Process() # define the subprocess if os.path.isdir(self.log_dir): self.flight_mode = RestartMode(self) else: os.makedirs(CISLUNAR_BASE_DIR, exist_ok=True) os.mkdir(LOG_DIR) self.flight_mode = BootUpMode(self) self.create_session = create_sensor_tables_from_path(DB_FILE) def init_comms(self): self.comms = CommunicationsSystem( queue=self.command_queue, use_ax5043=False ) self.comms.listen() def init_parameters(self): with open(PARAMETERS_JSON_PATH) as f: json_parameter_dict = load(f) try: for parameter in utils.parameters.__dir__(): if parameter[0] != '_': utils.parameters.__setattr__(parameter, json_parameter_dict[parameter]) except: raise Exception( 'Attempted to set parameter ' + str(parameter) + ', which could not be found in parameters.json' ) def init_sensors(self): try: self.gom = Gomspace() except: self.gom = None logger.error("GOM initialization failed") else: logger.info("Gom initialized") try: self.gyro = GyroSensor() except: self.gyro = None logger.error("GYRO initialization failed") else: logger.info("Gyro initialized") try: self.adc = ADC(self.gyro) self.adc.read_temperature() except: self.adc = None logger.error("ADC initialization failed") else: logger.info("ADC initialized") try: self.rtc = RTC() except: self.rtc = None logger.error("RTC initialization failed") else: logger.info("RTC initialized") try: self.nemo_manager = NemoManager(data_dir=NEMO_DIR, reset_gpio_ch=16) except: self.nemo_manager = None logger.error("NEMO initialization failed") else: logger.info("NEMO initialized") try: self.radio = Radio() except: self.radio = None logger.error("RADIO initialization failed") else: logger.info("Radio initialized") # initialize the Mux, select a camera try: self.mux = camera.CameraMux() self.mux.selectCamera(1) except: self.mux = None logger.error("MUX initialization failed") else: logger.info("Mux initialized") cameras_list = [0, 0, 0] # initialize cameras only if not a hard boot (or first boot) if not hard_boot() and os.path.isdir(self.log_dir): try: self.camera = camera.Camera() for i in [1, 2, 3]: try: self.mux.selectCamera(i) f, t = self.camera.rawObservation(f"initialization-{i}-{int(time())}") except Exception as e: logger.error(f"CAM{i} initialization failed") cameras_list[i - 1] = 0 else: logger.info(f"Cam{i} initialized") cameras_list[i - 1] = 1 if 0 in cameras_list: raise e except: self.camera = None logger.error(f"Cameras initialization failed") else: logger.info("Cameras initialized") else: self.need_to_reboot = True # make a bitmask of the initialized sensors for downlinking sensors = [self.gom, self.radio, self.gyro, self.adc, self.rtc, self.mux, self.camera] sensor_functioning_list = [int(bool(sensor)) for sensor in sensors] sensor_functioning_list.extend(cameras_list) sensor_bitmask = ''.join(map(str, sensor_functioning_list)) logger.debug(f"Sensors: {sensor_bitmask}") return int(sensor_bitmask, 2) def handle_sigint(self, signal, frame): self.shutdown() sys.exit(0) def attach_sigint_handler(self): signal.signal(signal.SIGINT, self.handle_sigint) def poll_inputs(self): self.flight_mode.poll_inputs() #Telemetry downlink if (datetime.today() - self.radio.last_telemetry_time).total_seconds()/60 >= params.TELEM_DOWNLINK_TIME: telemetry = self.command_definitions.gather_basic_telem() telem_downlink = ( self.downlink_handler.pack_downlink(self.downlink_counter, FMEnum.Normal.value, NormalCommandEnum.BasicTelem.value, **telemetry)) self.downlink_queue.put(telem_downlink) self.radio.last_telemetry_time = datetime.today() # Listening for new commands newCommand = self.radio.receiveSignal() if newCommand is not None: try: unpackedCommand = self.command_handler.unpack_command(newCommand) if unpackedCommand[0] == MAC: if unpackedCommand[1] == self.command_counter + 1: self.command_queue.put(bytes(newCommand)) self.command_counter += 1 else: logger.warning('Command with Invalid Counter Received. Counter: ' + str(unpackedCommand[1])) else: logger.warning('Unauthenticated Command Received') except: logger.error('Invalid Command Received') else: logger.debug('Not Received') def replace_flight_mode_by_id(self, new_flight_mode_id): self.replace_flight_mode(build_flight_mode(self, new_flight_mode_id)) def replace_flight_mode(self, new_flight_mode): self.flight_mode = new_flight_mode self.logger.info(f"Changed to FM#{self.flight_mode.flight_mode_id}") def update_state(self): fm_to_update_to = self.flight_mode.update_state() # only replace the current flight mode if it needs to change (i.e. dont fix it if it aint broken!) if fm_to_update_to is None: pass elif fm_to_update_to != NO_FM_CHANGE and fm_to_update_to != self.flight_mode.flight_mode_id: self.replace_flight_mode_by_id(fm_to_update_to) def clear_command_queue(self): while not self.command_queue.empty(): command = self.command_queue.get() logger.debug(f"Throwing away command: {command}") def reset_commands_to_execute(self): self.commands_to_execute = [] # Execute received commands def execute_commands(self): assert ( len(self.commands_to_execute) == 0 ), "Didn't finish executing previous commands" while not self.command_queue.empty(): self.commands_to_execute.append(self.command_queue.get()) self.flight_mode.execute_commands() def read_command_queue_from_file(self, filename="communications/command_queue.txt"): """A temporary workaround to not having radio board access""" # check if file exists if os.path.isfile(filename): text_file = open(filename, "r") for hex_line in text_file: self.command_queue.put(bytes.fromhex(hex_line)) text_file.close() # delete file os.remove(filename) # Run the current flight mode def run_mode(self): with self.flight_mode: self.flight_mode.run_mode() # Wrap in try finally block to ensure it stays live def run(self): """This is the main loop of the Cislunar Explorers and runs constantly during flight.""" try: while True: sleep(5) # TODO remove when flight modes execute real tasks self.poll_inputs() self.update_state() self.read_command_queue_from_file() self.execute_commands() # Set goal or execute command immediately self.run_mode() # Opnav subprocess management # TODO: This all needs to be moved to the OpNav Flight mode, and should not be in main!!! finally: # TODO handle failure gracefully if FOR_FLIGHT is True: self.replace_flight_mode_by_id(FMEnum.Safety.value) self.run() else: self.shutdown() def shutdown(self): if self.gom is not None: self.gom.all_off() if self.nemo_manager is not None: self.nemo_manager.close() logger.critical("Shutting down flight software")
from communications.satellite_radio import Radio from communications.commands import CommandHandler from flight_modes.flight_mode_factory import FLIGHT_MODE_DICT import time ch = CommandHandler() groundstation = Radio() commandCounter = 1 transmitInterval = 3 while True: commandInput = input('Enter command in the form "Mode ID,Command ID, Keyword Arguments" (e.g 2,3,delay=3,state=True):') commandArguments = commandInput.split(',') mode_id = int(commandArguments[0]) command_id = int(commandArguments[1]) kwargs = {} if len(commandArguments) > 2: argsList = commandArguments[2:] for i in range(len(argsList)): signIndex = argsList[i].index('=') argName = argsList[i][:signIndex] argValue = argsList[i][signIndex +1:] arg_type = FLIGHT_MODE_DICT[mode_id].command_arg_types[argName] if arg_type == 'int' or arg_type == 'short': argValue = int(argValue) elif arg_type == 'float' or arg_type == 'double': argValue = float(argValue)
from communications.satellite_radio import Radio from communications.commands import CommandHandler from communications.downlink import DownlinkHandler from flight_modes.flight_mode_factory import FLIGHT_MODE_DICT import time from utils.log import get_log logger = get_log() ch = CommandHandler() dh = DownlinkHandler() groundstation = Radio() commandCounter = 1 transmitInterval = 3 rx_wait_time = 20 # seconds while True: commandInput = input( 'Enter command in the form "Mode ID,Command ID, Keyword Arguments" (e.g 2,3,delay=3,state=True):' ) commandArguments = commandInput.split(',') mode_id = int(commandArguments[0]) command_id = int(commandArguments[1]) kwargs = {} if len(commandArguments) > 2: argsList = commandArguments[2:] for i in range(len(argsList)): signIndex = argsList[i].index('=')
def test_packers_unpackers_match(self): ch = CommandHandler() assert set(ch.packers.keys()) == set(ch.unpackers.keys())
"""command_queue_writer.py: a temporary workaround to not having functioning radio drivers. Instead of sending commands to the EDU/HITL through the radio board, use this to write commands to a txt file, which is then read by main.py's read_command_queue_from_file method """ from communications.commands import CommandHandler from utils.log import get_log logger = get_log() ch = CommandHandler() fm_num = int(0) filename = "command_queue.txt" command_counter = 1 while fm_num > -1: fm_num = int(input("What flight mode would you like to command in?\n")) if fm_num > -1: command_num = int(input("What command ID would you like to send?\n")) if command_num > -1: kwarg_str = input( "Please input any arguments as a valid python dictionary:\n") kwarg_dict = eval(kwarg_str) packed = ch.pack_command(command_counter, fm_num, command_num, **kwarg_dict) file = open(filename, "a") file.write(str(packed.hex()) + "\n") file.close() logger.info(f"Wrote hex bytes {str(packed.hex())}")
class MainSatelliteThread(Thread): def __init__(self): super().__init__() logger.info("Initializing...") self.init_parameters() self.command_queue = Queue() self.downlink_queue = Queue() self.FMQueue = Queue() self.commands_to_execute = [] self.downlinks_to_execute = [] self.telemetry = Telemetry(self) self.burn_queue = Queue() self.reorientation_queue = Queue() self.reorientation_list = [] self.maneuver_queue = Queue() # maneuver queue # self.init_comms() self.command_handler = CommandHandler() self.downlink_handler = DownlinkHandler() self.command_counter = 0 self.downlink_counter = 0 self.command_definitions = CommandDefinitions(self) self.init_sensors() self.last_opnav_run = datetime.now() # Figure out what to set to for first opnav run self.log_dir = LOG_DIR self.logger = get_log() self.attach_sigint_handler() # FIXME if os.path.isdir(self.log_dir): self.flight_mode = RestartMode(self) else: os.makedirs(CISLUNAR_BASE_DIR) os.mkdir(LOG_DIR) self.flight_mode = BootUpMode(self) self.create_session = create_sensor_tables_from_path(DB_FILE) def init_comms(self): self.comms = CommunicationsSystem( queue=self.command_queue, use_ax5043=False ) self.comms.listen() def init_parameters(self): with open(PARAMETERS_JSON_PATH) as f: json_parameter_dict = load(f) self.parameters = json_parameter_dict # try: # for parameter in self.parameters.__dir__(): # if parameter[0] != '_': # self.parameters[parameter] = json_parameter_dict[parameter] # except: # raise Exception( # 'Attempted to set parameter ' + str(parameter) + # ', which could not be found in parameters.json' # ) def init_sensors(self): self.radio = Radio() self.gom = Gomspace(self.parameters) self.gyro = GyroSensor() self.adc = ADC(self.gyro) self.rtc = RTC() # self.pressure_sensor = PressureSensor() # pass through self so need_to_burn boolean function # in pressure_sensor (to be made) can access burn queue""" # initialize the cameras, select a camera logger.info("Creating camera mux...") self.mux = camera.CameraMux() self.camera = camera.Camera() logger.info("Selecting a camera...") # select camera before reboot so that we will detect cam on reboot self.mux.selectCamera(random.choice([1, 2, 3])) def handle_sigint(self, signal, frame): self.shutdown() sys.exit(0) def attach_sigint_handler(self): signal.signal(signal.SIGINT, self.handle_sigint) def poll_inputs(self): # self.tlm.poll() self.flight_mode.poll_inputs() # Telemetry downlink if (datetime.today() - self.radio.last_telemetry_time).total_seconds() / 60 >= self.parameters[ "TELEM_DOWNLINK_TIME"]: self.enter_transmit_safe_mode() telemetry = self.command_definitions.gather_basic_telem() telem_downlink = ( self.downlink_handler.pack_downlink(self.downlink_counter, FMEnum.Normal.value, NormalCommandEnum.BasicTelem.value, **telemetry)) self.downlink_queue.put(telem_downlink) # Listening for new commands newCommand = self.radio.receiveSignal() if newCommand is not None: try: unpackedCommand = self.command_handler.unpack_command(newCommand) if unpackedCommand[0] == MAC: if unpackedCommand[1] == self.command_counter + 1: print('hello') self.command_queue.put(bytes(newCommand)) self.command_counter += 1 else: print('Command with Invalid Counter Received. ' + 'Counter: ' + str(unpackedCommand[1])) else: print('Unauthenticated Command Received') except: print('Invalid Command Received') else: print('Not Received') def enter_transmit_safe_mode(self): # TODO: Make sure that everything else is turned off before transmitting pass def replace_flight_mode_by_id(self, new_flight_mode_id): self.replace_flight_mode(build_flight_mode(self, new_flight_mode_id)) def replace_flight_mode(self, new_flight_mode): self.flight_mode = new_flight_mode self.logger.info(f"Changed to FM#{self.flight_mode.flight_mode_id}") def update_state(self): fm_to_update_to = self.flight_mode.update_state() # only replace the current flight mode if it needs to change (i.e. dont fix it if it aint broken!) if fm_to_update_to is None: pass elif fm_to_update_to != NO_FM_CHANGE and fm_to_update_to != self.flight_mode.flight_mode_id: self.replace_flight_mode_by_id(fm_to_update_to) def clear_command_queue(self): while not self.command_queue.empty(): command = self.command_queue.get() print(f"Throwing away command: {command}") def reset_commands_to_execute(self): self.commands_to_execute = [] # Execute received commands def execute_commands(self): assert ( len(self.commands_to_execute) == 0 ), "Didn't finish executing previous commands" while not self.command_queue.empty(): self.commands_to_execute.append(self.command_queue.get()) self.flight_mode.execute_commands() def execute_downlinks(self): self.enter_transmit_safe_mode() while not self.downlink_queue.empty(): self.radio.transmit(self.downlink_queue.get()) self.downlink_counter += 1 sleep(self.parameters["DOWNLINK_BUFFER_TIME"]) def read_command_queue_from_file(self, filename="communications/command_queue.txt"): """A temporary workaround to not having radio board access""" # check if file exists if os.path.isfile(filename): text_file = open(filename, "r") for hex_line in text_file: self.command_queue.put(bytes.fromhex(hex_line)) text_file.close() # delete file os.remove(filename) # Run the current flight mode def run_mode(self): with self.flight_mode: self.flight_mode.run_mode() # Wrap in try finally block to ensure it stays live def run(self): """This is the main loop of the Cislunar Explorers and runs constantly during flight.""" try: while True: sleep(5) # TODO remove when flight modes execute real tasks self.poll_inputs() self.update_state() self.read_command_queue_from_file() self.execute_commands() # Set goal or execute command immediately self.execute_downlinks() self.run_mode() finally: self.replace_flight_mode_by_id(FMEnum.Safety.value) # TODO handle failure gracefully if FOR_FLIGHT is True: self.run() else: self.gom.all_off() def shutdown(self): self.gom.all_off() logger.critical("Shutting down...")