Пример #1
0
def camera_thread(configuration: Configuration, start_time: float):
    """Start the camera and log the video"""
    try:
        logging.info("Starting video capture")
        runtime_limit = configuration.get("runtime_limit")
        output_directory = configuration.get("output_directory")
        camera = picamera.PiCamera(framerate=90)
        camera.start_recording(
            os.path.join(output_directory, f"video_{int(start_time)}.h264"))
        camera.wait_recording(runtime_limit)
        camera.stop_recording()
        logging.info("Video capture complete")
    except Exception as ex:  # pylint: disable=broad-except
        handle_exception("Video capture failure", ex)
Пример #2
0
def sensor_log_writing_loop(configuration: Configuration, start_time: float,
                            data_queue: Queue):
    """Loop through clearing the data queue until RUNTIME_LIMIT has passed"""
    try:
        logging.info("Starting sensor log writing loop")
        runtime_limit = configuration.get("runtime_limit")
        output_directory = configuration.get("output_directory")
        with open(
                os.path.join(output_directory,
                             f"sensor_log_{int(start_time)}.csv"),
                "w") as outfile:
            write_sensor_log(start_time, runtime_limit, outfile, data_queue)
        logging.info("Telemetry log writing loop complete")
    except Exception as ex:  # pylint: disable=broad-except
        handle_exception("Telemetry log line writing failure", ex)
Пример #3
0
def init_gps(configuration: Configuration):
    """Initialize the serial port to receive GPS data"""
    gps_serial_port = configuration.get_device_configuration("gps", "serial_device")
    if not gps_serial_port:
        return None
    ser = serial.Serial(gps_serial_port, 9600, timeout=5.0)
    sio = io.TextIOWrapper(io.BufferedRWPair(ser, ser))
    return sio
Пример #4
0
def init_magnetometer_accelerometer(configuration: Configuration):
    """Initialize the sensor for magnetic and acceleration"""
    assignments = configuration.get_pin_assignments("lsm303")
    if not assignments:
        return None, None
    i2c = busio.I2C(assignments.get("scl"), assignments.get("sda"))
    mag = adafruit_lsm303dlh_mag.LSM303DLH_Mag(i2c)
    accel = adafruit_lsm303_accel.LSM303_Accel(i2c)
    return mag, accel
Пример #5
0
 def __init__(self,
              configuration: Configuration,
              sql_file: str = "sessionstore.sqlite3"):
     """Create a new session store"""
     self.buffer = SafeBuffer()
     self.current_session = AtomicValue()
     self.output_dir = configuration.get("output_directory")
     self.sql_file = sql_file
     self.connection = None
     self.cursor = None
Пример #6
0
def test_write_queue_log():
    outfile = io.StringIO("")
    data_queue = Queue()
    configuration = Configuration(None, dict(output_directory="./data"))
    while data_queue.qsize() < 10:
        data_queue.put((random.random(), random.random(), random.random()))
    write_queue_log(outfile, data_queue)
    contents = outfile.getvalue()
    assert contents
    assert len(contents.split("\n")) == 11
Пример #7
0
def main():
    """Take a reading from each sensor to test its functionality"""
    # Load up the system configuration
    configuration = Configuration(os.getenv("AIR_CONFIG_FILE", None),
                                  Configuration.default_air_configuration)

    test_rfm9x(configuration)
    test_bmp3xx(configuration)
    test_lsm303dlh(configuration)
    test_gps(configuration)
Пример #8
0
def init_altimeter(configuration: Configuration):
    """Initialize the sensor for pressure, temperature, and altitude"""
    logging.info("Initializing altimeter")
    assignments = configuration.get_pin_assignments("bmp3xx")
    if not assignments:
        return None
    spi = busio.SPI(assignments.get("sck"), assignments.get("mosi"),
                    assignments.get("miso"))
    cs = digitalio.DigitalInOut(assignments.get("cs"))  # pylint: disable=invalid-name
    bmp = adafruit_bmp3xx.BMP3XX_SPI(spi, cs)
    return bmp
