def setup(self): self.logger.info("--------------------------------------") self.logger.info(" Balloon Mission Computer V4.01 ") self.logger.info("--------------------------------------") self.logger.debug("setup") # setup 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) 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) self.aprs = APRS(self.config['callsign'], self.config['ssid'], "idoroseman.com") self.modem = AFSK() self.gps = Ublox() self.gps.start() 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') for item in ["APRS", "APRS-META", "Imaging", 'Capture']: self.timers.handle({item: self.config['timers'][item] > 0}, []) self.timers.handle({"Buzzer": False}, []) 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)
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()
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 deviceStartComm(self, device): self.logger.debug(u"{}: Starting Device".format(device.name)) instanceVers = int(device.pluginProps.get('devVersCount', 0)) if instanceVers == kCurDevVersCount: self.logger.threaddebug( u"{}: Device is current version: {}".format( device.name, instanceVers)) elif instanceVers < kCurDevVersCount: newProps = device.pluginProps newProps["devVersCount"] = kCurDevVersCount device.replacePluginPropsOnServer(newProps) self.logger.debug(u"{}: Updated device version: {} -> {}".format( device.name, instanceVers, kCurDevVersCount)) else: self.logger.warning(u"{}: Invalid device version: {}".format( device.name, instanceVers)) device.stateListOrDisplayStateIdChanged() if device.deviceTypeId == "weatherlink": self.weatherlinks[device.id] = WeatherLink(device) device.updateStateImageOnServer(indigo.kStateImageSel.SensorOn) elif device.deviceTypeId == "aprs_sender": self.senders[device.id] = APRS(device) elif device.deviceTypeId == "pws_sender": self.senders[device.id] = PWS(device) elif device.deviceTypeId == "wu_sender": self.senders[device.id] = WU(device) elif device.deviceTypeId in [ 'issSensor', 'moistureSensor', 'tempHumSensor', 'baroSensor' ]: if device.pluginProps.get('status_state', None) in [ "temp", "temp_in", "dew_point", "dew_point_in", "heat_index", "heat_index_in", "wind_chill", "temp_1", "temp_2", "temp_3", "temp_4" ]: device.updateStateImageOnServer( indigo.kStateImageSel.TemperatureSensorOn) elif device.pluginProps.get('status_state', None) in [ "rain_15_min", "rain_60_min", "rain_24_hr" ]: device.updateStateImageOnServer(indigo.kStateImageSel.Auto) elif device.pluginProps.get('status_state', None) in [ "hum", "hum_in", "moist_soil_1", "moist_soil_2", "moist_soil_3", "moist_soil_4", "wet_leaf_1", "wet_leaf_2" ]: device.updateStateImageOnServer( indigo.kStateImageSel.HumiditySensorOn) elif device.pluginProps.get( 'status_state', None) in ["bar_sea_level", "bar_absolute"]: device.updateStateImageOnServer(indigo.kStateImageSel.Auto) elif device.pluginProps.get('status_state', None) in [ "wind_speed_last", "wind_speed_avg_last_2_min" ]: device.updateStateImageOnServer( indigo.kStateImageSel.WindSpeedSensor) else: device.updateStateImageOnServer(indigo.kStateImageSel.Auto) self.sensorDevices[device.id] = device else: self.logger.warning(u"{}: Invalid device type: {}".format( device.name, device.deviceTypeId)) if not device.pluginProps.get("pollingRounding", False): # Only do intial update if Polling Frequency rounding not in effect self.updateNeeded = True self.logger.debug( u"{}: deviceStartComm complete, sensorDevices = {}".format( device.name, self.sensorDevices))
out = subprocess.check_output(cmd, shell=True) end_time = time.time() self.logger.info("rust encoding %s messages took %s seconds" % (len(packets), end_time-start_time)) except Exception as x: self.logger.exception(x) if __name__ == "__main__": from modem import AFSK from ax25 import ax25 # from camera import Camera callsign = "4x6ub" ssid = 11 dest = "APE6UB" ssdv = SSDV(callsign, 11) aprs = APRS(callsign, 11) modem = AFSK() # cam = Camera() # cam.capture() # cam.resize((320,256)) # gpsdata = {'lat': 32.063331, # 'lon': 34.87216566666667, # 'alt': 129.7 # } # sensordata = {'outside_temp': -12 # } # cam.overlay('4x6ub', gpsdata, sensordata) # cam.saveToFile('tmp/ssdv.jpg') # ssdv.convert('data/testcard.jpg', 'tmp/image.ssdv') # packets = ssdv.prepare("tmp/image.ssdv") # print(len(packets),"packets")