• BetaFlight模块设计之三十五:RSSI信号强度&链路稳定性分析


    基于BetaFlight开源代码框架简介的框架设计,逐步分析内部模块功能设计。这里主要针对四轴飞控DIY集成FPV功能第七章节调试遇到的问题,进一步深入分析和理解。

    航模主要通过遥控器或者数传两个通道进行控制,而BetaFlight这种穿越机最主要的方式还是通过遥控器。

    遥控器控制飞控这个通道的可靠性、稳定性需要通过这个通信链路的一些指标量化体现,从BetaFlight代码上看,主要有以下几个指标:

    1. RSSI信号强度
    • Value
    • dBm
    1. 链路质量(Link Quality)

    1. RSSI信号强度

    定时更新RSSI信号强度,并将强度值范围线性映射到0-100之间。值越大表示信号强度越大,值为0表示信号丢失(飞控进入failsafe模式)。

    1.1 RSSI Value

    RSSI Value设置的几种方式:

    1. 直接设置:主要取决于数据发送端对数据的处理和定义
    2. 过滤处理:PT1低频滤波或者算术平均滤波
    3. MSP设置:根据MSP协议,对MSP对端RSSI值乘以2^2
    void setRssiDirect(uint16_t newRssi, rssiSource_e source)
    {
        if (source != rssiSource) {
            return;
        }
    
        rssi = newRssi;
    }
    
    void setRssi(uint16_t rssiValue, rssiSource_e source)
    {
        if (source != rssiSource) {
            return;
        }
    
        // Filter RSSI value
        if (source == RSSI_SOURCE_FRAME_ERRORS) {
            rssi = pt1FilterApply(&frameErrFilter, rssiValue);
        } else {
            // calculate new sample mean
            rssi = updateRssiSamples(rssiValue);
        }
    }
    
    void setRssiMsp(uint8_t newMspRssi)
    {
        if (rssiSource == RSSI_SOURCE_NONE) {
            rssiSource = RSSI_SOURCE_MSP;
        }
    
        if (rssiSource == RSSI_SOURCE_MSP) {
            rssi = ((uint16_t)newMspRssi) << 2;
            lastMspRssiUpdateUs = micros();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    1.2 RSSI dBm Value

    RSSI dBm Value设置的几种方式:

    1. 直接设置:主要取决于数据发送端对数据的处理和定义
    2. 过滤处理:算术平均滤波
    void setRssiDbmDirect(int16_t newRssiDbm, rssiSource_e source)
    {
        if (source != rssiSource) {
            return;
        }
    
        rssiDbm = newRssiDbm;
    }
    
    void setRssiDbm(int16_t rssiDbmValue, rssiSource_e source)
    {
        if (source != rssiSource) {
            return;
        }
    
        rssiDbm = updateRssiDbmSamples(rssiDbmValue);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    2. 链路稳定性

    1. 直接设置:主要取决于数据发送端对数据的处理和定义
    2. 丢包率计算:计算最近16个报文的丢包率
    void setLinkQualityDirect(uint16_t linkqualityValue)
    {
    #ifdef USE_RX_LINK_QUALITY_INFO
        linkQuality = linkqualityValue;
    #else
        UNUSED(linkqualityValue);
    #endif
    }
    
    static void setLinkQuality(bool validFrame, timeDelta_t currentDeltaTimeUs)
    {
        static uint16_t rssiSum = 0;
        static uint16_t rssiCount = 0;
        static timeDelta_t resampleTimeUs = 0;
    
    #ifdef USE_RX_LINK_QUALITY_INFO
        if (linkQualitySource == LQ_SOURCE_NONE) {
            // calculate new sample mean
            linkQuality = updateLinkQualitySamples(validFrame ? LINK_QUALITY_MAX_VALUE : 0);
        }
    #endif
    
        if (rssiSource == RSSI_SOURCE_FRAME_ERRORS) {
            resampleTimeUs += currentDeltaTimeUs;
            rssiSum += validFrame ? RSSI_MAX_VALUE : 0;
            rssiCount++;
    
            if (resampleTimeUs >= FRAME_ERR_RESAMPLE_US) {
                setRssi(rssiSum / rssiCount, rssiSource);
                rssiSum = 0;
                rssiCount = 0;
                resampleTimeUs -= FRAME_ERR_RESAMPLE_US;
            }
        }
    }
    
    STATIC_UNIT_TESTED uint16_t updateLinkQualitySamples(uint16_t value)
    {
        static uint16_t samples[LINK_QUALITY_SAMPLE_COUNT];
        static uint8_t sampleIndex = 0;
        static uint16_t sum = 0;
    
        sum += value - samples[sampleIndex];
        samples[sampleIndex] = value;
        sampleIndex = (sampleIndex + 1) % LINK_QUALITY_SAMPLE_COUNT;
        return sum / LINK_QUALITY_SAMPLE_COUNT;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    3. RSSI & LinkQuality来源

    3.1 RSSI_SOURCE_ADC

    1. 【RSSI Value】数据源来自ADC模拟采样,通过过滤处理进行数据保存。
    2. 【LinkQuality】此时没有LinkQuality来源。
    static void updateRSSIADC(timeUs_t currentTimeUs)
    {
    #ifndef USE_ADC
        UNUSED(currentTimeUs);
    #else
        static uint32_t rssiUpdateAt = 0;
    
        if ((int32_t)(currentTimeUs - rssiUpdateAt) < 0) {
            return;
        }
        rssiUpdateAt = currentTimeUs + DELAY_20_MS;
    
        const uint16_t adcRssiSample = adcGetChannel(ADC_RSSI);
        uint16_t rssiValue = adcRssiSample / RSSI_ADC_DIVISOR;
    
        setRssi(rssiValue, RSSI_SOURCE_ADC);
    #endif
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    3.2 RSSI_SOURCE_RX_CHANNEL

    1. 【RSSI Value】数据源来自AUX Channel[1000;2000],直接scaleRange进行0-100的百分比线性映射。
    2. 【LinkQuality】rxFrameCheck通过setLinkQuality进行丢包率计算。
    static void updateRSSIPWM(void)
    {
        // Read value of AUX channel as rssi
        int16_t pwmRssi = rcData[rxConfig()->rssi_channel - 1];
    
        // Range of rawPwmRssi is [1000;2000]. rssi should be in [0;1023];
        setRssiDirect(scaleRange(constrain(pwmRssi, PWM_RANGE_MIN, PWM_RANGE_MAX), PWM_RANGE_MIN, PWM_RANGE_MAX, 0, RSSI_MAX_VALUE), RSSI_SOURCE_RX_CHANNEL);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    3.3 RSSI_SOURCE_MSP

    1. 【RSSI Value】根据MSP协议调用专用函数setRssiMsp设置信号强度;当DELAY_1500_MS超时,信号强度强制赋值零。
    2. 【LinkQuality】rxFrameCheck通过setLinkQuality进行丢包率计算。
    \src\main\msp\msp.c
        case MSP_SET_TX_INFO:
            setRssiMsp(sbufReadU8(src));
    
            break;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    \src\main\rx\rx.c
        case RSSI_SOURCE_MSP:
            if (cmpTimeUs(micros(), lastMspRssiUpdateUs) > DELAY_1500_MS) {  // 1.5s
                rssi = 0;
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    3.4 RSSI_SOURCE_RX_PROTOCOL_CRSF

    1. 【RSSI Value】来自于CRSF_FRAMETYPE_LINK_STATISTICS或者CRSF_FRAMETYPE_LINK_STATISTICS_TX报文
    2. 【RSSI dBm】来自于CRSF_FRAMETYPE_LINK_STATISTICS或者CRSF_FRAMETYPE_LINK_STATISTICS_TX报文
    3. 【LinkQuality】来自于CRSF_FRAMETYPE_LINK_STATISTICS或者CRSF_FRAMETYPE_LINK_STATISTICS_TX报文

    注:CRSF具有双向通信功能以及协商速率的功能,BetaFlight模块设计之二十三:CRSF V3串口速率协商任务分析

    3.4.1 CRSF_FRAMETYPE_LINK_STATISTICS

    该报文使用链路统计数据:

    1. SNR映射:-10dB of SNR mapped to 0; 0dB of SNR mapped to 20; 41dB of SNR mapped to 99
    2. CRSF_RSSI_MIN(-130)-0线性映射
    3. 算术平均AUX Channel的rssiDbm
    static void handleCrsfLinkStatisticsFrame(const crsfLinkStatistics_t* statsPtr, timeUs_t currentTimeUs)
    {
        const crsfLinkStatistics_t stats = *statsPtr;
        lastLinkStatisticsFrameUs = currentTimeUs;
        int16_t rssiDbm = -1 * (stats.active_antenna ? stats.uplink_RSSI_2 : stats.uplink_RSSI_1);
        if (rssiSource == RSSI_SOURCE_RX_PROTOCOL_CRSF) {
            if (rxConfig()->crsf_use_rx_snr) {
                // -10dB of SNR mapped to 0 RSSI (fail safe is likely to happen at this measure)
                //   0dB of SNR mapped to 20 RSSI (default alarm)
                //  41dB of SNR mapped to 99 RSSI (SNR can climb to around 60, but showing that is not very meaningful)
                const uint16_t rsnrPercentScaled = constrain((stats.uplink_SNR + 10) * 20, 0, RSSI_MAX_VALUE);
                setRssi(rsnrPercentScaled, RSSI_SOURCE_RX_PROTOCOL_CRSF);
    #ifdef USE_RX_RSSI_DBM
                rssiDbm = stats.uplink_SNR;
    #endif
            } else {
                const uint16_t rssiPercentScaled = scaleRange(rssiDbm, CRSF_RSSI_MIN, 0, 0, RSSI_MAX_VALUE);
                setRssi(rssiPercentScaled, RSSI_SOURCE_RX_PROTOCOL_CRSF);
            }
        }
    #ifdef USE_RX_RSSI_DBM
        setRssiDbm(rssiDbm, RSSI_SOURCE_RX_PROTOCOL_CRSF);
    #endif
    
    #ifdef USE_RX_LINK_QUALITY_INFO
        if (linkQualitySource == LQ_SOURCE_RX_PROTOCOL_CRSF) {
            setLinkQualityDirect(stats.uplink_Link_quality);
            rxSetRfMode(stats.rf_Mode);
        }
    #endif
    
    #ifdef USE_RX_LINK_UPLINK_POWER
        const uint8_t crsfUplinkPowerStatesItemIndex = (stats.uplink_TX_Power < CRSF_UPLINK_POWER_LEVEL_MW_ITEMS_COUNT) ? stats.uplink_TX_Power : 0;
        rxSetUplinkTxPwrMw(uplinkTXPowerStatesMw[crsfUplinkPowerStatesItemIndex]);
    #endif
    
        DEBUG_SET(DEBUG_CRSF_LINK_STATISTICS_UPLINK, 0, stats.uplink_RSSI_1);
        DEBUG_SET(DEBUG_CRSF_LINK_STATISTICS_UPLINK, 1, stats.uplink_RSSI_2);
        DEBUG_SET(DEBUG_CRSF_LINK_STATISTICS_UPLINK, 2, stats.uplink_Link_quality);
        DEBUG_SET(DEBUG_CRSF_LINK_STATISTICS_UPLINK, 3, stats.rf_Mode);
    
        DEBUG_SET(DEBUG_CRSF_LINK_STATISTICS_PWR, 0, stats.active_antenna);
        DEBUG_SET(DEBUG_CRSF_LINK_STATISTICS_PWR, 1, stats.uplink_SNR);
        DEBUG_SET(DEBUG_CRSF_LINK_STATISTICS_PWR, 2, stats.uplink_TX_Power);
    
        DEBUG_SET(DEBUG_CRSF_LINK_STATISTICS_DOWN, 0, stats.downlink_RSSI);
        DEBUG_SET(DEBUG_CRSF_LINK_STATISTICS_DOWN, 1, stats.downlink_Link_quality);
        DEBUG_SET(DEBUG_CRSF_LINK_STATISTICS_DOWN, 2, stats.downlink_SNR);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    3.4.2 CRSF_FRAMETYPE_LINK_STATISTICS_TX

    该报文CRSF_V3使用,链路统计数据:

    1. 线性映射AUX Channel的uplink_RSSI_percentage
    2. 算术平均(-1 * stats.uplink_RSSI)
    3. 算术平均(stats.uplink_SNR)
    static void handleCrsfLinkStatisticsTxFrame(const crsfLinkStatisticsTx_t* statsPtr, timeUs_t currentTimeUs)
    {
        const crsfLinkStatisticsTx_t stats = *statsPtr;
        lastLinkStatisticsFrameUs = currentTimeUs;
        if (rssiSource == RSSI_SOURCE_RX_PROTOCOL_CRSF) {
            const uint16_t rssiPercentScaled = scaleRange(stats.uplink_RSSI_percentage, 0, 100, 0, RSSI_MAX_VALUE);
            setRssi(rssiPercentScaled, RSSI_SOURCE_RX_PROTOCOL_CRSF);
        }
    #ifdef USE_RX_RSSI_DBM
        int16_t rssiDbm = -1 * stats.uplink_RSSI;
        if (rxConfig()->crsf_use_rx_snr) {
            rssiDbm = stats.uplink_SNR;
        }
        setRssiDbm(rssiDbm, RSSI_SOURCE_RX_PROTOCOL_CRSF);
    #endif
    
    #ifdef USE_RX_LINK_QUALITY_INFO
        if (linkQualitySource == LQ_SOURCE_RX_PROTOCOL_CRSF) {
            setLinkQualityDirect(stats.uplink_Link_quality);
        }
    #endif
    
        DEBUG_SET(DEBUG_CRSF_LINK_STATISTICS_UPLINK, 0, stats.uplink_RSSI);
        DEBUG_SET(DEBUG_CRSF_LINK_STATISTICS_UPLINK, 1, stats.uplink_SNR);
        DEBUG_SET(DEBUG_CRSF_LINK_STATISTICS_UPLINK, 2, stats.uplink_Link_quality);
        DEBUG_SET(DEBUG_CRSF_LINK_STATISTICS_UPLINK, 3, stats.uplink_RSSI_percentage);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    3.4.3 rxFrameCheck

    超时场景下直接将RSSI Value & RSSI dBm以及链路质量清零。

    static void crsfCheckRssi(uint32_t currentTimeUs) {
    
        if (cmpTimeUs(currentTimeUs, lastLinkStatisticsFrameUs) > CRSF_LINK_STATUS_UPDATE_TIMEOUT_US) {
            if (rssiSource == RSSI_SOURCE_RX_PROTOCOL_CRSF) {
                setRssiDirect(0, RSSI_SOURCE_RX_PROTOCOL_CRSF);
    #ifdef USE_RX_RSSI_DBM
                if (rxConfig()->crsf_use_rx_snr) {
                    setRssiDbmDirect(CRSF_SNR_MIN, RSSI_SOURCE_RX_PROTOCOL_CRSF);
                } else {
                    setRssiDbmDirect(CRSF_RSSI_MIN, RSSI_SOURCE_RX_PROTOCOL_CRSF);
                }
    #endif
            }
    #ifdef USE_RX_LINK_QUALITY_INFO
            if (linkQualitySource == LQ_SOURCE_RX_PROTOCOL_CRSF) {
                setLinkQualityDirect(0);
            }
    #endif
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    4. 其他协议

    TBD,比如:SRXL2, 代码链接

    注:后续再做更新,分析方法类似。

  • 相关阅读:
    Java大数 -- BigInteger类
    LINUX 常用命令
    Vue:第一个Vue程序和vue-router路由
    【老生谈算法】matlab实现功率谱估计分析算法源码——功率谱估计分析
    Sqlmap 22.05.22.01
    线程池在业务中的实践
    ​数据库查询进阶--多表查询
    AutoRunner自动化测试工具
    使用弹性盒子flex对html进行布局和动态计算视口高度
    洗车小程序源码:10个必备功能,提升洗车体验
  • 原文地址:https://blog.csdn.net/lida2003/article/details/125491508