Пример #9
0
def telemetry_dashboard_server(configuration: Configuration,
                               buffer_session_store: BufferSessionStore):
    """Serve the static parts of the dashboard visualization"""
    try:
        logging.info("Starting telemetry dashboard server")
        buffer_session_store.initialize()
        klass = ground_http_class_factory(buffer_session_store)
        httpd = HTTPServer(("0.0.0.0", configuration.get("http_server_port")),
                           klass)
        httpd.serve_forever()
    except Exception as ex:  # pylint: disable=broad-except
        logging.error("Telemetry dashboard server failure: %s", str(ex))
        logging.exception(ex)
Пример #10
0
def init_altimeter(configuration: Configuration):
    """Initialize the sensor for pressure, temperature, and altitude"""
    try:
        logging.info("Initializing altimeter")
        assignments = configuration.get_pin_assignments("bmp3xx")
        if not assignments:
            return None
        i2c = busio.I2C(assignments.get("scl"), assignments.get("sda"))
        bmp = adafruit_bmp3xx.BMP3XX_I2C(i2c)
        bmp._wait_time = 0  # pylint: disable=protected-access
        return bmp
    except Exception as ex:  # pylint: disable=broad-except
        bmp = DummyBMP()
        logging.exception(ex)
        return bmp
Пример #11
0
def init_radio(configuration: Configuration):
    """Initialize the radio"""
    logging.info("Initializing transmitter")
    assignments = configuration.get_pin_assignments("rfm9x")
    if not assignments:
        return None
    spi = busio.SPI(
        assignments.get("sck"),
        MOSI=assignments.get("mosi"),
        MISO=assignments.get("miso"),
    )
    cs = DigitalInOut(assignments.get("cs"))  # pylint: disable=invalid-name
    reset = DigitalInOut(assignments.get("reset"))
    rfm9x = adafruit_rfm9x.RFM9x(spi, cs, reset, 915.0, baudrate=10000000)
    rfm9x.tx_power = 23
    return rfm9x
Пример #12
0
def init_magnetometer_accelerometer(configuration: Configuration):
    """Initialize the sensor for magnetic and acceleration"""
    try:
        logging.info("Initializing magnetometer/accelerometer")
        assignments = configuration.get_pin_assignments("lsm303")
        if not assignments:
            return None, None
        i2c = busio.I2C(assignments.get("scl"), assignments.get("sda"))
        mag = adafruit_lsm303dlh_mag.LSM303DLH_Mag(i2c)
        accel = adafruit_lsm303_accel.LSM303_Accel(i2c)
        return mag, accel
    except Exception as ex:  # pylint: disable=broad-except
        mag = DummyMag()
        accel = DummyAccel()
        logging.exception(ex)
        return mag, accel
Пример #13
0
def init_reset_button(configuration: Configuration, continue_running: AtomicValue):
    """Setup a listener for reset"""

    def handle_reset_button(_):
        logging.info("Reset button pressed!")
        continue_running.update(False)

    GPIO.setmode(GPIO.BCM)  # pylint: disable=no-member
    channel = int(configuration.get_device_configuration("reset", "pin"))
    GPIO.setup(  # pylint: disable=no-member
        channel, GPIO.IN, pull_up_down=GPIO.PUD_DOWN  # pylint: disable=no-member
    )
    GPIO.add_event_detect(  # pylint: disable=no-member
        channel,
        GPIO.RISING,  # pylint: disable=no-member
        callback=handle_reset_button,
        bouncetime=500,  # pylint: disable=no-member
    )
