def _recv_iq_thread(self): poller = zmq.Poller() poller.register(self._sock_iq, zmq.POLLIN) self._iqrxcnt = 0 while True: sock = poller.poll(1.0) for s, event in sock: data = s.recv() data_vals = list(np.frombuffer(data, dtype=np.complex64)) self._iqbuff += data_vals self._iqrxcnt += len(data_vals) # Dont like this but it is one way to check the abort flag and at the same time # trigger on the global abort event. try: if wait_loop(lambda: self._stop, timeout = 0.0001) is not None: log.debug("recv_iq_thread aborted") break except AbortAllException: log.debug("abort all exception caught, cancelling _recv_iq_thread") break
def _set_azel(self, az, el, timeout=.5): """ This method sends the command to set the position to the rotator. If anything goes wrong it raises an exception """ try: ret = self._send_ser_cmd('W{:03.0f} {:03.0f}'.format(az, el), timeout=timeout) except: raise #<--- timeout if not self._parse_set_reply(ret): estr = "Unexpected response when setting azel: '{}'".format(ret.encode('string_escape')) raise Error(estr) #Allow the controller time to settle wait_loop(timeout=self.ROT_SETTLETIME_SET)
def _send_ser_cmd(self, cmd, timeout=.5): ret = None exc = None t0 = time.time() while ret is None and exc is None: try: ret, exc = self._ser.send(cmd) except ValueError: if time.time() - t0 > timeout: raise Error("Timeout waiting for serial port to be free") else: wait_loop(timeout=.01) if exc is not None: raise exc return ret
def _pthr_update(self): while True: self._update_rotator() try: if not wait_loop(self._stopped, timeout = self._POLL_DT) is None: log.debug("Exiting GS232B polling loop due to stop() being called") break except: log.debug("Exiting GS232B polling loop due to global abort") break
def _poll_for_rpcconnection(self): # try to connect to api every minute while len(self._rpc_unavailable) > 0: _unavailable = {} sys.stdout.flush() for uri, rpcaddr in self._rpc_unavailable.items(): try: self._try_rpc_connection(uri, rpcaddr) except Exception as e: _unavailable[uri] = rpcaddr self._rpc_unavailable= _unavailable if wait_loop(self._abort_event, timeout=60) is not None: break
def _pthr_record(): spec = [None]*N ok_idx = [False] * N tvec = [0.0] * N fvec = [] abort.clear() t0 = time.time() last_spec = None for n in range(N): try: t0 = time.time() tvec[n] = t0 dt1 = (dt - (time.time() - t0)) if dt1 > 2.0: if abort in wait_loop([abort], timeout=dt1): break else: time.sleep(dt1) if abort.is_set(): break try: ret = self.get_spectrum() except: continue if len(ret) != 2: raise Error("get_spectrum did not return valid freq, spec") fv, sp = ret[0][::fdec], ret[1][::fdec] # Check if there is something new, if not skip if last_spec is not None and all(sp[i] == last_spec[i] for i in range(len(sp))): continue last_spec = sp[:] fvec, spec[n] = fv, sp ok_idx[n] = True except Exception as e: log.error("Error {} while recording spectrum. Ignoring and continuing: {}".format(e.__class__.__name__, e)) spec = [[0.0]*len(fvec) if x is None else list(x) for x in spec] lens = [len(s) for s in spec] if not all([l == lens[0] for l in lens]): raise Error("Unexpected error recording spectra. Not all spectra are same length") if len(fvec) == 0: raise Error("No data recorded") # Trim data that wasnt filled (in case abort wwas called) spec = spec[:(n+1)] tvec = tvec[:(n+1)] ok_idx = ok_idx[:(n+1)] spec = np.array(spec) # Get rid of bad data unless zeroes are requested if not add_zeroes: spec = spec[ok_idx, :] tvec = [tvec[k] for k,ok in enumerate(ok_idx) if ok] dvec = [conv_time(t, to="datetime", float_t="unix") for t in tvec] return fvec, dvec, spec
def execute_pass(self, i): """ Method to execute a pass on the ground station. Will initiate tracking of the satellite and perform all the communications that have been defined. For each communication, it will wait for a reply before continuing. If no reply is received it will retry as many times as specified in the schedule. If no reply is expected, the schedule should specify retries=0 Args: i (int): Pass number from the schedule """ # Wrap everything in try/except/finally to make sure that # if anything bad happens during a pass # it does not make everything fall over. try: stop = lambda: self._stop not_tracking = lambda: self.gs.state.state != self.gs.state.TRACKING metadata = self.schedule.passes[i].metadata p = self.schedule.passes[i].pass_data p.nid = self.schedule.passes[i].nid if self.enforce_signoffs and p.nid <= 0: log.warning( "The schedule does not have enough signoffs, but NID = {} is not a real satellite so allowing scheduler to continue anyway" .format(p.nid)) elif self.enforce_signoffs: if not 'signoffs' in metadata.keys() or len( metadata['signoffs']) < self.enforce_signoffs: log.error( "The schedule does not have enough signoffs. At least {} are required." .format(self.enforce_signoffs)) return # # 2) Slew to start and wait for satellite to come into view # self.gs.start_track(p, compute_ant_points=self.compute_ant_points) # # Give the GS a second to initiate the pass_id variable, then save schedule # to log. # time.sleep(1.0) log.debug("Storing schedule in database. pass_id = {}".format( self.gs.state.pass_id[1])) try: self.gs._passlog.put(module=__name__, pass_id=self.gs.state.pass_id[1], key="schedule", value=self.schedule.passes[i].to_json()) except Exception as e: log.exception( "Exception trying to insert schedule in database: {}: {}") if 'signoffs' in metadata.keys(): log.info("The pass has been signed by {}".format( metadata['signoffs'])) try: self.gs._passlog.put(module=__name__, pass_id=self.gs.state.pass_id[1], key="signoffs", value=metadata['signoffs']) except Exception as e: log.exception( "Exception trying to insert signoffs in database: {}: {}" ) log.info("Waiting for pass") if stop in wait_loop( [stop, lambda: self.gs.state.state == self.gs.state.TRACKING], timeout=None, dt=.5): log.debug("execute_pass aborted with stop event") return # # Set the protocol to use # if len(self.gs.protocols) == 0: raise Error("No protocol installed") # TODO: Logic to go here to choose the right protocol based on the schedule if 'protocol' in metadata.keys(): try: self.gs.protocol = metadata['protocol'] except Exception as e: raise Error( "Unknown protocol {} requested, pass cannot continue". format(metadata['protocol'])) else: log.info( "No protocol has been specified, using default {}".format( self.gs.protocols[0].name)) try: self.gs.protocol = self.gs.protocols[0] except Exception as e: raise Error( "Could not set default protocol. This error should really not be possible. {}: {}" .format(e.__class__.__name__, e)) # Make the pass metadata available to the protocol self.gs.protocol.sch_metadata = metadata # # Initialise # if self.schedule.passes[i].listen: log.debug("Listening pass, calling protocol.init_rx initiator") self.gs.protocol.init_rx() else: log.debug( "Non-listening pass, calling protocol.init_rxtx initiator") self.gs.protocol.init_rxtx() # # This is a bit tricky but needs to be done this way so # that the action/transmit can be aborted. # What happens is that the method is run in a threadpool # and while we are waiting for the result we also monitor # for whether the station has stopped tracking or the _stop # flag has been set # # def action_thread(desc, *args, **kwargs): # this little wrapper function is just to make sure we log exceptions properly try: self.gs.do_action(desc, *args, **kwargs) except Exception as e: log.exception( "{} performing action {}, {}, {}: '{}'".format( e.__class__.__name__, desc, args, kwargs, e)) raise pool = ThreadPool(processes=1) # # Send all communications/actions # for comm in self.schedule.passes[i].comms: retry_cnt = -1 while (retry_cnt < comm['retries']): # This flag gets set if someone calls stop() if self._stop: return # Make it possible to cancel schedule half-way through track elif (not_tracking()): log.error("Pass ended before comms finished") return if isinstance(comm, Action): ret = pool.apply_async(lambda: action_thread( comm['desc'], *comm['args'], **comm['kwargs'])) ret2 = wait_loop([ret.ready, not_tracking, stop]) if not_tracking in ret2 or stop in ret2: log.error("do_action aborted") return # <--- the do_action may still be running, but will be dealt with by the "finally" at the end of this function elif not ret.successful(): log.error( "Error performing action: %s. Trying again" % (comm)) retry_cnt += 1 continue else: wait = -1 if comm[ 'wait'] is False else Defaults.TX_REPLY_TIMEOUT log.info("Transmitting %s" % (comm['hexstr'])) ret = pool.apply_async(lambda: self.gs.transmit( comm['barray'], wait=wait)) ret2 = wait_loop([ret.ready, not_tracking, stop]) if not_tracking in ret2 or stop in ret2: log.error("transmit aborted") return # <--- the do_action may still be running, but will be dealt with by the "finally" at the end of this function elif not ret.successful(): sleept = 10 log.error( "Caught exception while transmitting. Waiting another %d sec, then continuing" % (sleept)) wait_loop( timeout=sleept ) #<-- safe way to sleep. will terminate on global abort (but not for a normal stop, ok since its just a few seocncds) retry_cnt += 1 continue # if ok, break retry loop break # # 4) End track, # (unless scheduler has been configured to follow track till its end) # if self.track_full_pass: log.info("Waiting for pass to finish") wait_loop([ stop, lambda: self.gs.state.state != self.gs.state.TRACKING ], timeout=None, dt=.5) return else: # Keep tracking for a bit since sometimes it seems data keeps coming log.info("Waiting up to %s before ending tracking" % (self.schedule.buffertime)) wait_loop([ stop, lambda: self.gs.state.state != self.gs.state.TRACKING ], timeout=self.schedule.buffertime, dt=.5) return except Exception as e: log.error( "Unhandled exception while executing pass: {}: {}".format( e.__class__.__name__, e)) finally: # # Turn off protocol/radio # try: self.gs.protocol.terminate() log.debug( "execute_pass.finally reached and protocol.terminate() called successfully" ) except Exception as e: log.error( "execute_pass.finally reached but with error calling terminate: {}:{}" .format(e.__class__.__name__, e)) # # Stop tracking if its still doing so # try: self.gs.stop_track(block=True) log.debug( "execute_pass.finally reached and groundstatio.stop_track() called successfully" ) except Exception as e: log.error( "execute_pass.finally reached but error calling groundstatio.stop_track(). {}: {}" .format(e.__class__.__name__, e))