def _average(self, name): data = [] Sensors.get_data( lambda obj: obj['name'] == name, data, max_age=self._period) if data: value = sum(elem['value'] for elem in data) / len(data) timestamp = max(elem['timestamp'] for elem in data) return self._make(name, value, timestamp)
def _any(self, name): data = [] Sensors.get_data( lambda obj: obj['name'] == name, data, max_age=self._period) if data: for obj in data: if obj['value']: return self._make(name, True, obj['timestamp'])
def run(self): while True: time.sleep(1 / Geometry._frequency) pos_lats = [] pos_lngs = [] Sensors.get_data(lambda obj: obj['name'] == 'latitude', pos_lats, 3) Sensors.get_data(lambda obj: obj['name'] == 'longitude', pos_lngs, 3) if len(pos_lats) < 3 or len(pos_lngs) < 3: continue p0, p1, p2 = ((a['value'], b['value']) for a, b in zip(pos_lats, pos_lngs)) t0, t1, t2 = (pos_lats[i]['timestamp'] for i in range(3)) Geometry._time = t2 Geometry._pos = p2 Geometry._r = Geometry._make_r(p1, p2) Geometry._v = Geometry._r / (t2 - t1) Geometry._a = (Geometry._v - Geometry._make_r(p0, p1) / (t1 - t0)) / ((t2 + t1) / 2 - (t1 + t0) / 2) if self._last_stamp != t2: if self._last_time is None: self._last_time = time.time() alph = 0.7 self.step_size = alph * self._step_size + \ (1 - alph) * (time.time() - self._last_time) if Geometry._inter_pos is None: Geometry._inter_pos = p2 # Stabilize self._dir = 0.3 * (np.array(p2) - Geometry._inter_pos) + 0.7 * ( self._dir if self._dir is not None else np.array(p2) - Geometry._inter_pos) self._last_stamp = t2 self._last_update_time = time.time() self._last_time = time.time() k = (time.time() - self._last_update_time) / self._step_size self._last_update_time = time.time() Geometry._inter_pos = list(self._dir * k + Geometry._inter_pos) Geometry._build_marker()
def run(self): while True: time.sleep(1.0 / self._frequency) wheel_rot = [] if Geometry._pos is None: continue lat, lng = Geometry._pos Sensors.get_last( lambda obj: obj['name'] == 'steering_wheel_angle', wheel_rot, max_age=1) if wheel_rot: wheel_rot = wheel_rot[0]['value'] if abs(wheel_rot) > 75 or self._street_address is None: self._last_turn = time.time() if self._last_turn is not None and time.time() - self._last_turn < 3: address = self._street_address try: # Haults (stucks) after quota of 2500 lookups per day. response = self._cli.reverse_geocode( (lat, lng), result_type="route") response = response[0] for line in response['address_components']: if 'route' in line['types']: address = line['long_name'] break except Exception as e: logging.debug(e) if self._street_address is None: self._street_address = address elif address != self._street_address: self._street_address = address signaled = [] Sensors.get_data( lambda e: e['name'] == 'turn_signals', signaled, max_age=12) signaled = [e for e in signaled if e['value'] != 'off'] if not signaled: self._event.set() Distributor.analyzes.put({'name': 'forgot_signals', 'value': 1, 'timestamp': Geometry._time})
class BalloonMissionComputer(): # --------------------------------------------------------------------------- def __init__(self): if not os.path.exists('tmp/'): os.makedirs('tmp/') logging.basicConfig( format='%(asctime)s %(levelname)s %(name)s: %(message)s', level=logging.DEBUG, datefmt='%Y-%m-%d %H:%M:%S') self.logger = logging.getLogger(__name__) hdlr = logging.FileHandler('tmp/program.log') formatter = logging.Formatter( '%(asctime)s - %(name)s - %(levelname)s - %(message)s', datefmt='%Y-%m-%d %H:%M:%S') hdlr.setFormatter(formatter) self.logger.addHandler(hdlr) #--------------------------------------------------------------------------- def handle_log(self, msg): self.webserver.log(msg) #--------------------------------------------------------------------------- def calc_status_bits(self, gpsdata, sensordata): bits = [ True, False, sensordata['cam2'] == "ok", sensordata['cam1'] == "ok", gpsdata['status'] == "fix", gpsdata['status'] == "lost", gpsdata['status'] == "comm error", gpsdata['status'] == "i2c error" ] return ''.join(['1' if val else '0' for val in bits]) status_names = [ 'gps i2c err', "gps comm err", "gps no fix", "gps ok", "cam1 ok", "cam2 ok" ] # --------------------------------------------------------------------------- def calc_balloon_state(self, gpsdata): current_alt = float(gpsdata['alt']) if self.state == "init" and current_alt > 0: self.state = "ground" self.send_bulltin() if self.state == "ground" and current_alt > self.min_alt + 2000: self.state = "ascent" self.send_bulltin() if self.state == "ascent" and current_alt < self.max_alt - 2000: self.state = "descent" self.send_bulltin() if self.state == "descent" and current_alt < 2000: self.state = "landed" self.send_bulltin() self.timers.set_state('BUZZER', True) if current_alt > self.max_alt: self.max_alt = current_alt if current_alt > 0 and current_alt < self.min_alt: self.min_alt = current_alt self.prev_alt = current_alt # --------------------------------------------------------------------------- def update_system_datetime(self, gpsdata): if gpsdata['status'] != "fix": return if 'date' not in gpsdata: return now = datetime.datetime.now() gpstime = datetime.datetime.strptime( gpsdata['date'] + " " + gpsdata['fixTimeStr'], "%d%m%y %H:%M:%S") diff = int(abs((now - gpstime).total_seconds() / 60)) if diff > 100: self.logger.info("system time %s" % now) self.logger.info("gps time %s" % gpstime) self.logger.info("updating") # os.system('date -s %s' % gpstime.isoformat()) proc = subprocess.Popen( ["date", "-s %s" % gpstime.isoformat()], stdout=subprocess.PIPE, shell=True) (out, err) = proc.communicate() self.logger.info("program output:" % out) self.logger.info("program error:" % err) #todo: verify we have premissions # --------------------------------------------------------------------------- def send_bulltin(self): try: self.logger.info("state changed to %s" % self.state) frame = self.aprs.create_message_msg( "BLN1BALON", "changed state to %s" % self.state) self.modem.encode(frame) self.modem.saveToFile(os.path.join(self.tmp_dir, 'aprs.wav')) self.radio_queue(self.config['frequencies']['APRS'], os.path.join(self.tmp_dir, 'aprs.wav')) except: pass # --------------------------------------------------------------------------- def capture_image(self, archive=True): # self.logger.debug("capture start") self.cam.capture() if archive: self.cam.archive() # self.logger.debug("capture end") # --------------------------------------------------------------------------- def prep_image(self, id, gpsdata, sensordata): self.logger.debug("image manutulation start") self.cam.select(id) self.cam.resize((320, 256)) self.cam.overlay(self.config['callsign'], gpsdata, sensordata) self.cam.saveToFile(os.path.join(self.tmp_dir, "image.jpg")) self.logger.debug("image manipulation end") # --------------------------------------------------------------------------- def process_ssdv(self): self.logger.debug("process ssdv start") self.logger.debug("jpg->ssdv") self.ssdv.convert('tmp/image.jpg', 'tmp/image.ssdv') self.logger.debug("ssdv->aprs") packets = self.ssdv.prepare(os.path.join(self.tmp_dir, "image.ssdv")) self.logger.debug("aprs->wav") self.ssdv.encode(packets, 'tmp/ssdv.wav') self.timers.trigger("PLAY-SSDV") self.logger.debug("process ssdv end") # --------------------------------------------------------------------------- def process_sstv(self): self.logger.debug("process sstv start") self.sstv.image = self.cam.image self.sstv.process() self.sstv.saveToFile(os.path.join(self.tmp_dir, 'sstv.wav')) self.timers.trigger("PLAY-SSTV") self.logger.debug("process sstv end") # --------------------------------------------------------------------------- def gps_reset(self): self.logger.warning("reset gps") GPIO.setup(self.config['pins']['GPS_RST'], GPIO.OUT) GPIO.output(self.config['pins']['GPS_RST'], GPIO.LOW) time.sleep(1) GPIO.output(self.config['pins']['GPS_RST'], GPIO.HIGH) # --------------------------------------------------------------------------- def setup(self): self.logger.info("--------------------------------------") self.logger.info(" Balloon Mission Computer V4.01 ") self.logger.info("--------------------------------------") self.logger.debug("setup") # setup files and directories with open('data/config.json') as fin: self.config = json.load(fin) self.images_dir = self.config["directories"][ "images"] if "directories" in self.config and "images" in self.config[ "directories"] else "./images" self.tmp_dir = self.config["directories"][ "tmp"] if "directories" in self.config and "tmp" in self.config[ "directories"] else "./tmp" if not os.path.exists(self.tmp_dir): os.makedirs(self.tmp_dir) if not os.path.exists(self.images_dir): os.makedirs(self.images_dir) # setup gpio GPIO.setwarnings(False) GPIO.setmode(GPIO.BCM) # Broadcom pin-numbering scheme GPIO.setup(self.config['pins']['BUZZER'], GPIO.OUT) GPIO.setup(self.config['pins']['LED1'], GPIO.OUT) GPIO.setup(self.config['pins']['LED2'], GPIO.OUT) GPIO.output(self.config['pins']['LED1'], GPIO.HIGH) # modules self.aprs = APRS(self.config['callsign'], self.config['ssid'], "idoroseman.com") self.modem = AFSK() try: self.gps = Ublox() self.gps.start() except: pass self.radio = Dorji(self.config['pins']) self.radio.init() self.radio_q = queue.Queue() self.radio_thread = threading.Thread(target=self.radio_worker) self.radio_thread.start() self.timers = Timers(self.config['timers']) self.sensors = Sensors() self.cam = Camera() self.ssdv = SSDV(self.config['callsign'], self.config['ssid']) self.sstv = SSTV() self.webserver = WebServer() self.radio_queue(self.config['frequencies']['APRS'], 'data/boatswain_whistle.wav') self.syslog = logging.getLogger() kh = MyLogHandler() kh._listeners.append(self.handle_log) kh.setLevel(logging.DEBUG) formatter2 = logging.Formatter('%(levelname)s: %(name)s - %(message)s') kh.setFormatter(formatter2) self.syslog.addHandler(kh) # timers for item in ["APRS", "APRS-META", "Imaging", 'Capture']: self.timers.set_state(item, self.config['timers'][item] > 0) self.timers.set_state("Buzzer", False) # lets go self.ledState = 1 self.imaging_counter = 1 self.state = "init" self.min_alt = sys.maxsize self.max_alt = 0 self.prev_alt = 0 self.send_bulltin() GPIO.output(self.config['pins']['LED1'], GPIO.LOW) # --------------------------------------------------------------------------- def run(self): self.logger.debug("run") telemetry = {} telemCoef = { 'SatCount': 1, 'outside_temp': 10, 'inside_temp': 10, 'barometer': 1, 'battery': 100 } exitFlag = False self.prev_gps_status = "" while not exitFlag: try: # blink self.ledState = 1 - self.ledState GPIO.output(self.config['pins']['LED1'], GPIO.HIGH) watchdog = Watchdog(60) # gps self.gps.loop() gpsdata = self.gps.get_data() self.calc_balloon_state(gpsdata) if gpsdata['status'] == "fix" and gpsdata['alt'] > 0: self.sensors.calibrate_alt(gpsdata['alt']) if gpsdata['status'] != "fix": gpsdata['alt'] = self.sensors.read_pressure() # sensors sensordata = self.sensors.get_data() sensordata.update(self.cam.status) status_bits = self.calc_status_bits(gpsdata, sensordata) telemetry[ 'Satellites'] = gpsdata['SatCount'] * telemCoef['SatCount'] telemetry['outside_temp'] = sensordata[ 'outside_temp'] * telemCoef['outside_temp'] telemetry['inside_temp'] = sensordata[ 'inside_temp'] * telemCoef['inside_temp'] telemetry['barometer'] = sensordata['barometer'] * telemCoef[ 'barometer'] telemetry[ 'battery'] = sensordata['battery'] * telemCoef['battery'] if gpsdata['status'] != self.prev_gps_status: frame = self.aprs.create_telem_data_msg( telemetry, status_bits, gpsdata['alt']) self.modem.encode(frame) self.modem.saveToFile( os.path.join(self.tmp_dir, 'aprs.wav')) self.radio_queue(self.config['frequencies']['APRS'], os.path.join(self.tmp_dir, 'aprs.wav')) self.prev_gps_status = gpsdata['status'] # UI self.webserver.update(gpsdata, sensordata, self.state) self.update_system_datetime(gpsdata) if self.timers.expired("APRS"): if gpsdata['status'] == "fix": self.logger.debug("sending location") frame = self.aprs.create_location_msg( gpsdata, telemetry, status_bits) else: self.logger.debug("sending only telemetry") frame = self.aprs.create_telem_data_msg( telemetry, status_bits, gpsdata['alt']) self.modem.encode(frame) self.modem.saveToFile( os.path.join(self.tmp_dir, 'aprs.wav')) self.radio_queue(self.config['frequencies']['APRS'], os.path.join(self.tmp_dir, 'aprs.wav')) with open(os.path.join(self.tmp_dir, "flight.log"), 'a+') as f: merged = dict() merged.update(gpsdata) merged.update(sensordata) merged['datatime'] = datetime.datetime.now().isoformat( ) f.write(json.dumps(merged, indent=2)) f.write(',\n') if self.timers.expired("APRS-META"): frame = self.aprs.create_telem_name_msg( telemetry, self.status_names) self.modem.encode(frame) self.modem.saveToFile( os.path.join(self.tmp_dir, 'aprs.wav')) self.radio_queue(self.config['frequencies']['APRS'], os.path.join(self.tmp_dir, 'aprs.wav')) frame = self.aprs.create_telem_eqns_msg(telemCoef) self.modem.encode(frame) self.modem.saveToFile( os.path.join(self.tmp_dir, 'coef.wav')) self.radio_queue(self.config['frequencies']['APRS'], os.path.join(self.tmp_dir, 'coef.wav')) if self.timers.expired("Capture"): self.capture_image() if self.timers.expired("Snapshot"): self.imaging_counter += 1 cam_select = self.imaging_counter % CAMERAS self.capture_image(archive=False) self.prep_image(cam_select, gpsdata, sensordata) self.webserver.snapshot() if self.timers.expired("Imaging"): self.imaging_counter += 1 cam_select = self.imaging_counter % CAMERAS cam_system = self.imaging_counter % (CAMERAS + 1) self.logger.info("imageing trigger") self.logger.debug("cam %s system %s" % (cam_select, cam_system)) self.capture_image(archive=False) self.prep_image(cam_select, gpsdata, sensordata) self.webserver.snapshot() if cam_system == 0: self.logger.info("->sstv") _thread.start_new_thread(self.process_sstv, ()) else: self.logger.info("->ssdv") _thread.start_new_thread(self.process_ssdv, ()) if self.timers.expired("PLAY-SSDV"): self.logger.debug("sending ssdv") self.radio_queue(self.config['frequencies']['APRS'], os.path.join("data", 'starting_ssdv.wav')) self.radio_queue(self.config['frequencies']['APRS'], os.path.join("data", 'habhub.wav')) self.radio_queue(self.config['frequencies']['APRS'], os.path.join(self.tmp_dir, 'ssdv.wav')) if self.timers.expired("PLAY-SSTV"): self.logger.debug("sending sstv") self.radio_queue( self.config['frequencies']['APRS'], os.path.join("data", 'switching_to_sstv.wav')) self.radio_queue(self.config['frequencies']['SSTV'], os.path.join("data", 'starting_sstv.wav')) self.radio_queue(self.config['frequencies']['SSTV'], os.path.join(self.tmp_dir, 'sstv.wav')) if self.timers.expired("Buzzer"): for i in range(3): GPIO.output(self.config['pins']['BUZZER'], GPIO.HIGH) time.sleep(0.5) GPIO.output(self.config['pins']['BUZZER'], GPIO.LOW) time.sleep(0.5) GPIO.output(self.config['pins']['LED1'], GPIO.LOW) watchdog.stop() time.sleep(1) except Watchdog: self.logger.error("task timedout!") except KeyboardInterrupt: # If CTRL+C is pressed, exit cleanly exitFlag = True self.gps.stop() break except Exception as x: self.logger.exception(x) self.logger.info("Done.") # --------------------------------------------------------------------------- def radio_queue(self, freq, filename): self.radio_q.put({'freq': freq, 'filename': filename}) # --------------------------------------------------------------------------- def radio_worker(self): while True: item = self.radio_q.get() self.radio.play(item['freq'], item['filename']) self.radio_q.task_done()