def test_buffer_session_store():
    configuration = Configuration(None, dict(output_directory="./data"))
    store = BufferSessionStore(configuration,
                               f"test_sessionstore_{time.time()}.sql")
    store.initialize()
    first_session = store.current_session.get_value()
    assert first_session >= int(time.time())
    store.buffer.append(random.random())
    time.sleep(1)
    store.create_new_session()
    next_session = store.current_session.get_value()
    assert next_session >= int(time.time())
    assert next_session != first_session
    assert store.buffer.size() == 0
    time.sleep(1)
    store.create_new_session()
    sessions = store.get_sessions()
    assert sessions == [next_session, first_session]
Пример #15
0
def camera_thread(
    configuration: Configuration,
    start_time: float,
    continue_running: AtomicValue,
    continue_logging: AtomicValue,
):
    """Start the camera and log the video"""
    try:
        logging.info("Starting video capture")
        output_directory = configuration.get("output_directory")
        camera = picamera.PiCamera(framerate=90)
        camera.start_recording(
            os.path.join(output_directory, f"video_{int(start_time)}.h264")
        )
        while continue_running.get_value() and continue_logging.get_value():
            time.sleep(5)
        camera.stop_recording()
        logging.info("Video capture complete")
    except Exception as ex:  # pylint: disable=broad-except
        handle_exception("Video capture failure", ex)
Пример #16
0
def sensor_log_writing_loop(
    configuration: Configuration,
    start_time: float,
    data_queue: Queue,
    continue_running: AtomicValue,
    continue_logging: AtomicValue,
):
    """Loop through clearing the data queue until cancelled"""
    try:
        logging.info("Starting sensor log writing loop")
        output_directory = configuration.get("output_directory")
        with open(
            os.path.join(output_directory, f"sensor_log_{int(start_time)}.csv"),
            "w",
            encoding="utf8",
        ) as outfile:
            write_sensor_log(
                start_time, outfile, data_queue, continue_running, continue_logging
            )
        logging.info("Telemetry log writing loop complete")
    except Exception as ex:  # pylint: disable=broad-except
        handle_exception("Telemetry log line writing failure", ex)
Пример #17
0
def telemetry_streaming_server(configuration: Configuration,
                               buffer_session_store: BufferSessionStore):
    """Serve the active buffer over websocket"""
    async def data_stream(websocket, _):
        """Handle a connection on a websocket"""
        try:
            logging.info("Client connected to streaming server")
            last_index = 0
            while True:
                if websocket.closed:
                    return
                end_index = buffer_session_store.buffer.size()
                if end_index > last_index:
                    await websocket.send(
                        json.dumps(
                            buffer_session_store.buffer.get_range(
                                last_index, end_index)))
                    last_index = end_index
                await asyncio.sleep(1)
        except websockets.exceptions.ConnectionClosed:
            logging.info("Client disconnected from streaming server")
        except Exception as ex:  # pylint: disable=broad-except
            logging.error("Telemetry streaming server failure: %s", str(ex))
            logging.exception(ex)

    try:
        logging.info("Starting telemetry streaming server")
        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)
        start_server = websockets.serve(
            data_stream, "0.0.0.0", configuration.get("streaming_server_port"))
        asyncio.get_event_loop().run_until_complete(start_server)
        asyncio.get_event_loop().run_forever()
    except websockets.exceptions.ConnectionClosed:
        logging.info("Client disconnected from streaming server")
    except Exception as ex:  # pylint: disable=broad-except
        logging.error("Telemetry streaming server failure: %s", str(ex))
        logging.exception(ex)
