From 869034bbb2e748b104c85600f5ea867bfa7fa4f3 Mon Sep 17 00:00:00 2001 From: Lloyd Date: Sat, 15 Nov 2025 20:38:50 +0000 Subject: [PATCH] Update installation instructions and add system dependency for libffi-dev --- NOISE_FLOOR_USAGE.md | 153 ------------------------------------------- README.md | 23 ++++--- manage.sh | 13 ++-- 3 files changed, 21 insertions(+), 168 deletions(-) delete mode 100644 NOISE_FLOOR_USAGE.md diff --git a/NOISE_FLOOR_USAGE.md b/NOISE_FLOOR_USAGE.md deleted file mode 100644 index 3934c31..0000000 --- a/NOISE_FLOOR_USAGE.md +++ /dev/null @@ -1,153 +0,0 @@ -# Noise Floor Measurement - Usage Guide - -## Overview -The noise floor measurement capability has been added to provide real-time RF environment monitoring. - -## Implementation - -### 1. Radio Wrapper (pyMC_core) -Added `get_noise_floor()` method to `SX1262Radio` class: - -```python -def get_noise_floor(self) -> Optional[float]: - """ - Get current noise floor (instantaneous RSSI) in dBm. - Returns None if radio is not initialized or if reading fails. - """ -``` - -### 2. Repeater Engine (pyMC_Repeater) -Added `get_noise_floor()` method to `RepeaterHandler` class: - -```python -def get_noise_floor(self) -> Optional[float]: - """ - Get the current noise floor (instantaneous RSSI) from the radio in dBm. - Returns None if radio is not available or reading fails. - """ -``` - -The noise floor is automatically included in the stats dictionary returned by `get_stats()`: - -```python -stats = handler.get_stats() -noise_floor = stats.get('noise_floor_dbm') # Returns float or None -``` - -## Usage Examples - -### Example 1: Get Noise Floor Directly -```python -# From the repeater engine -handler = RepeaterHandler(config, dispatcher, local_hash) -noise_floor_dbm = handler.get_noise_floor() - -if noise_floor_dbm is not None: - print(f"Current noise floor: {noise_floor_dbm:.1f} dBm") -else: - print("Noise floor not available") -``` - -### Example 2: Access via Stats -```python -# Get all stats including noise floor -stats = handler.get_stats() -noise_floor = stats.get('noise_floor_dbm') - -if noise_floor is not None: - print(f"RF Environment: {noise_floor:.1f} dBm") -``` - -### Example 3: Monitor RF Environment -```python -import asyncio - -async def monitor_rf_environment(handler, interval=5.0): - """Monitor noise floor every N seconds""" - while True: - noise_floor = handler.get_noise_floor() - if noise_floor is not None: - if noise_floor > -100: - print(f"⚠️ High RF noise: {noise_floor:.1f} dBm") - else: - print(f"✓ Normal RF environment: {noise_floor:.1f} dBm") - await asyncio.sleep(interval) -``` - -### Example 4: Channel Assessment Before TX -```python -async def should_transmit(handler, threshold_dbm=-110): - """ - Check if channel is clear before transmitting. - Returns True if noise floor is below threshold (channel clear). - """ - noise_floor = handler.get_noise_floor() - - if noise_floor is None: - # Can't determine, allow transmission - return True - - if noise_floor > threshold_dbm: - # Channel busy - high noise - print(f"Channel busy: {noise_floor:.1f} dBm > {threshold_dbm} dBm") - return False - - # Channel clear - return True -``` - -## Integration with Web Dashboard - -The noise floor is automatically available in the `/api/stats` endpoint: - -```javascript -// JavaScript example for web dashboard -fetch('/api/stats') - .then(response => response.json()) - .then(data => { - const noiseFloor = data.noise_floor_dbm; - if (noiseFloor !== null) { - updateNoiseFloorDisplay(noiseFloor); - } - }); -``` - -## Interpretation - -### Typical Values -- **-120 to -110 dBm**: Very quiet RF environment (rural, low interference) -- **-110 to -100 dBm**: Normal RF environment (typical conditions) -- **-100 to -90 dBm**: Moderate RF noise (urban, some interference) -- **-90 dBm and above**: High RF noise (congested environment, potential issues) - -### Use Cases -1. **Collision Avoidance**: Check noise floor before transmitting to detect if another station is already transmitting -2. **RF Environment Monitoring**: Track RF noise levels over time for site assessment -3. **Adaptive Transmission**: Adjust TX timing or power based on channel conditions -4. **Debugging**: Identify sources of interference or poor reception - -## Technical Details - -### Calculation -The noise floor is calculated from the SX1262's instantaneous RSSI register: -```python -raw_rssi = self.lora.getRssiInst() -noise_floor_dbm = -(float(raw_rssi) / 2) -``` - -### Update Rate -The noise floor is read on-demand when `get_noise_floor()` is called. There is no caching - each call queries the radio hardware directly. - -### Error Handling -- Returns `None` if radio is not initialized -- Returns `None` if read fails (hardware error) -- Logs debug message on error (doesn't raise exceptions) - -## Future Enhancements - -Potential future improvements: -1. **Averaging**: Average noise floor over multiple samples for stability -2. **History**: Track noise floor history for trend analysis -3. **Thresholds**: Configurable thresholds for channel busy detection -4. **Carrier Sense**: Automatic carrier sense before each transmission -5. **Spectral Analysis**: Extended to include RSSI across multiple channels diff --git a/README.md b/README.md index b22d5e2..a70e193 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,8 @@ Before You Begin Make sure SPI is switched on using raspi-config: -```sudo raspi-config +```bash +sudo raspi-config ``` 1. Go to Interface Options @@ -95,17 +96,19 @@ Make sure SPI is switched on using raspi-config: 3. Choose Enable 4. Reboot when prompted: -```sudo reboot -``` -After reboot, you can confirm SPI is active: -``` -ls /dev/spi* -``` -You should see something like: -``` -/dev/spidev0.0 /dev/spidev0.1 +```bash +sudo reboot ``` +After reboot, you can confirm SPI is active: +```bash +ls /dev/spi* +``` + +You should see something like: +```bash +/dev/spidev0.0 /dev/spidev0.1 +``` **Clone the Repository:** ```bash diff --git a/manage.sh b/manage.sh index afb1c1a..0638ae1 100644 --- a/manage.sh +++ b/manage.sh @@ -219,6 +219,10 @@ install_repeater() { echo "20"; echo "# Creating directories..." mkdir -p "$INSTALL_DIR" "$CONFIG_DIR" "$LOG_DIR" /var/lib/pymc_repeater + echo "25"; echo "# Installing system dependencies..." + apt-get update -qq + apt-get install -y libffi-dev + echo "30"; echo "# Installing files..." cp -r repeater "$INSTALL_DIR/" cp pyproject.toml "$INSTALL_DIR/" @@ -226,26 +230,25 @@ install_repeater() { cp setup-radio-config.sh "$INSTALL_DIR/" 2>/dev/null || true cp radio-settings.json "$INSTALL_DIR/" 2>/dev/null || true - echo "40"; echo "# Installing configuration..." + echo "45"; echo "# Installing configuration..." cp config.yaml.example "$CONFIG_DIR/config.yaml.example" if [ ! -f "$CONFIG_DIR/config.yaml" ]; then cp config.yaml.example "$CONFIG_DIR/config.yaml" fi - echo "50" + echo "55" if [ "$SETUP_RADIO" = true ] && [ "$REGION" != "custom" ]; then echo "# Configuring radio settings..." if ! command -v jq &> /dev/null; then - apt-get update -qq apt-get install -y jq fi fi - echo "70"; echo "# Installing systemd service..." + echo "75"; echo "# Installing systemd service..." cp pymc-repeater.service /etc/systemd/system/ systemctl daemon-reload - echo "80"; echo "# Setting permissions..." + echo "85"; echo "# Setting permissions..." chown -R "$SERVICE_USER:$SERVICE_USER" "$INSTALL_DIR" "$CONFIG_DIR" "$LOG_DIR" /var/lib/pymc_repeater chmod 750 "$CONFIG_DIR" "$LOG_DIR" /var/lib/pymc_repeater