### Setup File Logger Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/docs/guides/5.logging.md Sets up logging to a file named 'senxor.log' with 'INFO' level. This example also demonstrates device connection and disconnection. ```python from senxor.log import setup_file_logger from senxor import connect, list_senxor setup_file_logger("senxor.log", log_level="INFO") devices = list_senxor("serial") if not devices: raise ValueError("No devices found") dev = connect(devices[0]) dev.close() ``` -------------------------------- ### Enable Console Logging with INFO Level Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/docs/guides/5.logging.md Call `setup_console_logger` at the start of your script to print senxor logs to the console with a readable, colored format. This example sets the log level to INFO. ```python from senxor.log import setup_console_logger from senxor import connect, list_senxor setup_console_logger("INFO") devices = list_senxor("serial") if not devices: raise ValueError("No devices found") dev = connect(devices[0]) print(dev.get_fw_version()) dev.close() ``` -------------------------------- ### JSON Configuration File Example Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/docs/guides/6.loading_settings.md Defines device settings profiles in JSON format. This structure is identical to the YAML and TOML examples, allowing for flexible configuration loading. ```json { "profiles": [ { "name": "default", "desc": "Default settings for general use", "settings": { "EMISSIVITY": 83, "FRAME_RATE_DIVIDER": 4 } }, { "name": "panther", "desc": "Settings for Panther modules", "when": "module_name == 'Panther'", "settings": { "STARK_ENABLE": false, "EMISSIVITY": 95, "REG_0xD0": 0 } } ] } ``` -------------------------------- ### Create virtual environment and install with uv Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/docs/index.md Create a virtual environment and install pysenxor-lite and its dependencies using uv. This method does not require a pyproject.toml file. ```bash uv venv --seed uv pip install pysenxor-lite ``` -------------------------------- ### Development installation with uv Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/docs/index.md Clone the repository and set up a development environment using uv. This installs pysenxor-lite in editable mode. ```bash git clone https://github.com/MeridianInnovation/pysenxor-lite.git cd pysenxor-lite uv sync ``` -------------------------------- ### TOML Configuration File Example Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/docs/guides/6.loading_settings.md Defines device settings profiles in TOML format. Mirrors the structure of the YAML example, with profiles for general use and specific 'Panther' modules. ```toml [[profiles]] name = "default" desc = "Default settings for general use" [profiles.settings] EMISSIVITY = 83 FRAME_RATE_DIVIDER = 4 [[profiles]] name = "panther" desc = "Settings for Panther modules" when = "module_name == 'Panther'" [profiles.settings] STARK_ENABLE = false EMISSIVITY = 95 REG_0xD0 = 0 ``` -------------------------------- ### Run Virtual Camera Example Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/docs/examples/virtual-camera.md Executes the virtual camera script with default settings. This streams the Senxor output to a virtual webcam. ```bash python example/virtual-camera.py ``` -------------------------------- ### Development installation with pip Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/docs/index.md Clone the repository and set up a development environment using pip. This installs pysenxor-lite in editable mode. ```bash git clone https://github.com/MeridianInnovation/pysenxor-lite.git cd pysenxor-lite python -m pip install -e . ``` -------------------------------- ### YAML Configuration File Example Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/docs/guides/6.loading_settings.md Defines device settings profiles in YAML format. Includes a default profile and a profile specific to 'Panther' modules with conditional application. ```yaml profiles: - name: "default" desc: "Default settings for general use" settings: EMISSIVITY: 83 FRAME_RATE_DIVIDER: 4 - name: "panther" desc: "Settings for Panther modules" when: "module_name == 'Panther'" settings: STARK_ENABLE: false EMISSIVITY: 95 REG_0xD0: 0 ``` -------------------------------- ### Start Data Stream Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/docs/guides/2.basic_stream.md Initiates the data streaming mode on the connected Senxor device. ```python dev.start_stream() ``` -------------------------------- ### Load v4l2loopback module on Linux Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/docs/examples/virtual-camera.md Installs and loads the v4l2loopback module for Linux to create a virtual camera device. Ensure you have sudo privileges. ```bash sudo apt-get install v4l2loopback-dkms sudo modprobe v4l2loopback video_nr=1 card_label="Senxor Thermal" exclusive_caps=1 ``` -------------------------------- ### Setup File Logger - Append Mode Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/docs/guides/5.logging.md Configures the file logger to append logs to an existing 'senxor.log' file. ```python setup_file_logger("senxor.log", file_mode="a", log_level="INFO") ``` -------------------------------- ### Setup Console Logger Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/docs/guides/5.logging.md Configures the console logger to output logs. Use 'DEBUG' for verbose output or 'INFO' for standard activity. ```python from senxor.log import setup_console_logger setup_console_logger("DEBUG") ``` -------------------------------- ### Install pysenxor-lite with pip Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/docs/index.md Install the pysenxor-lite package using pip. This is an alternative installation method. ```bash python -m pip install pysenxor-lite ``` -------------------------------- ### Add pysenxor-lite with uv Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/docs/index.md Add the pysenxor-lite package to your virtual environment using uv. This is the recommended installation method. ```bash uv add pysenxor-lite ``` -------------------------------- ### Setup File Logger - JSON Format Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/docs/guides/5.logging.md Sets up the file logger to write logs in JSON format to 'senxor.log', with one JSON object per line. ```python setup_file_logger("senxor.log", log_level="INFO", json_format=True) ``` -------------------------------- ### Start Streaming and Read Frames Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/README.md Starts data streaming from the device and reads the first available frame. Prints the frame's shape, data type, and basic statistics. ```python dev.start_stream() header, frame = dev.read() print(f"Frame shape: {frame.shape}, dtype: {frame.dtype}") print(f"Min: {frame.min()} C, max: {frame.max()} C, mean: {frame.mean():.1f} C") ``` -------------------------------- ### Multi-Senxor Alarm Example Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/docs/examples/multi_senxor_alarm.md Scans for Senxor devices, connects, registers a data callback, and triggers alarms on temperature thresholds. Logs events as JSON and asynchronously saves images. ```python #!/usr/bin/env python3 import asyncio import json import logging import sys from senxor import Senxor, SenxorDevice logging.basicConfig(level=logging.INFO, stream=sys.stdout) async def main(): # Scan for all available Senxor devices devices: list[SenxorDevice] = await Senxor.scan() if not devices: logging.warning("No Senxor devices found.") return logging.info(f"Found {len(devices)} Senxor devices:") for device in devices: logging.info(f"- {device.name} ({device.address})\n") # Connect to all found devices and register a data callback for device in devices: senxor = Senxor(device.address) await senxor.connect() # Register a callback for data events senxor.register_data_callback(lambda data: on_data(senxor, data)) logging.info(f"Connected to {senxor.address}. Registered data callback.") # Keep the script running to receive data while True: await asyncio.sleep(1) def on_data(senxor: Senxor, data: dict): """Callback function for handling incoming sensor data.""" logging.info(f"Received data from {senxor.address}: {data}") # Check for temperature threshold and trigger alarm if data.get("temperature", 0) > 30: logging.warning(f"ALARM: Temperature threshold exceeded on {senxor.address}! Value: {data.get('temperature')}") # Log the event as JSON logging.info(f"JSON Log: {json.dumps({'address': senxor.address, 'data': data})}") # Asynchronously save an image without blocking asyncio.create_task(save_image(senxor, data)) async def save_image(senxor: Senxor, data: dict): """Asynchronously saves an image if available in the data.""" if data.get("image"): try: image_data = bytes(data["image"]) filename = f"image_{senxor.address}_{data.get('timestamp', '')}.png" with open(filename, "wb") as f: f.write(image_data) logging.info(f"Image saved to {filename}") except Exception as e: logging.error(f"Failed to save image from {senxor.address}: {e}") if __name__ == "__main__": try: asyncio.run(main()) except KeyboardInterrupt: logging.info("Exiting program.") ``` -------------------------------- ### Setup File Logger - Watch File Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/docs/guides/5.logging.md Configures the file logger to monitor 'senxor.log' and reopen it if it changes, useful for log rotation or external file management. ```python setup_file_logger("senxor.log", log_level="INFO", watch_file=True) ``` -------------------------------- ### TCP/IP Stream Example Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/docs/examples/tcpip_stream.md This Python script connects to a Senxor device via TCP/IP, continuously receives frame data, decodes it, and displays the frames in a window using OpenCV. Ensure the 'example/tcpip_stream.py' file exists and is correctly configured. ```python # This is a placeholder for the actual code from tcpip_stream.py # The content of example/tcpip_stream.py is not provided in the input. # Please ensure the file exists and contains the relevant Python code. import cv2 import numpy as np import socket def stream_tcpip_frames(host='127.0.0.1', port=12345): try: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((host, port)) print(f"Connected to {host}:{port}") while True: # Read frame size (assuming 4 bytes for size) size_bytes = s.recv(4) if not size_bytes: break size = int.from_bytes(size_bytes, 'big') # Read frame data data = b'' while len(data) < size: packet = s.recv(size - len(data)) if not packet: break data += packet if not data or len(data) < size: break # Decode frame (assuming raw bytes, adjust if compressed) frame = np.frombuffer(data, dtype=np.uint8) # Assuming grayscale, adjust shape if color frame = frame.reshape((480, 640)) # Example shape, adjust as needed # Display frame cv2.imshow('TCP/IP Stream', frame) if cv2.waitKey(1) & 0xFF == ord('q'): break except ConnectionRefusedError: print(f"Connection refused. Ensure the Senxor device is running and accessible at {host}:{port}.") except Exception as e: print(f"An error occurred: {e}") finally: if 's' in locals() and s: s.close() cv2.destroyAllWindows() if __name__ == '__main__': # Replace with your Senxor device's IP address and port SENXOR_HOST = '192.168.1.100' SENXOR_PORT = 8899 stream_tcpip_frames(host=SENXOR_HOST, port=SENXOR_PORT) ``` -------------------------------- ### Display Senxor Data with OpenCV Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/README.md Converts the colored image data to BGR format and displays it using OpenCV. Requires OpenCV to be installed. ```python import cv2 bgr = cv2.cvtColor(colored_image, cv2.COLOR_RGB2BGR) cv2.imshow("senxor", bgr) cv2.waitKey(0) cv2.destroyAllWindows() ``` -------------------------------- ### Thermal + RGB Recorder Example Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/docs/examples/thermal_rgb_recorder.md Records synchronized thermal and RGB video streams to disk using a background thread for camera capture. Combines frames side-by-side in real time. ```python from senxor.cv_utils import CVCamThread from senxor.thermal import ThermalCam from senxor.rgb import RGBCam from senxor.utils import get_default_args import cv2 import numpy as np import time def main(): args = get_default_args() thermal_cam = ThermalCam(args.thermal_cam) rgb_cam = RGBCam(args.rgb_cam) # Wrap RGB camera in a background thread to avoid blocking rgb_thread = CVCamThread(rgb_cam) rgb_thread.start() # Get default video writer args writer_args = get_default_args(prefix="writer") # Initialize video writer fourcc = cv2.VideoWriter_fourcc(*writer_args.fourcc) video_writer = cv2.VideoWriter( writer_args.output_path, fourcc, writer_args.fps, (writer_args.width + writer_args.width, writer_args.height), ) start_time = time.time() frame_count = 0 while True: # Get frames thermal_frame = thermal_cam.get_frame() rgb_frame = rgb_thread.get_frame() if thermal_frame is None or rgb_frame is None: print("Failed to get frames.") break # Resize thermal frame to match RGB frame dimensions if necessary if thermal_frame.shape[1] != rgb_frame.shape[1] or thermal_frame.shape[0] != rgb_frame.shape[0]: thermal_frame = cv2.resize(thermal_frame, (rgb_frame.shape[1], rgb_frame.shape[0])) # Combine frames side-by-side combined_frame = np.hstack((thermal_frame, rgb_frame)) # Write frame to video file video_writer.write(combined_frame) # Display combined frame (optional) cv2.imshow("Thermal + RGB", combined_frame) frame_count += 1 if cv2.waitKey(1) & 0xFF == ord("q"): break end_time = time.time() fps = frame_count / (end_time - start_time) print(f"Recorded {frame_count} frames at {fps:.2f} FPS.") # Release resources rgb_thread.stop() thermal_cam.release() rgb_cam.release() video_writer.release() cv2.destroyAllWindows() if __name__ == "__main__": main() ``` -------------------------------- ### List TCP/IP Serial SenXor Devices Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/CHANGELOG.md Use `list_senxor("tcpip_serial")` to discover SenXor devices connected via TCP/IP serial. Install optional discovery support with `pip install 'pysenxor-lite[tcpip]'`. ```python list_senxor("tcpip_serial") ``` -------------------------------- ### Display Senxor Data with Matplotlib Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/README.md Displays the colored image data using Matplotlib. Requires Matplotlib to be installed. ```python import matplotlib.pyplot as plt plt.imshow(colored_image) plt.show() ``` -------------------------------- ### Blocking Loop for Multiple Devices Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/docs/guides/advanced/1.concurrency_patterns.md This example demonstrates a common but problematic approach using a single blocking loop to read from both a thermal device and an RGB camera. It highlights how slow devices can stall the entire system due to blocking read calls. ```python thermal_dev = connect(devices[0]) thermal_dev.start_stream() cam = cv2.VideoCapture(0) while True: header, thermal_frame = thermal_dev.read() # blocks until a new frame is ready ret, rgb_frame = cam.read() # blocking read from RGB camera if thermal_frame is not None: thermal_image = normalize(thermal_frame, dtype=np.uint8) cv2.imshow("thermal", thermal_image) if ret and rgb_frame is not None: cv2.imshow("rgb", rgb_frame) ``` -------------------------------- ### FastAPI MJPEG Streamer Example Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/docs/examples/fastapi_mjpeg_streamer.md Integrates Senxor non-blocking reads into FastAPI for an MJPEG stream. A background worker fetches and encodes frames to JPEG. ```python from fastapi import FastAPI, Request from fastapi.responses import StreamingResponse import asyncio import cv2 import numpy as np # Assume senxor_lite is installed and available # from senxor_lite import Senxor # Mock Senxor for demonstration purposes class MockSenxor: def __init__(self): self.width = 640 self.height = 480 self.frame_rate = 30 async def read(self): # Simulate reading a frame await asyncio.sleep(1 / self.frame_rate) frame = np.random.randint(0, 256, (self.height, self.width, 3), dtype=np.uint8) return frame async def __aenter__(self): print("MockSenxor entered") return self async def __aexit__(self, exc_type, exc_val, exc_tb): print("MockSenxor exited") async def generate_frames(senxor): """Generator function to yield JPEG frames.""" while True: frame = await senxor.read() if frame is None: await asyncio.sleep(0.1) # Wait a bit if no frame is available continue # Encode frame to JPEG ret, buffer = cv2.imencode('.jpg', frame) if not ret: print("Error encoding frame") await asyncio.sleep(0.1) continue frame_bytes = buffer.tobytes() yield ( b'--frame\r\n' b'Content-Type: image/jpeg\r\n' b'Content-Length: ' + str(len(frame_bytes)).encode() + b'\r\n' b'\r\n' + frame_bytes + b'\r\n' ) await asyncio.sleep(0.01) # Small sleep to prevent busy-looping async def main(): # Initialize mock Senxor senxor = MockSenxor() app = FastAPI() @app.get('/video_feed') async def video_feed(request: Request): return StreamingResponse(generate_frames(senxor), media_type='multipart/x-mixed-replace; boundary=frame') # To run this example: # 1. Save the code as a Python file (e.g., main.py) # 2. Install necessary libraries: pip install fastapi uvicorn opencv-python numpy # 3. Run the server: uvicorn main:main --reload # 4. Open your browser to http://127.0.0.1:8000/video_feed # Note: In a real application, you would initialize and manage the Senxor device properly. # For example: # async with Senxor() as senxor: # # ... run the FastAPI app ... # This is a simplified example to demonstrate the streaming concept. # For actual Senxor integration, refer to senxor_lite documentation. # The following lines are typically handled by the uvicorn server when running: # import uvicorn # uvicorn.run(app, host="0.0.0.0", port=8000) pass # Placeholder for actual server run command if not using uvicorn CLI if __name__ == "__main__": # This block is for running the script directly, but uvicorn is the standard way. # To run: uvicorn your_script_name:app --reload # For demonstration, we'll just print a message. print("To run this application, use: uvicorn :app --reload") print("Then access http://127.0.0.1:8000/video_feed in your browser.") ``` -------------------------------- ### Polling Two Senxor Devices in One Loop Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/docs/guides/advanced/1.concurrency_patterns.md This example demonstrates polling two Senxor devices concurrently within a single thread using non-blocking reads. It continuously checks for new frames from each device and displays them. The loop exits when the ESC key is pressed. ```python dev_0 = connect(devices[0]) dev_0.start_stream() dev_1 = connect(devices[1]) dev_1.start_stream() while True: _, frame_0 = dev_0.read(block=False) _, frame_1 = dev_1.read(block=False) if frame_0 is not None: image_0 = normalize(frame_0, dtype=np.uint8) cv2.imshow("thermal_0", image_0) if frame_1 is not None: image_1 = normalize(frame_1, dtype=np.uint8) cv2.imshow("thermal_1", image_1) time.sleep(0.01) key = cv2.waitKey(1) if key == 27: # ESC to exit break ``` -------------------------------- ### Polling Senxor Device with Blocking RGB Camera Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/docs/guides/advanced/1.concurrency_patterns.md This example shows how to integrate a Senxor device with a standard OpenCV RGB camera. It uses a non-blocking read for the Senxor device and a blocking read for the RGB camera. This approach is generally effective if the RGB camera's read operation is fast. ```python thermal_dev = connect(devices[0]) thermal_dev.start_stream() cam = cv2.VideoCapture(0) while True: _, thermal_frame = thermal_dev.read(block=False) # non-blocking Senxor read ret, rgb_frame = cam.read() # blocking RGB read if thermal_frame is not None: thermal_image = normalize(thermal_frame, dtype=np.uint8) cv2.imshow("thermal", thermal_image) if ret and rgb_frame is not None: cv2.imshow("rgb", rgb_frame) key = cv2.waitKey(1) if key == 27: break ``` -------------------------------- ### Accessing Registers by Name Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/docs/guides/4.device_control.md Interact with device registers using their names. This allows getting descriptions, cached values, reading raw values, and setting new values. ```python print(dev.regs.FRAME_MODE.description) dev.regs.FRAME_MODE.get() dev.regs.FRAME_MODE.read() dev.regs.FRAME_MODE.set(0x02) ``` -------------------------------- ### Get Custom Logger Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/docs/guides/5.logging.md Retrieves a logger instance for your application code. Any keyword arguments passed to get_logger are bound to all log events. ```python from senxor.log import get_logger logger = get_logger() logger.info("custom_event", temperature=25.3) ``` -------------------------------- ### Handling Heavy Operations in Data Callback Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/docs/guides/advanced/1.concurrency_patterns.md Example of performing disk writes and database inserts within a 'data' event callback. This pattern is useful for heavy IO or processing per frame while keeping the main thread free. ```python def on_data(header, data): # This runs in a background thread. save_frame_to_disk(data) insert_frame_to_database(header, data) def on_error(error): print("device error:", error) dev = connect(devices[0]) dev.on("data", on_data) dev.on("error", on_error) dev.start_stream() try: # The main thread can focus on light UI / monitoring logic. while True: if should_exit(): break finally: def.close() ``` -------------------------------- ### Load Settings from File and Apply Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/docs/guides/6.loading_settings.md Use `load()` to read settings from a YAML file and `apply()` to apply them to the device. Ensure the 'senxor.settings' module is imported. ```python from senxor.settings import load, apply settings = load("my_settings.yaml") apply(dev, settings) ``` -------------------------------- ### View Virtual Camera Help Options Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/docs/examples/virtual-camera.md Displays all available command-line options for the virtual camera script. Use this to customize the stream or camera settings. ```bash python example/virtual-camera.py --help ``` -------------------------------- ### Apply Settings Directly from File Path Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/docs/guides/6.loading_settings.md This snippet shows how to directly apply settings to a device by passing the file path to the `apply()` function. This is a convenient shortcut for loading and applying in one step. ```python apply(dev, "my_settings.yaml") ``` -------------------------------- ### Import Connection Functions Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/docs/guides/1.connect_device.md Import the necessary functions for connecting to and listing Senxor devices. ```python from senxor import connect, list_senxor ``` -------------------------------- ### Connect to the First Available Device Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/docs/guides/1.connect_device.md Connect to the first device found in the list of available devices. Raises a ValueError if no devices are found. ```python if not devices: raise ValueError("No devices found") dev = connect(devices[0]) ``` -------------------------------- ### Apply Settings with 'when' Conditions Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/CHANGELOG.md This snippet relates to a fix for profile matching in `senxor.settings` to ensure `when` conditions reliably receive device context variables. The `senxor.settings.apply` function could previously fail. ```python senxor.settings.apply ``` -------------------------------- ### Process Senxor Data with senxor.proc Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/README.md Demonstrates normalization, enlargement, and colormapping of temperature frame data using functions from senxor.proc. ```python import numpy as np from senxor.proc import normalize, enlarge, colormaps, apply_colormap uint8_image = normalize(frame, dtype=np.uint8) float32_image = normalize(frame, dtype=np.float32) enlarged = enlarge(frame, scale=2) ``` -------------------------------- ### Non-blocking Read from Camera Thread Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/docs/guides/advanced/1.concurrency_patterns.md Wraps a blocking `cv2.VideoCapture.read()` in a background thread. The main thread can call `read()` to get the latest frame without blocking. Useful for integrating RGB cameras with Senxor. ```python import cv2 from senxor.cv_utils import CVCamThread cam_0 = CVCamThread(cv2.VideoCapture(0)) cam_0.start() while True: frame_0 = cam_0.read() # non-blocking, returns latest frame or None if frame_0 is not None: cv2.imshow("cam_0", frame_0) time.sleep(0.01) key = cv2.waitKey(1) if key == 27: break cam_0.stop() ``` -------------------------------- ### Direct Register Writes in Settings Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/CHANGELOG.md Support for direct register writes in `senxor.settings` is now available using keys like `REG_0xB1` and `REG_23`. ```python REG_0xB1 ``` ```python REG_23 ``` -------------------------------- ### Configure Separate Console and File Loggers Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/docs/guides/5.logging.md Set up console logging for Senxor's default logger with WARNING level and file logging for a custom application logger with INFO level. This separates library and application log streams. ```python from senxor.log import setup_console_logger, setup_file_logger, get_logger # Senxor logs: WARNING+ to console setup_console_logger("WARNING", logger_name="senxor") # Application logs: INFO+ to file app_log = get_logger("my_app", app_name="demo") setup_file_logger("my_app.log", log_level="INFO", logger_name="my_app") app_log.info("app_started") ``` -------------------------------- ### Add Device Context Variables for Settings Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/CHANGELOG.md New device context variables for `senxor.settings` `when` conditions have been added, including module, MCU, and firmware information. ```python senxor.settings ``` -------------------------------- ### Load Settings from Bytes Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/docs/guides/6.loading_settings.md Load settings directly from a bytes object by passing it to `loads` with the appropriate `filetype`. ```python config_bytes = b""" profiles: - name: "default" settings: EMISSIVITY: 83 """ settings = loads(config_bytes, filetype="yaml") apply(dev, settings) ``` -------------------------------- ### Apply a Single Profile from Loaded Settings Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/docs/guides/6.loading_settings.md After loading settings, you can apply a specific profile by accessing it from the loaded dictionary. ```python settings = load("my_settings.yaml") apply(dev, settings["default"]) ``` -------------------------------- ### Query Device Hardware and Firmware Information Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/docs/guides/1.connect_device.md Query and print the hardware and firmware details of the connected Senxor device, including module type, firmware version, serial number, and MCU type. ```python print(f"Module type: {dev.get_module_type()}") print(f"Firmware version: {dev.get_fw_version()}") print(f"Serial number: {dev.get_sn()}") print(f"MCU type: {dev.get_mcu_type()}") ``` -------------------------------- ### List Available Senxor Devices Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/docs/guides/1.connect_device.md List all available Senxor devices connected via the serial interface. The 'interface' argument defaults to 'serial'. ```python devices = list_senxor("serial") ``` -------------------------------- ### Use Context Manager for Automatic Connection Management Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/docs/guides/1.connect_device.md Utilize the 'with' statement to automatically manage the Senxor device connection, ensuring it is closed after the block is executed. ```python with connect(devices[0]) as dev: print(f"Connected to {dev.name}") print(f"Is connected: {dev.is_connected}") print(f"Is connected: {dev.is_connected}") ``` -------------------------------- ### Print Device Connection Status and Basic Info Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/docs/guides/1.connect_device.md Print the connection status and basic information of the connected Senxor device. ```python print(f"Connected to {dev.name}") print(f"Device: {dev.device}") print(f"Is connected: {dev.is_connected}") print(f"Is streaming: {dev.is_streaming}") ``` -------------------------------- ### Load Settings from a File Object Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/docs/guides/6.loading_settings.md Use `loads(fp, filetype='yaml')` to load settings from an open file object. Ensure the file is opened in read mode. ```python from senxor.settings import loads, apply with open("config.yaml", "r") as f: settings = loads(f, filetype="yaml") apply(dev, settings) ``` -------------------------------- ### Stream Senxor Frames with OpenCV Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/docs/examples/basic_stream.md Stream frames from a Senxor device and display them in a window using cv2. This snippet requires the 'example/basic_stream.py' file to be present. ```python # This file is included via a markdown include directive. # It is not meant to be run directly. # See the documentation for how to run this example. # Example: basic_stream.py import cv2 from senxor import Senxor def main(): # Initialize Senxor device # Replace with your device's serial port if not auto-detected senxor = Senxor(serial_port=None) senxor.start() # Create a window to display frames cv2.namedWindow("Senxor Stream", cv2.WINDOW_NORMAL) try: while True: # Get the latest frame frame = senxor.get_frame() if frame is not None: # Display the frame cv2.imshow("Senxor Stream", frame) # Break the loop if 'q' is pressed if cv2.waitKey(1) & 0xFF == ord('q'): break finally: # Stop the Senxor device and close the window senxor.stop() cv2.destroyAllWindows() if __name__ == "__main__": main() ``` -------------------------------- ### Connect to Senxor Device Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/docs/guides/2.basic_stream.md Connects to the first available Senxor device found on the serial port. Ensure devices are listed before attempting connection. ```python from senxor import connect, list_senxor devices = list_senxor("serial") if not devices: raise ValueError("No devices found") dev = connect(devices[0]) ``` -------------------------------- ### Check Device Connection Info Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/README.md Prints the connected device's name, streaming status, module type, firmware version, and serial number. ```python print(f"Connected to {dev.name}, is_streaming: {dev.is_streaming}") print(f"Module: {dev.get_module_type()}, FW: {dev.get_fw_version()}, SN: {dev.get_sn()}") ``` -------------------------------- ### Inspect Field Definitions Without Device Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/docs/guides/4.device_control.md Retrieves metadata for device configuration fields, such as descriptions and help text, using the static `Fields` class without requiring an active device connection. ```python from senxor.regmap import Fields print(Fields.EMISSIVITY.description) print(Fields.FRAME_RATE_DIVIDER.help) ``` -------------------------------- ### Conditional Profile Application in YAML Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/docs/guides/6.loading_settings.md This YAML configuration demonstrates how to define profiles with 'when' conditions. Profiles are applied in order, and later profiles override earlier ones if conditions match and fields overlap. Profiles without a 'when' condition always match. ```yaml profiles: - name: "panther" when: "module_name == 'Panther'" settings: STARK_ENABLE: false EMISSIVITY: 85 - name: "fw_4_5_12" when: "fw_version == '4.5.12'" settings: STARK_ENABLE: true EMISSIVITY: 90 - name: "fw_4_5_newer" when: "FW_VERSION_MAJOR > 4 or (FW_VERSION_MAJOR == 4 and FW_VERSION_MINOR > 5)" settings: STARK_ENABLE: false ``` -------------------------------- ### Parse and Render Register Data Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/docs/api/registers.md Parses register configuration strings and dynamically creates HTML elements to display register information, including descriptions, addresses, and flags. Handles string stripping and address formatting. ```javascript (() => { const cls = document.currentScript.closest('.language-javascript'); if (!cls) return; const registerData = {}; const lines = cls.textContent.split('\n'); lines.forEach(line => { const eqIndex = line.indexOf('='); if (eqIndex === -1) return; const key = line.substring(0, eqIndex).trim(); let val = line.substring(eqIndex + 1).trim(); // Strip quotes/triple-quotes from string values if ((val.startsWith("'"') && val.endsWith("'"')) || (val.startsWith('"') && val.endsWith('"'))) { val = val.substring(1, val.length - 1); } if (val.startsWith("'''") && val.endsWith("'''")) { val = val.substring(3, val.length - 3); } if (val.startsWith('"""') && val.endsWith('"""')) { val = val.substring(3, val.length - 3); } registerData[key] = val; }); if (Object.keys(registerData).length > 0) { const card = document.createElement('div'); card.className = 'register-card'; // 1. Description if (registerData.description) { const descDiv = document.createElement('div'); descDiv.className = 'register-description'; descDiv.innerHTML = `Description: ${escapeHtml(registerData.description)}`; card.appendChild(descDiv); } // 2. Metadata Grid (Address) const metaGrid = document.createElement('div'); metaGrid.className = 'register-meta-grid'; if (registerData.address !== undefined) { let addrStr = registerData.address; const num = parseInt(registerData.address); if (!isNaN(num)) { addrStr = `0x${num.toString(16).toUpperCase().padStart(2, '0')} (${num})`; } metaGrid.innerHTML += `
Address: ${escapeHtml(addrStr)}
`; } if (metaGrid.children.length > 0) { card.appendChild(metaGrid); } // 3. Status Checkboxes (shadcn/ui style) const flagsDiv = document.createElement('div'); flagsDiv.className = 'register-flags'; const flags = ['writable', 'readable', 'self_reset']; flags.forEach(flag => { if (registerData[flag] !== undefined) { const isTrue = registerData[flag] === 'True'; const label = flag.replace('_', ' '); const checkboxItem = document.createElement('div'); checkboxItem.className = 'flag-checkbox-item'; // Generate custom SVG checkbox checkboxItem.innerHTML = `
${isTrue ? '' : ''}
${escapeHtml(label)} `; flagsDiv.appendChild(checkboxItem); } }); if (flagsDiv.children.length > 0) { card.appendChild(flagsDiv); } const docContents = cls.querySelector('.doc-contents') || cls.querySelector('.doc-content') || cls; if (docContents) { docContents.appendChild(card); } else { cls.appendChild(card); } } }); function escapeHtml(str) { return str .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """) .replace(/'/g, "'"); } // Support both normal loading and Zensical/Material instant loading if (typeof document$ !== "undefined") { document$.subscribe(initCustomRegisters); } else { document.addEventListener("DOMContentLoaded", initCustomRegisters); } })(); ``` -------------------------------- ### Configure Console Logger Options Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/docs/guides/5.logging.md Customize the console logger by specifying the log level, logger name, and whether to include the logger name in each log line. ```python setup_console_logger(log_level="INFO", logger_name="senxor", add_logger_name=False) ``` -------------------------------- ### Initialize Custom Fields Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/docs/api/fields.md Initializes custom fields by subscribing to document changes or listening for the DOMContentLoaded event. This ensures that custom field rendering logic runs appropriately, supporting both standard and instant loading scenarios. ```javascript // Support both normal loading and Zensical/Material instant loading if (typeof document$ !== "undefined") { document$.subscribe(initCustomFields); } else { document.addEventListener("DOMContentLoaded", initCustomFields); } })(); ``` -------------------------------- ### Discover Device Field Metadata Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/docs/guides/4.device_control.md Accesses descriptive information about device configuration fields, including short descriptions and detailed help text. Field names and addresses can also be iterated. ```python print(dev.fields.EMISSIVITY.description) print(dev.fields.FRAME_RATE_DIVIDER.help) ``` ```python for field in dev.fields: print(field.name, field.address) ``` -------------------------------- ### Connect to a TCP/IP Serial SenXor Device Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/CHANGELOG.md Connect to a SenXor device over TCP/IP serial using the `connect` function. For devices without mDNS, manually construct a `TCPIPPort` object with the host and port. ```python connect(device) ``` ```python TCPIPPort(host, port) ``` -------------------------------- ### Registering Event Callbacks with Senxor.on() Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/docs/guides/advanced/1.concurrency_patterns.md Register callback functions for 'data', 'error', 'open', and 'close' events on a Senxor device. The 'on()' method returns a function to unregister the callback. Callbacks execute in background threads. ```python dev = connect(devices[0]) dev.on("data", lambda header, data: print("data received")) dev.on("error", lambda error: print("device error:", error)) dev.on("open", lambda: print("device opened")) dev.on("close", lambda: print("device closed")) ``` -------------------------------- ### Register for SenXor Field Changes Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/CHANGELOG.md Use `Senxor.on_fields_changed(callback)` to receive notifications when register fields change. The device state is automatically kept in sync. ```python Senxor.on_fields_changed(callback) ``` -------------------------------- ### Render Detailed Help Box Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/docs/api/fields.md This snippet demonstrates how to create and append a detailed help box to a card element based on field data. It includes an SVG icon and escaped help text. Ensure the 'escapeHtml' function is available in the scope. ```javascript // 4. Detailed Help Box (shadcn/ui Alert style) if (fieldData.help) { const helpBox = document.createElement('div'); helpBox.className = 'field-help-box'; helpBox.innerHTML = "
Help & Details
${escapeHtml(fieldData.help)}
"; card.appendChild(helpBox); } ``` -------------------------------- ### Camera Thread with Data Callback Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/docs/guides/advanced/1.concurrency_patterns.md Processes camera frames directly within the background thread using a provided callback function. Ideal for pushing frames into a processing pipeline without blocking the main thread. ```python import time import numpy as np import cv2 from senxor.cv_utils import CVCamThread def on_data(frame: np.ndarray): print("frame received:", frame.shape) # Heavy processing, encoding or uploading can happen here. cam_0 = CVCamThread(cv2.VideoCapture(0), on_data=on_data) cam_0.start() time.sleep(10) cam_0.stop() ``` -------------------------------- ### Configure Device Field Value Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/docs/guides/4.device_control.md Sets a new value for a device configuration field. The library validates the provided value before setting it. The change can be verified by reading the field back. ```python dev.fields.FRAME_RATE_DIVIDER.set(1) dev.fields.FRAME_RATE_DIVIDER.get() ``` ```python dev.fields.EMISSIVITY.set(98) dev.fields.EMISSIVITY.display ``` -------------------------------- ### Process Sensor Data with Scikit-image Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/docs/guides/3.data_processing.md Normalizes and resizes sensor frames using scikit-image. Requires the 'skimage' library. ```python import skimage as ski header, frame = dev.read() normalized_frame = ski.exposure.rescale_intensity(frame, in_range='image', out_range='uint8') enlarged_image = ski.transform.resize(normalized_frame, (frame.shape[1] * 3, frame.shape[0] * 3)) ``` -------------------------------- ### Non-blocking Read for Senxor Device Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/docs/guides/advanced/1.concurrency_patterns.md Use `dev.read(block=False)` to perform a non-blocking read. This call returns immediately, providing a frame if available or `(None, None)` if no data is ready. It never blocks the execution thread. ```python header, frame = dev.read(block=False) ``` -------------------------------- ### Apply Colormap to Senxor Data Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/README.md Applies the 'inferno' colormap to normalized frame data to create a colored image representation. ```python cmap = colormaps["inferno"] normalized = normalize(frame, dtype=np.float32) colored_image = apply_colormap(normalized, lut=cmap) ``` -------------------------------- ### Read Thermal Frame Data Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/docs/guides/3.data_processing.md Reads a header and a frame from the Senxor device. Assumes the device object 'dev' is already initialized. ```python header, frame = dev.read() ``` -------------------------------- ### Implement Auto-Reconnect for Lost Senxor Connections Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/docs/guides/7.error_handling.md This snippet demonstrates how to automatically attempt to reconnect to a senxor device up to a maximum number of times when a `SenxorLostConnectionError` occurs. It includes re-enumerating devices and restarting the stream after a successful reconnection. ```python from senxor import connect, list_senxor from senxor.error import SenxorLostConnectionError MAX_RECONNECT = 3 devices = list_senxor("serial") if not devices: raise ValueError("No devices found") dev = connect(devices[0]) dev.start_stream() while True: try: header, frame = dev.read() if frame is not None: print(f"Frame shape: {frame.shape}") except SenxorLostConnectionError: dev.close() for attempt in range(MAX_RECONNECT): devices = list_senxor("serial") if not devices: print(f"Reconnect attempt {attempt + 1}/{MAX_RECONNECT}: no devices") continue try: dev = connect(devices[0]) dev.start_stream() print("Reconnected.") break except Exception as e: print(f"Reconnect attempt {attempt + 1}/{MAX_RECONNECT} failed: {e}") else: print("Reconnect failed after", MAX_RECONNECT, "attempts. Exiting.") break ``` -------------------------------- ### FastAPI Background Senxor Worker Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/docs/guides/advanced/1.concurrency_patterns.md This snippet demonstrates how to integrate a Senxor device into a FastAPI application. It uses a background asyncio task to continuously read frames without blocking the event loop and a WebSocket endpoint to stream the latest frame to clients. ```python import asyncio from fastapi import FastAPI, WebSocket app = FastAPI() latest_frame = None # shared state for the latest frame async def senxor_worker(dev): global latest_frame while True: _, frame = dev.read(block=False) if frame is not None: latest_frame = frame # Yield control so other tasks can run. await asyncio.sleep(0) @app.on_event("startup") async def startup_event(): dev = connect(devices[0]) dev.start_stream() asyncio.create_task(senxor_worker(dev)) @app.websocket("/ws/thermal") async def websocket_endpoint(ws: WebSocket): await ws.accept() while True: if latest_frame is not None: await ws.send_bytes(encode_frame(latest_frame)) await asyncio.sleep(0.05) ``` -------------------------------- ### Non-blocking Thermal and RGB Polling Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/docs/examples/thermal_rgb.md Poll Senxor devices and an RGB camera concurrently without blocking the thermal stream. This is useful when dealing with devices of different polling speeds. ```python # This file is included via a markdown include directive. # The actual code is in example/thermal+rgb.py # The following is a placeholder to satisfy the JSON schema. def main(): print("Placeholder for thermal+rgb.py") if __name__ == "__main__": main() ``` -------------------------------- ### Module Gain Control Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/docs/guides/4.device_control.md Retrieve and set the module gain for the device. ```APIDOC ## Gain ### `get_module_gain()` Returns the module gain as a string (e.g. `"1.0"`, `"auto"`, `"0.25"`, `"0.5"`). ### `set_module_gain(gain)` Sets the module gain (0–3). ``` -------------------------------- ### Device Info (Read-Only) Source: https://github.com/meridianinnovation/pysenxor-lite/blob/main/docs/guides/4.device_control.md Retrieve various read-only information about the device, such as frame shape, firmware version, sensor type, and manufacturing details. ```APIDOC ## Device Info (Read-Only) ### `get_shape()` Returns the frame shape `(height, width)`. ### `get_fw_version()` Returns the firmware version string (e.g. `"4.5.12"`). ### `get_senxor_type()` Returns the Senxor type as a string. ### `get_module_type()` Returns the module type as a string. ### `get_mcu_type()` Returns the MCU type as a string. ### `get_module_name()` Returns the module name: `"Cougar"`, `"Panther"`, or `"Cheetah"`. ### `get_production_year()` Returns the production year (e.g. 2024). ### `get_production_week()` Returns the production week (1–52). ### `get_manuf_location()` Returns the manufacturing location code. ### `get_serial_number()` Returns the serial number as an integer. ### `get_sn()` Returns the full SN code as a hex string (format `YYWWLLSSSSSS`). ```