Пример #18
0
def main():
    """Inboard data capture and transmission script"""

    # Load up the system configuration
    configuration = Configuration(
        os.getenv("AIR_CONFIG_FILE", None), Configuration.default_air_configuration
    )

    # Queue to manage data synchronization between sensor reading, transmission, and data logging
    data_queue = Queue()

    # Timestamp to use for log files and log saving cutoff
    start_time = time.time()

    # Thread safe place to store altitude reading
    current_reading = AtomicValue()

    # Holds the most recent GPS data
    gps_value = AtomicValue((0.0, 0.0, 0.0, 0.0))

    altimeter_value = AtomicValue()
    altimeter_value.update((0.0, 0.0))

    magnetometer_accelerometer_value = AtomicValue()
    magnetometer_accelerometer_value.update((0.0, 0.0, 0.0, 0.0, 0.0, 0.0))

    gps_thread = create_gps_thread(configuration, gps_value)
    gps_thread.start()

    write_thread = Thread(
        target=sensor_log_writing_loop,
        args=(configuration, start_time, data_queue),
        daemon=True,
    )
    write_thread.start()

    camera_thread_handle = Thread(
        target=camera_thread,
        args=(configuration, start_time),
        daemon=True,
    )
    camera_thread_handle.start()

    transmitter_thread_handle = Thread(
        target=transmitter_thread,
        args=(
            configuration,
            start_time,
            current_reading,
        ),
        daemon=True,
    )
    transmitter_thread_handle.start()

    altimeter_thread = Thread(
        target=altimeter_reading_loop, args=(configuration, altimeter_value)
    )
    altimeter_thread.start()

    magnetometer_accelerometer_thread = Thread(
        target=magnetometer_accelerometer_reading_loop,
        args=(configuration, magnetometer_accelerometer_value),
    )
    magnetometer_accelerometer_thread.start()

    sensor_reading_loop(
        start_time,
        data_queue,
        current_reading,
        gps_value,
        altimeter_value,
        magnetometer_accelerometer_value,
    )
Пример #19
0
def main():
    """Inboard data capture and transmission script"""

    # Load up the system configuration
    configuration = Configuration(os.getenv("AIR_CONFIG_FILE", None),
                                  Configuration.default_air_configuration)

    # Queue to manage data synchronization between sensor reading, transmission, and data logging
    data_queue = Queue(1000)

    # Timestamp to use for log files and log saving cutoff
    start_time = time.time()

    # Thread safe place to store altitude reading
    current_readings = AtomicBuffer(50)

    # Holds the most recent GPS data
    gps_value = AtomicValue((0.0, 0.0, 0.0, 0.0))

    # pcnt counter to runtime limit
    pcnt_to_limit = AtomicValue(0.0)

    # Thread safe place to store continue value
    continue_running = AtomicValue(True)

    # Thread safe place to store continue value
    continue_logging = AtomicValue(True)

    # Setup listener for reset button
    init_reset_button(configuration, continue_running)

    gps_thread = create_gps_thread(configuration, gps_value, continue_running)
    gps_thread.start()

    write_thread = Thread(
        target=sensor_log_writing_loop,
        args=(
            configuration,
            start_time,
            data_queue,
            continue_running,
            continue_logging,
        ),
        daemon=True,
    )
    write_thread.start()

    camera_thread_handle = Thread(
        target=camera_thread,
        args=(configuration, start_time, continue_running, continue_logging),
        daemon=True,
    )
    camera_thread_handle.start()

    transmitter_thread_handle = Thread(
        target=transmitter_thread,
        args=(
            configuration,
            start_time,
            current_readings,
            pcnt_to_limit,
            continue_running,
        ),
        daemon=True,
    )
    transmitter_thread_handle.start()

    sensor_reading_thread = Thread(
        target=sensor_reading_loop,
        args=(
            configuration,
            start_time,
            data_queue,
            current_readings,
            gps_value,
            continue_running,
        ),
        daemon=True,
    )
    sensor_reading_thread.start()

    runtime_limit = configuration.get("runtime_limit")
    while continue_running.get_value(
    ) and time.time() - start_time <= runtime_limit:
        pcnt_to_limit.update((time.time() - start_time) / runtime_limit)
        time.sleep(1)
    logging.info("Stopping write activities")
    continue_logging.update(False)

    write_thread.join()
    camera_thread_handle.join()
    pcnt_to_limit.update(1)

    logging.info("Write activities ended")

    gps_thread.join()
    transmitter_thread_handle.join()
    sensor_reading_thread.join()