From 917cc5e7d07e7cfe049480fbf1d629299c645548 Mon Sep 17 00:00:00 2001 From: mstrens Date: Tue, 27 Oct 2020 10:37:47 +0100 Subject: [PATCH] add Rf link quality checks --- openXsensor/oXs_config_advanced.h | 11 +++- openXsensor/oXs_config_basic.h | 4 +- openXsensor/oXs_config_description.h | 42 ++++++++++++ openXsensor/oXs_general.cpp | 1 - openXsensor/openXsensor.ino | 123 ++++++++++++++++++++++++++++------- 5 files changed, 156 insertions(+), 25 deletions(-) diff --git a/openXsensor/oXs_config_advanced.h b/openXsensor/oXs_config_advanced.h index cc4a79b..a13127b 100644 --- a/openXsensor/oXs_config_advanced.h +++ b/openXsensor/oXs_config_advanced.h @@ -55,7 +55,10 @@ //#define FILL_TEST2_WITH_GPS_HDOP // uncomment to activate this option +//#define FILL_TEST_1_2_WITH_LQ // uncomment to activate this option + // --------- 3 - PPM settings --------- + #define PIN_PPM 2 // Uncomment this line in order to use a Rx channel to control oXs; default is 2 but my own device use 3 #define PPM_MIN_100 988 // default 1500 - 512 ; // pulse width (usec) when TX sends a channel = -100 #define PPM_PLUS_100 2012 // default 1500 + 512 ; // pulse width (usec) when TX sends a channel = +100 @@ -123,7 +126,7 @@ // ***** 6.2 - Voltage parameters ***** // Each of following lines contains 6 parameters, the first value is for VOLT_1, the second for VOLT_2, ... up to the sixth for VOLT_6 -#define PIN_VOLTAGE 6 , 8 , 8 , 8 , 8 , 8 // Fill 6 values; set to 0 up to 7 for analog pins A0 up to A7 ; set the value to 8 for the voltage(s) not to be measured. +#define PIN_VOLTAGE 6 , 8 , 8 , 8 , 8 , 8 // Fill all 6 values; set to 0 up to 7 for analog pins A0 up to A7 ; set the value to 8 for the voltage(s) not to be measured. #define RESISTOR_TO_GROUND 2.95 , 10 , 10 , 10 , 0 , 18 // set value to 0 when no divider is used for a voltage; can contains decimals #define RESISTOR_TO_VOLTAGE 46.9 , 8.7 , 22 , 27 , 0 , 47 // set value to 0 when no divider is used for a voltage; can contains decimals #define OFFSET_VOLTAGE 0 , 0 , 0 , 0 , 0 , 0 // optionnal, can be negative, must be integer, in principe in mv @@ -203,6 +206,12 @@ #define INIT_FLOW_PARAM 30 , 100 , 500 , 700 , 0 , 0, 0, 0 // define at 4 levels of flow (in mliter/min) (e.g. 30, 100, 500, 700) 4 correction parameters (in %; e.g. 20, 10, -5, 15); flow levels have to be sorted from low to high #define FLOW_SENSOR_RESET_AT_PPM 95 // when absolute value of ppm is greater than this, flow counter is reset. +// -------- 13 - Rf link quality ---- +#define PULSE_INTERVAL_MIN 17000 // minimum delay (micro second) between 2 PWM pulses generated by the Rx +#define PULSE_INTERVAL_MAX 19000 // maximum delay (micro second) between 2 PWM pulses generated by the Rx +#define LQ_COUNT_MAX 50 // number of PWM pulses used to calculate the 2 Rf quality parameters; 50 means that you get the measurement once per second (e.g. 18msec * 50) +#define WIDTH_ERROR_MAX 1 // PWM pulse is considered wrong if the width of 2 conscutive pulses differs by this parameter or less. Normally this parameter should be set on 1 or 2. + // --------- 20 - Sequencer --------- //#define SEQUENCE_OUTPUTS 0b100000 #define SEQUENCE_UNIT 2 diff --git a/openXsensor/oXs_config_basic.h b/openXsensor/oXs_config_basic.h index c34765f..cc27a05 100644 --- a/openXsensor/oXs_config_basic.h +++ b/openXsensor/oXs_config_basic.h @@ -1,7 +1,7 @@ // OpenXsensor https://github.com/openXsensor/ // started by Rainer Schlosshan and maintained by Michel Strens -// This is version : 8.2.14 (21 Apr 2020) +// This is version : 8.2.15 (27 Oct 2020) //******************************************************************************************************************************************************* // // // @@ -123,6 +123,8 @@ // --------- 12 - Locator --------------- #define A_LOCATOR_IS_CONNECTED NO // select between YES , NO +// --------- 13 - Rf link quality --------------- see oXs_config_advanced.h for additionnal parameters about link quality +#define MEASURE_RF_LINK_QUALITY NO // select between YES , NO // --------- 20 - Sequencer --------- see oXs_config_advanced.h (only when oXs has to generate signals in sequence) diff --git a/openXsensor/oXs_config_description.h b/openXsensor/oXs_config_description.h index 35caea2..c704733 100644 --- a/openXsensor/oXs_config_description.h +++ b/openXsensor/oXs_config_description.h @@ -41,6 +41,7 @@ See OpenXsensor https://github.com/openXsensor/ * 10 - IMU 6050 (accelerometer/gyro sensor) (optionnal) and HMC5883 (magnetometer) * 11 - Flow sensor * 12 - Locator +* 13 - Rf link quality * 20 - Sequencer (ON/OFF) for some digital outputs (E.g. for a light controller) * xx - Reserved for developer * @@ -1183,6 +1184,47 @@ See OpenXsensor https://github.com/openXsensor/ * Note: if you change the frequency, take care to use the same on openXsensor and locator receiver devices. +****** 13 - Rf link quality ******************************************************************************************************************** +* oXs can check the quality of the Rf link monitoring the PWM signal on a normally unused channel +* The principle is the following: +* The Tx generates on a channel a signal that changes continiously (just like when using a servo tester); easiest is probably to use a triangle wave form. +* The Rx receives this signal and generates a pulse at regular interval (e.g. every 18 ms for a Frsky X8R receiver) +* The width of the pulse varies with the signal (normally betwwen 1000 usec and 2000 usec) +* If the Rx does not receives the signal from the Tx, the Rx will still generates a pulse but the width will be the same as the last received (or after some time a predefined value set as failsafe) +* oXs can measure the width of each pulse and compare it with the previous one. Getting 2 identical pulses denotes a lost of Rf signal +* oXs calculates the % of signal losts over a certain time (e.g. based on 50 signals) and the number of consecutive signal losts +* This principle could normally be used will all Rf protocol (FRSKY, JETI, HOTT, MULTIPLEX) if the TX can be programmed in order to generate a varying signal. +* Still currently the 2 measurements are stored internally in TEST_1 and TEST_2 and only FRSKY protocol allows to select telemetry fields to transmit them. +* +* In order to use this feature you have: +* - let the Tx generates a e.g. triangular wave signal on a free channel. +* With openTx, this requires 2 set up: +* - create a logical switch (e.g. L01) coupled with the free channel (e.g. Channel 8) : set e.g. L01 with "a> 4 ) ; RpmCounter = 0 ; difference = eventTime - lastEventTime ; lastEventTime = eventTime ; diff --git a/openXsensor/openXsensor.ino b/openXsensor/openXsensor.ino index 5ff3cef..36e193e 100644 --- a/openXsensor/openXsensor.ino +++ b/openXsensor/openXsensor.ino @@ -112,6 +112,22 @@ #if defined(GPS_REFRESH_RATE) && ( ! ( (GPS_REFRESH_RATE == 1) || (GPS_REFRESH_RATE == 5) || (GPS_REFRESH_RATE == 10) )) #error When defined GPS_REFRESH_RATE must be 1, 5 or 10 #endif + +#if defined ( MEASURE_RF_LINK_QUALITY ) && ( MEASURE_RF_LINK_QUALITY == YES) + #ifndef PIN_PPM + #error When MEASURE_RF_LINK_QUALITY = YES , PIN_PPM must be defined (in file oXs_config_advanced.h) + #endif + #if defined ( VSPEED_SOURCE ) && ( VSPEED_SOURCE == PPM_SELECTION) + #error When MEASURE_RF_LINK_QUALITY = YES , VSPEED_SOURCE may not be set to PPM_SELECTION + #endif + #if defined (AIRSPEED_SENSOR_USE) && (not(AIRSPEED_SENSOR_USE == NO_AIRSPEED)) + #error When MEASURE_RF_LINK_QUALITY = YES , SEQUENCE_OUTPUTS may not be defined + #endif + #if defined ( SEQUENCE_OUTPUTS ) + #error When MEASURE_RF_LINK_QUALITY = YES , SEQUENCE_OUTPUTS may not be defined + #endif +#endif //defined ( MEASURE_RF_LINK_QUALITY ) && ( MEASURE_RF_LINK_QUALITY == YES) + #ifdef PIN_PPM #if PIN_PPM == 2 @@ -164,6 +180,7 @@ extern unsigned long millis( void ) ; //#define DEBUG_CALCULATE_FIELDS //#define DEBUGSEQUENCE //#define DEBUG_PPM_AVAILABLE_FROM_INTERRUPT +#define DEBUG_RF_LINK_QUALITY //#define DEBUGPPMVALUE //#define DEBUGFORCEPPM //#define DEBUG_SELECTED_VARIO @@ -1706,7 +1723,7 @@ void LoadFromEEProm(){ /* use its value to adjust sensitivity and other parameters */ /***************************************************************/ #if defined ( PPM_IS_USED ) -volatile uint16_t time1 ; + void ProcessPPMSignal(){ boolean getNewPpm = false ; // become true if a new ppm value has been acquired #if defined( PIN_PPM ) // when ppm is read from a rx channel @@ -1810,11 +1827,24 @@ void ProcessPPMSignal(){ #ifdef PPM_INTERRUPT uint16_t StartTime ; -//uint16_t EndTime ; - -volatile uint8_t PulseTime ; // A byte to avoid +volatile uint16_t time1 ; // pulsewidth (in millisec) +//volatile uint8_t PulseTime ; // A byte to avoid +volatile uint32_t ppmPulseMicros ; // timestamp when falling edges occurs in micros sec volatile boolean PulseTimeAvailable = false ; +#if defined ( MEASURE_RF_LINK_QUALITY ) && ( MEASURE_RF_LINK_QUALITY == YES) + uint32_t ppmTimestampPrev ; //Timestamp (micros) of the previous PPM signal (used to check that the delay between 2 pulses is about 18ms) + uint16_t widthPrev ; // width of the previous PWM pulse ( micros) + uint8_t lqError ; // count the number of errors (new PWM pulses having the same value as the previous one) + uint8_t lqCount ; // count total number of PWM pulses (to calculate a %); use to reset the counters after XXX (e.g.50) PWM + uint8_t prevFrameInError ; // boolean ; true when the previous PWM was wrong (used to calculate consecutive errors) + uint8_t lqConsecutiveError ; // count number of consecutive PWM errors + uint8_t lqConsecutiveErrorMax ; // keep the max of consecutive errors within a sequence +#endif +extern uint16_t lastTimerValue ; +extern uint32_t TotalMicros ; + + #if PIN_PPM == 2 // When pin 2 is used, arduino handle INT0 (see datasheet) ISR(INT0_vect, ISR_NOBLOCK) // NOBLOCK allows other interrupts to be served when this one is activated #else //// When pin 3 is used, arduino handle INT1 @@ -1824,6 +1854,8 @@ ISR(INT1_vect, ISR_NOBLOCK) uint8_t oReg = SREG; // store SREG value cli() ; uint16_t time = TCNT1 ; // Read timer 1 + uint16_t lastTimerValueTemp = lastTimerValue ; // save the value to avoid any change in interrupt (not sure it is required + uint32_t TotalMicrosTemp = TotalMicros ; // save the value to avoid any change in interrupt (not sure it is required SREG = oReg ; // restore SREG value if ( EICRA & PPM_INT_EDGE ) // a rising edge occurs { @@ -1832,25 +1864,29 @@ ISR(INT1_vect, ISR_NOBLOCK) } else // a falling edge occurs { -// EndTime = time ; + uint16_t elapsed = time - lastTimerValueTemp ; +#if F_CPU == 20000000L // 20MHz clock + #error Unsupported clock speed +#elif F_CPU == 16000000L // 16MHz clock + ppmPulseMicros = TotalMicrosTemp + ( elapsed >> 4 ) ; +#elif F_CPU == 8000000L // 8MHz clock + ppmPulseMicros = TotalMicrosTemp + ( elapsed >> 3 ) ; +#else + #error Unsupported clock speed +#endif + time -= StartTime ; #if F_CPU == 20000000L // 20MHz clock #error Unsupported clock speed #elif F_CPU == 16000000L // 16MHz clock - time >>= 4 ; // delay in usec + time>>= 4 ; // delay in usec #elif F_CPU == 8000000L // 8MHz clock time >>= 3 ; // delay in usec #else #error Unsupported clock speed #endif - -// if ( ppmInterrupted == 0 ) { // do not handle PulseTime if pin change interrupt has been delayed by another interrupt (Timer 1 compare A handling) - time1 = time ; - PulseTimeAvailable = true ; -// } else { -// ppmInterruptedCopy++ ; // used in order to test the value outside ISR -// } -// ppmInterrupted = 0 ; // reset indicator about pin change interrupt delay + time1 = time ; + PulseTimeAvailable = true ; EICRA |= PPM_INT_EDGE ; // allow a Rising edge to generate next interrupt } } @@ -1859,21 +1895,64 @@ void ReadPPM() { // set ppmus to 0 if ppm is not available or has not been co static uint8_t ppmIdx ; static uint16_t ppmTemp ; static uint16_t ppmMax ; // highest value of ppmTemp received ; Some ppm measurement are wrong (to low) because there are some interrupt some - ppmus = 0 ; -#if defined DEBUG_PPM_AVAILABLE_FROM_INTERRUPT - Serial.print("Read ppm at ") ; Serial.println(millis()) ; -#endif + ppmus = 0 ; // reset the pulse width if ( PulseTimeAvailable ) { // if new pulse is available #define PPM_COUNT_MAX 5 // select the max of 5 ppm (so once every 100 msec uint8_t oReg = SREG ; // save Status register cli() ; ppmTemp = time1 ; // use values from interrupt + uint32_t ppmTimestamp = ppmPulseMicros ; PulseTimeAvailable = false ; - SREG = oReg ; // restore Status register -#if defined DEBUG_PPM_AVAILABLE_FROM_INTERRUPT - Serial.print("ppm time1= ") ; Serial.println(time1) ; -#endif + SREG = oReg ; // restore Status register +#if defined ( MEASURE_RF_LINK_QUALITY ) && ( MEASURE_RF_LINK_QUALITY == YES) + uint32_t pulseInterval = (ppmTimestamp - ppmTimestampPrev) ; + if ( (pulseInterval > PULSE_INTERVAL_MIN) && (pulseInterval < PULSE_INTERVAL_MAX ) ) { + if ( abs (( int16_t) (ppmTemp - widthPrev)) <= WIDTH_ERROR_MAX ) { + lqError++ ; + if ( prevFrameInError == true) { + lqConsecutiveError++ ; // count consecutive errors + if (lqConsecutiveError > lqConsecutiveErrorMax) { + lqConsecutiveErrorMax = lqConsecutiveError ; + } + } + + prevFrameInError = true ; + } else { + lqConsecutiveError = 0 ; // reset the counter when a valid PWM is received + prevFrameInError = false ; // reset the flag when we get a valid PWM signal + } + lqCount++ ; +#if defined ( DEBUG_RF_LINK_QUALITY ) + Serial.print("Read ppm at ") ; Serial.print(ppmTimestamp) ; + Serial.print(" Delay= ") ; Serial.print(pulseInterval) ; + Serial.print(" Process at ") ; Serial.print(millis()) ; + Serial.print(" Width= ") ; Serial.print(ppmTemp) ; + Serial.print(" delta=") ; Serial.print( abs (( int16_t) (ppmTemp - widthPrev)) ) ; + Serial.print(" lqC=") ; Serial.print( lqCount ) ; + Serial.print(" lqE=") ; Serial.print( lqError ) ; + Serial.print(" cons=") ; Serial.print( lqConsecutiveError) ; + Serial.print(" Max=") ; Serial.print( lqConsecutiveErrorMax) ; + Serial.print(" %=") ; Serial.println( ( ( (LQ_COUNT_MAX - (uint16_t) lqError)) * 100 ) / LQ_COUNT_MAX ) ; +#endif + if ( lqCount >= LQ_COUNT_MAX ) { +#if defined ( FILL_TEST_1_2_WITH_LQ ) + test1.value = ( ( (LQ_COUNT_MAX - (uint16_t) lqError)) * 100 ) / LQ_COUNT_MAX ; + test1.available = true ; + test2.value = lqConsecutiveErrorMax ; + test2.available = true ; +#endif // FILL_TEST_1_2_WITH_LQ + lqCount = 0 ; + lqError = 0 ; + lqConsecutiveError = 0 ; + lqConsecutiveErrorMax = 0 ; + // to do : save the values in data that can be transmitted + } + } + ppmTimestampPrev = ppmTimestamp ; + widthPrev = ppmTemp ; +#endif // defined ( MEASURE_RF_LINK_QUALITY ) && ( MEASURE_RF_LINK_QUALITY == YES) + if ( ppmIdx >= PPM_COUNT_MAX ) { ppmus = ppmMax ; // we keep the highest value ppmIdx = 0 ; -- 2.11.4.GIT