From 693f9f58eb7da3254fc86e93b112b51890ec9bc2 Mon Sep 17 00:00:00 2001 From: Pascal Langer Date: Sun, 16 Feb 2020 20:05:29 +0100 Subject: [PATCH 01/27] V911S: new sub protocol E119 Model: Eachine E119 Protocol: V911S -> 46 Sub protocol: E119 -> 1 CH5: left button ??? CH6: right button ??? --- Multiprotocol/Multi_Names.ino | 4 +- Multiprotocol/Multiprotocol.h | 5 +-- Multiprotocol/V911S_xn297l.ino | 57 ++++++++++++++++++---------- Multiprotocol/XN297Dump_nrf24l01.ino | 4 ++ Multiprotocol/XN297L_EMU.ino | 8 ++-- Multiprotocol/_Config.h | 3 +- Protocols_Details.md | 13 ++++++- 7 files changed, 61 insertions(+), 33 deletions(-) diff --git a/Multiprotocol/Multi_Names.ino b/Multiprotocol/Multi_Names.ino index f5e0fe9..35d67ae 100644 --- a/Multiprotocol/Multi_Names.ino +++ b/Multiprotocol/Multi_Names.ino @@ -117,7 +117,7 @@ const char STR_SUBTYPE_FLYZONE[] = "\x05""FZ410"; const char STR_SUBTYPE_FX816[] = "\x03""P38"; const char STR_SUBTYPE_XN297DUMP[] = "\x07""250Kbps""1Mbps\0 ""2Mbps\0 ""Auto\0 "; const char STR_SUBTYPE_ESKY150[] = "\x03""4CH""7CH"; -const char STR_SUBTYPE_V911S[] = "\x04""Std\0""E119"; +const char STR_SUBTYPE_V911S[] = "\x05""V911S""E119\0"; const char STR_SUBTYPE_XK[] = "\x04""X450""X420"; const char STR_SUBTYPE_FRSKYR9[] = "\x07""915MHz\0""868MHz\0""915 8ch""868 8ch"; @@ -274,7 +274,7 @@ const mm_protocol_definition multi_protocols[] = { {PROTO_E01X, STR_E01X, 3, STR_SUBTYPE_E01X, OPTION_OPTION }, #endif #if defined(V911S_NRF24L01_INO) - {PROTO_V911S, STR_V911S, 1, STR_SUBTYPE_V911S, OPTION_RFTUNE }, + {PROTO_V911S, STR_V911S, 2, STR_SUBTYPE_V911S, OPTION_RFTUNE }, #endif #if defined(GD00X_NRF24L01_INO) {PROTO_GD00X, STR_GD00X, 2, STR_SUBTYPE_GD00X, OPTION_RFTUNE }, diff --git a/Multiprotocol/Multiprotocol.h b/Multiprotocol/Multiprotocol.h index 6823672..443d53f 100644 --- a/Multiprotocol/Multiprotocol.h +++ b/Multiprotocol/Multiprotocol.h @@ -19,7 +19,7 @@ #define VERSION_MAJOR 1 #define VERSION_MINOR 3 #define VERSION_REVISION 0 -#define VERSION_PATCH_LEVEL 67 +#define VERSION_PATCH_LEVEL 68 //****************** // Protocols @@ -879,9 +879,6 @@ Serial: 100000 Baud 8e2 _ xxxx xxxx p -- sub_protocol==XK X450 0 X420 1 - sub_protocol==V911S - V911S_STD 0 - V911S_E119 1 sub_protocol==FRSKY_R9 R9_915 0 R9_868 1 diff --git a/Multiprotocol/V911S_xn297l.ino b/Multiprotocol/V911S_xn297l.ino index 664e240..08cf41e 100644 --- a/Multiprotocol/V911S_xn297l.ino +++ b/Multiprotocol/V911S_xn297l.ino @@ -30,6 +30,8 @@ // flags going to packet[1] #define V911S_FLAG_EXPERT 0x04 +#define E119_FLAG_1 0x08 +#define E119_FLAG_2 0x40 // flags going to packet[2] #define V911S_FLAG_CALIB 0x01 @@ -56,10 +58,21 @@ static void __attribute__((unused)) V911S_send_packet(uint8_t bind) } if(rf_ch_num&2) channel=7-channel; + XN297L_Hopping(channel); + hopping_frequency_no++; + hopping_frequency_no&=7; // 8 RF channels + packet[ 0]=(rf_ch_num<<3)|channel; - packet[ 1]=V911S_FLAG_EXPERT; // short press on left button - packet[ 2]=GET_FLAG(CH5_SW,V911S_FLAG_CALIB); // long press on right button - memset(packet+3, 0x00, V911S_PACKET_SIZE - 3); + memset(packet+1, 0x00, V911S_PACKET_SIZE - 1); + if(sub_protocol==V911S_STD) + { + packet[ 1]=V911S_FLAG_EXPERT; // short press on left button + packet[ 2]=GET_FLAG(CH5_SW,V911S_FLAG_CALIB); // long press on right button + } + else + packet[ 1]=GET_FLAG(CH5_SW,E119_FLAG_1) // short press on left button + |GET_FLAG(CH6_SW,E119_FLAG_2); // short press on right button + //packet[3..6]=trims TAER signed uint16_t ch=convert_channel_16b_limit(THROTTLE ,0,0x7FF); packet[ 7] = ch; @@ -68,25 +81,31 @@ static void __attribute__((unused)) V911S_send_packet(uint8_t bind) packet[ 8]|= ch<<3; packet[ 9] = ch>>5; ch=convert_channel_16b_limit(ELEVATOR,0,0x7FF); - packet[10] = ch; - packet[11] = ch>>8; - ch=convert_channel_16b_limit(RUDDER ,0x7FF,0); - packet[11]|= ch<<3; - packet[12] = ch>>5; + if(sub_protocol==V911S_STD) + { + packet[10] = ch; + packet[11] = ch>>8; + ch=convert_channel_16b_limit(RUDDER ,0x7FF,0); + packet[11]|= ch<<3; + packet[12] = ch>>5; + } + else + { + ch=0x7FF-ch; + packet[ 9]|= ch<<6; + packet[10] = ch>>2; + packet[11] = ch>>10; + ch=convert_channel_16b_limit(RUDDER ,0x7FF,0); + packet[11]|= ch<<1; + packet[12] = ch>>7; + } } - if (!bind) - { - XN297L_Hopping(channel); - hopping_frequency_no++; - hopping_frequency_no&=7; // 8 RF channels - } - if(sub_protocol==V911S_STD) XN297L_WritePayload(packet, V911S_PACKET_SIZE); else - XN297L_WriteEnhancedPayload(packet, V911S_PACKET_SIZE, bind); - + XN297L_WriteEnhancedPayload(packet, V911S_PACKET_SIZE, bind?0:1); + XN297L_SetPower(); // Set tx_power XN297L_SetFreqOffset(); // Set frequency offset } @@ -95,9 +114,9 @@ static void __attribute__((unused)) V911S_init() { XN297L_Init(); if(sub_protocol==V911S_STD) - XN297L_SetTXAddr((uint8_t *)"KNBND",5); // V911S Bind address + XN297L_SetTXAddr((uint8_t *)"KNBND",5); // V911S Bind address else - XN297L_SetTXAddr((uint8_t *)"XPBND",5); // E119 Bind address + XN297L_SetTXAddr((uint8_t *)"XPBND",5); // E119 Bind address XN297L_HoppingCalib(V911S_NUM_RF_CHANNELS); // Calibrate all channels XN297L_RFChannel(V911S_RF_BIND_CHANNEL); // Set bind channel } diff --git a/Multiprotocol/XN297Dump_nrf24l01.ino b/Multiprotocol/XN297Dump_nrf24l01.ino index 81aca45..b04c333 100644 --- a/Multiprotocol/XN297Dump_nrf24l01.ino +++ b/Multiprotocol/XN297Dump_nrf24l01.ino @@ -84,6 +84,10 @@ static boolean __attribute__((unused)) XN297Dump_process_packet(void) // init crc crc = 0xb5d2; + /*debug("P: 71 0F 55 "); + for(uint8_t i=0; i> 8) << 6) | ((crc & 0xff) >> 2); buf[last++] = (crc & 0xff) << 6; } - NRF24L01_WritePayload(packet, last); pid++; - if(pid>3) - pid=0; + pid &= 3; // stop TX/RX CC2500_Strobe(CC2500_SIDLE); @@ -262,7 +260,7 @@ static void __attribute__((unused)) XN297L_WriteEnhancedPayload(uint8_t* msg, ui // packet length CC2500_WriteReg(CC2500_3F_TXFIFO, last + 3); // xn297L preamble - CC2500_WriteRegisterMulti(CC2500_3F_TXFIFO, (uint8_t*)"\x71\x0f\x55", 3); + CC2500_WriteRegisterMulti(CC2500_3F_TXFIFO, (uint8_t*)"\x71\x0F\x55", 3); // xn297 packet CC2500_WriteRegisterMulti(CC2500_3F_TXFIFO, buf, last); // transmit diff --git a/Multiprotocol/_Config.h b/Multiprotocol/_Config.h index 5a907b4..5b4a89a 100644 --- a/Multiprotocol/_Config.h +++ b/Multiprotocol/_Config.h @@ -139,6 +139,7 @@ //If you have 2 Multi modules which you want to share the same ID so you can use either to control the same RC model // then you can force the ID to a certain known value using the lines below. //Default is commented, you should uncoment only for test purpose or if you know exactly what you are doing!!! +//The 8 numbers below can be anything between 0...9 and A..F //#define FORCE_GLOBAL_ID 0x12345678 //Protocols using the CYRF6936 (DSM, Devo, Walkera...) are using the CYRF ID instead which should prevent duplicated IDs. @@ -244,7 +245,7 @@ //Some models (X-Vert, Blade 230S...) require a special value to instant stop the motor(s). // You can disable this feature by adding "//" on the line below. You have to specify which channel (14 by default) will be used to kill the throttle channel. // If the channel 14 is above -50% the throttle is untouched but if it is between -50% and -100%, the throttle output will be forced between -100% and -150%. -// For example, a value of -80% applied on channel 15 will instantly kill the motors on the X-Vert. +// For example, a value of -80% applied on channel 14 will instantly kill the motors on the X-Vert. #define DSM_THROTTLE_KILL_CH 14 //AFHDS2A specific settings diff --git a/Protocols_Details.md b/Protocols_Details.md index 0960a5f..b710b28 100644 --- a/Protocols_Details.md +++ b/Protocols_Details.md @@ -123,7 +123,7 @@ CFlie|38|CFlie||||||||NRF24L01| [Traxxas](Protocols_Details.md#Traxxas---43)|43|RX6519||||||||CYRF6936| [V2x2](Protocols_Details.md#V2X2---5)|5|V2x2|JXD506|||||||NRF24L01| [V761](Protocols_Details.md#V761---48)|48|V761||||||||NRF24L01|XN297 -[V911S](Protocols_Details.md#V911S---46)|46|V911S*||||||||NRF24L01|XN297 +[V911S](Protocols_Details.md#V911S---46)|46|V911S*|E119*|||||||NRF24L01|XN297 [WFly](Protocols_Details.md#WFLY---40)|40|WFLY||||||||CYRF6936| [WK2x01](Protocols_Details.md#WK2X01---30)|30|WK2801|WK2401|W6_5_1|W6_6_1|W6_HEL|W6_HEL_I|||CYRF6936| [YD717](Protocols_Details.md#YD717---8)|8|YD717|SKYWLKR|SYMAX4|XINXUN|NIHUI||||NRF24L01| @@ -1306,16 +1306,25 @@ CH1|CH2|CH3|CH4|CH5 Gyro: -100%=Beginer mode (Gyro on, yaw and pitch rate limited), 0%=Mid Mode ( Gyro on no rate limits), +100%=Mode Expert Gyro off ## V911S - *46* -Models: WLtoys V911S, XK A110 This protocol is known to be problematic because it's using the xn297L emulation with a transmission speed of 250kbps therefore it doesn't work very well with every modules, this is an hardware issue with the accuracy of the components. If the model does not respond well to inputs or hard to bind, you can try to switch the emulation from the default NRF24L01 RF component to the CC2500 by using an option value (freq tuning) different from 0. Option in this case is used for fine frequency tuning like any CC2500 protocols so check the [Frequency Tuning page](/docs/Frequency_Tuning.md). +### Sub_protocol V911S - *0* +Models: WLtoys V911S, XK A110 + CH1|CH2|CH3|CH4|CH5 ---|---|---|---|--- A|E|T|R|CALIB +### Sub_protocol E119 - *1* +Models: Eachine E119 + +CH1|CH2|CH3|CH4|CH5|CH6 +---|---|---|---|---|--- +A|E|T|R|L_BUT|R_BUT + ## YD717 - *8* Autobind protocol From 68a6af0eb5159e113ac324b26fa86d3e02380555 Mon Sep 17 00:00:00 2001 From: Pascal Langer Date: Sun, 16 Feb 2020 20:18:02 +0100 Subject: [PATCH 02/27] Update E119 channels and flags Default is high rate CH5 is calib --- Multiprotocol/V911S_xn297l.ino | 8 ++++---- Protocols_Details.md | 11 +++-------- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/Multiprotocol/V911S_xn297l.ino b/Multiprotocol/V911S_xn297l.ino index 08cf41e..17ba4f2 100644 --- a/Multiprotocol/V911S_xn297l.ino +++ b/Multiprotocol/V911S_xn297l.ino @@ -30,8 +30,8 @@ // flags going to packet[1] #define V911S_FLAG_EXPERT 0x04 -#define E119_FLAG_1 0x08 -#define E119_FLAG_2 0x40 +#define E119_FLAG_EXPERT 0x08 +#define E119_FLAG_CALIB 0x40 // flags going to packet[2] #define V911S_FLAG_CALIB 0x01 @@ -70,8 +70,8 @@ static void __attribute__((unused)) V911S_send_packet(uint8_t bind) packet[ 2]=GET_FLAG(CH5_SW,V911S_FLAG_CALIB); // long press on right button } else - packet[ 1]=GET_FLAG(CH5_SW,E119_FLAG_1) // short press on left button - |GET_FLAG(CH6_SW,E119_FLAG_2); // short press on right button + packet[ 1]=E119_FLAG_EXPERT // short press on left button + |GET_FLAG(CH5_SW,E119_FLAG_CALIB); // short press on right button //packet[3..6]=trims TAER signed uint16_t ch=convert_channel_16b_limit(THROTTLE ,0,0x7FF); diff --git a/Protocols_Details.md b/Protocols_Details.md index b710b28..1b0c981 100644 --- a/Protocols_Details.md +++ b/Protocols_Details.md @@ -1306,25 +1306,20 @@ CH1|CH2|CH3|CH4|CH5 Gyro: -100%=Beginer mode (Gyro on, yaw and pitch rate limited), 0%=Mid Mode ( Gyro on no rate limits), +100%=Mode Expert Gyro off ## V911S - *46* - This protocol is known to be problematic because it's using the xn297L emulation with a transmission speed of 250kbps therefore it doesn't work very well with every modules, this is an hardware issue with the accuracy of the components. If the model does not respond well to inputs or hard to bind, you can try to switch the emulation from the default NRF24L01 RF component to the CC2500 by using an option value (freq tuning) different from 0. Option in this case is used for fine frequency tuning like any CC2500 protocols so check the [Frequency Tuning page](/docs/Frequency_Tuning.md). -### Sub_protocol V911S - *0* -Models: WLtoys V911S, XK A110 - CH1|CH2|CH3|CH4|CH5 ---|---|---|---|--- A|E|T|R|CALIB +### Sub_protocol V911S - *0* +Models: WLtoys V911S, XK A110 + ### Sub_protocol E119 - *1* Models: Eachine E119 -CH1|CH2|CH3|CH4|CH5|CH6 ----|---|---|---|---|--- -A|E|T|R|L_BUT|R_BUT - ## YD717 - *8* Autobind protocol From 8b7bd00a487bce665fc8f196fa401674389e3680 Mon Sep 17 00:00:00 2001 From: Pascal Langer Date: Sun, 16 Feb 2020 20:48:49 +0100 Subject: [PATCH 03/27] Update Multi.txt --- Multiprotocol/Multi.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Multiprotocol/Multi.txt b/Multiprotocol/Multi.txt index 9b72083..dd9666c 100644 --- a/Multiprotocol/Multi.txt +++ b/Multiprotocol/Multi.txt @@ -43,7 +43,7 @@ 43,Traxxas,RX6519 44,NCC1701 45,E01X,E012,E015,E016H -46,V911S,Std +46,V911S,V911S,E119 47,GD00X,GD_V1,GD_V2 48,V761 49,KF606 From 73d7728e08a7829dd1a8fc40f6a9da11f7db4420 Mon Sep 17 00:00:00 2001 From: Pascal Langer Date: Mon, 17 Feb 2020 11:44:53 +0100 Subject: [PATCH 04/27] Change CC2500 emulation layer to support NRF24L01 @250K --- Multiprotocol/GD00X_nrf24l01.ino | 2 +- Multiprotocol/KF606_nrf24l01.ino | 2 +- Multiprotocol/MJXQ_nrf24l01.ino | 2 +- Multiprotocol/Multiprotocol.h | 2 +- .../{XN297L_EMU.ino => NRF250K_EMU.ino} | 85 ++++++++++++++++++- Multiprotocol/V911S_xn297l.ino | 2 +- Multiprotocol/XK_nrf24l01.ino | 2 +- Multiprotocol/ZSX_nrf24l01.ino | 2 +- .../{iface_xn297l.h => iface_nrf250k.h} | 16 +++- 9 files changed, 105 insertions(+), 10 deletions(-) rename Multiprotocol/{XN297L_EMU.ino => NRF250K_EMU.ino} (84%) rename Multiprotocol/{iface_xn297l.h => iface_nrf250k.h} (57%) diff --git a/Multiprotocol/GD00X_nrf24l01.ino b/Multiprotocol/GD00X_nrf24l01.ino index d4f7f7b..9c8c49d 100644 --- a/Multiprotocol/GD00X_nrf24l01.ino +++ b/Multiprotocol/GD00X_nrf24l01.ino @@ -16,7 +16,7 @@ Multiprotocol is distributed in the hope that it will be useful, #if defined(GD00X_NRF24L01_INO) -#include "iface_xn297l.h" +#include "iface_nrf250k.h" //#define FORCE_GD00X_ORIGINAL_ID diff --git a/Multiprotocol/KF606_nrf24l01.ino b/Multiprotocol/KF606_nrf24l01.ino index e50d6a4..290f444 100644 --- a/Multiprotocol/KF606_nrf24l01.ino +++ b/Multiprotocol/KF606_nrf24l01.ino @@ -16,7 +16,7 @@ Multiprotocol is distributed in the hope that it will be useful, #if defined(KF606_NRF24L01_INO) -#include "iface_xn297l.h" +#include "iface_nrf250k.h" //#define FORCE_KF606_ORIGINAL_ID diff --git a/Multiprotocol/MJXQ_nrf24l01.ino b/Multiprotocol/MJXQ_nrf24l01.ino index 3db83c0..04c0a07 100644 --- a/Multiprotocol/MJXQ_nrf24l01.ino +++ b/Multiprotocol/MJXQ_nrf24l01.ino @@ -18,7 +18,7 @@ #if defined(MJXQ_NRF24L01_INO) #include "iface_nrf24l01.h" -#include "iface_xn297l.h" +#include "iface_nrf250k.h" #define MJXQ_BIND_COUNT 150 #define MJXQ_PACKET_PERIOD 4000 // Timeout for callback in uSec diff --git a/Multiprotocol/Multiprotocol.h b/Multiprotocol/Multiprotocol.h index 443d53f..d13ce1a 100644 --- a/Multiprotocol/Multiprotocol.h +++ b/Multiprotocol/Multiprotocol.h @@ -19,7 +19,7 @@ #define VERSION_MAJOR 1 #define VERSION_MINOR 3 #define VERSION_REVISION 0 -#define VERSION_PATCH_LEVEL 68 +#define VERSION_PATCH_LEVEL 69 //****************** // Protocols diff --git a/Multiprotocol/XN297L_EMU.ino b/Multiprotocol/NRF250K_EMU.ino similarity index 84% rename from Multiprotocol/XN297L_EMU.ino rename to Multiprotocol/NRF250K_EMU.ino index 773d57f..6cf366d 100644 --- a/Multiprotocol/XN297L_EMU.ino +++ b/Multiprotocol/NRF250K_EMU.ino @@ -13,7 +13,7 @@ along with Multiprotocol. If not, see . */ #ifdef NRF24L01_INSTALLED -#include "iface_xn297l.h" +#include "iface_nrf250k.h" static void __attribute__((unused)) XN297L_Init() { @@ -350,4 +350,87 @@ static void __attribute__((unused)) XN297L_SetFreqOffset() } #endif } + +static void __attribute__((unused)) NRF250K_SetTXAddr(uint8_t* addr, uint8_t len) +{ + #ifdef CC2500_INSTALLED + if(option==0) + #endif + {//NRF + NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, addr, len); + return; + } + //CC2500 + #ifdef CC2500_INSTALLED + if (len > 5) len = 5; + if (len < 3) len = 3; + xn297_addr_len = len; + memcpy(xn297_tx_addr, addr, len); + #endif +} + +static void __attribute__((unused)) NRF250K_WritePayload(uint8_t* msg, uint8_t len) +{ + #ifdef CC2500_INSTALLED + if(option==0) + #endif + {//NRF + NRF24L01_FlushTx(); + NRF24L01_WriteReg(NRF24L01_07_STATUS, _BV(NRF24L01_07_TX_DS) | _BV(NRF24L01_07_RX_DR) | _BV(NRF24L01_07_MAX_RT)); + NRF24L01_WritePayload(msg, len); + return; + } + //CC2500 + #ifdef CC2500_INSTALLED + uint8_t buf[35]; + uint8_t last = 0; + uint8_t i; + + //nrf preamble + if(xn297_tx_addr[xn297_addr_len - 1] & 0x80) + buf[0]=0x55; + else + buf[0]=0xAA; + last++; + // address + for (i = 0; i < xn297_addr_len; ++i) + buf[last++] = xn297_tx_addr[xn297_addr_len - i - 1]; + // payload + for (i = 0; i < len; ++i) + // bit-reverse bytes in packet + buf[last++] = msg[i]; + + // crc + uint16_t crc = 0xffff; + for (uint8_t i = 1; i < last; ++i) + crc = crc16_update(crc, buf[i], 8); + buf[last++] = crc >> 8; + buf[last++] = crc & 0xff; + + // stop TX/RX + CC2500_Strobe(CC2500_SIDLE); + // flush tx FIFO + CC2500_Strobe(CC2500_SFTX); + // packet length + CC2500_WriteReg(CC2500_3F_TXFIFO, last); + // nrf packet + CC2500_WriteRegisterMulti(CC2500_3F_TXFIFO, buf, last); + // transmit + CC2500_Strobe(CC2500_STX); + #endif +} + +static boolean __attribute__((unused)) NRF250K_IsPacketSent() +{ + #ifdef CC2500_INSTALLED + if(option==0) + #endif + { //NRF + return NRF24L01_ReadReg(NRF24L01_07_STATUS) & _BV(NRF24L01_07_TX_DS); + } + #ifdef CC2500_INSTALLED + return CC2500_ReadReg(CC2500_35_MARCSTATE)==0x01; // State is IDLE + #endif +} + #endif \ No newline at end of file diff --git a/Multiprotocol/V911S_xn297l.ino b/Multiprotocol/V911S_xn297l.ino index 17ba4f2..6df074b 100644 --- a/Multiprotocol/V911S_xn297l.ino +++ b/Multiprotocol/V911S_xn297l.ino @@ -16,7 +16,7 @@ #if defined(V911S_NRF24L01_INO) -#include "iface_xn297l.h" +#include "iface_nrf250k.h" //#define V911S_ORIGINAL_ID diff --git a/Multiprotocol/XK_nrf24l01.ino b/Multiprotocol/XK_nrf24l01.ino index 91797d2..d47df04 100644 --- a/Multiprotocol/XK_nrf24l01.ino +++ b/Multiprotocol/XK_nrf24l01.ino @@ -16,7 +16,7 @@ Multiprotocol is distributed in the hope that it will be useful, #if defined(XK_NRF24L01_INO) -#include "iface_xn297l.h" +#include "iface_nrf250k.h" //#define FORCE_XK_ORIGINAL_ID diff --git a/Multiprotocol/ZSX_nrf24l01.ino b/Multiprotocol/ZSX_nrf24l01.ino index a2c974c..23b55a0 100644 --- a/Multiprotocol/ZSX_nrf24l01.ino +++ b/Multiprotocol/ZSX_nrf24l01.ino @@ -16,7 +16,7 @@ Multiprotocol is distributed in the hope that it will be useful, #if defined(ZSX_NRF24L01_INO) -#include "iface_xn297l.h" +#include "iface_nrf250k.h" //#define FORCE_ZSX_ORIGINAL_ID diff --git a/Multiprotocol/iface_xn297l.h b/Multiprotocol/iface_nrf250k.h similarity index 57% rename from Multiprotocol/iface_xn297l.h rename to Multiprotocol/iface_nrf250k.h index 8943a29..8b7d0a1 100644 --- a/Multiprotocol/iface_xn297l.h +++ b/Multiprotocol/iface_nrf250k.h @@ -1,6 +1,6 @@ -#ifndef _IFACE_XN297L_H_ +#ifndef _IFACE_NRF250K_H_ -#define _IFACE_XN297L_H_ +#define _IFACE_NRF250K_H_ #if defined (CC2500_INSTALLED) #include "iface_cc2500.h" @@ -9,6 +9,7 @@ #include "iface_nrf24l01.h" #endif +//XN297L static void __attribute__((unused)) XN297L_Init(); static void __attribute__((unused)) XN297L_SetTXAddr(const uint8_t*, uint8_t); static void __attribute__((unused)) XN297L_WritePayload(uint8_t*, uint8_t); @@ -19,4 +20,15 @@ static void __attribute__((unused)) XN297L_RFChannel(uint8_t); static void __attribute__((unused)) XN297L_SetPower(); static void __attribute__((unused)) XN297L_SetFreqOffset(); +//NRF250K +#define NRF250K_Init() XN297L_Init() +#define NRF250K_HoppingCalib(X) XN297L_HoppingCalib(X) +#define NRF250K_Hopping(X) XN297L_Hopping(X) +#define NRF250K_RFChannel(X) XN297L_RFChannel(X) +#define NRF250K_SetPower() XN297L_SetPower() +#define NRF250K_SetFreqOffset() XN297L_SetFreqOffset() +static void __attribute__((unused)) NRF250K_SetTXAddr(uint8_t*, uint8_t); +static void __attribute__((unused)) NRF250K_WritePayload(uint8_t*, uint8_t); +static boolean __attribute__((unused)) NRF250K_IsPacketSent(); + #endif \ No newline at end of file From 5124c2a96d1e564eeee5a9d50f81098c04a76820 Mon Sep 17 00:00:00 2001 From: Pascal Langer Date: Mon, 17 Feb 2020 11:45:28 +0100 Subject: [PATCH 05/27] SLT: use the CC2500 emulation layer if requested/available --- Multiprotocol/Multi_Names.ino | 2 +- Multiprotocol/SLT_nrf24l01.ino | 57 +++++++++++++--------------------- 2 files changed, 23 insertions(+), 36 deletions(-) diff --git a/Multiprotocol/Multi_Names.ino b/Multiprotocol/Multi_Names.ino index 35d67ae..2844e9d 100644 --- a/Multiprotocol/Multi_Names.ino +++ b/Multiprotocol/Multi_Names.ino @@ -169,7 +169,7 @@ const mm_protocol_definition multi_protocols[] = { {PROTO_SYMAX, STR_SYMAX, 2, STR_SUBTYPE_SYMAX, OPTION_NONE }, #endif #if defined(SLT_NRF24L01_INO) - {PROTO_SLT, STR_SLT, 5, STR_SUBTYPE_SLT, OPTION_NONE }, + {PROTO_SLT, STR_SLT, 5, STR_SUBTYPE_SLT, OPTION_RFTUNE }, #endif #if defined(CX10_NRF24L01_INO) {PROTO_CX10, STR_CX10, 7, STR_SUBTYPE_CX10, OPTION_NONE }, diff --git a/Multiprotocol/SLT_nrf24l01.ino b/Multiprotocol/SLT_nrf24l01.ino index 924aaaa..066caa6 100644 --- a/Multiprotocol/SLT_nrf24l01.ino +++ b/Multiprotocol/SLT_nrf24l01.ino @@ -16,7 +16,7 @@ #if defined(SLT_NRF24L01_INO) -#include "iface_nrf24l01.h" +#include "iface_nrf250k.h" //#define SLT_Q200_FORCE_ID @@ -25,6 +25,7 @@ #define SLT_PAYLOADSIZE_V2 11 #define SLT_NFREQCHANNELS 15 #define SLT_TXID_SIZE 4 +#define SLT_BIND_CHANNEL 0x50 enum{ // flags going to packet[6] (Q200) @@ -53,25 +54,8 @@ enum { static void __attribute__((unused)) SLT_init() { - NRF24L01_Initialize(); - NRF24L01_WriteReg(NRF24L01_00_CONFIG, _BV(NRF24L01_00_EN_CRC) | _BV(NRF24L01_00_CRCO)); // 2-bytes CRC, radio off - NRF24L01_WriteReg(NRF24L01_01_EN_AA, 0x00); // No Auto Acknoledgement - NRF24L01_WriteReg(NRF24L01_02_EN_RXADDR, 0x01); // Enable data pipe 0 - NRF24L01_WriteReg(NRF24L01_03_SETUP_AW, 0x02); // 4-byte RX/TX address - NRF24L01_WriteReg(NRF24L01_04_SETUP_RETR, 0x00); // Disable auto retransmit - NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70); // Clear data ready, data sent, and retransmit - NRF24L01_WriteReg(NRF24L01_11_RX_PW_P0, 4); // bytes of data payload for pipe 1 - NRF24L01_SetBitrate(NRF24L01_BR_250K); // 256kbps - NRF24L01_SetPower(); - if(sub_protocol==SLT_V1) - NRF24L01_WriteRegisterMulti(NRF24L01_0A_RX_ADDR_P0, (uint8_t*)"\xC3\xC3\xAA\x55", SLT_TXID_SIZE); - else // V2 - NRF24L01_WriteRegisterMulti(NRF24L01_0A_RX_ADDR_P0, (uint8_t*)"\x7E\xB8\x63\xA9", SLT_TXID_SIZE); - NRF24L01_FlushRx(); - NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, rx_tx_addr, SLT_TXID_SIZE); - NRF24L01_FlushTx(); - // Turn radio power on - NRF24L01_SetTxRxMode(TX_EN); + NRF250K_Init(); + NRF250K_SetTXAddr(rx_tx_addr, SLT_TXID_SIZE); } static void __attribute__((unused)) SLT_set_freq(void) @@ -109,21 +93,25 @@ static void __attribute__((unused)) SLT_set_freq(void) } } } + + //Bind channel + hopping_frequency[SLT_NFREQCHANNELS]=SLT_BIND_CHANNEL; + + //Calib all channels + NRF250K_HoppingCalib(SLT_NFREQCHANNELS+1); } static void __attribute__((unused)) SLT_wait_radio() { if (packet_sent) - while (!(NRF24L01_ReadReg(NRF24L01_07_STATUS) & _BV(NRF24L01_07_TX_DS))); + while (!NRF250K_IsPacketSent()); packet_sent = 0; } static void __attribute__((unused)) SLT_send_packet(uint8_t len) { SLT_wait_radio(); - NRF24L01_FlushTx(); - NRF24L01_WriteReg(NRF24L01_07_STATUS, _BV(NRF24L01_07_TX_DS) | _BV(NRF24L01_07_RX_DR) | _BV(NRF24L01_07_MAX_RT)); - NRF24L01_WritePayload(packet, len); + NRF250K_WritePayload(packet, len); packet_sent = 1; } @@ -132,7 +120,7 @@ static void __attribute__((unused)) SLT_build_packet() static uint8_t calib_counter=0; // Set radio channel - once per packet batch - NRF24L01_WriteReg(NRF24L01_05_RF_CH, hopping_frequency[hopping_frequency_no]); + NRF250K_Hopping(hopping_frequency_no); if (++hopping_frequency_no >= SLT_NFREQCHANNELS) hopping_frequency_no = 0; @@ -183,23 +171,22 @@ static void __attribute__((unused)) SLT_build_packet() static void __attribute__((unused)) SLT_send_bind_packet() { SLT_wait_radio(); - BIND_IN_PROGRESS; //Limit TX power to bind level - NRF24L01_SetPower(); + NRF250K_Hopping(SLT_NFREQCHANNELS); //Bind channel + BIND_IN_PROGRESS; //Limit TX power to bind level + NRF250K_SetPower(); BIND_DONE; - NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, (uint8_t *)"\x7E\xB8\x63\xA9", SLT_TXID_SIZE); - - NRF24L01_WriteReg(NRF24L01_05_RF_CH, 0x50); + NRF250K_SetTXAddr((uint8_t *)"\x7E\xB8\x63\xA9", SLT_TXID_SIZE); memcpy((void*)packet,(void*)rx_tx_addr,SLT_TXID_SIZE); if(phase==SLT_BIND2) SLT_send_packet(SLT_TXID_SIZE); else // SLT_BIND1 SLT_send_packet(SLT_PAYLOADSIZE_V2); - SLT_wait_radio(); //Wait until the packet's sent before changing TX address! + SLT_wait_radio(); //Wait until the packet's sent before changing TX address! - NRF24L01_SetPower(); //Change power back to normal level - if(phase==SLT_BIND2) // after V1 bind and V2 second bind packet - NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, rx_tx_addr, SLT_TXID_SIZE); + NRF250K_SetPower(); //Change power back to normal level + if(phase==SLT_BIND2) //After V1 bind and V2 second bind packet + NRF250K_SetTXAddr(rx_tx_addr, SLT_TXID_SIZE); } #define SLT_TIMING_BUILD 1000 @@ -253,7 +240,7 @@ uint16_t SLT_callback() } else {// Continue to send normal packets - NRF24L01_SetPower(); // Set tx_power + NRF250K_SetPower(); // Set tx_power phase = SLT_BUILD; if(sub_protocol==SLT_V1) return 20000-SLT_TIMING_BUILD; From 5503502badda09ebf973930636e31dfaed56f2ae Mon Sep 17 00:00:00 2001 From: Pascal Langer Date: Mon, 17 Feb 2020 13:10:57 +0100 Subject: [PATCH 06/27] SLT: fix going from NRF to CC2500 --- Multiprotocol/Multiprotocol.h | 2 +- Multiprotocol/NRF250K_EMU.ino | 18 +++++++++--------- Multiprotocol/SLT_nrf24l01.ino | 17 +++++++++++++++-- 3 files changed, 25 insertions(+), 12 deletions(-) diff --git a/Multiprotocol/Multiprotocol.h b/Multiprotocol/Multiprotocol.h index d13ce1a..0a55345 100644 --- a/Multiprotocol/Multiprotocol.h +++ b/Multiprotocol/Multiprotocol.h @@ -19,7 +19,7 @@ #define VERSION_MAJOR 1 #define VERSION_MINOR 3 #define VERSION_REVISION 0 -#define VERSION_PATCH_LEVEL 69 +#define VERSION_PATCH_LEVEL 70 //****************** // Protocols diff --git a/Multiprotocol/NRF250K_EMU.ino b/Multiprotocol/NRF250K_EMU.ino index 6cf366d..cb0e205 100644 --- a/Multiprotocol/NRF250K_EMU.ino +++ b/Multiprotocol/NRF250K_EMU.ino @@ -353,7 +353,11 @@ static void __attribute__((unused)) XN297L_SetFreqOffset() static void __attribute__((unused)) NRF250K_SetTXAddr(uint8_t* addr, uint8_t len) { + if (len > 5) len = 5; + if (len < 3) len = 3; #ifdef CC2500_INSTALLED + xn297_addr_len = len; + memcpy(xn297_tx_addr, addr, len); if(option==0) #endif {//NRF @@ -361,12 +365,6 @@ static void __attribute__((unused)) NRF250K_SetTXAddr(uint8_t* addr, uint8_t len return; } //CC2500 - #ifdef CC2500_INSTALLED - if (len > 5) len = 5; - if (len < 3) len = 3; - xn297_addr_len = len; - memcpy(xn297_tx_addr, addr, len); - #endif } static void __attribute__((unused)) NRF250K_WritePayload(uint8_t* msg, uint8_t len) @@ -388,16 +386,15 @@ static void __attribute__((unused)) NRF250K_WritePayload(uint8_t* msg, uint8_t l //nrf preamble if(xn297_tx_addr[xn297_addr_len - 1] & 0x80) - buf[0]=0x55; - else buf[0]=0xAA; + else + buf[0]=0x55; last++; // address for (i = 0; i < xn297_addr_len; ++i) buf[last++] = xn297_tx_addr[xn297_addr_len - i - 1]; // payload for (i = 0; i < len; ++i) - // bit-reverse bytes in packet buf[last++] = msg[i]; // crc @@ -407,6 +404,9 @@ static void __attribute__((unused)) NRF250K_WritePayload(uint8_t* msg, uint8_t l buf[last++] = crc >> 8; buf[last++] = crc & 0xff; + //for(uint8_t i=0;i= SLT_NFREQCHANNELS) hopping_frequency_no = 0; @@ -258,7 +260,13 @@ uint16_t SLT_callback() return 20000-SLT_TIMING_BUILD-SLT_V1_TIMING_BIND2; else //V2 return 13730-SLT_TIMING_BUILD-SLT_V2_TIMING_BIND1-SLT_V2_TIMING_BIND2; - } +/* case SLT_TEST: + for(uint8_t i=0;i<10;i++) + packet[i]=0x10+i; + NRF250K_WritePayload(packet,10); + NRF250K_SetFreqOffset(); // Set frequency offset + return 5000; +*/ } return 19000; } @@ -279,6 +287,11 @@ uint16_t initSLT() SLT_set_freq(); SLT_init(); phase = SLT_BUILD; + +/* phase=SLT_TEST; + NRF250K_SetTXAddr((uint8_t*)"\x01\x02\x03\x04\x05",5); + NRF250K_RFChannel(0); +*/ return 50000; } From a7ac09375399a45ea7c570912cf42d87d18d6eb5 Mon Sep 17 00:00:00 2001 From: Pascal Langer Date: Mon, 17 Feb 2020 16:44:02 +0100 Subject: [PATCH 07/27] SLT: CC2500 fix --- Multiprotocol/Multiprotocol.h | 2 +- Multiprotocol/NRF250K_EMU.ino | 4 +--- Multiprotocol/SLT_nrf24l01.ino | 21 +++------------------ 3 files changed, 5 insertions(+), 22 deletions(-) diff --git a/Multiprotocol/Multiprotocol.h b/Multiprotocol/Multiprotocol.h index 0a55345..4bba508 100644 --- a/Multiprotocol/Multiprotocol.h +++ b/Multiprotocol/Multiprotocol.h @@ -19,7 +19,7 @@ #define VERSION_MAJOR 1 #define VERSION_MINOR 3 #define VERSION_REVISION 0 -#define VERSION_PATCH_LEVEL 70 +#define VERSION_PATCH_LEVEL 71 //****************** // Protocols diff --git a/Multiprotocol/NRF250K_EMU.ino b/Multiprotocol/NRF250K_EMU.ino index cb0e205..37051dc 100644 --- a/Multiprotocol/NRF250K_EMU.ino +++ b/Multiprotocol/NRF250K_EMU.ino @@ -428,9 +428,7 @@ static boolean __attribute__((unused)) NRF250K_IsPacketSent() { //NRF return NRF24L01_ReadReg(NRF24L01_07_STATUS) & _BV(NRF24L01_07_TX_DS); } - #ifdef CC2500_INSTALLED - return CC2500_ReadReg(CC2500_35_MARCSTATE)==0x01; // State is IDLE - #endif + return true; // don't know on the CC2500 how to detect if the packet has been transmitted... } #endif \ No newline at end of file diff --git a/Multiprotocol/SLT_nrf24l01.ino b/Multiprotocol/SLT_nrf24l01.ino index 5bbed53..71fdf44 100644 --- a/Multiprotocol/SLT_nrf24l01.ino +++ b/Multiprotocol/SLT_nrf24l01.ino @@ -50,7 +50,6 @@ enum { SLT_DATA3, SLT_BIND1, SLT_BIND2, - SLT_TEST }; static void __attribute__((unused)) SLT_init() @@ -183,12 +182,6 @@ static void __attribute__((unused)) SLT_send_bind_packet() SLT_send_packet(SLT_TXID_SIZE); else // SLT_BIND1 SLT_send_packet(SLT_PAYLOADSIZE_V2); - - SLT_wait_radio(); //Wait until the packet's sent before changing TX address! - - NRF250K_SetPower(); //Change power back to normal level - if(phase==SLT_BIND2) //After V1 bind and V2 second bind packet - NRF250K_SetTXAddr(rx_tx_addr, SLT_TXID_SIZE); } #define SLT_TIMING_BUILD 1000 @@ -206,6 +199,8 @@ uint16_t SLT_callback() telemetry_set_input_sync(sub_protocol==SLT_V1?20000:13730); #endif SLT_build_packet(); + NRF250K_SetPower(); //Change power level + NRF250K_SetTXAddr(rx_tx_addr, SLT_TXID_SIZE); phase++; return SLT_TIMING_BUILD; case SLT_DATA1: @@ -260,13 +255,7 @@ uint16_t SLT_callback() return 20000-SLT_TIMING_BUILD-SLT_V1_TIMING_BIND2; else //V2 return 13730-SLT_TIMING_BUILD-SLT_V2_TIMING_BIND1-SLT_V2_TIMING_BIND2; -/* case SLT_TEST: - for(uint8_t i=0;i<10;i++) - packet[i]=0x10+i; - NRF250K_WritePayload(packet,10); - NRF250K_SetFreqOffset(); // Set frequency offset - return 5000; -*/ } + } return 19000; } @@ -288,10 +277,6 @@ uint16_t initSLT() SLT_init(); phase = SLT_BUILD; -/* phase=SLT_TEST; - NRF250K_SetTXAddr((uint8_t*)"\x01\x02\x03\x04\x05",5); - NRF250K_RFChannel(0); -*/ return 50000; } From 7217e8c41d75ede793d2ca4b090238aef3211a6e Mon Sep 17 00:00:00 2001 From: Pascal Langer Date: Tue, 18 Feb 2020 15:50:54 +0100 Subject: [PATCH 08/27] SLT: fix? --- Multiprotocol/Multiprotocol.h | 2 +- Multiprotocol/NRF250K_EMU.ino | 2 ++ Multiprotocol/SLT_nrf24l01.ino | 1 - 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Multiprotocol/Multiprotocol.h b/Multiprotocol/Multiprotocol.h index 4bba508..bb40431 100644 --- a/Multiprotocol/Multiprotocol.h +++ b/Multiprotocol/Multiprotocol.h @@ -19,7 +19,7 @@ #define VERSION_MAJOR 1 #define VERSION_MINOR 3 #define VERSION_REVISION 0 -#define VERSION_PATCH_LEVEL 71 +#define VERSION_PATCH_LEVEL 72 //****************** // Protocols diff --git a/Multiprotocol/NRF250K_EMU.ino b/Multiprotocol/NRF250K_EMU.ino index 37051dc..bfd070a 100644 --- a/Multiprotocol/NRF250K_EMU.ino +++ b/Multiprotocol/NRF250K_EMU.ino @@ -361,6 +361,7 @@ static void __attribute__((unused)) NRF250K_SetTXAddr(uint8_t* addr, uint8_t len if(option==0) #endif {//NRF + NRF24L01_WriteReg(NRF24L01_03_SETUP_AW, len-2); NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, addr, len); return; } @@ -403,6 +404,7 @@ static void __attribute__((unused)) NRF250K_WritePayload(uint8_t* msg, uint8_t l crc = crc16_update(crc, buf[i], 8); buf[last++] = crc >> 8; buf[last++] = crc & 0xff; + buf[last++] = 0; //for(uint8_t i=0;i Date: Thu, 20 Feb 2020 17:37:58 +0100 Subject: [PATCH 09/27] SLT CC2500 fix --- Multiprotocol/Multiprotocol.h | 2 +- Multiprotocol/NRF250K_EMU.ino | 6 ++++-- Multiprotocol/SLT_nrf24l01.ino | 3 +-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Multiprotocol/Multiprotocol.h b/Multiprotocol/Multiprotocol.h index bb40431..94dced6 100644 --- a/Multiprotocol/Multiprotocol.h +++ b/Multiprotocol/Multiprotocol.h @@ -19,7 +19,7 @@ #define VERSION_MAJOR 1 #define VERSION_MINOR 3 #define VERSION_REVISION 0 -#define VERSION_PATCH_LEVEL 72 +#define VERSION_PATCH_LEVEL 73 //****************** // Protocols diff --git a/Multiprotocol/NRF250K_EMU.ino b/Multiprotocol/NRF250K_EMU.ino index bfd070a..daf959a 100644 --- a/Multiprotocol/NRF250K_EMU.ino +++ b/Multiprotocol/NRF250K_EMU.ino @@ -356,8 +356,6 @@ static void __attribute__((unused)) NRF250K_SetTXAddr(uint8_t* addr, uint8_t len if (len > 5) len = 5; if (len < 3) len = 3; #ifdef CC2500_INSTALLED - xn297_addr_len = len; - memcpy(xn297_tx_addr, addr, len); if(option==0) #endif {//NRF @@ -366,6 +364,10 @@ static void __attribute__((unused)) NRF250K_SetTXAddr(uint8_t* addr, uint8_t len return; } //CC2500 + #ifdef CC2500_INSTALLED + xn297_addr_len = len; + memcpy(xn297_tx_addr, addr, len); + #endif } static void __attribute__((unused)) NRF250K_WritePayload(uint8_t* msg, uint8_t len) diff --git a/Multiprotocol/SLT_nrf24l01.ino b/Multiprotocol/SLT_nrf24l01.ino index 8e8ee07..b5f62e6 100644 --- a/Multiprotocol/SLT_nrf24l01.ino +++ b/Multiprotocol/SLT_nrf24l01.ino @@ -272,10 +272,9 @@ uint16_t initSLT() /* rx_tx_addr[0]=0x01;rx_tx_addr[1]=0x02;rx_tx_addr[2]=0x0B;rx_tx_addr[3]=0x57;*/ #endif } - SLT_set_freq(); SLT_init(); + SLT_set_freq(); phase = SLT_BUILD; - return 50000; } From 62250d2f258acf0ef066ca657343e1c681753dbb Mon Sep 17 00:00:00 2001 From: Pascal Langer Date: Sat, 21 Mar 2020 15:16:01 +0100 Subject: [PATCH 10/27] ESky: addition of sub-protocol ET4 --- Multiprotocol/ESky_nrf24l01.ino | 105 ++++++++++++++++++++++---------- Multiprotocol/Multi.txt | 4 +- Multiprotocol/Multi_Names.ino | 3 +- Multiprotocol/Multiprotocol.h | 10 ++- Multiprotocol/_Config.h | 3 +- Protocols_Details.md | 8 ++- 6 files changed, 96 insertions(+), 37 deletions(-) diff --git a/Multiprotocol/ESky_nrf24l01.ino b/Multiprotocol/ESky_nrf24l01.ino index fdf7613..a7f3f5a 100644 --- a/Multiprotocol/ESky_nrf24l01.ino +++ b/Multiprotocol/ESky_nrf24l01.ino @@ -18,8 +18,13 @@ #include "iface_nrf24l01.h" +//#define ESKY_ET4_FORCE_ID + #define ESKY_BIND_COUNT 1000 -#define ESKY_PACKET_PERIOD 3333 +#define ESKY_STD_PACKET_PERIOD 3333 +#define ESKY_ET4_PACKET_PERIOD 1190 +#define ESKY_ET4_TOTAL_PACKET_PERIOD 20300 +#define ESKY_ET4_BIND_PACKET_PERIOD 5000 #define ESKY_PAYLOAD_SIZE 13 #define ESKY_PACKET_CHKTIME 100 // Time to wait for packet to be sent (no ACK, so very short) @@ -63,28 +68,37 @@ static void __attribute__((unused)) ESKY_init() static void __attribute__((unused)) ESKY_init2() { NRF24L01_FlushTx(); - hopping_frequency_no = 0; - uint16_t channel_ord = rx_tx_addr[0] % 74; - hopping_frequency[12] = 10 + (uint8_t)channel_ord; //channel_code - uint8_t channel1, channel2; - channel1 = 10 + (uint8_t)((37 + channel_ord*5) % 74); - channel2 = 10 + (uint8_t)(( channel_ord*5) % 74) ; + if(sub_protocol==ESKY_STD) + { + uint16_t channel_ord = rx_tx_addr[0] % 74; + hopping_frequency[12] = 10 + (uint8_t)channel_ord; //channel_code + uint8_t channel1, channel2; + channel1 = 10 + (uint8_t)((37 + channel_ord*5) % 74); + channel2 = 10 + (uint8_t)(( channel_ord*5) % 74) ; - hopping_frequency[0] = channel1; - hopping_frequency[1] = channel1; - hopping_frequency[2] = channel1; - hopping_frequency[3] = channel2; - hopping_frequency[4] = channel2; - hopping_frequency[5] = channel2; - - //end_bytes - hopping_frequency[6] = 6; - hopping_frequency[7] = channel1*2; - hopping_frequency[8] = channel2*2; - hopping_frequency[9] = 6; - hopping_frequency[10] = channel1*2; - hopping_frequency[11] = channel2*2; + hopping_frequency[0] = channel1; + hopping_frequency[1] = channel1; + hopping_frequency[2] = channel1; + hopping_frequency[3] = channel2; + hopping_frequency[4] = channel2; + hopping_frequency[5] = channel2; + //end_bytes + hopping_frequency[6] = 6; + hopping_frequency[7] = channel1*2; + hopping_frequency[8] = channel2*2; + hopping_frequency[9] = 6; + hopping_frequency[10] = channel1*2; + hopping_frequency[11] = channel2*2; + } + else + { // ESKY_ET4 + hopping_frequency[0] = 0x29; //41 + hopping_frequency[1] = 0x12; //18 + hopping_frequency[6] = 0x87; //135 payload end byte + hopping_frequency[12] = 0x84; //132 indicates which channels to use + } + // Turn radio power on NRF24L01_SetTxRxMode(TX_EN); } @@ -111,20 +125,32 @@ static void __attribute__((unused)) ESKY_send_packet(uint8_t bind) } else { - // Regular packet - // Each data packet is repeated 3 times on one channel, and 3 times on another channel - // For arithmetic simplicity, channels are repeated in rf_channels array - if (hopping_frequency_no == 0) + if (packet_count == 0) for (uint8_t i = 0; i < 6; i++) { uint16_t val=convert_channel_ppm(CH_AETR[i]); packet[i*2] = val>>8; //high byte of servo timing(1000-2000us) packet[i*2+1] = val&0xFF; //low byte of servo timing(1000-2000us) } - rf_ch = hopping_frequency[hopping_frequency_no]; - packet[12] = hopping_frequency[hopping_frequency_no+6]; // end_bytes - hopping_frequency_no++; - if (hopping_frequency_no > 6) hopping_frequency_no = 0; + if(sub_protocol==ESKY_STD) + { + // Regular packet + // Each data packet is repeated 3 times on one channel, and 3 times on another channel + // For arithmetic simplicity, channels are repeated in rf_channels array + rf_ch = hopping_frequency[packet_count]; + packet[12] = hopping_frequency[packet_count+6]; // end_bytes + packet_count++; + if (packet_count > 6) packet_count = 0; + } + else + { // ESKY_ET4 + // Regular packet + // Each data packet is repeated 14 times alternating between 2 channels + rf_ch = hopping_frequency[packet_count&1]; + packet_count++; + if(packet_count>14) packet_count=0; + packet[12] = hopping_frequency[6]; // end_byte + } } NRF24L01_WriteReg(NRF24L01_05_RF_CH, rf_ch); NRF24L01_FlushTx(); @@ -137,9 +163,17 @@ uint16_t ESKY_callback() if(IS_BIND_DONE) { #ifdef MULTI_SYNC - telemetry_set_input_sync(ESKY_PACKET_PERIOD); + if(packet_count==0) + telemetry_set_input_sync(sub_protocol==ESKY_STD?ESKY_STD_PACKET_PERIOD*6:ESKY_ET4_TOTAL_PACKET_PERIOD); #endif ESKY_send_packet(0); + if(sub_protocol==ESKY_ET4) + { + if(packet_count==0) + return ESKY_ET4_TOTAL_PACKET_PERIOD-ESKY_ET4_PACKET_PERIOD*13; + else + return ESKY_ET4_PACKET_PERIOD; + } } else { @@ -150,16 +184,25 @@ uint16_t ESKY_callback() BIND_DONE; } } - return ESKY_PACKET_PERIOD; + return ESKY_STD_PACKET_PERIOD; } uint16_t initESKY(void) { bind_counter = ESKY_BIND_COUNT; rx_tx_addr[2] = rx_tx_addr[3]; // Model match + #ifdef ESKY_ET4_FORCE_ID + if(sub_protocol==ESKY_ET4) + { + rx_tx_addr[0]=0x72; + rx_tx_addr[1]=0xBB; + rx_tx_addr[2]=0xCC; + } + #endif rx_tx_addr[3] = 0xBB; ESKY_init(); ESKY_init2(); + packet_count=0; return 50000; } diff --git a/Multiprotocol/Multi.txt b/Multiprotocol/Multi.txt index dd9666c..6d80d43 100644 --- a/Multiprotocol/Multi.txt +++ b/Multiprotocol/Multi.txt @@ -13,7 +13,7 @@ 13,CG023,CG023,YD829 14,Bayang,Bayang,H8S3D,X16_AH,IRDRONE,DHD_D4 15,FrskyX,CH_16,CH_8,EU_16,EU_8 -16,ESky,4CH,7CH +16,ESky,Std,ET4 17,MT99xx,MT,H7,YZ,LS,FY805 18,MJXq,WLH08,X600,X800,H26D,E010,H26WH,PHOENIX 19,Shenqi @@ -32,7 +32,7 @@ 32,GW008 33,DM002 34,CABELL,CAB_V3,C_TELEM,-,-,-,-,F_SAFE,UNBIND -35,ESKY150 +35,ESKY150,4CH,7CH 36,H8_3D,H8_3D,H20H,H20Mini,H30Mini 37,CORONA,COR_V1,COR_V2,FD_V3 38,CFlie diff --git a/Multiprotocol/Multi_Names.ino b/Multiprotocol/Multi_Names.ino index 2844e9d..a468ffe 100644 --- a/Multiprotocol/Multi_Names.ino +++ b/Multiprotocol/Multi_Names.ino @@ -120,6 +120,7 @@ const char STR_SUBTYPE_ESKY150[] = "\x03""4CH""7CH"; const char STR_SUBTYPE_V911S[] = "\x05""V911S""E119\0"; const char STR_SUBTYPE_XK[] = "\x04""X450""X420"; const char STR_SUBTYPE_FRSKYR9[] = "\x07""915MHz\0""868MHz\0""915 8ch""868 8ch"; +const char STR_SUBTYPE_ESKY[] = "\x03""Std""ET4"; enum { @@ -187,7 +188,7 @@ const mm_protocol_definition multi_protocols[] = { {PROTO_FRSKYX2, STR_FRSKYX2, 4, STR_SUBTYPE_FRSKYX, OPTION_RFTUNE }, #endif #if defined(ESKY_NRF24L01_INO) - {PROTO_ESKY, STR_ESKY, 0, NO_SUBTYPE, OPTION_NONE }, + {PROTO_ESKY, STR_ESKY, 2, STR_SUBTYPE_ESKY, OPTION_NONE }, #endif #if defined(MT99XX_NRF24L01_INO) {PROTO_MT99XX, STR_MT99XX, 5, STR_SUBTYPE_MT99, OPTION_NONE }, diff --git a/Multiprotocol/Multiprotocol.h b/Multiprotocol/Multiprotocol.h index 94dced6..cab1554 100644 --- a/Multiprotocol/Multiprotocol.h +++ b/Multiprotocol/Multiprotocol.h @@ -19,7 +19,7 @@ #define VERSION_MAJOR 1 #define VERSION_MINOR 3 #define VERSION_REVISION 0 -#define VERSION_PATCH_LEVEL 73 +#define VERSION_PATCH_LEVEL 74 //****************** // Protocols @@ -334,6 +334,11 @@ enum FRSKY_R9 R9_915_8CH = 2, R9_868_8CH = 3, }; +enum ESKY +{ + ESKY_STD = 0, + ESKY_ET4 = 1, +}; #define NONE 0 #define P_HIGH 1 @@ -884,6 +889,9 @@ Serial: 100000 Baud 8e2 _ xxxx xxxx p -- R9_868 1 R9_915_8CH 2 R9_868_8CH 3 + sub_protocol==ESKY + ESKY_STD 0 + ESKY_ET4 1 Power value => 0x80 0=High/1=Low Stream[3] = option_protocol; diff --git a/Multiprotocol/_Config.h b/Multiprotocol/_Config.h index 5b4a89a..9889e6f 100644 --- a/Multiprotocol/_Config.h +++ b/Multiprotocol/_Config.h @@ -548,7 +548,8 @@ const PPM_Parameters PPM_prot[14*NBR_BANKS]= { E015 E016H PROTO_ESKY - NONE + ESKY_STD + ESKY_ET4 PROTO_ESKY150 ESKY150_4CH ESKY150_7CH diff --git a/Protocols_Details.md b/Protocols_Details.md index 1b0c981..2e7a36e 100644 --- a/Protocols_Details.md +++ b/Protocols_Details.md @@ -80,7 +80,7 @@ CFlie|38|CFlie||||||||NRF24L01| [DM002](Protocols_Details.md#DM002---33)|33|DM002||||||||NRF24L01|XN297 [DSM](Protocols_Details.md#DSM---6)|6|DSM2-22|DSM2-11|DSMX-22|DSMX-11|AUTO||||CYRF6936| [E01X](Protocols_Details.md#E01X---45)|45|E012|E015|E016H||||||NRF24L01|XN297/HS6200 -[ESky](Protocols_Details.md#ESKY---16)|16|ESky||||||||NRF24L01| +[ESky](Protocols_Details.md#ESKY---16)|16|ESky|Std|ET4||||||NRF24L01| [ESky150](Protocols_Details.md#ESKY150---35)|35|ESKY150||||||||NRF24L01| [Flysky](Protocols_Details.md#FLYSKY---1)|1|Flysky|V9x9|V6x6|V912|CX20||||A7105| [Flysky AFHDS2A](Protocols_Details.md#FLYSKY-AFHDS2A---28)|28|PWM_IBUS|PPM_IBUS|PWM_SBUS|PPM_SBUS|||||A7105| @@ -851,6 +851,12 @@ CH1|CH2|CH3|CH4|CH5|CH6 ---|---|---|---|---|--- A|E|T|R|GYRO|PITCH +### Sub_protocol Std - *0* + +### Sub_protocol ET4 - *1* +Models compatible with the ET4 transmitter like ESky Big Lama +**Multiple IDs but only one frequency...** + ## ESKY150 - *35* ESky protocol for small models since 2014 (150, 300, 150X, ...) From 2b8ed25843f1d52b8bc19e3dbaceea170c1a8917 Mon Sep 17 00:00:00 2001 From: Pascal Langer Date: Sat, 21 Mar 2020 15:17:46 +0100 Subject: [PATCH 11/27] FrSkyD: Change hopping frequencies WARNING: all receivers must be rebound !!! --- Multiprotocol/FrSkyD_cc2500.ino | 13 ++++++++++++- Multiprotocol/Multiprotocol.h | 2 +- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/Multiprotocol/FrSkyD_cc2500.ino b/Multiprotocol/FrSkyD_cc2500.ino index 49a5aad..8de5296 100644 --- a/Multiprotocol/FrSkyD_cc2500.ino +++ b/Multiprotocol/FrSkyD_cc2500.ino @@ -21,6 +21,7 @@ static void __attribute__((unused)) frsky2way_init(uint8_t bind) { FRSKY_init_cc2500(FRSKYD_cc2500_conf); + CC2500_WriteReg(CC2500_1B_AGCCTRL2, bind ? 0x43 : 0x03); CC2500_WriteReg(CC2500_09_ADDR, bind ? 0x03 : rx_tx_addr[3]); CC2500_WriteReg(CC2500_07_PKTCTRL1, 0x05); CC2500_Strobe(CC2500_SIDLE); // Go to idle... @@ -95,7 +96,17 @@ static void __attribute__((unused)) frsky2way_data_frame() uint16_t initFrSky_2way() { - Frsky_init_hop(); + //FrskyD init hop + for(uint8_t i=0;i<50;i++) + { + uint8_t freq = (i * 0x1e) % 0xeb; + if(i == 3 || i == 23 || i == 47) + freq++; + if(i > 47) + freq=0; + hopping_frequency[i]=freq; + } + packet_count=0; if(IS_BIND_IN_PROGRESS) { diff --git a/Multiprotocol/Multiprotocol.h b/Multiprotocol/Multiprotocol.h index cab1554..1e5f789 100644 --- a/Multiprotocol/Multiprotocol.h +++ b/Multiprotocol/Multiprotocol.h @@ -19,7 +19,7 @@ #define VERSION_MAJOR 1 #define VERSION_MINOR 3 #define VERSION_REVISION 0 -#define VERSION_PATCH_LEVEL 74 +#define VERSION_PATCH_LEVEL 75 //****************** // Protocols From 3df836e6b857ffb402b7b93c9354385fb24aaadd Mon Sep 17 00:00:00 2001 From: Pascal Langer Date: Sat, 21 Mar 2020 19:00:40 +0100 Subject: [PATCH 12/27] Hitec: fix a bind issue --- Multiprotocol/Hitec_cc2500.ino | 9 +++++++-- Multiprotocol/Multiprotocol.h | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Multiprotocol/Hitec_cc2500.ino b/Multiprotocol/Hitec_cc2500.ino index f380074..0f95c77 100644 --- a/Multiprotocol/Hitec_cc2500.ino +++ b/Multiprotocol/Hitec_cc2500.ino @@ -146,6 +146,11 @@ static void __attribute__((unused)) HITEC_build_packet() break; case 0x7B: packet[5]=hopping_frequency[13]>>1; // if not there the Optima link is jerky... + packet[14]=0x2A; + packet[15]=0x46; // unknown but if 0x45 then 17=0x46, if 0x46 then 17=0x46 or 0x47, if 0x47 then 0x45 or 0x46 + packet[16]=0x2A; + packet[17]=0x47; + packet[18]=0x2A; break; } if(sub_protocol==MINIMA) @@ -302,8 +307,8 @@ uint16_t ReadHITEC() { // bind packet: 0A,00,E5,F2,7X,05,06,07,08,09,00 debug(",bind"); boolean check=true; - for(uint8_t i=5;i<=10;i++) - if(packet_in[i]!=i%10) check=false; + for(uint8_t i=5;i<10;i++) + if(packet_in[i]!=i) check=false; if((packet_in[4]&0xF0)==0x70 && check) { bind_phase=packet_in[4]+1; diff --git a/Multiprotocol/Multiprotocol.h b/Multiprotocol/Multiprotocol.h index 1e5f789..a56dae4 100644 --- a/Multiprotocol/Multiprotocol.h +++ b/Multiprotocol/Multiprotocol.h @@ -19,7 +19,7 @@ #define VERSION_MAJOR 1 #define VERSION_MINOR 3 #define VERSION_REVISION 0 -#define VERSION_PATCH_LEVEL 75 +#define VERSION_PATCH_LEVEL 76 //****************** // Protocols From dfd3386319392a720e9709840f4425573f4bbc26 Mon Sep 17 00:00:00 2001 From: Pascal Langer Date: Sun, 29 Mar 2020 18:44:03 +0200 Subject: [PATCH 13/27] FrSkyX v2.1: initial support Rewrite of the FrSkyX code to support both v1 and v2.1.0 with FCC and LBT. FrSky v1 accessible as usual FrSky v2.1.0 accessible through the protocol 64=FrSkyX2 with the same subprotocols as v1 The LBT feature is now fully implemented on the TX and turned on for both v1 LBT and v2.1.0 LBT. For v2.1.0, to access the bind functions Telem=on/off, CH1-8/9-16 and bidirectional SPort (SxR setup for example), you need to update OpenTX to the latest 2.3.8 nightly (not available yet). --- Multiprotocol/FrSkyDVX_common.ino | 66 ++++++-- Multiprotocol/FrSkyX_cc2500.ino | 257 ++++++++++++++++++------------ Multiprotocol/Multi_Names.ino | 2 - Multiprotocol/Multiprotocol.h | 11 +- Multiprotocol/Multiprotocol.ino | 25 +-- Multiprotocol/Telemetry.ino | 6 +- Multiprotocol/Validate.h | 8 +- Multiprotocol/_Config.h | 14 +- 8 files changed, 225 insertions(+), 164 deletions(-) diff --git a/Multiprotocol/FrSkyDVX_common.ino b/Multiprotocol/FrSkyDVX_common.ino index 7bfc346..ec9fbf8 100644 --- a/Multiprotocol/FrSkyDVX_common.ino +++ b/Multiprotocol/FrSkyDVX_common.ino @@ -70,6 +70,41 @@ void Frsky_init_hop(void) hopping_frequency[i]=i>46?0:val; } } + +void FrSkyX2_init_hop(void) +{ + uint16_t id=(rx_tx_addr[2]<<8) + rx_tx_addr[3]; + //Increment + uint8_t inc = (id % 46) + 1; + if( inc == 12 || inc ==35 ) inc++; //Exception list from dumps + //Start offset + uint8_t offset = id % 5; + + debug("hop: "); + uint8_t channel; + for(uint8_t i=0; i<47; i++) + { + channel = 5 * (uint16_t(inc * i) % 47) + offset; + //Exception list from dumps + if(sub_protocol & 2 )// LBT or FCC + { + //LBT + if( channel <=1 || channel == 43 || channel == 44 || channel == 87 || channel == 88 || channel == 129 || channel == 130 || channel == 173 || channel == 174) + channel += 2; + else if( channel == 216 || channel == 217 || channel == 218) + channel += 3; + } + else // FCC + if ( channel == 3 || channel == 4 || channel == 46 || channel == 47 || channel == 90 || channel == 91 || channel == 133 || channel == 134 || channel == 176 || channel == 177 || channel == 220 || channel == 221 ) + channel += 2; + //Store + hopping_frequency[i] = channel; + debug(" %02X",channel); + } + debugln(""); + hopping_frequency[47] = 0; //Bind freq +} + #endif /******************************/ /** FrSky V, D and X routines **/ @@ -142,43 +177,43 @@ void Frsky_init_hop(void) /*15_DEVIATN*/ 0x42 }; #endif - #if defined(FRSKYX_CC2500_INO) || defined(FRSKYX2_CC2500_INO) + #if defined(FRSKYX_CC2500_INO) const PROGMEM uint8_t FRSKYX_cc2500_conf[]= { //FRSKYX - /*02_IOCFG0*/ 0x06 , + /*02_IOCFG0*/ 0x06 , /*00_IOCFG2*/ 0x06 , - /*17_MCSM1*/ 0x0c , + /*17_MCSM1*/ 0x0c , //X2->0x0E -> Go/Stay in RX mode /*18_MCSM0*/ 0x18 , /*06_PKTLEN*/ 0x1E , /*07_PKTCTRL1*/ 0x04 , - /*08_PKTCTRL0*/ 0x01 , + /*08_PKTCTRL0*/ 0x01 , //X2->0x05 -> CRC enabled /*3E_PATABLE*/ 0xff , /*0B_FSCTRL1*/ 0x0A , /*0C_FSCTRL0*/ 0x00 , /*0D_FREQ2*/ 0x5c , /*0E_FREQ1*/ 0x76 , /*0F_FREQ0*/ 0x27 , - /*10_MDMCFG4*/ 0x7B , - /*11_MDMCFG3*/ 0x61 , + /*10_MDMCFG4*/ 0x7B , + /*11_MDMCFG3*/ 0x61 , //X2->0x84 -> bitrate 70K->77K /*12_MDMCFG2*/ 0x13 , /*13_MDMCFG1*/ 0x23 , /*14_MDMCFG0*/ 0x7a , /*15_DEVIATN*/ 0x51 }; const PROGMEM uint8_t FRSKYXEU_cc2500_conf[]= { - /*02_IOCFG0*/ 0x06 , + /*02_IOCFG0*/ 0x06 , /*00_IOCFG2*/ 0x06 , /*17_MCSM1*/ 0x0E , /*18_MCSM0*/ 0x18 , /*06_PKTLEN*/ 0x23 , /*07_PKTCTRL1*/ 0x04 , - /*08_PKTCTRL0*/ 0x01 , + /*08_PKTCTRL0*/ 0x01 , //X2->0x05 -> CRC enabled /*3E_PATABLE*/ 0xff , /*0B_FSCTRL1*/ 0x08 , /*0C_FSCTRL0*/ 0x00 , - /*0D_FREQ2*/ 0x5c , + /*0D_FREQ2*/ 0x5c , /*0E_FREQ1*/ 0x80 , /*0F_FREQ0*/ 0x00 , - /*10_MDMCFG4*/ 0x7B , + /*10_MDMCFG4*/ 0x7B , /*11_MDMCFG3*/ 0xF8 , /*12_MDMCFG2*/ 0x03 , /*13_MDMCFG1*/ 0x23 , @@ -222,8 +257,6 @@ void Frsky_init_hop(void) uint8_t val=pgm_read_byte_near(&FRSKY_common_end_cc2500_conf[i][1]); CC2500_WriteReg(reg,val); } - if(protocol==PROTO_FRSKYX2) - CC2500_WriteReg(CC2500_08_PKTCTRL0, 0x05); // enable CRC CC2500_SetTxRxMode(TX_EN); CC2500_SetPower(); CC2500_Strobe(CC2500_SIDLE); // Go to idle... @@ -257,6 +290,15 @@ static void __attribute__((unused)) FrSkyX_set_start(uint8_t ch ) static void __attribute__((unused)) FrSkyX_init() { FRSKY_init_cc2500((sub_protocol&2)?FRSKYXEU_cc2500_conf:FRSKYX_cc2500_conf); // LBT or FCC + if(protocol==PROTO_FRSKYX2) + { + CC2500_WriteReg(CC2500_08_PKTCTRL0, 0x05); // Enable CRC + if(!(sub_protocol&2)) + { // FCC + CC2500_WriteReg(CC2500_17_MCSM1, 0x0E); // Go/Stay in RX mode + CC2500_WriteReg(CC2500_11_MDMCFG3, 0x84); // bitrate 70K->77K + } + } // for(uint8_t c=0;c < 48;c++) {//calibrate hop channels diff --git a/Multiprotocol/FrSkyX_cc2500.ino b/Multiprotocol/FrSkyX_cc2500.ino index 0e20dd1..d204045 100644 --- a/Multiprotocol/FrSkyX_cc2500.ino +++ b/Multiprotocol/FrSkyX_cc2500.ino @@ -21,34 +21,65 @@ static void __attribute__((unused)) FrSkyX_build_bind_packet() { - packet[0] = (sub_protocol & 2 ) ? 0x20 : 0x1D ; // LBT or FCC - packet[1] = 0x03; - packet[2] = 0x01; - // - packet[3] = rx_tx_addr[3]; - packet[4] = rx_tx_addr[2]; - int idx = ((state -FRSKY_BIND) % 10) * 5; - packet[5] = idx; - packet[6] = hopping_frequency[idx++]; - packet[7] = hopping_frequency[idx++]; - packet[8] = hopping_frequency[idx++]; - packet[9] = hopping_frequency[idx++]; - packet[10] = hopping_frequency[idx++]; - packet[11] = 0x02; - packet[12] = RX_num; - // - uint8_t limit = (sub_protocol & 2 ) ? 31 : 28 ; - memset(&packet[13], 0, limit - 13); - if(binding_idx&0x01) - memcpy(&packet[13],(void *)"\x55\xAA\x5A\xA5",4); // Telem off - if(binding_idx&0x02) - memcpy(&packet[17],(void *)"\x55\xAA\x5A\xA5",4); // CH9-16 - // - uint16_t lcrc = FrSkyX_crc(&packet[3], limit-3); - // - packet[limit++] = lcrc >> 8; - packet[limit] = lcrc; - // + uint8_t packet_size = 0x1D; + if(protocol==PROTO_FRSKYX && (sub_protocol & 2 )) + packet_size=0x20; // FrSkyX V1 LBT + //Header + packet[0] = packet_size; // Number of bytes in the packet (after this one) + packet[1] = 0x03; // Bind packet + packet[2] = 0x01; // Bind packet + + //ID + packet[3] = rx_tx_addr[3]; // ID + packet[4] = rx_tx_addr[2]; // ID + + if(protocol==PROTO_FRSKYX) + { + int idx = ((state -FRSKY_BIND) % 10) * 5; + packet[5] = idx; + packet[6] = hopping_frequency[idx++]; + packet[7] = hopping_frequency[idx++]; + packet[8] = hopping_frequency[idx++]; + packet[9] = hopping_frequency[idx++]; + packet[10] = hopping_frequency[idx++]; + packet[11] = 0x02; // Unknown but constant ID? + packet[12] = RX_num; + // + memset(&packet[13], 0, packet_size - 14); + if(binding_idx&0x01) + memcpy(&packet[13],(void *)"\x55\xAA\x5A\xA5",4); // Telem off + if(binding_idx&0x02) + memcpy(&packet[17],(void *)"\x55\xAA\x5A\xA5",4); // CH9-16 + } + else + { + //packet 1D 03 01 0E 1C 02 00 00 32 0B 00 00 A8 26 28 01 A1 00 00 00 3E F6 87 C7 00 00 00 00 C9 C9 + packet[5] = 0x02; // Unknown but constant ID? + packet[6] = RX_num; + //Bind flags + packet[7]=0; + if(binding_idx&0x01) + packet[7] |= 0x40; // Telem off + if(binding_idx&0x02) + packet[7] |= 0x80; // CH9-16 + //Unknown bytes + memcpy(&packet[8],"\x32\x0B\x00\x00\xA8\x26\x28\x01\xA1\x00\x00\x00\x3E\xF6\x87\xC7",16); + packet[20]^= 0x0E ^ rx_tx_addr[3]; // Update the ID + packet[21]^= 0x1C ^ rx_tx_addr[2]; // Update the ID + //Xor + for(uint8_t i=3; i> 8; + packet[packet_size] = lcrc; + + /*//Debug + debug("Bind:"); + for(uint8_t i=0;i<=packet_size;i++) + debug(" %02X",packet[i]); + debugln("");*/ } #define FrSkyX_FAILSAFE_TIME 1032 @@ -81,10 +112,14 @@ static void __attribute__((unused)) FrSkyX_build_packet() failsafe_count++; #endif - packet[0] = (sub_protocol & 0x02 ) ? 0x20 : 0x1D ; // LBT or FCC - packet[1] = rx_tx_addr[3]; - packet[2] = rx_tx_addr[2]; - packet[3] = 0x02; + uint8_t packet_size = 0x1D; + if(protocol==PROTO_FRSKYX && (sub_protocol & 2 )) + packet_size=0x20; // FrSkyX V1 LBT + //Header + packet[0] = packet_size; // Number of bytes in the packet (after this one) + packet[1] = rx_tx_addr[3]; // ID + packet[2] = rx_tx_addr[2]; // ID + packet[3] = 0x02; // Unknown but constant ID? // packet[4] = (FrSkyX_chanskip<<6)|hopping_frequency_no; packet[5] = FrSkyX_chanskip>>2; @@ -128,8 +163,7 @@ static void __attribute__((unused)) FrSkyX_build_packet() chan_offset^=0x08; //sequence and send SPort - uint8_t limit = (sub_protocol & 2 ) ? 31 : 28 ; - for (uint8_t i=22;i>8;//high byte - packet[limit]=lcrc;//low byte + //CRC + uint16_t lcrc = FrSkyX_crc(&packet[3], packet_size-4); + packet[packet_size-1] = lcrc >> 8; + packet[packet_size] = lcrc; + + /*//Debug + debug("Norm:"); + for(uint8_t i=0;i<=packet_size;i++) + debug(" %02X",packet[i]); + debugln("");*/ } uint16_t ReadFrSkyX() { - static bool transmit=true; #ifdef DEBUG_SERIAL static uint16_t fr_time=0; #endif @@ -241,7 +281,61 @@ uint16_t ReadFrSkyX() BIND_DONE; state++; //FRSKY_DATA1 break; - case FRSKY_DATA5: + + case FRSKY_DATA1: + CC2500_Strobe(CC2500_SIDLE); + if ( prev_option != option ) + { + CC2500_WriteReg(CC2500_0C_FSCTRL0,option); //Frequency offset hack + prev_option = option ; + } + FrSkyX_set_start(hopping_frequency_no); + FrSkyX_build_packet(); + if(sub_protocol & 2) + {// LBT + CC2500_Strobe(CC2500_SRX); //Acquire RSSI + state++; + return 400; // LBT v2.1 + } + case FRSKY_DATA2: + if(sub_protocol & 2) + { + uint16_t rssi=0; + for(uint8_t i=0;i<4;i++) + rssi += CC2500_ReadReg(CC2500_34_RSSI | CC2500_READ_BURST); // 0.5 db/count, RSSI value read from the RSSI status register is a 2's complement number + rssi>>=2; + #if 0 + uint8_t rssi_level=convert_channel_8b(CH16)>>1; //CH16 0..127 + if ( rssi > rssi_level && rssi < 128) //test rssi level dynamically + #else + if ( rssi > 72 && rssi < 128) //LBT and RSSI between -36 and -8.5 dBm + #endif + { + POWER_FLAG_off; // Reduce to low power before transmitting + debugln("Busy %d %d",hopping_frequency_no,rssi); + } + } + CC2500_Strobe(CC2500_SIDLE); + CC2500_Strobe(CC2500_SFTX); + CC2500_SetTxRxMode(TX_EN); + CC2500_SetPower(); + hopping_frequency_no = (hopping_frequency_no+FrSkyX_chanskip)%47; + CC2500_WriteData(packet, packet[0]+1); + state=FRSKY_DATA3; + if(sub_protocol & 2) + return 4000; // LBT v2.1 + else + return 5200; // FCC v2.1 + case FRSKY_DATA3: + CC2500_Strobe(CC2500_SIDLE); + CC2500_SetTxRxMode(RX_EN); + CC2500_Strobe(CC2500_SRX); + state++; + if(sub_protocol & 2) + return 4100; // LBT v2.1 + else + return 3300; // FCC v2.1 + case FRSKY_DATA4: #ifdef MULTI_SYNC telemetry_set_input_sync(9000); #endif @@ -251,11 +345,19 @@ uint16_t ReadFrSkyX() len = CC2500_ReadReg(CC2500_3B_RXBYTES | CC2500_READ_BURST) & 0x7F; if (len && (len<=(0x0E + 3))) //Telemetry frame is 17 { + //debug("Telem:"); packet_count=0; CC2500_ReadData(packet_in, len); #if defined TELEMETRY - frsky_check_telemetry(packet_in,len); //Check and parse telemetry packets + if(protocol==PROTO_FRSKYX || (protocol==PROTO_FRSKYX2 && (packet_in[len-1] & 0x80)) ) + {//with valid crc for FRSKYX2 + //Debug + //for(uint8_t i=0;i 72 && rssi < 128) //LBT and RSSI between -36 and -8.5 dBm - { - transmit=false; - debugln("Busy %d %d",hopping_frequency_no,rssi); - } -#endif - CC2500_Strobe(CC2500_SIDLE); - CC2500_Strobe(CC2500_SFRX); - CC2500_SetTxRxMode(TX_EN); - CC2500_SetPower(); - hopping_frequency_no = (hopping_frequency_no+FrSkyX_chanskip)%47; - if(transmit) - { - #ifdef DEBUG_SERIAL - uint16_t fr_cur=millis(); - fr_time=fr_cur-fr_time; - if(fr_time!=9) - debugln("Bad timing: %d",fr_time); - fr_time=fr_cur; - #endif - CC2500_WriteData(packet, packet[0]+1); - } - state=FRSKY_DATA3; - return 5200; - case FRSKY_DATA3: - CC2500_SetTxRxMode(RX_EN); - CC2500_Strobe(CC2500_SIDLE); - state++; - return 200; - case FRSKY_DATA4: - CC2500_Strobe(CC2500_SRX); - state++; - return 3100; + return 500; // FCC & LBT v2.1 } return 1; } @@ -340,15 +389,23 @@ uint16_t ReadFrSkyX() uint16_t initFrSkyX() { set_rx_tx_addr(MProtocol_id_master); - Frsky_init_hop(); + + if(protocol==PROTO_FRSKYX) + Frsky_init_hop(); + else + { + #ifdef FRSKYX2_FORCE_ID + rx_tx_addr[3]=0x0E; + rx_tx_addr[2]=0x1C; + FrSkyX_chanskip=18; + #endif + FrSkyX2_init_hop(); + } + packet_count=0; while(!FrSkyX_chanskip) FrSkyX_chanskip=random(0xfefefefe)%47; - //for test*************** - //rx_tx_addr[3]=0xB3; - //rx_tx_addr[2]=0xFD; - //************************ FrSkyX_init(); if(IS_BIND_IN_PROGRESS) diff --git a/Multiprotocol/Multi_Names.ino b/Multiprotocol/Multi_Names.ino index a468ffe..0819026 100644 --- a/Multiprotocol/Multi_Names.ino +++ b/Multiprotocol/Multi_Names.ino @@ -183,8 +183,6 @@ const mm_protocol_definition multi_protocols[] = { #endif #if defined(FRSKYX_CC2500_INO) {PROTO_FRSKYX, STR_FRSKYX, 4, STR_SUBTYPE_FRSKYX, OPTION_RFTUNE }, -#endif -#if defined(FRSKYX2_CC2500_INO) {PROTO_FRSKYX2, STR_FRSKYX2, 4, STR_SUBTYPE_FRSKYX, OPTION_RFTUNE }, #endif #if defined(ESKY_NRF24L01_INO) diff --git a/Multiprotocol/Multiprotocol.h b/Multiprotocol/Multiprotocol.h index a56dae4..1d41fb1 100644 --- a/Multiprotocol/Multiprotocol.h +++ b/Multiprotocol/Multiprotocol.h @@ -19,7 +19,7 @@ #define VERSION_MAJOR 1 #define VERSION_MINOR 3 #define VERSION_REVISION 0 -#define VERSION_PATCH_LEVEL 76 +#define VERSION_PATCH_LEVEL 79 //****************** // Protocols @@ -214,13 +214,6 @@ enum FRSKYX EU_16 = 2, EU_8 = 3, }; -enum FRSKYX2 -{ - FRSKYX2_CH_16 = 0, - FRSKYX2_CH_8 = 1, - FRSKYX2_EU_16 = 2, - FRSKYX2_EU_8 = 3, -}; enum HONTAI { HONTAI = 0, @@ -382,7 +375,7 @@ enum MultiPacketTypes //*************** //*** Tests *** //*************** -#define IS_FAILSAFE_PROTOCOL ( (protocol==PROTO_HISKY && sub_protocol==HK310) || protocol==PROTO_AFHDS2A || protocol==PROTO_DEVO || protocol==PROTO_SFHSS || protocol==PROTO_WK2x01 || protocol== PROTO_HOTT || protocol==PROTO_FRSKYX ) +#define IS_FAILSAFE_PROTOCOL ( (protocol==PROTO_HISKY && sub_protocol==HK310) || protocol==PROTO_AFHDS2A || protocol==PROTO_DEVO || protocol==PROTO_SFHSS || protocol==PROTO_WK2x01 || protocol== PROTO_HOTT || protocol==PROTO_FRSKYX || protocol==PROTO_FRSKYX2 ) #define IS_CHMAP_PROTOCOL ( (protocol==PROTO_HISKY && sub_protocol==HK310) || protocol==PROTO_AFHDS2A || protocol==PROTO_DEVO || protocol==PROTO_SFHSS || protocol==PROTO_WK2x01 || protocol== PROTO_DSM || protocol==PROTO_SLT || protocol==PROTO_FLYSKY || protocol==PROTO_ESKY || protocol==PROTO_J6PRO || protocol==PROTO_PELIKAN ) //*************** diff --git a/Multiprotocol/Multiprotocol.ino b/Multiprotocol/Multiprotocol.ino index 233916f..18bc127 100644 --- a/Multiprotocol/Multiprotocol.ino +++ b/Multiprotocol/Multiprotocol.ino @@ -514,11 +514,6 @@ void setup() option = FORCE_FRSKYX_TUNING; // Use config-defined tuning value for FrSkyX else #endif - #if defined(FORCE_FRSKYX2_TUNING) && defined(FRSKYX2_CC2500_INO) - if(protocol==PROTO_FRSKYX2) - option = FORCE_FRSKYX2_TUNING; // Use config-defined tuning value for FrSkyX2 - else - #endif #if defined(FORCE_SFHSS_TUNING) && defined(SFHSS_CC2500_INO) if (protocol==PROTO_SFHSS) option = FORCE_SFHSS_TUNING; // Use config-defined tuning value for SFHSS @@ -759,7 +754,7 @@ bool Update_All() BIND_CH_PREV_off; //Request protocol to terminate bind #if defined(FRSKYD_CC2500_INO) || defined(FRSKYX_CC2500_INO) || defined(FRSKYV_CC2500_INO) || defined(AFHDS2A_A7105_INO) - if(protocol==PROTO_FRSKYD || protocol==PROTO_FRSKYX || protocol==PROTO_FRSKYV || protocol==PROTO_AFHDS2A ) + if(protocol==PROTO_FRSKYD || protocol==PROTO_FRSKYX || protocol==PROTO_FRSKYX2 || protocol==PROTO_FRSKYV || protocol==PROTO_AFHDS2A ) BIND_DONE; else #endif @@ -1127,20 +1122,13 @@ static void protocol_init() #endif #if defined(FRSKYX_CC2500_INO) case PROTO_FRSKYX: + case PROTO_FRSKYX2: PE1_off; //antenna RF2 PE2_on; next_callback = initFrSkyX(); remote_callback = ReadFrSkyX; break; #endif - #if defined(FRSKYX2_CC2500_INO) - case PROTO_FRSKYX2: - PE1_off; //antenna RF2 - PE2_on; - next_callback = initFrSkyX2(); - remote_callback = ReadFrSkyX2; - break; - #endif #if defined(SFHSS_CC2500_INO) case PROTO_SFHSS: PE1_off; //antenna RF2 @@ -1624,11 +1612,6 @@ void update_serial_data() option=FORCE_FRSKYX_TUNING; // Use config-defined tuning value for FrSkyX else #endif - #if defined(FORCE_FRSKYX2_TUNING) && defined(FRSKYX2_CC2500_INO) - if(protocol==PROTO_FRSKYX2) - option=FORCE_FRSKYX2_TUNING; // Use config-defined tuning value for FrSkyX2 - else - #endif #if defined(FORCE_SFHSS_TUNING) && defined(SFHSS_CC2500_INO) if (protocol==PROTO_SFHSS) option=FORCE_SFHSS_TUNING; // Use config-defined tuning value for SFHSS @@ -1732,7 +1715,7 @@ void update_serial_data() if( ((rx_ok_buff[1]&0x80)==0) && ((cur_protocol[1]&0x80)!=0) ) // Bind flag has been reset { // Request protocol to end bind #if defined(FRSKYD_CC2500_INO) || defined(FRSKYX_CC2500_INO) || defined(FRSKYV_CC2500_INO) || defined(AFHDS2A_A7105_INO) || defined(FRSKYR9_SX1276_INO) - if(protocol==PROTO_FRSKYD || protocol==PROTO_FRSKYX || protocol==PROTO_FRSKYV || protocol==PROTO_AFHDS2A || protocol==PROTO_FRSKY_R9 ) + if(protocol==PROTO_FRSKYD || protocol==PROTO_FRSKYX || protocol==PROTO_FRSKYX2 || protocol==PROTO_FRSKYV || protocol==PROTO_AFHDS2A || protocol==PROTO_FRSKY_R9 ) BIND_DONE; else #endif @@ -1791,7 +1774,7 @@ void update_serial_data() #endif if(rx_len>27) { // Data available for the current protocol - #if defined FRSKYX_CC2500_INO || defined FRSKYX2_CC2500_INO + #if defined FRSKYX_CC2500_INO if((protocol==PROTO_FRSKYX || protocol==PROTO_FRSKYX2) && rx_len==28) {//Protocol waiting for 1 byte during bind binding_idx=rx_ok_buff[27]; diff --git a/Multiprotocol/Telemetry.ino b/Multiprotocol/Telemetry.ino index e458096..4d4bb39 100644 --- a/Multiprotocol/Telemetry.ino +++ b/Multiprotocol/Telemetry.ino @@ -379,7 +379,7 @@ void frsky_check_telemetry(uint8_t *packet_in,uint8_t len) #endif #if defined SPORT_TELEMETRY && defined FRSKYX_CC2500_INO - if (protocol==PROTO_FRSKYX) + if (protocol==PROTO_FRSKYX||protocol==PROTO_FRSKYX2) { /*Telemetry frames(RF) SPORT info 15 bytes payload @@ -514,7 +514,7 @@ void frsky_link_frame() telemetry_link |= 2 ; // Send hub if available } else - {//PROTO_HUBSAN, PROTO_AFHDS2A, PROTO_BAYANG, PROTO_NCC1701, PROTO_CABELL, PROTO_HITEC, PROTO_BUGS, PROTO_BUGSMINI, PROTO_FRSKYX + {//PROTO_HUBSAN, PROTO_AFHDS2A, PROTO_BAYANG, PROTO_NCC1701, PROTO_CABELL, PROTO_HITEC, PROTO_BUGS, PROTO_BUGSMINI, PROTO_FRSKYX, PROTO_FRSKYX2 frame[1] = v_lipo1; frame[2] = v_lipo2; frame[3] = RX_RSSI; @@ -856,7 +856,7 @@ void TelemetryUpdate() #endif #endif #if defined SPORT_TELEMETRY - if (protocol==PROTO_FRSKYX && telemetry_link + if ((protocol==PROTO_FRSKYX || protocol==PROTO_FRSKYX2) && telemetry_link #ifdef TELEMETRY_FRSKYX_TO_FRSKYD && mode_select==MODE_SERIAL #endif diff --git a/Multiprotocol/Validate.h b/Multiprotocol/Validate.h index 0c554b6..d68f744 100644 --- a/Multiprotocol/Validate.h +++ b/Multiprotocol/Validate.h @@ -89,11 +89,6 @@ #error "The FrSkyX forced frequency tuning value is outside of the range -127..127." #endif #endif -#ifdef FORCE_FRSKYX2_TUNING - #if ( FORCE_FRSKYX2_TUNING < -127 ) || ( FORCE_FRSKYX2_TUNING > 127 ) - #error "The FrSkyX2 forced frequency tuning value is outside of the range -127..127." - #endif -#endif #ifdef FORCE_HITEC_TUNING #if ( FORCE_HITEC_TUNING < -127 ) || ( FORCE_HITEC_TUNING > 127 ) #error "The HITEC forced frequency tuning value is outside of the range -127..127." @@ -209,7 +204,6 @@ #undef FRSKYD_CC2500_INO #undef FRSKYV_CC2500_INO #undef FRSKYX_CC2500_INO - #undef FRSKYX2_CC2500_INO #undef SFHSS_CC2500_INO #undef CORONA_CC2500_INO #undef REDPINE_CC2500_INO @@ -337,7 +331,7 @@ #if not defined(FRSKYD_CC2500_INO) #undef HUB_TELEMETRY #endif - #if not defined(FRSKYX_CC2500_INO) and not defined(FRSKYX2_CC2500_INO) + #if not defined(FRSKYX_CC2500_INO) #undef SPORT_TELEMETRY #undef SPORT_SEND #endif diff --git a/Multiprotocol/_Config.h b/Multiprotocol/_Config.h index 9889e6f..3a9cda6 100644 --- a/Multiprotocol/_Config.h +++ b/Multiprotocol/_Config.h @@ -231,12 +231,6 @@ /*** PROTOCOLS SETTINGS ***/ /***************************/ -//FrSkyX specific setting -//----------------------- -//EU LBT setting: if commented the TX will not check if a channel is busy before transmitting. -//!!! Work in progress !!! it's currently known to cause telemerty issues. Enable only if you know what you are doing. -//#define FRSKYX_LBT - //DSM specific settings //--------------------- //The DSM protocol is using by default the Spektrum throw of 1100..1900us @100% and 1000..2000us @125%. @@ -578,10 +572,10 @@ const PPM_Parameters PPM_prot[14*NBR_BANKS]= { EU_16 EU_8 PROTO_FRSKYX2 - FRSKYX2_CH_16 - FRSKYX2_CH_8 - FRSKYX2_EU_16 - FRSKYX2_EU_8 + CH_16 + CH_8 + EU_16 + EU_8 PROTO_FRSKY_RX NONE PROTO_FX816 From 9f32a1f22b2c2a1efe7a67cd13f449608d2fb857 Mon Sep 17 00:00:00 2001 From: Pascal Langer Date: Sun, 29 Mar 2020 18:49:37 +0200 Subject: [PATCH 14/27] FrSkyRX protocol: chanskip test --- Multiprotocol/FrSky_Rx_cc2500.ino | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Multiprotocol/FrSky_Rx_cc2500.ino b/Multiprotocol/FrSky_Rx_cc2500.ino index f1b7758..f15c6d6 100644 --- a/Multiprotocol/FrSky_Rx_cc2500.ino +++ b/Multiprotocol/FrSky_Rx_cc2500.ino @@ -414,12 +414,19 @@ uint16_t FrSky_Rx_callback() RX_RSSI -= 128; else RX_RSSI += 128; + bool chanskip_valid=true; // hop to next channel if (frsky_rx_format == FRSKY_RX_D16FCC || frsky_rx_format == FRSKY_RX_D16LBT) - frsky_rx_chanskip = ((packet[4] & 0xC0) >> 6) | ((packet[5] & 0x3F) << 2); + { + if(rx_data_started) + if(frsky_rx_chanskip != ((packet[4] & 0xC0) >> 6) | ((packet[5] & 0x3F) << 2)) + chanskip_valid=false; // chanskip value has changed which surely indicates a bad frame + else + frsky_rx_chanskip = ((packet[4] & 0xC0) >> 6) | ((packet[5] & 0x3F) << 2); // chanskip init + } hopping_frequency_no = (hopping_frequency_no + frsky_rx_chanskip) % 47; frsky_rx_set_channel(hopping_frequency_no); - if (telemetry_link == 0) { // send channels to TX + if (telemetry_link == 0 && chanskip_valid) { // send channels to TX frsky_rx_build_telemetry_packet(); telemetry_link = 1; } From 1a631908f438e42c717d6c52f62e67ea5cf97512 Mon Sep 17 00:00:00 2001 From: Pascal Langer Date: Sun, 29 Mar 2020 19:09:56 +0200 Subject: [PATCH 15/27] Update FrSky_Rx_cc2500.ino --- Multiprotocol/FrSky_Rx_cc2500.ino | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Multiprotocol/FrSky_Rx_cc2500.ino b/Multiprotocol/FrSky_Rx_cc2500.ino index f15c6d6..8bf3c01 100644 --- a/Multiprotocol/FrSky_Rx_cc2500.ino +++ b/Multiprotocol/FrSky_Rx_cc2500.ino @@ -419,8 +419,10 @@ uint16_t FrSky_Rx_callback() if (frsky_rx_format == FRSKY_RX_D16FCC || frsky_rx_format == FRSKY_RX_D16LBT) { if(rx_data_started) - if(frsky_rx_chanskip != ((packet[4] & 0xC0) >> 6) | ((packet[5] & 0x3F) << 2)) + { + if(frsky_rx_chanskip != (((packet[4] & 0xC0) >> 6) | ((packet[5] & 0x3F) << 2))) chanskip_valid=false; // chanskip value has changed which surely indicates a bad frame + } else frsky_rx_chanskip = ((packet[4] & 0xC0) >> 6) | ((packet[5] & 0x3F) << 2); // chanskip init } From 358a77cf7cf6d01b04f3bf28e666663d96b59ea9 Mon Sep 17 00:00:00 2001 From: Pascal Langer Date: Sun, 29 Mar 2020 19:15:32 +0200 Subject: [PATCH 16/27] Update Protocols_Details.md --- Protocols_Details.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Protocols_Details.md b/Protocols_Details.md index 2e7a36e..cb3073e 100644 --- a/Protocols_Details.md +++ b/Protocols_Details.md @@ -91,6 +91,7 @@ CFlie|38|CFlie||||||||NRF24L01| [FrskyR9](Protocols_Details.md#FRSKYR9---65)|65|FrskyR9|R9_915|R9_868||||||SX1276| [FrskyV](Protocols_Details.md#FRSKYV---25)|25|FrskyV||||||||CC2500| [FrskyX](Protocols_Details.md#FRSKYX---15)|15|CH_16|CH_8|EU_16|EU_8|||||CC2500| +[FrskyX2](Protocols_Details.md#FRSKYX2---64)|64|CH_16|CH_8|EU_16|EU_8|||||CC2500| [FrskyX_RX](Protocols_Details.md#FRSKYX_RX---55)|55|FCC|EU_LBT|||||CC2500| [FX816](Protocols_Details.md#FX816---58)|28|FX816|P38|||||||NRF24L01| [FY326](Protocols_Details.md#FY326---20)|20|FY326|FY319|||||||NRF24L01| @@ -333,7 +334,7 @@ CH1|CH2|CH3|CH4|CH5|CH6|CH7|CH8 CH1|CH2|CH3|CH4|CH5|CH6|CH7|CH8 ## FRSKYX - *15* -Models: FrSky receivers X4R, X6R and X8R. Also known as D16. +Models: FrSky v1.xxx receivers X4R, X6R and X8R. Protocol also known as D16. Extended limits and failsafe supported @@ -370,6 +371,9 @@ CH1|CH2|CH3|CH4|CH5|CH6|CH7|CH8 ---|---|---|---|---|---|---|--- CH1|CH2|CH3|CH4|CH5|CH6|CH7|CH8 +## FRSKYX2 - *64* +Same as FrSkyX but for v2.1.0. + ## FRSKYX_RX - *55* The FrSkyX receiver protocol enables master/slave trainning, separate access from 2 different radios to the same model,... From d66709ea878597ec30d4f1f528aa3206e4098512 Mon Sep 17 00:00:00 2001 From: Pascal Langer Date: Sun, 29 Mar 2020 19:23:28 +0200 Subject: [PATCH 17/27] Update Protocols_Details.md --- Protocols_Details.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Protocols_Details.md b/Protocols_Details.md index cb3073e..7a790e2 100644 --- a/Protocols_Details.md +++ b/Protocols_Details.md @@ -92,7 +92,7 @@ CFlie|38|CFlie||||||||NRF24L01| [FrskyV](Protocols_Details.md#FRSKYV---25)|25|FrskyV||||||||CC2500| [FrskyX](Protocols_Details.md#FRSKYX---15)|15|CH_16|CH_8|EU_16|EU_8|||||CC2500| [FrskyX2](Protocols_Details.md#FRSKYX2---64)|64|CH_16|CH_8|EU_16|EU_8|||||CC2500| -[FrskyX_RX](Protocols_Details.md#FRSKYX_RX---55)|55|FCC|EU_LBT|||||CC2500| +[FrskyX_RX](Protocols_Details.md#FRSKYX_RX---55)|55|||||||||CC2500| [FX816](Protocols_Details.md#FX816---58)|28|FX816|P38|||||||NRF24L01| [FY326](Protocols_Details.md#FY326---20)|20|FY326|FY319|||||||NRF24L01| [GD00X](Protocols_Details.md#GD00X---47)|47|GD_V1*|GD_V2*|||||||NRF24L01| @@ -377,6 +377,8 @@ Same as FrSkyX but for v2.1.0. ## FRSKYX_RX - *55* The FrSkyX receiver protocol enables master/slave trainning, separate access from 2 different radios to the same model,... +Auto selection of FrSkyD and FrSkyX v1.xxx at bind time. + Available in OpenTX 2.3.3, Trainer Mode Master/Multi Extended limits supported From 44a676b8095fb566d32db7ac35e333bf09193dfb Mon Sep 17 00:00:00 2001 From: Pascal Langer Date: Mon, 30 Mar 2020 18:22:13 +0200 Subject: [PATCH 18/27] Update Multiprotocol.h --- Multiprotocol/Multiprotocol.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Multiprotocol/Multiprotocol.h b/Multiprotocol/Multiprotocol.h index 1d41fb1..7cdd826 100644 --- a/Multiprotocol/Multiprotocol.h +++ b/Multiprotocol/Multiprotocol.h @@ -907,6 +907,10 @@ Serial: 100000 Baud 8e2 _ xxxx xxxx p -- Disable_Telemetry => 0x02 0=enable, 1=disable Disable_CH_Mapping => 0x01 0=enable, 1=disable Stream[27.. 35] = between 0 and 9 bytes for additional protocol data + Protocol specific use: + FrSkyX and FrSkyX2: Stream[27] during bind Telem on=0x00,off=0x01 | CH1-8=0x00,CH9-16=0x02 + FrSkyX and FrSkyX2: Stream[27..34] during normal operation unstuffed SPort data to be sent + HoTT: Stream[27] 1 byte for telemetry type */ /* Multimodule Status From d1518d763b62140851f7a1278c4693d32dc2d271 Mon Sep 17 00:00:00 2001 From: pascallanger Date: Mon, 30 Mar 2020 21:22:12 +0200 Subject: [PATCH 19/27] Add files via upload --- docs/images/warning.png | Bin 0 -> 106185 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/images/warning.png diff --git a/docs/images/warning.png b/docs/images/warning.png new file mode 100644 index 0000000000000000000000000000000000000000..4019ff4571a24e2dd4fae44164ebd2d5e47f4336 GIT binary patch literal 106185 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

G+bH!q4GI>(^!`wX44WOWvJ_5*2Y%lf z=)nUP(Mdp!mly-gXke8gDvdlW&J7*>QU48_jwdo5@>V?==4)4T#A9O0QVA>&)2B#B ze;{XHL;c8ExE9T-xSAX99^BGX^HFW+e36BUN}&YW?w{Lsd!miVWz{MCzn` zmA~zM&=(4%x8EM!)R960Xd7McOsY4(P?5iU2@!mF;I=21z-~?1+x&hgLKlT=R~KAk zX2U2wvJ=a>Y=D$CTo+eiTZDqMh&hd-ALdAoK@uN8_al-!fiu<;)k8ZpGAKlOJ$T~^ zpVj~!MUInqR!+3KYNIY4&zdWA`Qw&$uAhZaF4DWFxslk)=TnALeOFd~nT$|kvhdp~sJeH=b<NZCg&KU8DOKeNO>VtA^k-mz~V|6qol*Y0pV@F40p7bLXx zHVYe|4sZIRUI1|PA$A@`I%_sX=R=05z5Wb~s}hHP>qqLdNAAb4{0Ge@A7VyfEL09&N$U@*G_@Hycq&WOo5Bd{H0ylG(}-D;iBcWsMhQ$V%h9U`;H@*kD* z-fFSA+LRfw6gRP&??;#3o3p9*DEOfp<9>0|=R7YfE6Xl?S7!T?w zNg)B?+`tQRh`v`x>gf-Up_Da7BDzCij;ki|Otjyl7yM`^%R0n9igc95OG`}A z^i2}KmhFquVU%bi*vvzTBBaQmuD6&>vQdlqzQ<`>xX*Dcur?rNEg>vZj%3GQ z(Z>+zoLtmDMv#sE(9ya%>>M#dzbejz2%p0cTKF{PDn1qI+FD9%w==1$M+NMLAsPU= z6cV;?kt=%?U|-BE64I-Ch0FQ6zYHz$M4C@yVy6&r#ELczc!iWtWp z8PnyY#LU0I`Y~plh#TnFRa5pnY9ovkTAn|npLmJy!aK#qSV!7*ME-dx=@&3HlyoZf z!|vo0oMZ4&GpXrE&hE)+_15I$84Oy3vM=WL`NlQo&gC=Eb`dlD@him;PMMGJ+D}Dl%4S8qtird$Z?7OHW7|Hq(sldFTU;h+F&0`;D5z*@`6m@aiz&!$GgFa<8uy#a9SRi zilI{2*&*cSrL^$A&Bf}(L~7gY%_>gL;eE{l(+dXG+2e{Ual2UXvGl{Wat|^fnyIf` z>^^6o9G(Gci4W**$Eg#UmTldK6MH|?8?Hzk^cM4NjRI&8(I-fA=iLPX=u^b8GbN0m zVO%;%FBJY8TlKp9=BWnRffvC8RotmUB9Z`wSgPB zi-YfvS98b5AAubW|%) zku378?|L-6yu=RVm5#>WZyx|x-my%m8iEg{>u`fd&iQgbSHay4a=_z-au2?1f;R#5 zj6&Wa^5dLqhbd2BjXlut?QX?NhInPgdrik(Yg%)G;Pi1Uf$yDw~^0mOwg&jJ{?jzm3-Ra3(GPX0Sy zMNKxaW`rl>Q=zFvr+1Xp`#0CxgpdV86`&6Ax{*fh1#;$&t^)D+-dvIzNtUO8X@@PZ zpfElx>i1opRww{n-gC@%J{Q}W*4qHN&Xu#o7;S}Uv%8iS`n>y2CjwKFlxsF>)cxV{ z27E=Lsz}S^e%ii^bZa#R4VpLLJo&xak1d3JlZV`JIF?xJ&*)+WN1 zm;D@oy4OY&Rf|Tl9I~TP>Hd8Panf!DDi zgBZE6EjXAA^mSyyA>w%Cc~3(q{OKJ=BYU+d1i!T@q(i({pDiwL`z~af@X1X8$SGgC ztpzkXa!+dyj(b(q&lTVDQ?Qj*R%Sft2a`JL5fZ3Cll0lJ=k57_2g#d{G~)b5`JQjN z88!I2wr9HPK_908Lzh<;p&&nyK~gN4F?1wL8d;A~+y^>Fk!aX~uAhUNiq*)}_Zp2n zqq#ff@!lHzZXclW%hJ)Ll&-P(8v3yWJ`^qkr-LP8X}q`t($o7;-(G-%082y+dd{5v zmO0%%_Oa-T`ajK#`ZH&es2U6#gH1P>R)17G4IO>Ib)zCiVNryfZ4%AHXr%slbpU`hoLYFcOYn~!n@HP{!Ub|S zZ*6#WCuw%U8;t~IMq3EL(xIlGsDLCgt1#=)t4M5BAIXz0wvl$fH5kr;3)$a|D2Y{0 ztS&bMyDHC7URcv8GUJG#68soDuWOSw^A4L0Vif>#*#w@{(gEVh9B;i)UtBE*wF$SP zm6`Kwa>jjYdIiu&e@NJ(=c<@|S+1pU=C7-&?-~D=b#qKP`E`+2wWGDA8#Hs^2QP+| z<*4P)HVmRL!$k`B9-ntiVD@qP%OeYGfDAc(JoN+6N$_);+s4n1_vn`YF@D<@ymK#( z5H|ugGMI7ngEv=-51|}JEK5z4vsP{KU+(4C0I}^}u|Dsm#VahoDwR4gUw6$3^R+s` zcnq8@d;=EU{6}TO((kg?w&**yL1kE!rKXHm&cY}k}7$W z3+;7dWCLAAOiGi2iXQ7LFd0L!z&M|?s+TT_*1lngUrDEx-Gru}E5U}KTkfpnwxG}hh(A0r~UTU&p z;Y&j>F98u%pFi6Tt18${=;>Ek8cja>xDRSMU@fcpTCwSU!+RKP@{`M%F@4F%s~=@+ z+Dq+o`_lkJl1$|IuuWpefK`V>KBb7_Qq*OiURVAkKxeZaJ7ri3Ax6Ig7%;`;yuG;p z4Yc86DMCa0eBqE@;`CPm{x?81`wvU5X+8aV590ef4zIb%H@L2~8fPHW86faNA#|UV zBVnOetCIBi^}e25Sen?VIkHVbaskCb$8r&bYaKrlv?Nlv$V##Q5#4oVWt()ZafvaVm=9^=idG1NM z()4&Po)VUvR@~FMPGGq!3QWK@+m&g=%__PhNGq;TpB>#~jHG@XbNgotpBcVvv9S}{ z&w0F|K@23oUqZ}ZwWUv)#chN|JYXZJmcqO#h)bjar25zY_Q{jh@()2*)q+P2I_FssFYlxviFwwpc=kz)7E;NjoKL2gMQ>M>j?Wm_1 zdAM{N`SXeO6W-X3;1ugUM2&>`ci5VaFesr=G=&c+`}(L0c4q9LLw{%-1WY9Wez+K=?ft56-3P{(LluI|C*+n4Sxo1Dt` z=#s&;uwzfBKlum8wy^GrIOh*qIq)Z0@S> zaVOfLk!?i@TFvWJ0et7gq+cVBc=^elmW}wtW5za1Vv>{DvoVyW1rf;XqH zs|le21f?)+$NxDo#Hj`HZ&uBqHS@hf*K?$I2WG@>Okw?(D{5*3j$t<^t5JrhORPB? z)=x8dN9+Dc0@Hr^@|OsG7?drc+ispa1M5_VsEJIkPWn|=X|9W$vQXg=9zxs!Z8GV( zO=0_DBo}a96~>N=L$G~z!2GN#$NwxpCRu-IZGiN>)^-Eb>GIT=bxV!59&nJ*x;x0phfBw^-@HW|v%A)|Dt`T9S%O*?8 z;mtKZ00m)$4aQ+yLt{{$4^Il+pY>WRuUPyDkX%eA`!guD70#qrl z-8gtLBt2xGbl3o1P~cFaxExa1ipfGXGibf4UlY1H`sg5FW3Jv}ws>5?21Zt&@>7JbjpG==qaj(F?J2kg*?aT>i2;ht)A=mh2lUm zH9`9-^tVQS(3V4Eh^a>@0AdF@um=w7gwdYmWZ)w>tH`hGLStyI`FTYk5N@kW)5%^U z)_)g~)aPs?-|Q}6xNp?gl{hIk3eHqaV}f$o`35DnMJ#fvRFu@#tOOh1aFJ=zdXZ{0 z0Ob9W%|LTE+&9%M;gNKhGx~8UPLG_Nif=Z`Sj>Jqw+=D z_z3Ymg=|IWkU{hG#vUfc@NqYogE+EZz#1K!opCA%o#c(*;0Ed{*&wBwRevv%C`V}5 zr9CA4KOxw>k_&>&*I8$G?QYdnN1Z;o=92qDCHljv@G5y9A#m3k84j+o9ynB6*^|g! ztoYGXZ|>msL{{5to#SN{axAlFQ*IgZ6A8fiwVMkMo#zLI-Qgz;3NCHmM#;#e&yr!D z*U!8kZ3zu)YQMvMLcoE~eq-)3ea{46y?IWagYZElf{OfbS@aD9g-*nYdylDt>!G@7{EX(Al_Z(s>2+Cb*hwU9B8SVSY zV9KXW*30c2-hdB0-f%F=*;pJ*FU$N=2=5*?w^{f@iqtfn2BI!fnYcd&9@|a9D|!fe*8O zU&7BIzeO0zoZ~0!lxv63EzA)8ZeD$h?f5T$Ai-{ivc4eHubM}>NGghXwm^jS2}2Y@ z&a0TT1B9_#s#GtJxjXisGw1~N^7@v4zD>VDK* zqev)?l|Ag|l(M~~0D}?HRu1G$)2;1;BX1fC+Jt9o9CxEf5^|2n{>lgN=kU*+Ya`83 z`G11sp!R);vvV4T0ueKbsuv|is9*39scduo$5Ia#H1=!~$=3-rBSUOLJI}z-u%Axj z@0ZE`as_($(Gv&Q2$N}l_cPJ$+?5XY{K$;gRdlm4tOxf9rOB7!Wv#&E5Kf>K@EHU*B0J_;9)sOzqRG{>Yx$koE z<;43gHdOGUIW?&4)!i!ekt1IZpolfYW1+XN&WMEh^MEvY;)*Ant}n!U2^)NHWW119 z-()(t0Cz5#tXNs8K3;XDvrjart2mw2dT{hl{JmYDuO@oJr2^b0DLuvuJ0deJ0&V~( zR5gYuzd80~RGS$*+7#5&nBIuIFm*YpeZ1?lriHU%j9pAzFGmVWt-dK^5Ewv)RLb+5 zkDX}MlB;3&OQfYC#;VAiq#|TzQ=D`ArQi3O7V&BT9!3QIIbRS&U9pza2FuUOlFcpeOD~YvSMH_nfDIDY5y~K>^eE+v#mq0!2 zd3;p97+a^lr(1b%`SMyDly1*B`7n-~LrkDV)%cC*vh8BoK@;Yvwvs9Gxqo+jcV#FT z&&`SE+O+IT>zWV(7zrTiqoz*+2^?s&?q9&gxhs( z-p3whlLHz*qDRMQn8Vi&YAU=1y=(81Kkx3?oMwM6)zZ(_Ycf=>P}46x-}(Nd?IvU0 z-lo*kn@A{q8tx{w_}3&ie9uV;=2NC%M}OhwegCRRJ1vvKFSJEA3{ zIS}Milc;gLPc{<+>{&+%u&VB!x_7pDPo6U#G=MODw|BDgEvx)rF&uxVpGFGLeYZlz zpPk4v>vw6=Atsbn&QZ8@-o_iTE zjqLqLGqx|pk-nRNyf_--p8HC=SZNT*_s<%FDwe!!&}(vCD+FGTCEyT13RJs z55;tjI=9)_ZA>=o4_-&aci%X%Q=@QR!_FxR=il9P-8Amq19KD?i z`q=?&R6s4MXNqgo|q!)HzL<{A9pMN=&! z4fyr!)}_9OHE$j**k$Xhlf6>gnkkpk8B5{fYy&t+rqW})_;IZR)PBVXm{g515HuGl zM<2tYBx184NRqzJR7dUj?-hb}kN*HRX5H7XzDRfcvf_g6rqAEao)K-o-{+BQo{XFA zkZ1Mcqs8!IrZ**uvrLuF;2m^PyIO#^A&JcuSvB}rFO#_2dXUwH8tMrEDBh<3(fBo|;g znqyLBm>=gYz28b=y$l--aVh#c+aiF^&o0th>L9!zWe9-a1GG(Ri_n#_Hr6IU?B|#Q z&Wo15+cRp!-r0=^0l!62e$f7tUOpORjwB_KAp`H>>$DSTv`i!R}Lwhi^ZqEQL$uK9INg(Y-n-%eYO?p$WR zx{PS0SXvTnZe3*4@e7(yq4T{(mX;@A#mOsQm}EY*`GY*cabH(`lEj0nrH_%K?ldcS zX6;65jjC9){Nt7Oy&W@)f{(Eh)uuKJz`EXKk^^DYh4J$`ZwyIoN;}8k!}PC5)E2Yd z9s+1An|C7po6P{ei=aAIkH%tdONvM_@89t@K=Z#*EMV{yz6Q#kd%Ut}=p2h;wR#GV z)m%fSBT%`5i)q1A=t@J>i$ygH5>HIBdp1KmA`?*S-!bl9&tINq%>hFK?i?n^t$;~9i4hhRnXLYx)I>w_!P(+6%ZIYhteGLd9>w7W-=huC<1IRR zKzsxpkH!+<;(P|hfMEi{?#HXzj>DZkpe*^SOSXn22(-WUcq(<>`Xw5U3@joc5Kz34 zk6w5q+n9PULt+$P^u|40E6uZ)#fH-m7!BPo@mb%94~SMPa$v*_aF{w1@+HgYi1p#v z_=9W%i2Y6ZT-KAL}2~TxO<>pVB{TV!ir`lNLIy?L<=P(mZ&D^y3V_lJy8EPj5yp^+D+PT zyyrTQpyCY5@wr+=LzN78*)O<<@jEIjPpsSihN3{_+Vu;-M#Jfn&-#g-=UJP6nd*`Z zm_Z4f`JTQGqd+uTB3e^vC;=ZJD?b3`AI1>2ZD)1bE4yfCJ*~BRFydM)I1q`|*s`l~ zK>>ono(Dz_ic%*k{K>ukQG9QQiTIICP%MQjuWY@RgHAc%1lD|zipN$UgK=5nVpAwa_eKAietvZdB=8WfxZ@HH zkH)&z&Z;Gwu$dkt=_e&`UAi=SD1u!m z~X8CJWXZNHg4=0gU|O!ijT_t>R_-~HC8 z=iJ$-M{*iGEpO~_RiZM86bqix21taDRWRDsQcAk_uI-=igoFo zrudWe;Sm3GtEV#7%qpSB1<$PoT=w+MDHW#2;XA9`rT#ip)R$^`vv||a2#eVujC?Qd zjY4gX9~WB-^U8c_Y)`XE1y9^4D{93`(6f}<>Yee~CqCb3hJbz+aCW*bJ)^a~@@BgS zPF~zyocFGQ6ySDlmCT|iAVZ8F6Q;~*%r6yd_a;2!-BWCzg14@PrnOpscg<|1Aj!?* z@uj}V%;=9Zv9_VZiiLL#7$TTx3lvd&f5uTJwK89){?4#Lmyorv&YTB)y>B1a3pPhR zG$`*dyqU?6_K)is6-m)acg5MKQF6wGYG^{De}-2mc~PjdlJP6<5S|^npR8kACWspz zI#i-p*#gZKqW|9BF`uF)&}l~p%v2^(8bn&$hM_(0?w7_ca>~ zCwAI(Uky8zaYr$V9>b;mMLW=kmTkyP~%kPf-H@oNJ#(5*%XF3 zdq6pq!Q_&#PO-;#vZllB2g^naKt;_(3DaduI9x#q;(Rpb=2eY5_I%#%=-YWSC0gBU zCZl(3)7-JoT;j0wyFPp|f?5IQosj9edIHd(CUPhy_6JDCXgE}qy{^h<2n+q)sl0z* zMfqW{rAOr8A!xm;YyI)hwzJ?h9}7%Tv}qI&^9-XX<;1X;#wxUCiIAW{6*A{`#=n2< zL?iRMaC=r<-SH%H~!VLi}8mH%xD65g1THSAa8Og~NQ3Z-P8G(^kd2v)l2IWI91lP6G7)!ATDkLpzrL^Rrd4m-9*EWpeQhen+o z<^Vkszm);malQCWqCPf0ZSR-n`jU?fwuhPtCC80NXSoN92iDSgotzgy&CSv6_6sFKEt=XZqN-&0P4O)BoGq11~S ziplnM(w{F~Z@}H%$qAtcCEk%=H=6f5v#y5$wu*Z=n6VW#ZGDB>M{*=1X37xV4K3;& z^}gQcFYchrB0jv7bd>;u)VVX0b2xniDj*Wjw-b=QPr^%6b%^1+Ng+!vL<74%z6NYu zoc_2PS~<#PomG6M-W9D#sdO7e@ZHQ@7g6GP>GF@iyUr_*6LNtor8>bWe~Mq4itD@9 zN^eM$y&v&egUK8DO+B8eM&yc|$?kGVUyH3DrT)^ytCC;>ozS`&-E@Es z5oFGs2eA+3oiC~LByS(gVzr9~YAn}g0#gG;;IPc|xMge;VwumjscmfzDG!I6^qhfi6$W}LOu2y1?dgoSv&CWF`DxT#|7whF&vB7Of56%I z-2E;mPdRg2+o$%*GgAhVLrD-*FGV9j7iW0|8}TcflJ)YSL=km${3i0D=x4VNv*Fa- zMN7$QltC;jjN<@4`kvt8^>fAu4+11?s1nSEptX7 z-4}@WAO%dYaOdIYWe2+1Q7PEs8dD(2T2UlQg#P5*{#)7#k*`0ky;sie>AC!}Mt+pt z<6nvUFcvoE}GJ_VW`ynKh(7$JgzHO6t%b6fs!7Au&5T zT3!w~50^fHpAJ*)ee!`Ju4Up)*0JqByAVWtmqgVYzt~fK7V+%#@N&S%!O31vLyvg^ zJcW>85NQr~uDUijCy;djc6!`R1@$T-V8LZ5pvQAd37z&qABOq%P5t{ym48C;1EI*^ z$2McgDNq!x0;a4E^_&UHwSo0Bn#{pWE1bx#SttZ+z{r`7_}8zBpx zT$VG6{Hn*!9NSA+C58|8hH+Vc#s_#y_DMH)IjXA@Ah#TE3RB@^hoz8 zUIXu_grm9~uUi|dF{tZSVZCIxJG$c{2b>G;3ks679){p)Bds*LeyMO>nJT%hFnj4- zT!`(8xoC6z5|`TN3W@qrou!J9-7IKk-P7hpI=Jo965A+-K956kO0b?x=dA{8<%g`% zw()TRM=T3Kp%)`&dvHcxtU|}Kt_6)_)G3g0N@$Y*#aa6VB_LZx?o}HD^C+4wDn?Oc z#02lZ6mVg^z9#Ltd)ndKWw)5p>Y;n>p2kJ!7QG-CzBiEFQ?imLru^)QH|vDvu5S2* zY=z{jC~nmV#~VCcnlG$VrCxY+N^BnkgYsv5W4-o`1XF~*G&ULBQc8Tr)!$w^EXgE| zCM8g8z0~=Nl<2Ec?f)rW+!uCs$814@>s^X^Y z8-maf{fI*JE$kZKXcCQk%A1pvy%ScP#lF5KZm}7PAWoWlPzpSFO33VeMwIe?uy)} z&ARF&h@*dPS~29pjqP1HF^ZMEZ*mo%D(bCKN&9iT={>;At9OD^M>{AfiOv-m6hf~8 z^`QB4y0W`6XrQPlOV6`^iIT{{vH>G(r@_T$my_``*o-i2yzbd*U%&=@y|Rtj=Mn{d zL<7{*-bf%#MIeKD?ZG&dK96gwBEFAmfKjq*{I3`?$n?{RdOh{oQkxDzV2<5jepUkK zg~5cGx{zEnO%48UDO;qz4@zMSgxG zJGJ`Qw?dCXMiIq(etvZ1OMCf!F*V8*oO{dY;G!))V%`(TV_}}~eJiu_JZlKW5|pC- zc5&suchJ=&gv4-;lxDPi9XJpUAB2o&wd6*DEF|ZW-;LUSh8sm9Mup z&Y4i8lP&Fpd>om7^DBf93#_(ZY}803ussL4&TJA16F)7rHznNp#H>2DZx)mlHB-bZ zWIeUfN?E|>ab(SBHS9Za+wX<9sN}74pwwsq7dN6Q`p--o`3Pm>NxkuJA@Kqoo{Ux-j@WjwtsSM0u6ZHXQ0Z? zp~V;+-6zkk}(qJBy3!+Fjlq3q)>`Uy9gZ=73Vc=ObZCPouK=sAGx zMG9>BEqv$DLovdqym->`I|0-myWa~`>!?~7&-Z=D;l7%5+HFx`z(R{hKRsQ?q}Ol; z+C_+%6${5Vp!@^7coKX&egUEG-QJ6)?|EV_CTlc@9)SXsjG3|psGC=EeSB4Zs4(t= zXD;(g8H*d=UY*QnX7ZbVnPc57a5+)Y8!mI)nSv;!KWUWzc%~-=PnbDH9!h8PjbwTD z>ck)2IxSCBP%-zN zK4DF1N$%T79^U&ACKREbyXEgxh^ z5^Wx+k&=!gVtY#CuXKXfk)U6O3C8xq3sO+cj%qD^s?G1s2^g$pUwiJ^5 z$VKAB;8}l;i(=mCxNX$N?oSmv`Rt2jjVjEamh?7ygl!8w@n~D=Cl*^$4D-d1GY+nH z5HiDEqxjB4So-LrhosfS35uze4_mL}F^8V)hYUL1iK#nE7C&zts-c?8B4aP@hdVam za&Hi0)wY$xAJivOl_`x>r9z8YY0Nrk`8AQ1CHG@S|3;XYra=M=rAHgav8d4RT|}(v4za61ej)gYd@iU&TstL zYL{$VOd3GDTIxjR{PWR^adO(IndLa=m*Q)a!~6VmFNUfw9p1K^$}iW7e?U{-tCTl+ zUa_h$PzX*TC$e&HG}{AGYYm^dA*&^ro7U6(M2(X+M;GDt`D!2c!pKOeIji+&#%Os)7L zd=IcgE2EVCl{w>&25uzB zDJHV4PI%5^(qXhF^KM_78-Kt;+Meg+Iy^Z3Z5?Dz4ACSbF`jo7#LQN1i&u2U9~Mb^ z13*}9r(tLUr&a*Yb?6TxhwUxMM)SAGrC-3o=lU6Vyv3ojksn9-dcHnx;6FkILjH}i z1pc&uZk{8`rAfY?Aiq~f&wU^5mu1F(&=3Jq2+p<|`R-KjTQ*QL_PDFk0yUa zc>Rs-`@`eU5n4?6{v;~k47&w(z0$MhJoe~BHoSCU$WX)#S1a5XG1gEGgUv`t=zmms z6)C(gMr!DK_HA*;STV&Lrf-j#ft9=Y=TsPvz+^ayyTQQ|kFpjJDctB*X-VMH5cTtUWUvWLxp?Zc*TK1*1<6U`h>OfG-sj`_?1-H z`r#pzN#zw$B-Ry}ad?tdn_+Zx^lkqC-?KX?00G`rzd!Ydou`ORn!pwl^NddKxH9Rb z=t52Yu=;zVdq~Xd)>)S_4jNk>ih4Dv02y4W*2M5{mjKs|Z*j{Y>J8jmRd#;wuD$F& zR%*{y>^nXfFTc>QFJ`i~3$rga(ge076QX)1?}A-sUG z0tNXHPYn&Az?vU1H3@Naf1Mmn(6{%LnsH|8{3LwF84~Ocuz1PT^-Wj`=>MP^ar5J6 z01I$e2@5!laqIVKh@f_ZvPu(K=5k7*hGF1u04Sk;x=nhbmcE?IMFE`UNJ{2{<`A4G zNF&7L%k4~bG~tLMObJ6)CBBg?zH*qE3!A#g{B=T_)BNeu)`=C8cZtrY%bfZ^USip$ zZ~UbU5deP6XOgl^p-zXFFe8MJA#bQnnKR)Dx&wRsPZUbHuo8Js!ylq^F!261ev z<&Y_h*^-c4X>_0I;QgN6lM^054)JU2hgeJgEGn|%tvzn&<<}pze<3gp@i(`9)F~6| zYm)atK5K0ugIE1YjqM;LSy`wKCjmNrw9&12YRX(2b-WR)@H-X$I+Jv6lksa@D=VkB1&YbsSc>cf*u`+Fp# zL=wi4($I!19@2Osi~Vmh|L{rsz^4F^^a8x2)S(8>Wa%`GFqTFR#R!!mxTBv&BieKr zd3diu^GniZ)QkF)zwn-&&Uuq(^SjFO7dm#=q{h0b+=zE$zAU#VeF1ek?rO8kY&2(W+(xt+!Q^%c`Y-0JFoW7@i@xkt-Rz~)S)*svF zlBo7+VYQtP_3Z1IdY=k1Q`rUfL~&>sJ%#Z=Tl&AOw?Kn0btb!Y*Ku28u5O(>f3XNf zh-0Q~272mMB{f>7jAk3ngpN%u02!ZMy7&x5k%a9GU-!SAPB@|+Kb4FHG!EpjZ6Ny;t{2IfBc#*6}zksU^AKlb6I)g3}jXyL?73aULOYo3D` z!)r^w&$-Q$nmrw2Ky~=#Ip3j>gY$I3d`y@A$l*#>jzIsds=oALxb#hTs}B>{p;&R% z5`UL;Am0iJq+)Aj_7A~ow^?6ny|`&v?>gMg$be|C-ccJgfRO4`xyzTH>-%1ZbovUW zzA`gQ_aDt5qb)u?8ZYM1q*qBNS41wGX>-~E=2DFY6rJjq?QgqQNX!-+qrKOUH)j~n zMkO?bw?I0$)}J5$!L_I5|AM zp1D)+L&l_&ZyXlUX8#{cSHTcf*LA1paOe^Qq+97u=?+1z}$;gqdYSIMAO% z7jE$??3dcm2z~%hNm*bXpbLjs=aciP@h#%U&SN8lAjfZ1&;-fx4GwaXP5R?k6^&_z z@%sDB9p%ze=73AsHb1ZVY0FSV>F(Tg^S9xEQl?lj$LqGgA7nT(0HV|zGHUYhD{8lP zaE^gPA0CsS)8zk)(P0QN+UO}a@YQ`MeLYkf6*r*_5IRIiYt$K>{`p6NLt=9-9!>^{ zIJ6~fB+Lw8z!e{OCwd=`>vFLAi~Z93s1n;R1 znlX9F*GhgrY6-%Vn>mX5O5L!9NdX6(*@EYWQQ(KQPuKOi{?d!tUt0z!%3G^xVYGqHkC#5Q*ylz-w! za&CPZjxjw?1AtdG@jT z23-;d$OKA+h{x;=F_c7pA*En>OZ2`X&F>+1*1f0nB>nN5W^@_)M~*DyxFkg-oKmf! z;Q3;Qa}QVj4}q3n4zha((EDS0#DM;BAHHJRHIq|!hR)^SpG@kheNO*&R#~WGV}bDo zXr^-^pwS?oO1DPS1}op0GT?txNRd^FHjGu#OHpHwgHX%M(S4zo#x^GJmI`nfrNn<+ zH{IQN0#P|x3LKJ=9K@=n3!a2y^GAjCn{N@Y_K(LOdhUBk-JALjnsFax`!BalO z78rVyTo!intvrlgPUEe1n;4{gsa4wQ;blIo47Bp0y#z9LxQ4FyYNk$}3noC!X<7WX zm5fc&uaZ1h39$*sE^lI0Q;5m5>R{C#FlBlMVU|1G(}^;C$ES)}0($lnHn6jDWDc7# z&Uqp&mg0QCx4i%K|M%Ac`}F66M=KW?3em|8fdH}I5GZ-Vwn=fZ{E*Oh2KSaydr8GO zYpE1F`EGMTbA(o+Wg0RpQse$lSN`cO`EV+Ei@;67Ct5QW%yBNE5YDYPsdLismfOjL zfynp<*x(D?I$Vh&Q~OyGvvCY}-ZR^p;oBX*#>%Yx(|^#mt4Vj#IkO#mFE{iT^m{8| z)p}mPafEy3oFpY*_X!mX)aX%-)CYHe#br5_^?9uY=z#s#IK_(8JW8iK4s4 z!{4MPgZoJY!KuwrhqfWTKS3T#l8uX!5J^#OjBSMM@ib+9jh|Fs%fF6Aw_agJFp-$SzGV#8&vzdzrEegcGf_Uc=X^Jya%d1be*2}bWwcmiHHkRzZiq0FFxFNmi;<##1QxB=Ug3H3rxiUIC z!BG6k4tE1-w~Cef-YkNQJPD?6HcU&X&SfpMgW@APWShF&z4nl>tLS1E_ z1EHh&{6!1nT}2Sgm3J;;jF^zv$<9(bsCAZDfdUG~A4(VkO6zCP)fJyz@vcq;8W;Y0 zbB98H_f6gM>22k`*jr>)L}yYCo%m%ey^n#+-nO2W4pfP}NB4(a<2iAa<^608YNZX_Q+riVk|k*to_29iyHYQCIxqkCgFU>AGuB&lA(cO z=ictS5d7|}gdy2G8UHcr|M~Q8n3s)92@Tb2fG&v`>qXFul{tR`Sc4QT`A`!MEi zBd8&X13yHCTmC;&Tv)W){XZi=-|3q>xWR+YmCEoVBMWOyY>$x%1^$qIIp1q#(XZaa zA!I+dU6lI$GNTU3Zf0#T=|%PWE?0ymN1rr@_D-A`*xy=5#Dp4?J~i5A z{URHwwav5O?RW#__i2hayW${8+VKO-HQ#@@q$)59U-dr*o^C3hO9)nr8ad8X7s!fr z-M}51kR1BlrNl7vyJ}yR69`OSZEuYH*C2iA#G2W1o@^oo&;Z>U%RkL}`S6yqxkpPF zTqQ_XjtdyAHqt556ZB~aY`%>w7{JEI@=lC&S^jj;8xxNd$SzC+(+Y>`o7po(r>GhL zou|v~avLwFze|TQT|QV#;MSEhe#QZv|7MzAF_7x^@;u@JSRs9|dr~~4WY|S(eT>f~ z|BDR6F!=p(cywKG^`-^{vw9VpY?cR0=+K@PGL>34xtq5 zRhZ;?hvz}+j-qOl$lOt3qV{*vjw}4Csm%!aAcXVsaesfQcMql7MX#V(onB z`CT3DXS~x-k2_L)AzL)cWbvBfIC`VjjSPp+pZ4Cmau0rFjd!x{Y%qEGpuh1ry1%-M zZ+;5>1$N#fcK41Nk461rsxN%IVGxP`4>FYsx@s?IqQYYH?%Oy5awrb0p~IS5M$zpu z9RS7|epncfJvlc~)A)Z&*!>y1t~!tS#nuo?wV?3j;qq(X+KWK%D|WVvdb^qa$BVh7 zMbU>fTuRwLk_ix;pwPyU0<~SvnO5dD+wvScWY_m9DjT3)v#l2mzTr%Y^E-FuIb`x| zuWxUACr(OhC>8{Iep{&$xa%@`-dob+3+t5;AE$^hFD#~2e}>Rtlw;YWQQ@#F(7))P z6VW_dMx{;;5%oqD6GbQn+3Pd>Z8`%gW3+U>ugY_^@SZ=5ry*hXC1xAr&W>E_I!=r91i4-gk!G%w5rUTY$4w-QY$DP(N zP36IoKUkhK^PR6UG-xHbuNem(_9$fYYlV=DE? zNLygHsjiOi*28gxs2kA`LIxHTHvBj>><761_Dc7fwxqX2JOdBEdh{_2MC=vGy81(b zrfT;#=gUx*i2W#(n}#qp&H>&ekdGvq+Z4e6mQ>p%$TjVs4TZs|9d%U?6>JSC*TvlXM)^H{h$}-(n`lK zTXMCQ!_cg(T+4)%e&@+hR;WoPlHV?WN!+Vr!hp@$F(ts*&ySpu zHj=>mSRFVACK@2%IOJ7JoH3&Fu>bcuyZdIP{0tHN;34|(V*|a*e(VtkAP!+vALoUf z!8!gBPN}OE`l|h-RjM*}%-I~>-A|n|K)Oj|xt=Q4i{7!W){GwOVx~vKvmr!Cz1l7v zC4+1v+e{V9lRm=P2_u#up}jN_E(pK}J_P=Z|jVT7F42efag6)cdVnGs`h^ieL^$V}yvTAK--C;NCXpz7e)p64+WXHCn8wKee$$xz`(~dj$a=VEoeLc zzLhC9T6&CV#*z3|;^w0d(P)k+ONEGrz?@YO;ri%?7hcCZs0}$~?OV{=E*@9zcRywv zW84UKKzL=&9H8oRqBZP%w$YJ%R9302IZ+|=rP@45LhUb4Qn5P z<#UOBFZa^`$1<;$?nI0x*D0ln&7KIE=0#0m=F+?G+{9?kn^aoyHB;nV8U1bN)XB8J zY;l%EaI;}HRn0ZN*RS%==UOf!ZXHb<)5^|mTehy#&aszbk$2fO7D(H~f1=&mKK>5t zuHryd#}b=uciR?dbzR<5tAPY5a0g_iuo7kGC!cw%y(kx zJDE}E6bU$?xMoU=@$n=&x;hhVlQ^$+$vH$$2N0{o5F_m#uuux^uHaH!gnqS~V{3bQ zyd%fkeqJi*)8VTB5Ev%<82j%ELmdNw7J620hWk+=^zXi>oY2)~m1jE}O~I7tkde6m z8O-yIzSj0u`aHU1-`^Bw{Hs486ga+smGjLm0BG# zF6%tcT0WOQGgFSbh>yUY%v>Jrx*EDXR(B3Rcs2{Gw_G4qf1b-du7Nne|A7O?BcW5( znS__Lsk0b4+A!xzVTgE5X^s^*hsEN&@r?)Ji}Ksx+I+5UHW6RHU?U_=FLMtV?sYG*tFYc@h>S=sPvTdc0`e=1IcN})o{tvj&WTqv@{-^ z+JPeE089`71O7=0>O&zIG_~h-;Q|VlY6FAF(>{-ynHX2O@BA(o(K8rS(D8j$5ryqz zEmbynR6@kW?$N3(*E4N=U?)D+HtVSb|DuFkI%Fo^c_*gcDQSvKNcg@n%^^_&KQzY3 zSL2BSkQgNms7*C_(nX?6;2JT)MlGdfLx z_l*f36i-<}uKmVNIIyzu@og>VjLHYDo1dZ@Bsa13!!kI>T66em(bulZWGs|am_vuT zXxEJ+GLsqs-~W^jZ|+O!wtMZ$>Q6C9d28BEJ!R^u7EltHyJ1d*R3~jHrcxBBNe=>FGTB;X{`?gXcE@x1tD!EiKCm;ClK;K1 z(k9+lAXm+XCQP@=NEoQ6)n52H1KEz?H!lxXuRY3mx-uVrgJn7`rZN8L+zBGF^L|;( zPFsIUmfKPdc|2H5aM=s}D%jJXc7^Gf5!fE#$=*YZ!V4EVuMRKq=5(jWahNd-98z|LW1VBP!OQ_c zaW~I^$pf1jx3=ry5(+rHv9YOmFk@k>(h&^Q_CtycoiP@?{yBHRJw-e$=(0U&+SW*F zx0hv~<`NuKgZsK7tvM-su2o?PT9jFc zb~+}=yh`4JT*gMGE)MA>M0J_)xbo`45v5MTT=9u8TPkYA*l`vu7?$K7tj{$6JUw!L z`!rPM5ePLmdwT!J+ur=iv{jUJ?@?{aMC+GGl(%rN5h-2R*;Bs2N7-162(z2gvjtgz z5ajX@W%2P|4Qr>~(4EUrfS~pWTpa(FdI$eiylB-1g|3RCLU|;;@vP;apN{8U6~|D+ zqg*pWXTU0R!}HBG{s@1silack=H@$2}#c21dHGa5hi5+>#a8sGg7ML z6Nft!dxG-ib;RC8H-s zMZGS=del?#gs@(ZPNXy~S$;je;ypsPnXOns z+Gv{<{cr3)9Z|m~Y$}CikuQA{Nzf(`hZJDt$i5V}jq@qoZ@%-Kt!x; zZqb%g8UZXx^;CV*+S?CduslM6~TPg zDxepeMolB5E9kNt3Z*_ZyBse3rUM^OrJ-=}2licpx zO9Uk4=5-gnk;S{L<)DQ!$l=u)SXDJ!m9JcV(^=K5^_J3DLW#-ZrwI0P8-dNt*2oyR4i3C>#rzf=)uUWn@kF|w>!4ft|hb<;(}%T4|GEjCpf zXC`|lX*_L0l?{*(Y)zxC7dS%TdWr6;0ZPbrrmHH^VfqUD@0m^#_<|y5BtlxOEe9&n znn)kHJY38Pay*}y-tiILk4k8Or(vs0KiPNs`47y$AE|&YrEZ)Fu?IWdZ94v%^SuDt zw`^o=zu&SKI3=^LLjVvsflLx}@#aRCoQgs>2Fnq6+Bt-7ytgSKrR|5Ra(#LX`K3}u zb!-5{lpaIljHQA!gc0!uAG{3N!s*3Sgvr6$`jlL-wE|d}@w#M}&TmQh-VL|wA(*8H1divub$&>5|Tam~Ku4Ug{ z)!#rNR33W&QCW&c)MMEK$@(Q(LAe)pdw2F#UIYAt1K67jaDnC9x{g}gHBNmsqsXkd z*D+bfE&E_01692TT{>eergBo=U<7UH{2y((Wtl!v=m_KOE^@_#xh9R*9D~ycmEaR} zC87V#3V81?G{^wpefg_be;EEML#Z0S1{S~K0s(%2We4;ec{B(%PcEv;&v~?&<|vZ_ zYg^vS$FMA{H)aktp1ikj7`YrRpyxn_Cq{FhFS2ZGnl}r%bMVYBGGX=ML*D|EObvRJ zsadNV3*>By)6u!V5mcCVc2a9Md=jeWvL)~xGTWl!8Cq04?rO}-9&5xqi`Dt|+RMZQ zOPy)C^WCpp3%@n@<#s@0Qbc7Dvhl6|Z~`)$2V{%JWGsh&L%rvDQNAJW7P%)BK2NO; z2RLsI%+ezU40_TNq%VKL)85J5dVc+2oaAS8At4z#)%MorT@Qmrb5F&}2Z(>eAsS1P z^4hz#@f#9VK6;mj3JH|sG^5???w$EnF6}HI)u!M7JPA{d08-eE;W$ z{=W0u>bMUTY^!(#D7+wsP3Kv!ZUwdU#3Dxwf_b_uIn*NL0u|Bv^FPi|53q&FlV9gy zJ^%_eqtuiHUwjQg*~=2d+`Rl?laZ1rEBIPV;$vgxxJi)G$#w>*U>Eu9;oX(*sltOUOdk{d2RwE+V9)oGB_MdMKnJ+4yUT~yMIZM}%`+fH9VBu5mjGItwHQw^ou0rf=a7i;PB zYb?87>b`kI)QG(7#r)feWWr0QiTzt3AQ)17{+7Cs2jg4s5f+BRhe-(nF(VQG65NT+ zINa?zSblLgPh-Aun%L~iXfD;Su%IBjxH-eK=F1GU&nQI|?f{i+lAoH3-jr^5OZ=92 z#EsKOB!Z_}6`HS~D9b)hzP-It=iVb|2pmh>cQyD9)CnBa-5(g8Sm4T|s6Ypxdj&)~ zdcx`$Fj=Wy`AYvOnQO$pKmYg04H)zJsELtb@t_;lSf?%fusQ!95AV8cs_o$qc^>JE zTOW1uu7toMMBDFPo5&v2eYsTGDH{X6pk+mM_fXQKIwPVq$C|yjQzNEd`^Q zp6b4QlR4|MRHTrY_>THOUI2mg>r0ib7xuQ;Lv6wR7OU7-{D=qYTGy%-U=6tLdwMzd z*jaw@Nd|L2f9w0cQGE?fdn5|A4Q4%R$L2q{8{^8zs-?e(lElOUZ5oLPMkBN#ARE+T z2|OOX?$CpHFO2q=n*efza#(aG>;pwI{bG{Sp>D=rCx0jUN3BmYN5Q6AiZ&Igue$VX=e9XQRAq~|ZdfXxO1VAyNpZs>e zA^xCzHe;&7RhI43qRH=>B~vsyd;2y|X5d^5xXUK!CF6!bo5G72mwuLH44byAesKUf zB|6VjjuPp@v~vW_V!|;MSMVgq+?hqLPfQY9-SnA7V1F3b8x_i{w`h?p5?vsM>i&=1 ztB4B&aDPJ~Z~qf$V%;r@j-n9G&1InR4}Flo01=dom?myDGDZD=O!-6n-0GF?$v;Fl z5CYSFO0n9%zYrSemB@P-X48y<6%JAsnRAK)x6E~oJ+?UN{dYDQB@}RUqjDsF7!4W2pIm-y!&00L6B0gXU1 z^X~4inq?K;A!UFx^1!#^pxD=f|4)cX;v%|E=_vqy4-M_FFhMiuTEds{+{rppUx2!q z)2(LrxQUT`(Fa+(xm3+g)Er;20W_$@$N)lEGRmN6_KfVZe2oZ&0(`u9%Q9T7Fk;?B zY9z&NL}1|?Kb9$cD=Q@QC(GL8dQ=+H%g|?y-hiVfOi0SIct2__ zq91d=E~v%%Q4m`n9c}tqPd|Xh1YqUHQ`BD#k%jLr3Ygo`@uia?p-+J@ffCsefrJE} zS`+`ERROz;pY9Im zg=RjKmY^iIY^>hqgoAA2V~5S1!WNvr(x!3~j5NoOBV0)q9CkwcVBndu>+dx66p z4UvkMJNK`6P6N1&<^;@WFp=7pwXO{{la5az+b3lN0gsATjVbd)j1c?=SWyw4&K91& zi1ZfbIs&LIM2SPUKwTGMX928{#wJg*l*0Zp)e`&HiG;Cu-m{vuHNM(&zr{0;{At%J zGcJ~R_Lm4@72W{9k|7y@4w5p3JR(VGRCx`7eNM&p4RNLQ+;(jZ+imJYL~sOBeh!Xr z&z7~3x3_ZDqa9XB&c2&6(f2@l^J7}E|Muio<9jJeOw`^7`eF#-tz8LM9kjq|Ogy(+ z<>%GPfRgP1(8Lht z%EtXk>h0=8(f!LUTJKof#rxd*gOxtXno|BV;1FE4dITs>~hI7y>Z;n29& zAagL)!PMw3m@$6vj{ZwNx_b!$>W>)lAv=>9%{e`89>ya9eG}g{1m=}R7y{pbhM-a| zUC~)WRM}+`MkUzj^Vi7nqm~vtXy+d?bNO-O2|>iz$;xY?NGY6tBfq*+)@fbRaNNH) z8NJ}lsQ!zG-yZFiibSRaD2f)i>PnqRj0t<(EiYtut)1YsDvZ-#zX;rHK70-w;rbeJ zX3G^!yp-O{;YKuwAWaQWrk7C82yF`LlP~$Uf%h^h<)7xe6NrP>dVi{~onj&_{7X5y z4iqL#WsI+23C?bsA0yq)Za}357_3QAqY6jo!c9Q8%H#tBTrm5Iz9AoYfvg)uI5JTY zPb*3?dpys8y^)lBc80dG(-CC;lkQz;?7PP1y%L>*?mEOw(Thu|f~`EW`%^nXPtbNK zdcoVZGnU*QT^}ALr1t;%^Y$*X-5X%|)@ti{J*6sMeIT*c11`sxyN$_d4Z@EUWj?sv zM6e(anh7mj2ihL&>jSTGZD}2}cAjYBsDZt^l24GBoWT#SV7PG>_#CS6!;O+EY0P9c zxQ?`7RG0S$mVoEpQ2+F>rFqw{pspR5(3pr-Egpmk={g`ZAW+q(B^`8r-GHwB>gwvZ zx7i*t;FtWTv;m4SPOf15KweTc#zR-o#4~3rx*jv~&(84n>a!5`9*jL$t#g0OnioZ` zW5UNTS%%$yYBxVgo4AUec>ucE{>$e8V|jQY&C@-$a6^%K4L5k_d?adj`)XB;^Aa6spHmdW@G&tb-&UPavG06R_PBD83PE8CsxyIC<3ZS1 zs%)B=KJ`>G$GljcYbS#dNkd7k670<)*$6N-BxRwHmfbIvFEO6!+Gw2U!&C%fNM|RMY+Koct-S@-YE#IFzAK4_|Rz5adKZssC^%ChXCY9BucZ;Ci zrIR|vG{Vajhvxqp5ZAlwMbHW`T@vT&%{qmw_O;bH5{+y3v8OXAS5mY_@(|7Q`VF+) za{#F)m~w(&91Df?&{5$rW-5ncP4l0>_l5RR3x;L6hvLl=e8ev8mADDbCN99HqE_!oElVzsKC+ zZV^mMvbjebXi~3OdGVPxZ>u;b&f7?m1KEN~h)`(@kG0{4ZY5B_67qR99%^D_Sn9uz zERC2WmdW4GREh#njy(Y3X&a6|(JiO?mLZrPlxsoqV+2j=eOh91tcNsoo$$#cr8t_> zjcNg~QXnS@nT|P7FxNzmXgob3Ubx!%_I9>J`{gMcN*&YRALRYWAa}(z4U^fwk5;HLzC+Jwo7d{edjF(ncBewf08Kd;^p=g{?8mLc+M`{){~7vaY27df zXk5k5qaHhd87p%7>@n)~^EDdETLi?)p9qS3QObgsB7X*t=h`jxzn2vwIQIJ=5PtM@x?YP?S;+AV>*|F1uWx= zFf%0=bMCnA-!XY{vMH$}5b1ErHh!-hDD!@}#~GYEbumN1guEz}Hc=sTg(Y5~4$4(F zHV3{!w70m~9Yqmm>EAcVv$&qn)gmMYCN7{HBN8nJ0R%c^GP}Z4izqp0j)ljUNQAYhg&L4QN4?7;C7VcI9s9F ziBP8kwdL@yz4usYS>Y1hzCQ((?ozP^=L?oUDU*Hgx(gZ&#}(klZ3{i*Dpc+xSO|BE zh4p!@1}5seZN`ZaU`3A(u>lIAKYq@#Mj2}R4Thj5z9=y6@$!>C=J~!%SV0<=;vV4Y zX!VHD`oG8heRaz#3%Ksj)_e5|TSuyG%!szGu0_D|i3?9I_nkJaG6n0qZt9~8IgUil)+5Pu#QJXo0m0UT}c31Fbm%j_*MdnIRj*_*; zK`j*-QI#y!Q-7EZU0ieVUUQZ}+Lo#OL=nU~Ni1Hd@OV?(&FHJIntl~NG8%@?H$)uH zzbUTcz89kV|9eEuj@y~C@9`AgG#!6V;{p+1?4Vmypgb^d(9A^j3#Y}-6m>tK9`*}F z5gn^=&RL^}sVqd`Em>i!bK8x;0uzY8nim6yYn7GMmw zXD(7?PpYWlHAmTt3|mm1peObiNk5_lk>M*uwc=r!aV=d1RCsn6h6u^|2#iWDQy zQa~>jFI;h4v(#RuM{y~G4ooCGCLkAAYzmUq{{x`M*g#aE*iY?8BCosd2aT|6`NSSs z=Y~*TdH0Syva(=8rG4gJ$j2mD{&G`-DZRo)zrEl;m$Wv4Rp zgpy-iBMdE%{z()qm7W+d*WhuUYv3KSn2rxbPDd=N2zAcTx7rN&+)^36UYzS<1Fm)Z z9384)&f+i3i`smA0FdQNWttJaNVMxc|Iuls9C0IF;*sv0vzQu>3Tlp8 zt^D3QM(#+Bn(}rwh_y5*3?S@^y*vKh@W`R8ogM7_et^XoPaEn$6%55TeeQ&OnXtV= zOr!L+r-@KhF}fbC_M#VS4q&K!Rvs*#WluGYTt|dI1a&jA+jFAaH#2W5h~UxY!L?~H zja$BB5!s*|SG)$K;k_`fe;B(<6YaOBgeoQxx6;&rD8;wM3-rwP>i&4@uLv+L;EoR} zjeVyZ2|CHD`(l6>DfUaK4>9ZT48_9HUw#vr*+u60*$WYew*RF6^WF(jb*f%>JyeSl zjlBHr^{&?aHtYZ;kPCP>2mr1BHm6D+Ys~4u*kt(EP9C)CahBe+y);ye$L9_hqdmQ`Yu~^aAVZ((9~TJYI^|I4uH&j^GKFRwGWR(40RJzedY3G|_c*bf>;9 z^sl*Y5qF<1u5<#-iG#3|D0iflgn|rY>JVL&SyvUjPrgU3^nQic`SgElZ}KE5AI|L??nzG6W>r?Q`*B(rbG#!fxcmT+ zb`}`0wfU!?`W0uxi;|32&>xsAY}MkCA5%^HBa!W5jQk~^904z-b@_=6gbT`TQ4xY* z_1bf9ktRHZz;yHYX0Y6U2}5_!^l6=F&^<7i7kWR+pt|^Rjkglxkp-5>fMb{!WbGY# z_KUu}hb}LU{{pa5b(Mv|<2>+u{C2%6m||S&yTLuJFC%dD72oUElIdgyOxCIKkC*J!nMmUkXToZJp)Ly@7tE`4i6`n(KSIaaN0m z9AW(p$=GoCp?_%D*sLeWDDYiQQr=B+^VGnCvN@R7Sd@p*87gsud;KH34dJIXanuA` zyCMHKevRz1$LxsnwAuZ1)JtSAyZieTbVJ2__pNR=<{{hSm^W-5cTHxD!f|~_77#+I z+@2?tbZ?I#()R;=y`3Te^JF6a{r4(^6q;iJm7NmIOF=q+_{dt2QXrGB+48h`80(wf zFK&qmA<^hEjMatf%Wx!V*6IC0{40}B|CW6r{~_TivnOlmo>l3FfU~Y`2gz}5kdazP zj(z~234qr-{HF)N$O8h;Is@d%Q1ZDBirC#1y#eXy>VhkAISNvN)26SU-AlEgx}bTF^OsetLcnN7;A>C;GW_uj%^`44AgH!GCd(UOHWu_JX1>ee@0JVY z_{+)Y;m9Z(V=A#FWLeUE5PPI3Te#|2Zl&B*pLD!PqRoWiqx%r$=s&eW%3>TP@jNO8 z3C)>&x6*Y%OHc?C?YNPC*!fR$5ZeEuNU{)7B+Ifv$>Gos#!sQrc&daor`vbaB>miA z#A#2B7p?G&={6lO_V(^`-(h`Wgmo+FiFtfgh{S^1J44j=-#DL+Z+d|6fF>UW4FYIR z0EF3Y%DD$ir7MFPz#z`5lew1wP~PD7)vz>39hN6l>Aav8c+tpHYnXg?j zRxcrRIL;4*-iRMh*Oy&uQaS{9G&_;KfMA>rjnoBc%wA{XLk5b2Ex%!kT)o_oymCJT zj-$VJ25uAB0ir<@3qEihRJWF22=JPT7=V5Xyc`o6N&3=tx4O!&<=MR~%-jIy;KbzM zgmd(D{estF4%aab7rlKN>QrE$*8z0WrNlj&=3rHu&iMwviGZ!CE&CupQX zWW_Q{%F=iKIcjZn1ZplHwz*e2uKKoL>TP-dbd0F^qOruEfA-+;iv!@b?w}CR`W|PT zmQ=Vh5G|xzOsOjbH2^0BJD3EmV`w`8lL6O@4df*Zv?zFw!H+_Tb?m!7=Y6jZ{>=4w z?u|)BjISlrHGcTMpWpgC68=Gtv(n>8RRx8k`k1Fl<%8IR8Je8sO9{8#HEb9)*j3;e64Q?j~PW7XJo}hTpCuKDy|NA zxqW%A5$O2$Vabre$V2+FCvT>kp=XsErMEeVuKqr_buEp!z3H6@F5ODr$Y^kKjR1S z4gC_WS$&Tl#CoR4qb*ZY&{P5YQUNmb6t`R&n7Nli)6}t(s?!Kd2=fEVp}w&~R#xg^ z1Ym2vruH~fs+}7X3i{dZQRjJ4{8(~K)D;Egxu$KQJKo7=Z1J^jHk-p$Rk8!Tc!_#z zf*Z#Ok(XmgWp@5DKRZr|4-)ax3t)#ur8soKrqnF_@*kV%g)3f4qcmnu{pWO~TJ8p9PR|b4hFuMw@|vm#fV=RKV=~i5I0OE zGX_FiRm}abt-io9$nEaeRa7>1oN?Zi+tWBA|7w=Z56A~bms!AjEcSNjQVlDo%8#?Uobd^}F7A;*BiyIT&Vf3i7XcaOXoI&4=|D`#uAv^O0;=2QFPLM`) z{D1_}{PXC+f6r&LY1a#EbV;E}Amgu#M1fDP`$P2k)5^M%eMASUD_z}W7wlo(*UF@7 zNZleZ_VJ!iz;cjYJm*-Y&ExN{bo&k~vST=Kk)|#f#||Yi3b!+yQWaH94HEU;Ru743 zOw3F}!hWSZ#c3h69RSDAzd|y{-ZcJ(P8G*Gik1N~USA}048%i}8DHz|jTw^8hTF18 zXOsx>&~BQLRS*Xb-Eot9Dkk8!8|K-?4c80rTT77CD;&5u$VB&&RRFrJSGsF`52HqIZl}xkocXs@pYu@W>$06fT)LO|4JC#sr_6bwJj%gT1A$cT5dTgpZ=G9ne#&bHiT02yv;t{4+(( zH3VgV6qE~`A|WbG2=*%+KG*2B(pq4Y>#u@Fq;;j@H^zX$^1g4iC*f7O?Vlf&obMoy$Bi zTs+F;tNSaex+5p1btnUpD#e%JH#_ISyQx|IufO5fhMX+xny9#|ftJ4nk8mqZsaqpH zRg!}*m6vrxpU@80$!|*9l{Hp8;Hpm<{v{UIk6O3dPZBR>Uru#Mxl^S?pj zhD)n!p{~yhE62Hnb~RUs0hUy_>_EQ$q-V|cI+AOzAEf{&NTClb=%#4FY6p7&v4WJ) zkKTlne-x&X&coSGdi=ld0k$O~Or=50JlqL_#i42f|BxZD#Kc5=YIQ)2)sp{^^1orD z+ayE@ZanYRxOo`ceT8B45KVgjomP#jNP~jybX;b6Zk86g0F={cBZ50W1suO0X||^< zu$BgB2-!V(IMBsiOT0}vVhMxSWI-_(^uevcvuQ-C|JLpXA(wgC{N7W#0n{*?0o^Yl z4CK2Iy`I{41k>)hfVDlNCxOYIl#GCZufkUjuRq^xT(NssspG`$=HpAB9CXce)AX(u zD9Z}xF(-|00fk)N0**u7&m>dR=@vH&J?w!6S5V+f7mod(QZLuOJs;_)9eoJWIfkIyf17Qq6`Wjvo z*3ISmY`TBRJnq_HzA;r|G%XJ7RWtM;j6-(0BF((8lmjufYVp5k@IGFG8VHAEXchRL z5j(MGgjN`Hzgy}TBV@5lE|x04R#I!pAIJ|T=VNic zO1)8#M{1TPDo^r_U8|Yw;nLG3p+SP{G}YHL0^l-7EFznPH_S*JT$2&E=``15=TZ}D z;O_F9CT=KBvo)?VI%M%r5Q#T1%=*qVt44x_ zgsx*R{5J)lAx5_-Fjuh$gEo}sFgHb34;@x-E!FS1>>17HiJ=$J!*e+s*A-YoG1Fb# zV}!2-5RE6{K8|)qq$R>gohPL^adoa7L0}jw!kbNu15a!{hvmQ9rX>DnRtnv2sV#l; z7elN3%gX-_JdBQm#a;p_ogxgb$W8U$V>TWc;%Tnfk=9go4Fp=MZ|TnW8~$OIng!|5 ze3GyRqXbf=wxMI*o2uJ?%C0cxsdf4SSjn)>j^J}|z`>#}@&SYVlxqTlU0CnZBnoPw zKloKy<`zfbF+p@zl(R1pxeB$TM(D-^MIbg!@_w|(jCVS6L)1uc>u;axC7(am_RKUR zH{DHElmUoW%uII>qVNYBVKUp}0xw8ab;s)Lf3(Z4mA=_&9EEJZX{Dut8?R9kYt<>@ zLVVn?vWyuBD^>9)nuFDetxeU}40t!MKn%Me0X1!O&E_NJR2^O`h9rvgfFf5EGyx2c3JiCH@ zVPMFAE|sE~4H2z|uu72;Y;)%-+b^xasv=_T1+qQttz{xyB_wbC-QC!qhV;8ZIaV`~ zAK|e8P(clr&g!o(w|62afZg=j{o?AOuKzN1a2LBQ39djrw+ zohyZYH^CuuUT*1){Raw=##?(jK{xC!x){29X2ov^%Vo{`+y79R@1VQ-Q0xVLVMs-E z(nG&}O*31LfjGQSIvsb58pn&BJAfp+vn^<1ILIHBt}4ajz;V(qPapFf47>ny*|mBE zPBKDx@r}*l;LS9K#^oDy%H{t(U>*6>o1jVbjQpn#;~G`eWfDm%xlgIvJWhnd@E5hJ zD1rU88Lu-ySc;~#ZR6-x+-m{V-D$c3@dR(Chs+Ae!7t8w}~Kd{cjr-PVXO0HYn71+iBbwoh|vXReNBxo%Xh%C|(yD#>IVz$*kh0piI*C%lm=k8da28;=wnONa3Z$GJYFz&GMRZml zE5@QmkFuT_G8Z+4+wAKxjIl^Qq1?P-O^9l##{4jbro1nYE{TzCDF<@9AB_*U&;S=_ zD%{1@x*M37mGY~s`1jxi6r!d->=YI2TSp3>mY%6SI3e}1-y$Uww~wd6QDM^NezB{ zkBtY>MF*7mi&SKug;=>Qt5vz~NO7aBehkFg8;ajf{8^25AdD#Aojx9rf3?7{*H}mT z$uQvcAHo@tw|8Hv^XF3I!ciUo;opY{muvbm@Lo~*RH3mUm>dLU zw>*kpqij)rTEtGL^(J9pkl}19OSL5gPEJ0mbQtH+*RR{T%V28R!D~s0rb8)+B%->e ziKgJTUOYbj`Ia9G)2xJyyvc0I7Yvn zx1bm&5Hxan83LKSj%uu^5OgpG$X?n+OkO`4xPiYVEPt!RWBSjx_sV8?;}cTrkX`G; zG9SXf)y(EZz&cLLMHuyQ^!o#UY8(qd=K$_#78WBJop+)lJ1fxTM3!ICa)8OH_H7M@ z4Ev2)wFZz2Fj>?F#z{>H9E963Yvu6DTL>J=%H0ig&|3Ss?6TyIU$-Mce;J`)t6B#+ z2X5)?&dcN;KGTdMAO5V1A+VQx!&i=@P~Zq z3huO!NvccyFL?CzqR}8pq&wBxYU$6a%0FbM%CN-R7A0&K%83?AX`M3*v$tfZrfotM z{dz|Aog!*V+C=I`v>g6oUof{ESVObO2V=AZ4@sN9u9F5iZ(C;W*cIw^XbsUP0Hz(~ zbs7q9;~u)ftfY<@h;^NL(g`rMDU#20_IW0N6hR__HlL)enQQ6!9UrCYi88gDq{NX< z*w0J}hZ;9drESvZ5%!5JLRd*e@-Hlr5ZfNq_>EZq{-|QHI=l+}Z!6ykAt%*P$t7V<0<~V=}-(S~t_R!+tix5$2D262ErQ`aB4a7b&hFxQW zu<|35If=lsLmW=i3t}Sa2)>_ND`y}35*qRWNx4bw(k)?Evj3`}wJFhKaA=4Cm zzQ_;U#&ajVv)!>(jgqU+haT?Goz5CvlKQBvguHu3cU3A1(V#|!$8d9@8UW8} zZPkNA8E7s+!)5{dyi);l?yl-+l#2a=5J&X0Nh1OCSu+$xq>u4Jmx=hs$j{n`gvg5B zZ0E5=od@=FC8n%td~y1av7K0QApF-{x`DaSYsJIVQc(<<+=bCO=AGLK3(qboV>lyh zeOS}}4Ue@sLWYp8V!u~*Y)CI`&lf&%HptD0pwd4J`vd-WcaLXxAG!v%yeP!@JwtK> z8d}Er_sWWN8bXz?Kp0tj3s7w`Bk`@H9kO3Scs;hSg;oMaZ~MP|}N zoaWc<(%7-npNlf!Xv=Po+sRSSx%S9ZHZ@cT(-2bilD7?C!roE1-CNb0h76ltO0jw7 zK9eQ=)6Vn^On5w|jsi}NNxewry1;n?`X zKGw=sDVUo8Vtx;j?Xk>*YX?*2_4jm)wdEdp>nwQ>MjV)3lG0w$6y6x4ofKJLVD0n2 zBQXLD0WH=_Gl~O^9&L~ywsS%9L!V}n7uF7=Nq}y0k}ttmE8e4R>)S&3%9U;au0R5VsYB zo(r2?d`HchmQKSxxiEU(9@c4%ys5E`_K0(<(Z-}8JHC3Ey4ItDVxjiEHx`K263>jl zrZR!sDfjMBl!(upM+jb!6J5mKrIi|hF+jeFVf<1fvee}rB3Rwrs6Wc&;r6vfY_3J1MEerJ7CQ@x_d zhthpf{Ze##ei}GBzURY6hp=~n=7tjIp53LK)xjNH5q)BRW^y#V;dJguqpw`L+H=zd z)}*eL8SmmSUC4;hCNE7RoD1^@(~P+4Ed|UGhRSaj;{4E#VwJV5tV|FmBY$NI*{rvT zJgpcQH&CqGQjl*svN9kGG{;XRx7=5>JLtQXeU>v8Aev6lA=4T0{c)jEhOa`~jD?gL z^v>37j-VyMxN9$*;=^sI-kTZ>RIC``%M~*8plqjl0;vY)yk7vd6m6;8vk*snPzYf$ z4$$oUN~Oxi@hiEnR%13S*KLJ>g;($66}Fg9Z$y=(-`&~qAOS1v9<41cE3u+HyH%Ym z)Xp*G4MqFZDn~MboEr~LrI}!QW*phKxI-7-byWbBcRfR7>Y&w+sHwz`k37J9#(*Rz z(t7(`8pUs(&WGAt-`2RQ1=*LJemjyCSdwnHH<)1yW&{x(s!{es z1MMNFBUMyuqHux(VRvSg_6bbwfE1A_qnnrY!q)V1J%D^c>IRvoUcNh@|`L3R)sqT)}wDUi4MX$ zS&muJ4hQf1E@c=5vV|TzGzI}r6=#Y~-O^5*7+Mq~s9&F@lu02Ax%+Mi^m=_0##1pN zB9}L1(sv{Tag;ArsTMH_f65MXfe#r--)UHCI%!Q4?iDYMu;{ZmXarQJiBN zRL#BNlO~yjtl-~z7XL8Fk|acFB+-ul#OdM6!rKa9#>DECA3gX9!r~O}e?DLBIFqi9 zL|>TDHzGxhT9{YsGrTt~!5V{3(MViW=w_$bTFQ;+ayz+NPX%C+sWtD3cVB zufHSWPk~_?u?mMw3a`UkY}Ai;EW6NGA^rg(w}!_-PA zI$wDhZtRvp?xHFE$nEz^uD4i3Mrmp8&`1+)Y9Zj-%R-DnGRZNvdFjC?D_wy=Ze{el zYND?9MtGT3_xovdzuNx&PFk zft-#5Uf)HnfKAz|#)7b;OBSAS@U$%I0(5>AX>=#Q0viO>1(S`54g%%{YB9#Za)}V5 zr}XGL&A$NNZbcdfam$~(M*Lz1GbYH~dP}S|kA^R25L_W5FR{yQL4pz9+PPXJgaTlL z;aT5}@#o=qp}!qTf)^o*PmH>1V>l4)-9P$TKjhdd+ba*Xl{|f_aA5JjZqK|Mt)c0E z0UBK7X(i46Ng9^xK*khb%drlP;uy-Wzo$*CW@K15x%-N(Zog_f$mZX$khI zRgInb8w~$&u0?Cl4%T_}Gt~YC4=hoUQG7=^#tx2cVCw(9D&>P@WRM&iw2pGHv-lw< z4)@U)1S4?I-@B3lYh<|dWBOyT>+{WJFOY=j&l2Ix-c(f-r@NrcYJqY$hzaa2zxP`h z+)>zn2m7u328OK{ziH(QKQ;bH519OWiK46tpX7>@JJ(|$*MG{+j@aL{yfrGk`OKSo zre!tg+p3tab-K*SI&wYGDnv$wxceE`r?^>-`7nFk>&|dK9+%-gA2X}G*4ooI4p$Sm zcz@+rESBH*e7t~DHmn7{e;1#>h~ui5Xpno~x_DtC-r#SB7h~RwjcWMvz($`LPVu#Cc2!p0jjT?h z2kQwpt7HJzH!rsLXW&J0`5D6oz1y<|k(4K;5oKoNzu2pwJ>ryK_GuC zD+k?Mp6R$M`1bFI2YDR_UIgxOWy#vxEWw_eBJ(7B} zV$uiYSLBI*q=&R*Rx&^`zPWE$|3%OY=^CAftw?`ijQdD>Q&jOf4cV_1TDPv{1%k)r0MUX4daDHhIs(G# zotAbQGtL-LESBRUfvuFj@f``!H~h7xKw}jt$V9D^!wvwBVuZFD)|5b72lEwaC#ygiKwkL^P4C=zgg@pooO6otL6uEA9Jkr&q5hHHRfYVsEB>2A0KC{s;ip%xFc}+BnW@vRZ4Pjrz&j z?0(v%Vul)T319IuITQT!^6aa#R&!#Hj~z*EcD}RIukB_dc2C2%0V09gl<<}cRf5eZ{ck$+u`o#Vwz@V-`pe!6436jd;Ha1Lfl~ zp0sTEW4!BRs$F;+B=#Gq%1o?Xte(uE;<7aH^7!{zj29Rhp)dca3s(Qob`n6J#?y2h z2S$xX*evuEhan$QFMfX>bNT4}2(z&S{W&T*x4a{+)Tm z8{5&{X=jZyU;wBMt%)(T?U$J;8j)rT=ZYTBi5SJ{?zrctyimEje_V-{hS*nXP<8Gi z-Zm#I2jf4Ib?llW^2&I-Bke=48AiWO=YhaYh?2?r5m3x!(VL_QM%rpK(DpzPTEBJ+ z|FT3y@kpSzW7lizJScLnpFaV~b>GTn;qHD_U|m7rV@XnAWRl374v}B^`CoHh6mXoD zy1jS@|GOH0@okW~HTo>aND?&vATA+KJ7ZW4%`(#3W)9jV{VejK1fl|pVj<|$D(G>z z7u3EG0jL8%d*EuHM41 z4~?J|O|H?vV;BpyxY%ntU>7Xu6m9bSM$av-7ah9rFvn=_H^S7Pk)R{g1CHqRn2i+_ z+?8j>ZriUUbd$HZwXW&qL(p)6fe(JJ7UyRVl$rZ79Iq2Vo)}!zr=*Xs2d}3h_WGcw za#(i=T7wSvMqP|nUgU_es3Km={`Cq+gOPHnoOP5@)61qwW~x)U-MIok3t08{b(rhW zaE(-BlbGf#J=s0&+$cECv%bwZFPD+FWm~u?xbtYpw&er*TXLNB=d%8JEFo4#jaftK zm9}B&T9E_ClIqYq3IQf;Bk{?b2I9<-iQPkQrW=v5OdBG4`_qcHnq};&>bTEKZ@aUO z-Ge_pXSR$ov~`DjMcCw>21=@|wzy5A)B>(%a%$Q3b%(vle9+h9r*90MN2?un5C1M7QRK}T4;ZS`0!96M1(!fvgLhL$9c1= zyHf(y%I)5>Gkps+G+S|KXpt-Bw&gn12{>a}p6y+HCAI0M+Iw zoIshG5&ohkV+F4EI~Yr1*gtxH{;k0WXY@D1tGj$W^&?ce)L9gHODvwUKq%g$<4X)Y z^%=M?8NSNiUAWHAwG{f2bm(xSaF!YTwj`NSF=SCn7>Q^KGn5Yn-QfRt zHE!{LiT_(z!^jRC*+XWB+I}r~ZNJe5;?+hXze(>#f|+Z{i9CX47$;gi`a5iuV$vWR zuG(6M?lHYgI#`z?*=5+{VY;&`eqKU~l_NvZ=+Bt+rCtpheh-k(5Ag|%P#`(UKbO4t z#!i%(B5tOg*eiedApA)_vnDa4^s^7u$0e!CNID&}GNA-L1P?)eVP^1J?+;0U*`J7jseooCwD7jyZ$#TZpzBL8>CDmtIcwF4u)jn0 zIFJ3}_5DGbK~)&IcurwIldFO*$WHQY;Gp_p?Wc_Gbvp(4p*Pn2i9>!QEr7PYp%Mu# z{^(24NRMms_E=!Q8ct|^n$*-o61Do{J5C2LY-H=8L#@IIE3tD~ELY;V_V*|GV?i0m zoflMO6@Cv!*giGCF9Z?Wv0mFpfNt1_*Zg+wXDxzh-QJ zLZb-&WT`yB2#?w8_gXj%l4r4YBq;RNHrzZtz0}^JmIyuo?!2Q|nu4V&*8esrqP2I1 zKklv}PBK{F4RXV9WG1@#8}6)L1z+iYur|aI=ENgYsVf!jhkB3ut1~~!cXA(VStHUYb08RSdRLhTBJ0Emq|C|Kw z)rwB{+mWA^-rAYR;4E))+!VZ0jI55Njw6#8|M&uWb>tqUaeaDtBhhs{Q04Q*pBpqS z9d97_zAb+wVkQVflJ+g$N!WB0W@~`8@f$M=Ixu>&%LWjCG|viN%Kmi-#MD5)596jT zFwWw5zj$m~TR0|9moME*LVyHI0kB7oKQl1Ub%Y!m2w(lydUwWfQ0s?UY~Y_1(ZsIN z+anBzVAZq_gK-$e-VZe^)Za0VLXjQ#D(L))N)+hqan_X%d88N)b1IzcdDCP0l~z?l zAK`2$4yrM>xSkGlQtA^~hnTN)-|7?Mhg2iuUXTOg+4gD4dgMH$bR$m#hCO`1U33VX zOf!Ss32}z;z#WlQesAl(`xUr5GY#(H!VIgmsV+q~shLuAH$_38O5|6py5mq($s!G{-< zpIJ|@bbA@WQzfEdl&P8@MN5fck$%nrSx{+74G(qHg}H7^q6yXigW=B>MTqPzmqG&A z81F$h@g?159S^F}T%5d9;H98oElNA-;g@JMOL}2VLq`-!9;H?Ln{8?4c`YU)+@Gwi z@L%WWf3gfp(hpaqn|D34|Nbslog5-vGo|moPQPi++1*+ga}A*-0Yt9O&N~`wpEhsA z>nl8D>u~fUv9CE}@KA(FUiOk*y%YO%N=|IcZU?m_x%lOsxWd8aIquD}kdHf_!C$NX zz$I#Wg!-9`6Q}6mVO=@CO*DC%#_uT&&GI72Q0;?TFQ+j5_oI$~6uxK9t)uiBg;hV& zB>!#7o-3i}Ss7RI(yE1T2KcAD}wU`4}nX_jmQ_r${l}aml}0NoMyu(c3s^Z;aYl)R2qP-B_Q^2Tqi_!-?Fg z9D+s&Lx`a`+I)Dv0ACLQNli)dhp4{rb$2b**PnI7I2ST45k8~zIYPkh(S}ZymkdEp zYSsF@eG}c4oJsT<0qUnAUSaUF|? zFv12@vZR`xi;n3c96uu$*`$|1Ime)zFr#W95;p5eFsp}q()^6?=-2Dm3iY{;*~H@O zEE!klR|t7Gxa?TB-eroalO(1S$pYc1 zA)?)B^sPp;Y5u&mpT*bVr7`@*AhaLa9e%|pc;YQD#Y+Ucu0p`a!Q(eyhtBzKzWer1-nHg(hWxN1HfCs2|`4g{-&8bG5_i5WDE z;wC;&Qxqp-VucW$+ECcKWRNQeh8H?n_WqOpJ@0Q4O8Aa|cLkhtGa7Y10lIjgSgwl9 zAiEMTs}B7(FGml^;b?Q{uHp%|dYyy8LTsxAAP+ z+7OC*$@y0V&xwm);f{%Zx2n`G4l98l?OhNqFqcim{J-4xX83nuRBt7s*~?VjHppmO z4j3=wbTYZhq!6^X1}z_J`Btv;kpS}-D`8qjNw?(VV=|3$Sy>N2mJYm&Q#_DC8F}jDMNS1ggT1lyR%laLVMtHT=F{D`L ztd4*8bFbqmL{Y2){|HqW`u>A&PZ2*BK4RW3bK}`%JlieImQ2B-VlD*smIgh-J-ISEOOJy(`CbH+ z5}@fiRlkr0-^T+p5mrSlUxdt_s81$`XLBlc01Qk|qzFwOk1}xkb{_uBaL$bPU$az+ z7uEu`?j&K5iSjuY0$VqyhZYS2Bmp)W)>g)VVY=}^i|xxznNafg!Qp(sVZ%JV1C8RC zlGtS^1WgzId=Xdj3}W(zaRxL^`?v}vYx||~f&=*rbgUJ!U(o7e`%f&$mS_F(L8OK3 z$<-&Qv~8|@#Fk%Vp+s$Tb+eoOdqoca8j`Q;-&yv(kgo`Fu0z?o3Ftm0ei>f!dJ!c@3Cg4(89rYf zBDX2XE$r{PTHSjINgsq#&S!Rzpn6`Am9~0?L#PU8CG-jgNrUGjj(bu_94RrzQmvWq z!jJV-s$N=^*=}AxWl1?>AaUcfxlG!{&=Xx4b?+mdDMf^K`QrmUimby}i~OxQkr@A8 zZG^Qmo(p6FocfnH48^yY6l7pq;H-kb!9&+J+|)YM%miT?hQT%4Qwj3YiQ~s_G<=|W z?<;f!1fMQjxC6WWFJr-H{K1VL_Q8fus4=Utf z0hHb%YBCl}j-3AzhL$OWTMF#~vZ02trRxtW80iU^*%(^U-iv_1RuBP(3|uQZl1 zifA7(nBw{7n^!le89aeZV-H}rQ<_3vG;~IOZyGGA`X}v3@)s#T*y2E?9!E!U8^uj4 zvFfaK;%_&xPlzI5g77$L1M&nCaQ{S(FV?cYiC7^ys?vpHs-Elc<@AyW1UXD}*NQne z`IaW+WoKHS4_wdlwAg>Hab|?4xez5Fc8aP-Q=5@H5u%mM z%BgUl3kWux+(#fAso5xEH;J(@9v{Y0q}Timk`rkLp>0VO{$FwcRV>}8j11iSheLLx zSS;z9s=te!^x^1BeVMVDU$%;WZ{8IYHErH7>mIoHuWo|=wFnMsAA9%UuYV! zGIQgfnH7v2BhWweCE(LN0y{aO|g&)r|wcf268M#BZU2*aPBribg~}ttB^9| z$jgx*r+|x$zaH7v5~ZeaCx^eCVc`(&6&$w{er}bRX{->wu?s$9guVsYJhj|l}&BZhER z|N3VumMQ5#J_u%Z^x8In>iqP+ZzK^%YYX+!%BE&dr%NSMc82Sdf;@rU?dki)jDblP z^f7y5H_Ne>9~zSs42;1VAV2bBuBsrx@n}VHGNg&g{-=7}awYo$2Vg(`;K6N=Smsx!+!#skh^$7t>kv-2;_To;T11SIm}y%_GEuR` zWmId3UGsGfM%iqKN7oi^#<<47mF%s1pK|PXQB;>s8_+&h8kR^Kl;(JQwC6+Ehp3m~ z_U~#YAJ76^nBqOe(lh=9anh$jFqQ5rkH_2ufzX-AL-Ar`$im#TY zv~?fQzE)ZMH;vwQL39CW8~4rXjn7gr>9UBOP&Sz9lelXT%TyUSpUV%;29eie0=y-1 z*1m-e%+PmfG&>zH8yQeG>-7W0EM&K9F}~WwDq?Xu`_o0%f91{6S4T~;-ynw5GUhMx zQBu>pQx~%aY%_E~v|SX%>p#+t93_8HAuyliCg!5EB1|ceAJ;8e8WOwTv{Pf*l!%OQ zh9Jf+dH0(?6R~PM3jJ7)UQOAJ^q;;3Tel-RH*6u?ZSyYNknypHKdmL{y=Ov$gl3bA zdcAYnOfODdKWB;}{ih({&LpQF+%W^lfxtn7pBFY#(p)rp_0OmYU&shzNPrKQ--o`{ zS^VAS=Vm-*8E`JBMMo8SE-Z|~?rW1c^@s23OA*Fsc@1lR8XR%84CHdCv^!Ce1L-@L zdDXnfv~nwbfj{DuoEe2I7M6-EJW)%p!80`gZqRTtCwRq=SrLxJ3^|GwkrKT+gQ(k3 zBqMl#`cK=amNSRo=L|y~15Cl>s)Y{6f4vC(Lbynz|L|3eBBkE5T9OI%s2lYwGFO&R zXR&Cd{MwbWoh1~xWxyBCME5=K4i01>r&iy#nna1{(q3S7UYwIH?(#P@$Tt0bd8U(R zU{u^ob)QNVeUybb5=d0h&b1`#i&v-g5oFnmIFB2-`$k(H-=7SY8E<1JI~q+(W5Tgd zOz_v7&h)r0LVm1E0*T#DJ1Z+(x9;vgcdSLb77zFw^~8LO-p5~STU8RBQ*@xe;M zZt}FTPet5C6+YX?-is2aPvHF&Ls&m*Bc#dtW5aP77JRZ%hu0qH4FMxs z41HZnz|o~j%q8A<->Q*jsr!baO@tP{0xfL8+*T@o+2)?eAUVRD`PE0^!&_) zgu<^Q(xi*WeI#M62_)Whnxxe2LFoX@chiG6Xf*582B?&6T85e4QANBrCwvmB>f=R+ zgP|Muyk%%td5kYrjfHYn#-)tGCV8E^iR9_&o6ziY@37YD%Tr%qK23~5)O_Q-=22;mCqF|FYFf8Cvdd5TN~gKt zhC-99eQeLhZoHw{X{+&J1HJ~N4a0x@Xw}hXe0@1iRqYplaz1nzkVe@Ja`Z=Wm*KwC5|2ij=dq;yHsy@*=YDqD zdxKHofD_S_bInlf65g4xdZIJrDC@3_12kZypRvvXOQaM+9E6H8f{p%REPUd>ub>gW#J&QXde6Fm85d(>i5vFM(&7850r^A7>{phYG!^zJFy zfy&36@O@VJ?|PwQPZIq!62>I^H2B@-aMWlu&22RD z6%uvLJ4O(-L3QzTGeOdy@E6r$(gGUaScjN60u~N37~9V$$&iAgR)5MyuTdQw{7m*w z=_pqwCO?TtsAaNr{l~QV-D9~^&3duWiko{QUgc#uY>k`}8Zw+S}qLEWeKf{oOXj?Kl zvfL2r)*a3ik;_^S;q_7mMrG#{k#%V6MR?qf;&X_^lbSzjN0Nyi_T6*)nuss(JjT$m z)pQhNw4u(Rf%4fi>!cq>za;kA)9QN9tb=S+4Px&D&R87{UMn8?zH-TQBY1rGKy9`SJYxuUmL z-b;EKGpj(ViX=DtI@#P8g2$DWmBv7TCx7wlPstZ-k&Xcq+=t$2o)YTc$f!$Jf3=mpNNRItiO661!v_)mCfd=x>u9D=ja3SUn-GhVFg)PJ-I^ zpZAx?JfQ=wdV<<3036{wGW<&|{l(B2IMp1v1{0n=KlE~bW==$HaaqW0f%$BKS*{}< zMTT$DUEm*e_MN_D6&KN#SXWT4nf9=M9DYWihR}V- zB;#>O^Z!xA%@X9l9(Mt_<*ZjGdjop=i#0Q2*nO&PN0Nupoz)f4WIAB`N0Su{6K|Vy zKD3s*U35HZFyGM%Gk#{_<=e&}YEZY>vhEsxoeEovn{dF23jfWsv)IjWeLd42t8`Dh zPcTvOOBcx-O!32uS?@^A&Q!cYyq*>8KW}sEdQ@jDdYW$Z}hzw zmuQ&QT)RfETF(dW4wWIVfD^M`g=6jD#`|y@&WMrPI57B=K{{Tl>J(w zP4>1KC)4~*W=QX}yG=;M#DuKLFH7QE>D;;WUd%n6Cc7Rit+9paPFHC@Ylq{rj70Rh ze65nnT1j$+=b}G80ta>JJi*x>f~am3Z3gbn!+tvi)~y!QK&Hh`DBd9&5sWfQgK=As z25+XCqwUt7j=R>5NTMz3WgqKSevt>%o36aOJ@M*dXVRYm#@C-mY9hUJVQ(v8LWFyD zWRB-^z`py99`DfiyCJuKqC0 zkwJRYKoQD@I+szz-mer;qdxN0^peSc zW2F$jghnrm)Q$?U-q-7UahE-K%9y|Pu6V1<6zSCPe8p+l8~sK&hnZQ{Q+4ixGhBMw z7lqRs?BLtKs&k+Ix-1jnH}&&)Gzh#ngB@S&w`c_qMZ8Y-btc#MTS=tV2e?IGZyL0% z8Oua1AV2_Y^%g-i=*d1ekaiW0?`R>)DcUhpy zS9VB%Q4o9(Rg2~ zIeK*gtxuRZK4BI)v8M18g~Z1Dw2R&3WPNNZIHO89l6Xj;vC=g*EHn9gD(56AuVJs- zyeWdrnwWH*wY>(D)Jma7EhBfY>%g>L4?zc&c|_hf0-7ct{zSW9lywSN*j%YuI3FKBoS7?a6ac z7J05Za95;Wa=unX*_+?mrX#$W8{05kZk3PnTHW~W}gODoAIx+AWK z4eUU2RQ~kFeAT}J6eE~oOJ%{Qnlz~nMZY6LFM<^Ky+`_?VK1Vha1e8dRlVr1MB9dQ zRcZi&@n)Sh}D zp;zT%@;_al-%vY;_sZ!d=@PE*7JJj|&uJ^SSx3=r@jqM}W5R&>-I1`xFt#*4(jb{RF?Gqv2Qe85ZNOl!(@v0^&FiFIIST4pz97YNdiF3(F99W|_zpRhihF1J7nsyZq z`DLRM^kJ7K%?K0?61>ifTRhJZz9@b3mE`1Zfoy3vPbi)lj&2Nw&ybF5Q>@>?|JFbHJ^MdnB6!56QUBPmz?tu^p7jpa-#S;dxTx zhbWuWoo&7MQi(1*TK?dytLPZNJ>*Rd&UY|hJ2a4%PiMKdU@|n%vf>bPOUTkAUmb8( zD6U#ubl9BlP;BI{G(o3FMU9y_Q_%d0wT9hZX1X&M7j?-! z#;jdOBEYeKm&%DJwJq@A!Q^e11eCGk)Cx?-32HZaaaSBIBo&>zFQIV_UW4($sQ+bK!| z7gGVN9CP<8*ay{FIi~v5oAq9?CE+;JqNF-LBcyC#IbXw5B!UbomXC}0jn2i(fjK)8 z*(%4h=|8bFt+}us&ei*w5s-*YWr0!2=T4oBhQbzwgP%Y-x(MexwD%P_;p7q`Gx=Ju zSuRTd{;%)tS1v|WBO#3q-eV*S7VUfY-<_^l@c^u6(aU&f+=DmtM7{SMBx={$uhBvZ zk$A-~79B((Q$V_e0^-{sq&hw+C%c%HW70*FuK?tp}Rzmq_)>L2C3M`V@=Zrum>xQ5jUc0NEl!IqBEmM9>U%NOpW*^ zUaEIq#LTo%ziNQFSbe#5ma=2mxg{>sU(jLmkAnvV30Cq}GMNb&3?bId0o`Ej_3fjh z?7T}Y`bF6pLrn;WLrOvflv|1G(}98%IkwVYT(;VOYWTsT^C^pl1Lxo$oAL)wrA82HtClVD0bG4Bcv%9zkeVXtSV^1o^p9GraNf*Y<{Wzli4)RkPP( zUl01<TYN#m9itgPG_a6hOd~Y|GrA{)du~8gSY2>?C>;BtguzxW77vDSa}hwOP<>c$;Z^r zsyU4tlfUt_$-etL&yQG*TL`9w@dbBjLB>spswudwX| z+G?YC3fg-eQZ4fp2#N~#x%*N?0gu(zeLnRQc&!9|`}_I<-~HFM50Y5ZtMl}=3CmX; z=hScT3}k}E{-Dok zpl7S2x_<$ACbxE}Q*fLlPsa)BgvXK)RGzhn@l29)xOUq@RZ)Q+BI_j z{AtIEdWg(0r!>|@yvK+1z}G*m63EW^VgzV(H1F9Vx&^beRVNgA%Ax&SXFMoFP1Is2 zsAsvy96xLNVdd#7C5AxQJ)0}($HN!1loF3Z9`+d+y^A68e{YhcD4r6-8jPi937-Q+OL8xARK~!<$B&T;!j{6Hn5UsEf$5?{GP#of7U#}@s1*5Qmhxh9{;r1 z_N>2~5XjUE^qwjOM()v}>S$(ss;O_@T3+M?(D|*styQLUhq&e5%}9>Q95w(5D^@+k7kPLo_xo0*>EDywXmrY0Ovr{JBXD zk^vWn2qRiE7mP7l#QH$S*Vn{IL0pAqHu85xD#xuzJSrl~Wd7CCk7%Ib(|C|6)R2cU z$3780sEM3Bc~19@VFq;U0R2f~Z^ujxa2H0iGcwFXgu47r(Hjs1Dw({K&dlBwvaX7# z49F_tRN^U4ugIX{kmAT*S!Upa_xu=bJcWm0a$46^5<|@1?#=S#m1(+ZrAgsE8m(%k z0({W~n~SKp&*L91vUMS5zS19?^B%u9g{%%?Nq@4g@A18>Y0*c!8eQfqU4Kw%xw!u+ zV}F&C_DU1sJY2e|Rk!(jcWkSfYSvZbdG@rQ?H``;)O3m8Htw?RKP_I}b+F-@cbhSt zI9053Hq|*0jXkUg!T%f}V&flJB^MI(8yd#F(hFr<@*L!yc>L$JlW47!QlnJQYJ9(~ zSfnOaLL%wgpQ8mb8`QMqXZE%xpW*gYwo}&{EA#HHI%ob0RT4nPZ3Jle+7+d|% zrQMBYV+Os{F)zLsWexgP49Z?Rpbc0T&dVmVe5}a|7~Z}-zd&dB7ePx8;mW18)o#eC zN2_I7;OehFmIL22NrVQj9IS9)4g~tj90}7k_A#$!hB9L|+Kq=+zk3h8Go|;JC0xBt z!N#ievKIUvOprkhMya0)N9`6Ae-vH+iA{51&OJ1A#&2@tShDS@eipIjC#*b1nK#%z zwXlnrl-4AjAl%)Zwzx8!?)j{r-l6>4(&6}0Lu0)4g!!kYIis?U%7p%(EQyl?ZJ?x@kUm~N*fvC==^ zt#o(U!AcCoJ>UsQV9tKdm7o@8cVBwCWHwX&Tj-F~WCI&uwfJQ(NEu~9N8}OIF9Fqa zr@%f3~TQ6&U2!?F$Gcmf=Ig>2A>H+%#^XNEFbkv5~wwhq%Tke7KPd(tg?Ip>wA zqDH>nwbfaleB&BkmLKUqNZOj#umDwfRL*GBkeP@7KLDUWU%xq*(#d{b9Db*ax#fEE zTXefWbNy&;itRZ$9AoZpGY-pZ5vad z2#Jg?kkNHy^oFrDG!PLavu&MT@lIP1jUsZ4AkLYzebTun`qwQ0o|WJ^u`r1CkoBsK zPjYpr4t&BHX#cpt9hc>I4&~WeU7*g$u>uHR!13Ghjw*`3UV4Ekfa8C%`bUj=<0NPG zkFdg8^&>##ED!~-&prv^tn_7bt*VWbWLeR4P|JupFFHC z4wb-_*0H6oK^%89W54jbGk@~&FU)?{Q~_sQ6u_!2BG_6P#9kwKu2+7kfW89YM?dn5 z>W4q_vBo>Er9Hp?$f|F=@Uup5zJC}{|38xvxGLJLe`H4Qb_G=nnRs1ch8)bk3z&yn zN{QLdN0xo1?7>vKkQv(PZJ8n$wTdIPByqBq17PY#Mca4MYM5URwfP=PkZ~LO>StplV&# zj=6R0?Eo|)2r1`M!Z-n7)dGMGyB}b4pi>k@YMK^L0Bp9yUtjTWLYG@-ozy=;IBL}- z6esm>00EH5WF#(;$?4ASU(yL^9AM8e!qQ1qbINb$=J)3UZ{(l`60_x^GyUfL1i-e) z;T|J|?GMB8zuOW~cMLY6E+L970CP@*+d}RV0zG*keJ7P%4$|VI*;AzwKVdL#5EwHRF!b8MDF6lnfFzA_>6xOwz(U+* z{9{IrKz)|YyT~ul4Mh={oGf5stk9Ae8UQp6V#6_kt0Gb-*90Da*xaS_rdv4vRfKEW$&4cwiv3oE3yOo)2v>vOg zK#zs~eIbXMCgfRiow@~nwEepo;3De|uxzPbBAXRU>J~Gy?OYP5WmGgcn>Qbu9~t@a z2$BSWgKh?n5NxG^AoCi6k`>@t*~5fP&TcFcsLk-s*|4lbs{jtXHqw?&OBaw!N{n7U zq)nW1@Ax|aWQb6cvP}W3*b+nABZ75@tpD@NagTw`7Z1bRZrev+{Nn%90N~+UN{_qc z{R1S1Bb@io6TnR5M>*A>iB_c;gK0M}{o5q-WX(xq=s5yEahFscQPtd4Y@&LiDcDh{wM z-H5|d3z;mI4mg;0rR{gJ`6Al<4O`OKWG(xe8_m}{R&_uTt^1GwX0w@=TR~mUAB!GC zWQ@kULC)2emXeFseUgA$6h>)M*=bSVr5L~@)S`f>5k#Fj&+o!e0gJ^dCMOF}SZjH> zv1x&mH>o4nou4`9kN&FuUC!pp!Paw>+FutjDp0}B)BOHMC zp*x4K{^h%KTg|+G%t`%Itu=prgsmt5BN66bT+^SAxU{JfWJ?fEE)yQAUP<5(i(6gjl|R~CG47kBuy2tVP3g3P12ZG5Cy>H{6s=R z;{Uhz?$Nd+Wu4&fi^$xMbIv{Y)~z>1QN>dP0T-~EfmupRH!w7aq7GEs(lR>JK5#@w z1)afmT-Xa|mSaC`T6((Crl)(B4L)E2@^B$dBSQ~h!RSJJ_V`J|j10vzFn`j3dS_^ESm|<^kYxR~fc(Tv*v){M%UGM(*{7-TPZjnN; z%SN$Zo9mDs^I^sSUViVR{&G5-KY7w8_7B%j25>Qk5t4yIGSHqel$M#HE6ki7nD5fA z^MN(JNfZ^m3nhC6nsvgOhK_` z`5qB9bPlehl90|OwDg^{fQ;i>M2LVkdI&h1z%_To1M^=AKYZ6<&S$%UJ@np`9dDII{uC6QvgQ;AwYiYo)RsZxjKvL&AmrNPP( zTM{llw=ek_IP$r3$-^pkWbG)_O^X0#(v=b2#5VQ;hui?5LL_21tk>kG?K#k`%hr66 z*I*y*)l@?*VfL_))_Mz2sjO?a!EsiCz^tB_!j*1gIr(N&ibg>R0VRYnRxp&5n`Hy} zh_^i|7VvtJgNcbCSCs)|1_s;O!2wYCOB}FT3$Z`AyEM zAGyGIoHbYE>g#RX2p=99`$z=QkL~OFUs3zQuQb)KW=Bvxla zW;v83;0^bTUjkst>>OA`c*&~6y4VdefbVaYt#2*D<-T-7RI%2;b!?(&J!((A8^F|8S@E(LrmC|h)Ei* z-{-UF!tyn_eaYdnMh6!zx*fOiyTc3s3uWlI<$QUSdFC$rTf8}|S$d{>ot68zD?-rh z@2$CQJ(E==qjHI^$42rL(EBB0s8(6kMI{*xXvV={04=FpRY3I3_CCsZz~d?j^GJ;Y zj8ZVpE`tOk+0q~(9OLS`Ai(Muh`}0A6H#OYx4;vbFdjN{67yM77lsCSAVE80fye#5 zb3b+VvOXsWQZI#}r@-R#++YIdhA4E!04h5cFilhJ?CjwD`SWXow~fUB4hAv-kp1#& zPJZVvy=L^JFW&FS{LKZ%+s7dGZ;grIU}Saf(rm=$OJK+1!D1}n_#G{Y9>!#W-~m{! z*h0|`ddQ<6M@hIM?^ON5b`T*lY%u&D3MT#8iZ<+}3%FzR~z$^9JZ(27UWY5I^<38gOTm_h9cyhovDPsp+i z7cN{lUJPJm7vFQ+U(M(FlP2r3f5(UcTnw867;LHfcDgO|)s>9l1Aq+-A+e#SX6x9s z0YLI3YJ>nl;Yrr;C3;XpvI!bMCR(IzLulyG?RYJ-#zZENboN%{T4V#*f1v z*9QPyY#70D+_rs%CSEwsj}TC9E#ReZx~9%dAl&SguU)i(B%*r433FhN$^oai4gh2a znC2d%K|@Kp6+%%M-VaMQh;7!Jt%(La0@n3Te$`DZikL*Mg!n~Sws25pEHi*HklavA z`gpGdgOlQ%cnatY5WrC97^-~P_eqKUB!ys;$GM6uTVy8VeL9Xqt_I|gAx8y*0*Mm* zp@lREA$0=F#A${cGvt6V-;?c0p9Jh|PjTVGHjG>`2m!h>fqwj>rTCGTjlbicziaZl zf^bstN=9&61~9V$_LL@uTa(*(yO^Is$U$6$a2O4>vcMz}(6Pt^$&IQyIZ)r{RA5PySN^0B^Ww@ZC3_7EknN{?3pN z^25vg-9X5VkOk!|B!iM7{y-!Z>(G?d>5)Z&@FzP2XP1U#vu+U`lneQjFrxGZg%xYx?@jW8Q>EQ{YNd8DMw1WqieuB6;%0MHnq zEkJAmavP8*$}HoZYB9C9b#(!|u60A_;{A>Tr`*&d z%lIm+$6fd5(+6y-CSv|CJk_p^Vh}zVi_hvjO>yC>*gB4f&i@; zXU^QPGL$F4vN+*y|MqXkM?d<} zUO;ZG7{HHy^pBJ0KmP^!FaLL^zWbM6Gkk)t=I?ksMVjVN>KN(%m0p` zU6=2uL)oD|NhJ;6lZ&k94v8LgeAlzVKDX`9j`xG7%pBZFAkHc&>`UP{KBwiOv-kxY{4Dk}uYFBCC^&=_iskm>v4aS)`yx?K#ACgfq%qos6SM}Blri3(Vej&|Rvu1D(Amox zf8!~G+rHyl(`S9;v)LyTiS1k=GctjzWRJuUfvz%wg*E$KTv(c>$g&K#+;Yp+WdP5= z`_?byx$?7rw_QMzs&4MzLu3F+FhrSGAyNiO4gf!BX0V)P*F;0LkA}EEXTv0i&Rz&$ zLqJVlUpyijVgnH>g)weH6*)QpH7`&UJsKRXw+(e|*rB=w$CwceTF*i_ z&T@Lb>L#A-VGb>6XRvy%Eah6QM>|6p7BH-S-dolxW?cY8-p71Vp03zl5gl$8@lmjV z*FzlOBe5p%A*?UtGTT><$mrpk3LpgbiRReCXi#?6?s3*s5+DVAz|acNiW;w32#q{X z@ZgzKE0sYq1$D=bLVo14*?lkhg^OPP9h5eY+XX~?k#33k-y^(A1K zpvYJ#w4w)j07gjcTvP0!>+N&JoS|dfp=f#2+v3|o^yWlBoF1AP*~SHapxk;7u6CeM zm!y+``%a%WB604AEYw;ChK|qj(M1qp=2L0wm*Np`iYtsn3C@4-ZAE z=6fvwF4X{N1n>Z2OG6A9ZQAZy3#4g=(RjXsby5lW&_#_GJ!|l!Z@D}9W&r!5hz0bO zpK7WOvbYgi7aDXAJn#UB`U#J&%>Z6`@1wprnav;9$o?e?n#JM1JmjaFnE^Q{6vf%B zC%+=MVzX4$|I%NE#^;J9ZMcZ6pjBr*U2~9j!~i<0bJ*u0Rx5w7YGBnu1(~T~y(VT9 z?bq8SR*~JjG)(@sN8zJ>upF)q-#m&4amW>0y-ZcA14a{B0M1ljEy+Fo~o3sbn z<7RHCYwU|PTGWzM%UZ;Xa*Z08+y^FAFh1ouDIu{h6BOO8UY*GwMKoS-&tiAK%}sIJjuWx7{z+Q>ak?bdl3U96aSCK-0nDH?Am5jMTv>%` z2#xtH#X}FBf|P271-$E4A^-0W&p!S4fA#X4iEyga1T^CT?O4DnBRF@kfVvchW@bi` zB*?O?^V97`%l*R7|7;2X|I;hzdrq9<$6c9#o$TM%)n)$(Kr+@ytD3yp#7Pdejf}+x z-&J-Xxz?j)i8d-T2^Yye!r0bk3hezkl75IR=B8<%b^?I5gq%H@vn$8&)ou-35B4=t zfF5EKOV>6d^mHK9ummxQp)4Z>o`)^Xvk7J@goWKi-QKpvm{i$qI%sLwu205%(|*^v z-i3F-&L9>o*~RwvR^k@PvR?5J5Q9Q{N(_L(qd@7MKzav|-U)~;2(7WD6-H`~kiJ?&U|rr#GkKfHKIqESaKm|UgAQODyVu5q@@m^+?O_@+6_zy}s<(Ht^=k zBo+<;<^z2;;IqrlA&^~b6K+O>tG6v_BLwudWeYcKPe%aiT;F$g?XCm#d|y+G3-mAm zp*Y0j^xS8wOpMD-$Z!qkg^LP<0uav6tTcp~ z93UfMoIN-5VG#gE0Jy337hzXGIwqrBDjc$DRcpb zNkEK@eI(_isw~El-w!>TAiJXdqR`g;5klj^GpCT}$x3;+(GZkM!kzb={qcht_J|;u zv0nnciSgH#gvl%bsLTTBeTAVr$-vz6%Kw&r=N<3N0pMp}b>i0^dN@ZS98W|5ySo$c zQAiT#vU10^b{~&IST}TI*hqT7O%uB)0xEJ)hbkeVl`)!ROhQ0fh}K^VgGdw}FX#oH zXqRE3!1a2Az&@F}ZERy-px3rej~NvA*$*2TcgM(97`o62r%y+bJ&Y&S>yHxErMfua zxsnxQ%!a&7O_;P&g-DBSSOM~8V6?kDwweI5uOQVkB-SYt3mhcF8YmWs7HxyC9e{LP zv@9ZWBu@y(IN8z-U{WzUP9B8ym_0>r7}Z?X+RJCI@8B0?lO`X=-C z$^zbd-=qHeU^>4mDE9BLUgpv}A_t7*gtk<~;mJCshHTmv!DI5Jn76r+(UL9PRu8wY zi<&%)qL0y{z__}An8KBFZ)5R%aeG~|j6=5R!P<3>-^1oyyKAwGO}`&@v03i8>0!8o zee1hivSz0ex?SLJY{v81F6yS*#jxrR!*U;MYoMGhSrQ=(RwD;E0p?!@re{{VuW2$w zMxqGO(tud)NH_N=aCO|3(FJ@+=VU0TjmG&>;GBl_m8#ggF8!1<9?&D|OjRz074IiX z5QxFj{foqK`BJeSmmzkX0eZfS*VQvXUjg(Up!PAlA{#>}YCtd1xO8y~7cXwrgrNZ- zSJ01rv=py-^Ob+|_76_pmk6B5^+FfWtP?yl^Z_%&73^{Y*E*C?S8%Wk_~x5Vrw^Y! zpS|(>2e152^ zM&2@P2#AOC0YYJM$xUuwv{wQNuIj;oKu&Z6Wx;WNUBF+LZ9*${P)nQJbUC-cb!g4) zJAu_Mz5W`NK|Q&zlK)9AepXR7f@vPc1aWwk+GLK5$1BRl0qU}LTIt_$l!RlV+7QU{9K+*L-gC>cN`B@-y> z0I?RPLB{ubDZ?bTOBeJU(B-iM9GhATZ0}64w>MgS#00u>h4E9b7`^=MADnzRFNy;t zysQLbRtJ$*RS@A&oG(U;G?Zo|kGmF2dGz2|K{SX~d%!96Nu+4g2Sh;0GztWNAsT~ySiemr zimU^wsO2kK;|1x^MwfMY)BEnr3i{5EJMIesHHuwX$s#a2*=8GU_E1}FqX~Sc z!1S+;c;+G?Ug;~)de5rE4JZ=sY9!YHgLYLJKwJk}fM$G~)CC^WjC4F$CQs4GidBWD zOscDBLICnzRKGIE%F5Fsz@B(yAngODig8t@^fDNre^@p0JR6? z4?|4}S_7=Y-h(93xNu<`moJT1SwIau_E8dl=M5MC>H9x9|9B#?ldDBC$h2euXGV5# zR{ozEx`4S=A6R!~07Ido1c3kfp24e5-5?)xW#RyThaCII1jw;oRPbmHuI}QF^Tp0J zRi|hIH=&w$SVOzbcm92tpb#Cpb*8f+t8E(i;E?tggNzP`^4Qevr4B%x!V{gG6R#$Q zvFW&?B{osEoKt8Yk(PDm2bO1{`n&~fJslW51sFZG_&a_Iq&$T*2U4wS3qhlZ7!KHwn`LGX^I>|&}PhQS}=SxE{|Jk`O&D* z^Ao$}|HC5TLyXI1xCDij4q#0!!J)y+dYKS{4A2a@qao7vsvt{34A|M3!bQjtLrtcP zpL^BT%K>n%7B@~>I)I5IcoLqHU=Tp6U50etU!-$O5Z*>@JYe}8b(S8|PR?B6C{ zvJ&qNPbj0!GyoxLa=rQ(Jkde61s*qYsLNIaxOusq^;=;T;lFMKWtIt1V25M&9u-2xXxDZ7(uPN}tIZBN7(xoHgu;E!y9YDKUAIGkHethhotePO zWmfhXoLTZSmS`KRrOTPCz`y}`|Ia}4%k?o5z(fv9)zv^|Axgm!UG9nr+z0Z90Rq&7pr*o2Cc2n2sd4V?4)*s)D=eU9;I3OGp8aFzf8|f_ z%ReV1Mp`Z6|0YH};DNya&da5W^RQV!?ji}(8Ur{Sr3rxSAN}yDSN$)q9zN!a_p9*O zzfG71#DELrEvrt+12(9O>l7ViRvH)vLJR0aKrCLU%eG10rGe%1YAl7gebFP)0~>I^ zhBAhIVgNTaoj;Cl-%aj|I*cN;=6bLuKEBiO#Q;)=!#K9%4c^LNSGF>t*z>LEuAF=8Xx z11*|#O(0KL4?X|@l23|(0unXQQdy)0coO`spP<^fP1QC+0p(!>z>fP^8vs&6qD2=5 z@VEiMqXd(O$@tMB?Ym(+4rSN^*gkICj{3(PdgJgygufl@IJ1;_wYmua1c({b1f3W_07#_5Ll2$AY&NI~Lj%R>-+WTwiLW{T^Iv^XeN7Nj&5P8M33F9?VsAUT5?M3`MblUf)>m#a7wXsq+O z?*QMB@yS-kr;nKddS>|^C1ZC~$s^o@9Xtg6r(XN!4xrrng4xriTy2a!tRNXoUqjY& z7dEe^;YXfpw7z3@OyHyu@5DywEv_dVa=v;mw*M>R5II02$obc?b?}AN(S;L4miIEwstqZEfj}Z}=8N=bQxwmZqhi2yQ*XFtQ zvVXVTb`COOfAUAyo2CI8Fyxpa1RHK&1Z37Wv3I_hxR44Lu52c1(4=#K#MTU43e@AH zX4kX#IuKZ`qX|&13FatN7<%3KKeUTbw=!{EUCIIL5RN$8_ox^`4%pXG>iunc8(+4v ztxcxaV@er;Y-3N>k3-@WU4MNU%Z3EdZbqr$>n)%h*4)jF;8ZLJLqZ)PaJUa3=reo%$XaIWvk`khAC(~6u9gA&;8`3eSJv~P_lqCM>g=>$OiW50&=J>U=w900pJ(E zfAH#4H_As}IamdMc`mTEHA9-}&1L>FKxqN|gk*NC72Qm{PJFyel`c7z4?zfIJN(#v)Gvas-j=f|3)C zWd}0336dlvGDs+6iL7Aos7B@ZSuF&JWC$S##*#oJDTJ&tYDZRFo!Jf&D;ZTfJ{yUu zRkke+3a}>zWj5}1xr~bmff$w5IgD)G#rNaodW+275(utn*@nEcRQHr>1L)PrG;6}M z_)e}RK}7_dIB~F28AJ%s{tWu$3E(HcXY^tKdqnmNwvrhv8_E;aI?*I)PV-3GdbP*i zCT~H;AX*3!8Yj=#-QC}K&EIsamzD3x$Nc%?=mOX*>RLaw#eGX{j8j`8MqluH6a#S| z#_q7Lp=$yGH@h>LdYr}@Zeco%dvhGvKXga};eh+g0W2JMIHe}7!A+~~b`XhnZAVRQ zj04(b3yT=yQf_@F-1668V=8t~&w#CO2JlqJdx(V#dL)Lxm5&426p;1Na>@dFrSDy> zFtK{auRrj}&fCs_CZr<;Vahv-0g&EMfF78l$tjlcRkON`3%T_AXtgaYGHngd*P_w} zmYKUWGQg!d7Gs$NLI#)=1Ivr1R*%oxGnunxWK8z2(zKZ__sQDI9L|=Z4wb02NC7?o zn7~Y(d&#OiT#+a%Yb;L*4fhF16vWLx&o60(E zb3^*XoIN0o0g8&tubL_lybA<# zX=|77|5WzH{rBB+ag^dzrWeItiWM|rG8tkx%%L{)?j%(Pl1590Y+KA)Ks?(<-Lzc< zWT^$%^OCkYbj|Eg=D7d0z{>!KvG)_cOg(rVpJyaSkIfJmBDLyrD_?x=CPFl z04>JPtns^$U@38k7&|;=Ww!4EtJnmFUFRAOmoAm~2dl{)oG;HAE(sWOeG~ZE;d7n} zVFp0u9&cx!3v6#saOKJtv_=gCGJ&%x<7LkoJnOCRnf#vCEB^H+CQv&-1sBK6`!Rrp zpx_|QvU&E&&u4%9oTsGU^U!&%A(wYV2;ksgfZg5w&5WN37gavAeTPG;c^oCUudf~mz#b(rTw9x{&u!O3AfCpJ1IxglJ^{n}zvNH4*VJh1PFhY{jDpl#Y< zLIz}`u6xZQu-$gu|AtYfIp9*)_nbPOE3esq(3W zq1J4KfJwWaHCpQIiY4HpkHw zlqPklk!307vlQtt@1F>w0lBRqQ?}fDT{3fz3urn_;o(hyNry|0n^+!pyG)(4{{VL` z+oHEVmvGylvJU9e4q2Gc=S5b>1*3|L*@*J|0Fay9l%gGIal^glx%Hk7 z8eENi0WfcXQM-!oVr%RndnUDTR1pec*yxD*G|{8)X*+*c9eKMg$x`T7m*e5ATz99w ztyaL+bAfGbP1q`t;u)alfEXb;_%zb&Qn#kV00uk2)>8`ib<^`*G~uNilA((@E%q_2 zc^SKBmS7=6c$FA@`a^0G&US_#|VGv{nx&|9HezSW0U#L>^4+bkZFcNA_l@+n{z>->z@lD-= z(pm$96lsZmP0(j&WZ584nw3E6K64P2^zvvlYG(l7_O`dtTi)`PlCo3^0RGpv@BO~| zqgyaY7SsDB!OV;+dn4R9+FK8%*xHbd^)Jy-sRQB3B8LvV=;{tW3s(TOfGywuT>usz z;`MR!HLkc2Lhf}_OqvP=fFZ0{%eC+(?@wRAm3vMk@VaWQC2hHA(j#aG+ynGb$M=?d zuyS3%kShtq1n(6i>!FG$?RmW+=iGF!rX8e_WP6|JO;T~)KWPlk+8yUDDz~F!ce-L9 za)1)h_-Vj!zuq^HRU2*P*-T3PxuF5ds18J`^CKIaX^U;t4lf4-Qdz)G`P(ctWO<9! zXv!9OL?x~u>ACN0v9KO0_vTm>?h(?RZ+B z@?TBQ-^Z+39c<9RfflzUEo(pmo)TR)Q(-fRq6g?a47L}ZS-5?@vwB@k@4XQL_n58N zL3soLeC)ZtK5Tp+07wB0<+|~H71QcDH?p6dID-354d>)|XBJtfP4~M)l^=FZd@RjU zf))60fNFvYMlCO9-$I!dgHR>NgTldoMm-(_3>Qd?EZTes%0!S)eBu*@KiCk3PF7DYuK_@%k(V2N?16XQ{N)p4Ja&It z7llp|je~9>wAaA$>-hd0Z6`*-s&y688>{yS(SfaJYOY3E;fh{sl&8{(R z&LqVWY*E?Xbmm}?s%@-5V&)dW1Z(EiVUT5RW~XY*MMuVxZI^RML*aV+Gdsf-@VbeH zsRJhuZPGcuvSx;>hw~P#WPM26dY{ZsiuI4()bbQ%3o@v`4rGwO~JP>5N=V zxgBU<4Y)?1L?a6fa>IM#V(GaYb2Zu4_2bi|WGj`Cq+!Mf zk`=pGg+iH0B+63@SbM78gvgc!q8kS0^Q4vmTv?g|Upk{71Rw-KXkNrkm?hv`T7W*k z|1zMiEDhJn0J=_C3XA|u-uC{^U(*4} zEO&Vh?3f5B0|2Uef)ULe=Sl`M=wcSh!|b6sPMob+H1YvimwR0r8Ln)N!)T6Ir#`Xa zX5Xk<`Y2np*)9;ltIS@_#m!cBvy08=)qbPK4%!od(oN3xM11Orxpo}CI>sXQPb}Mz zeOy-c{!3?;yV}$UWD6K`e*&UyDyRwbdAi!SG8mHZnJ;8tHxfU(E}c8mB{^UfGIPp!JZLTF45QtV&Z!uIY2Di>>XuD(E13aEd3wT(PrOt@rvS~LvVpsQ`L zWPfj~rn3WZkr6X00>PdQ2!v5D5grib=rm0bR0}5N08Gbz_Y}1OBA?re8tS(;#li(j zSu9+7qMZv<)VkH5?7jLUNv@PMt)OR&M0|=3_0GKuav*BIT6AMt1BCZa7dV_gHo@!k3{fI?_YrvoS@AZ7g-CJCgPGd}fK*;fIK zbiMqF@YHFvfA3s}HmK)i21|i00Q>*+_YZ#Vmw#yc-+bw7nrs?VBpMelZDBCV)^r84 zpWN!byAnvUcFcgSS+X57$K(zqcFs;!1F)-PZU%0uyf#yJxiWOvi(kEfBs>Ff2s@;j zT_G!-w|O5N0MKN5xGBq)yPYee!Tr+)-hB6O-m}r!rRAEWP|R3KVZ|2p$!2A%gC+l9 zu<~;VtJx6ix^iiGsLC-nrFwp+Amva#}0 zi%6dNF0N#u`p!pN?ZnZ(NjUlo83rgPmLW4z^Y_y_lnp^){bNjURL#3Owh`4 zNr<}J*k&CmFT(;1FAc4aLx_OAz440Zx7Og@Z9!lBhCaCOesu=Gn0dj{_~HO_4-oXR zWMNsrEiB-WncsfjzrFEiZoNT1?ZOqEOR?Ms0nki1`|t_edfO!kftBLN{Sh3s$Up>C z|6sCaZE(xQa0_Ecu2{JMW^Xau$Qx$NZLu{7NXpP2*GNNr7`X=RHo#=NnpFoj&Nm>? z`c+W4iKz<#mR)CiKJCIT)fMs$3;4Q@t=16Brj9}A)a})F4$y}PsyI|vP#;1q+2aBh zJ(GuE0e-VzY3if_aDT^R1U4geX8D1vE*5m66I0 zmd?TUQ1x(pC5}Raglz_k9BfuwSvT#_bFyY=iy5JCWM*{%8eOvBT!OvkJsAanw-(BN zCCktJ4Ibvn{y(WJ()Sur9q}nXCd>)MLu(6*aO|}La$V;uI_?Pl-Ns$q?Oj33LfRp) zZM)uUN;|$AZRhOJBC7!kZfP%AGb10a*zBtW9%(f@y&B-joIr^N7MmQbE;Wa(fcxLN z#tBS^ceC}r>QdSzRJ7Xlo35XS*y+hWhu)(qV|LnJ5vcKar`i&jj?09Ji5rH!mXK z(xq+ZZ&E-q6W;os>3;?=BwAsSx-6YKiDdOez1vcm5qyf7Klxj)Kk-j~=q2Ny`qJ06 zN|Sm@pAZ_;S&H*#cX8v*Wv#}R{xv@kz(H9W=Y%pmVjR$+y9RYvd|XRss?8aj^2CUd zB@Kdhcbm=3)dnFK?N1$nNT?98^|;vt(0Pyj+3U#m?OYWlYLoZ*awfZ|D6xq_P_)Fe zxr&hck|E3iNe9ZpbR5hHOL5kXfq{@L>e&X5mG>Ns%B(>W0RqQOAhhc{LJiM3l)-A7 z>FNeXa#QW6jXf&+B0UXAJdpMGkkFA4?fI^*v%Q|K!P;&Dpj-W{+IRGMz6^eHNK+v| zW0Dsu*Rp7M}18B-%mEzetUrp8_c8|H#4#HV6 zvMY1rwsO7i?C?k#9mA5_Kvtr$w>QRQGF*xMQwn_Z4T3)Tm)Y4r`*Qvz0K2(bP?q*0 zTB>89rBX&a0Ay~Y1~d^g5p6N^Pk-s(-}us7PRS=ed`YWB)<>!&(Kwh4zz?6ojW=B? zkaa^$U`^t2ChAF=L7iOFfZkr9oEb<4stgTr#LkfiOtPJ9;W>_7%hGi%Y+suQya#;+ zXhRvJ#M3c`mU`-N4$iuo+~JL+4KWB^2A?&UO7AXB;Zk&Pi}Mj(OXwiFjt(6=Dg^`% z(9DN$ovx6Iya52(Z8&X?ciRYvvSKQmy7M4cJ(n5tMi#p>iD83`X3d=`y5AGi6&d`> z@EZ5rv^=1nF}+!0$(WKb+?^cQUb`@rq6B$!54XY&-A>PWa9J9T_sUp3%dNYJkFP(A z>_CES?*O|<6Va{7m(TDh)51c*-)x%T{P`USpe+`-|31gK8(hvlsOp4j*iL(9ZWjH&^6vQua4uEp7r~(m0$8@VoqVN% zy0SiPtObWLCO$a~LO>#i>!BFggGB4sTV7WvH^o=xPIB4lJRv~M3AFMod%*6t*ZmlnNHnrc;^DKqPSQ75;M-3N`q*dY zXa2)~oPG+xiCi76UKIHlKO&+eNvH&bWMrF208$XPl;$%peD?4KfAqhf`tSqi6y_ON zh}CDPwSW)`r*GQBc$_(vu-BS=ZEAhC=RTs_OmdBL5>jI@)Zycgpa6-EhTnu4=g}qL zzDcS;cn-U+M1~ccNn(SIp{W<)j@$VZ@@Fksk{uh?iff$KybYVw;iFYW{!N1flAHC> ztV}xeA-(KIz&ve*x3OAw<+>&1`T+8V#32$5j3@a!*P!D!l`ow1c%N6l0F`O zXt%^a?zVjAtrE}v(ewZ6Pw&gWCeJIg4=Tdlk#)59F*V9%KvA^Hp*C- zh;)VZ8Y_WVAkfu&F#x3*b1CT#rTCA3;BSwA_}~4~?%Te4zap(yL14lB5ze03#feiB z?4CSW0K?66+C97^p0TUm?P+kQbo@2OJQm1i)BymZ0|2b|gPO_eni<34yhnDLw6gz1 zz)R$kry+;?+jni+&ppC zoLZIB@U=Boff;C6BmS%FL_w4R@8yBT&{2H=4Zx~p=DHE^Hx2Y2STqj}JP9B!HkUh` zvZV*dp`|eQ?LlqXk5u(3q#_ht+MgCSzvR1+@Je??(3ZMd2VzXPiV9`dq!8~^LrkxdV-Wlq?(yLagr>^o$OeE z!ML|M`t$Ep?nv4wvXNR~XENf8lMKP8E)Oj_|MM;x=wowd*kF+i{2T2ZRTDVRy}1AB z?~2jOWE31q_e2L>C7DT6=I6luqKl)of)c*Vmef0VX3d~FB;G1w!a*;~n!>AYlFSeB z!U6FXrXS|8aO1%Y`qG>{z>s}waLSTZ|4yoiK8Q)>GOGa3vD7WsW`8Gj=Pgq|y|_vc zE3>|atu^Ez2#Rh*rA%=xE{EKI`R9yJ?4`Nkaiz%RpuRii3ffrws3sg>z3TLxTI@Z2 za=!iae{9+%2yI)!q;6jMKzIYkvCZ)Rf-ux^UgA_5Vru(!Lx8Oq-kz%*d>~>?o}V=) z@jZWqU>FrYqTn}XH$pf<0jxYYO$II{AbyvaDD4nNV6ZOuyd>t>u~3uPFy^ z@7NvwT)0-d+u%<5RJbD9`7zcJV*9o4R>ZFxNG;?=W78*Ny>75~wd@|n?RJa{A0(L9 z&Sf#eSPbIATJF9m0vy+Q{-K0Qr;gKvQ0vvI_0z_1 zP4QhWd|{&8Oy)npAJDWJ{sV^)MDm#tWUi!Y^GDYxH@nT5KBSxY7PiS~5pZa zpW6EJr+gFpY0&oToRzuAm4fadw4zZ7p61agyrqU=q8H?px~(N<@y{g7WHl0=1=&-e zC^#z?%uIU!uF2Bmlxl)UVqcCZ@?%~;KnVWjGpm{Hp z?c=3Hb|ZjIjKbngn;+ra62C7Rvot-R;oQXI3 z$yfg~sPq?OqWEL{O{omF=5qM@Z4+F*Zis9O4e}T6{$uB*(jl^d!|%Hd>lWVgV(8L2 z6xXT|gw$&MDo1oI0|GY(1`ilOBNS&#Kk*p^!vIXUti;2$T=`G=n&Z(={l$F_`qj-3 zY^1jqdG6vlC^|;J-#Rj!ip;6eC6!mFaHrKsheNj`+fFG!yJt=SKU zdm4*umM}oqSpcW?o+F0kqr9FBj(%0i6DlzK!iIIuX1P)a)e2uwjDznR4jF87Za!9c zK@+WNI8E+~?1Uzox-@8LeUQtg z3#shee`2Qy%RN}M%M=O5pY$y@fCLq9cQn3r~pf0W0} z+-EEf=UHp6yc5FwY=*2)=&f5gEyg62DKt{}^RFtDR7Y^oLC*ZF#dfCAh@a#aALzAE zY{XE1WOi~2DG^y*$k8o{dU{>eF}7x<-fz10Df5_71cv@Ug?pmspR$|MDQ`?#H0JRz zp0W~I1oxrS*0|k%D2n^&uLN7&pUrn1#JxB1`uf*iiFR0YHoMsitCU{lEyB6%<~=_K zDP~e(-}Nk5O<0)!!Uu&C$QJ{6RJPRI%#Dy4)>YG$-2E@?L5@f9?Pg=|paUt;!jvl( zed)3qS#2z&;ZNXJZkvE~pHXU!w+3HXqiex)9Zq(4tQ;$SA|2F3LUrK5ZI=7^kQ*X` zF2;e%=d58ABqpFB;kz}q`;KV0kb_pVUVW7G1eGQuz64`42*JFJTCcY6`VZG&udH~6 zQVHroehJTKy|ZLVka2cehuT@j56VG3M1`H^{*Jfrz<^+5Iw+|Y+_^6uGs138f$wj?=xb z8zp%rEHgP`r?YUDd-va+-^7dQp1{@c7b`m>v+?yyE%AwGNum-fu(WO2ncMUcns8<~ z53kUZ|Bc{t&ZLybe8n*na%^xZU6mSZm71Z1yIA}qmC0=-xxW$*kH$9iM~_-|XLas6 z>XKslK}kM-IjNUE5mngH%Yonql1BLM%M zpxiaf`W(yM4a+xwmH+(Q4OrRH@9D5%ZBH4n)1jognrgrrdA=cc=})DNr4z!%c^+Jg z4WmU1$z*7NvGX49Qg???;1+8GmyskZN#7)+K{lFYa{cw59{Ce;mbcGI;}x~IwWY%O z(v_XQ@hZhx;TFc6t$`^i{d#GiA8m!?EeVvfel)G_R&q$Ug8XXr<^OTwn5#Dlyc)rl za3BnLg4LnX;HBBE(H7~i(*a*o0&)W8A)afDK^X^|nn%P%om_$h@u-n0pcHNaB{4(Z zBx4HlMKUgT+?DpMR@=C^4zSWa-phB$QZjZO?*TQ(Iwc?zoTthB^&YhCDh~|FZXeR{ zWm}y^>zr|8Z4^_nc#_VR%XV~pQkBxQvroT!ZqkU)y0Fh=$%W3AnukmZH41mnrX*iw z9r8Plseega@_IF0WX`Akr>L4z=7Rru&5z2{``;jTX7xjPg`%KYWKqb^oVaTzITo&2>Kh zE^3CqL{)gK)0)o@>q_H2*pFqrQPRgJ}w~We@{=FgVuUsFG~080ZrIO%B$$nHi#Cl7VpdFz>nfF)0@v_gUK6f z97GEE@0~dH{{iSv$#`i%wrcSEj1dLA_mYW(&I3o=dp_pp>6mj|A=v2zzxOJI$9C&^^f#`9|&n(1Cf%`~+P1iJ)qhUX*P@PV1UpEN0Z|USTqEO_^r!0gL;(fJr~?U? z@Hjqj68*eZRLJ9($59N0q;DI&sY88aAoHG8vgVFY!Jek!F5hQg7|DIMJzqW31$U`D zp)BBq6WYZAk7Go_(pfSt*uHvS@%eQN{nG&ilyCLKRnKK1Dn{i<4ymATZU^Ud{%H7^ zk*Y(NGEGVE6Av6)c8%Y+Yhy$dgr2H?gv8c3ElsuGlnwDLWIKlMq#U0|dop9V@(N=KzL}VYEf`Y=M799%)|9>2% zaYDnSnLsD+q9U)dGF;~Ie+P+NeG>2Q*|v6w&q&G8NQN|?+$^L{98*S#@8N1e+X&P+ zXsT-3mQ1>BpQCj}@ZPmuo;mt;2FMr)QSdMeH(zOye}u(U;;Mf@gBoEVlLbpJ3wd!e zgE@w%rj6u^?G>}(td^}{?vX_U!*ek3I^SJjp=;p=zYwo@ z;RZWM5VJL4x9YG(roi?y03G#h(y?yq#BqC2th`L@&{d)=MAb-^cI)1rc)KP!q~MIj zP9u7&9v@4<8F^XQ!2;d~mklTwJ-GAt0}4=TQelca=r>MhH z3%Ob-CVIzD>H#L*bYU=>qkhqXd!b1^OfOiOBN)ZvJ>qkAxo+Enj=ozaypQdwF#6fY z;7nFNyfMG51l@pjuSORMc$bBwL_r(j_?{&Thf*xY9mk1-*lc$Ld8cHk8 z01+R4MR-iaX&IogVAIr=GSolk>z#NdRr23z@Yr1+wI>&V;y+IkPm_^`-@S~eD$L1c jX0iXz2ghgn-ya;I& Date: Thu, 2 Apr 2020 04:39:26 -0600 Subject: [PATCH 20/27] redpine updates to make released betaflight and deviation builds (#341) Co-authored-by: Bryce Johnson --- Multiprotocol/Redpine_cc2500.ino | 52 +++++++++++++++----------------- 1 file changed, 24 insertions(+), 28 deletions(-) diff --git a/Multiprotocol/Redpine_cc2500.ino b/Multiprotocol/Redpine_cc2500.ino index 6ae5ea7..7f881db 100644 --- a/Multiprotocol/Redpine_cc2500.ino +++ b/Multiprotocol/Redpine_cc2500.ino @@ -17,10 +17,10 @@ #include "iface_cc2500.h" -#define REDPINE_LOOPTIME_FAST 25 //2.5ms -#define REDPINE_LOOPTIME_SLOW 6 //6ms +#define REDPINE_LOOPTIME_FAST 20 //2.0ms +#define REDPINE_LOOPTIME_SLOW 20 //20ms -#define REDPINE_BIND 1000 +#define REDPINE_BIND 2000 #define REDPINE_PACKET_SIZE 11 #define REDPINE_FEC false // from cc2500 datasheet: The convolutional coder is a rate 1/2 code with a constraint length of m=4 #define REDPINE_NUM_HOPS 50 @@ -105,10 +105,9 @@ static uint16_t ReadREDPINE() } if(IS_BIND_IN_PROGRESS) { - if(bind_counter == REDPINE_BIND) - REDPINE_init(0); - if(bind_counter == REDPINE_BIND/2) - REDPINE_init(1); + if (state == REDPINE_BIND) { + REDPINE_init(0); + } REDPINE_set_channel(49); CC2500_SetTxRxMode(TX_EN); CC2500_SetPower(); @@ -121,7 +120,7 @@ static uint16_t ReadREDPINE() BIND_DONE; REDPINE_init(sub_protocol); } - return 9000; + return 4000; } else { @@ -149,23 +148,19 @@ static const uint8_t REDPINE_init_data[][3] = { {CC2500_07_PKTCTRL1, 0x04, 0x04}, {CC2500_08_PKTCTRL0, 0x05, 0x05}, {CC2500_09_ADDR, 0x00, 0x00}, - {CC2500_0B_FSCTRL1, 0x0A, 0x0A}, + {CC2500_0B_FSCTRL1, 0x0A, 0x06}, {CC2500_0C_FSCTRL0, 0x00, 0x00}, - {CC2500_0D_FREQ2, 0x5D, 0x5c}, - {CC2500_0E_FREQ1, 0x93, 0x76}, - {CC2500_0F_FREQ0, 0xB1, 0x27}, - {CC2500_10_MDMCFG4, 0x2D, 0x7B}, - {CC2500_11_MDMCFG3, 0x3B, 0x61}, - {CC2500_12_MDMCFG2, 0x73, 0x13}, - #ifdef REDPINE_FEC - {CC2500_13_MDMCFG1, 0xA3, 0xA3}, - #else - {CC2500_13_MDMCFG1, 0x23, 0x23}, - #endif - {CC2500_14_MDMCFG0, 0x56, 0x7a}, // Chan space - {CC2500_15_DEVIATN, 0x00, 0x51}, + {CC2500_0D_FREQ2, 0x5D, 0x5D}, + {CC2500_0E_FREQ1, 0x93, 0x93}, + {CC2500_0F_FREQ0, 0xB1, 0xB1}, + {CC2500_10_MDMCFG4, 0x2D, 0x78}, + {CC2500_11_MDMCFG3, 0x3B, 0x93}, + {CC2500_12_MDMCFG2, 0x73, 0x03}, + {CC2500_13_MDMCFG1, 0x23, 0x22}, + {CC2500_14_MDMCFG0, 0x56, 0xF8}, // Chan space + {CC2500_15_DEVIATN, 0x00, 0x44}, {CC2500_17_MCSM1, 0x0c, 0x0c}, - {CC2500_18_MCSM0, 0x08, 0x08}, //??? 0x18, 0x18}, + {CC2500_18_MCSM0, 0x18, 0x18}, {CC2500_19_FOCCFG, 0x1D, 0x16}, {CC2500_1A_BSCFG, 0x1C, 0x6c}, {CC2500_1B_AGCCTRL2, 0xC7, 0x43}, @@ -181,7 +176,7 @@ static const uint8_t REDPINE_init_data[][3] = { {CC2500_2C_TEST2, 0x88, 0x88}, {CC2500_2D_TEST1, 0x31, 0x31}, {CC2500_2E_TEST0, 0x0B, 0x0B}, - {CC2500_3E_PATABLE, 0xff, 0xff} + {CC2500_3E_PATABLE, 0xff, 0xff} }; static void REDPINE_init(uint8_t format) @@ -190,8 +185,9 @@ static void REDPINE_init(uint8_t format) CC2500_WriteReg(CC2500_06_PKTLEN, REDPINE_PACKET_SIZE); - for (uint8_t i=0; i < ((sizeof REDPINE_init_data) / (sizeof REDPINE_init_data[0])); i++) + for (uint8_t i=0; i < ((sizeof(REDPINE_init_data)) / (sizeof(REDPINE_init_data[0]))); i++) { CC2500_WriteReg(REDPINE_init_data[i][0], REDPINE_init_data[i][format+1]); + } prev_option = option; CC2500_WriteReg(CC2500_0C_FSCTRL0, option); @@ -215,7 +211,6 @@ static uint16_t initREDPINE() uint32_t idx = 0; uint32_t rnd = MProtocol_id; #define REDPINE_MAX_RF_CHANNEL 255 - hopping_frequency[idx++] = 1; while (idx < REDPINE_NUM_HOPS-1) { uint32_t i; @@ -226,8 +221,9 @@ static uint16_t initREDPINE() for (i = 0; i < idx; i++) { uint8_t ch = hopping_frequency[i]; - if ((ch <= next_ch + 1) && (ch >= next_ch - 1) && (ch > 1)) - break; + if ((ch <= next_ch + 1) && (ch >= next_ch - 1) && (ch >= 1)) { + break; + } } if (i != idx) continue; From 0a5b97a17756ed7ae073e2ced01b2dcacd1204aa Mon Sep 17 00:00:00 2001 From: Pascal Langer Date: Fri, 3 Apr 2020 19:36:05 +0200 Subject: [PATCH 21/27] New Protocol: PROPEL Compatible model: PROPEL 74-Z Speeder Bike Protcol: PROPEL (66) Sub protocol: none Autobind protocol Extended limits not supported Telemetry supported 14 channels in use due to many features --- Multiprotocol/FrSkyX_cc2500.ino | 4 - Multiprotocol/Multi.txt | 1 + Multiprotocol/Multi_Names.ino | 7 +- Multiprotocol/Multiprotocol.h | 4 +- Multiprotocol/Multiprotocol.ino | 10 +- Multiprotocol/Propel_nrf24l01.ino | 328 ++++++++++++++++++++++++++++++ Multiprotocol/Telemetry.ino | 4 +- Multiprotocol/Validate.h | 1 + Multiprotocol/_Config.h | 3 + Protocols_Details.md | 14 +- 10 files changed, 365 insertions(+), 11 deletions(-) create mode 100644 Multiprotocol/Propel_nrf24l01.ino diff --git a/Multiprotocol/FrSkyX_cc2500.ino b/Multiprotocol/FrSkyX_cc2500.ino index d204045..6c00054 100644 --- a/Multiprotocol/FrSkyX_cc2500.ino +++ b/Multiprotocol/FrSkyX_cc2500.ino @@ -256,10 +256,6 @@ static void __attribute__((unused)) FrSkyX_build_packet() uint16_t ReadFrSkyX() { - #ifdef DEBUG_SERIAL - static uint16_t fr_time=0; - #endif - switch(state) { default: diff --git a/Multiprotocol/Multi.txt b/Multiprotocol/Multi.txt index 6d80d43..59e0da2 100644 --- a/Multiprotocol/Multi.txt +++ b/Multiprotocol/Multi.txt @@ -63,3 +63,4 @@ 63,XN_DUMP,250K,1M,2M,AUTO 64,FrskyX2,CH_16,CH_8,EU_16,EU_8 65,FrSkyR9,915MHz,868MHz,915_8ch,868_8ch +66,PROPEL,74-Z diff --git a/Multiprotocol/Multi_Names.ino b/Multiprotocol/Multi_Names.ino index 0819026..29e7a6b 100644 --- a/Multiprotocol/Multi_Names.ino +++ b/Multiprotocol/Multi_Names.ino @@ -79,6 +79,7 @@ const char STR_TIGER[] ="Tiger"; const char STR_XK[] ="XK"; const char STR_XN297DUMP[] ="XN297DP"; const char STR_FRSKYR9[] ="FrSkyR9"; +const char STR_PROPEL[] ="PROPEL"; const char STR_SUBTYPE_FLYSKY[] = "\x04""Std\0""V9x9""V6x6""V912""CX20"; const char STR_SUBTYPE_HUBSAN[] = "\x04""H107""H301""H501"; @@ -121,6 +122,7 @@ const char STR_SUBTYPE_V911S[] = "\x05""V911S""E119\0"; const char STR_SUBTYPE_XK[] = "\x04""X450""X420"; const char STR_SUBTYPE_FRSKYR9[] = "\x07""915MHz\0""868MHz\0""915 8ch""868 8ch"; const char STR_SUBTYPE_ESKY[] = "\x03""Std""ET4"; +const char STR_SUBTYPE_PROPEL[] = "\x04""74-Z"; enum { @@ -327,7 +329,10 @@ const mm_protocol_definition multi_protocols[] = { {PROTO_XN297DUMP, STR_XN297DUMP, 4, STR_SUBTYPE_XN297DUMP, OPTION_RFCHAN }, #endif #if defined(FRSKYR9_SX1276_INO) - {PROTO_FRSKY_R9, STR_FRSKYR9, 4, STR_SUBTYPE_FRSKYR9, OPTION_NONE }, + {PROTO_FRSKY_R9, STR_FRSKYR9, 4, STR_SUBTYPE_FRSKYR9, OPTION_NONE }, +#endif +#if defined(PROPEL_NRF24L01_INO) + {PROTO_PROPEL, STR_PROPEL, 4, STR_SUBTYPE_PROPEL, OPTION_NONE }, #endif {0x00, nullptr, 0, nullptr, 0 } }; diff --git a/Multiprotocol/Multiprotocol.h b/Multiprotocol/Multiprotocol.h index 7cdd826..ab072b2 100644 --- a/Multiprotocol/Multiprotocol.h +++ b/Multiprotocol/Multiprotocol.h @@ -19,7 +19,7 @@ #define VERSION_MAJOR 1 #define VERSION_MINOR 3 #define VERSION_REVISION 0 -#define VERSION_PATCH_LEVEL 79 +#define VERSION_PATCH_LEVEL 80 //****************** // Protocols @@ -92,6 +92,7 @@ enum PROTOCOLS PROTO_XN297DUMP = 63, // =>NRF24L01 PROTO_FRSKYX2 = 64, // =>CC2500 PROTO_FRSKY_R9 = 65, // =>SX1276 + PROTO_PROPEL = 66, // =>NRF24L01 }; enum Flysky @@ -727,6 +728,7 @@ Serial: 100000 Baud 8e2 _ xxxx xxxx p -- XN297DUMP 63 FRSKYX2 64 FRSKY_R9 65 + PROPEL 66 BindBit=> 0x80 1=Bind/0=No AutoBindBit=> 0x40 1=Yes /0=No RangeCheck=> 0x20 1=Yes /0=No diff --git a/Multiprotocol/Multiprotocol.ino b/Multiprotocol/Multiprotocol.ino index 18bc127..c557464 100644 --- a/Multiprotocol/Multiprotocol.ino +++ b/Multiprotocol/Multiprotocol.ino @@ -737,7 +737,7 @@ bool Update_All() update_led_status(); #if defined(TELEMETRY) #if ( !( defined(MULTI_TELEMETRY) || defined(MULTI_STATUS) ) ) - if((protocol == PROTO_BAYANG_RX) || (protocol == PROTO_AFHDS2A_RX) || (protocol == PROTO_FRSKY_RX) || (protocol == PROTO_SCANNER) || (protocol==PROTO_FRSKYD) || (protocol==PROTO_BAYANG) || (protocol==PROTO_NCC1701) || (protocol==PROTO_BUGS) || (protocol==PROTO_BUGSMINI) || (protocol==PROTO_HUBSAN) || (protocol==PROTO_AFHDS2A) || (protocol==PROTO_FRSKYX) || (protocol==PROTO_DSM) || (protocol==PROTO_CABELL) || (protocol==PROTO_HITEC) || (protocol==PROTO_HOTT) || (protocol==PROTO_FRSKYX2)) + if((protocol == PROTO_BAYANG_RX) || (protocol == PROTO_AFHDS2A_RX) || (protocol == PROTO_FRSKY_RX) || (protocol == PROTO_SCANNER) || (protocol==PROTO_FRSKYD) || (protocol==PROTO_BAYANG) || (protocol==PROTO_NCC1701) || (protocol==PROTO_BUGS) || (protocol==PROTO_BUGSMINI) || (protocol==PROTO_HUBSAN) || (protocol==PROTO_AFHDS2A) || (protocol==PROTO_FRSKYX) || (protocol==PROTO_DSM) || (protocol==PROTO_CABELL) || (protocol==PROTO_HITEC) || (protocol==PROTO_HOTT) || (protocol==PROTO_FRSKYX2) || (protocol==PROTO_PROPEL)) #endif if(IS_DISABLE_TELEM_off) TelemetryUpdate(); @@ -1485,6 +1485,12 @@ static void protocol_init() remote_callback = XK_callback; break; #endif + #if defined(PROPEL_NRF24L01_INO) + case PROTO_PROPEL: + next_callback=initPROPEL(); + remote_callback = PROPEL_callback; + break; + #endif #if defined(XN297DUMP_NRF24L01_INO) case PROTO_XN297DUMP: next_callback=initXN297Dump(); @@ -2067,7 +2073,7 @@ void pollBoot() #if defined(TELEMETRY) void PPM_Telemetry_serial_init() { - if( (protocol==PROTO_FRSKYD) || (protocol==PROTO_HUBSAN) || (protocol==PROTO_AFHDS2A) || (protocol==PROTO_BAYANG)|| (protocol==PROTO_NCC1701) || (protocol==PROTO_CABELL) || (protocol==PROTO_HITEC) || (protocol==PROTO_BUGS) || (protocol==PROTO_BUGSMINI) + if( (protocol==PROTO_FRSKYD) || (protocol==PROTO_HUBSAN) || (protocol==PROTO_AFHDS2A) || (protocol==PROTO_BAYANG)|| (protocol==PROTO_NCC1701) || (protocol==PROTO_CABELL) || (protocol==PROTO_HITEC) || (protocol==PROTO_BUGS) || (protocol==PROTO_BUGSMINI) || (protocol==PROTO_PROPEL) #ifdef TELEMETRY_FRSKYX_TO_FRSKYD || (protocol==PROTO_FRSKYX) || (protocol==PROTO_FRSKYX2) #endif diff --git a/Multiprotocol/Propel_nrf24l01.ino b/Multiprotocol/Propel_nrf24l01.ino new file mode 100644 index 0000000..3d3b8ae --- /dev/null +++ b/Multiprotocol/Propel_nrf24l01.ino @@ -0,0 +1,328 @@ +/* + This project is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + +Multiprotocol is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Multiprotocol. If not, see . + */ +// Compatible with PROPEL 74-Z Speeder Bike. + +#if defined(PROPEL_NRF24L01_INO) + +#include "iface_nrf24l01.h" + +//#define PROPEL_FORCE_ID + +#define PROPEL_INITIAL_WAIT 500 +#define PROPEL_PACKET_PERIOD 10000 +#define PROPEL_BIND_RF_CHANNEL 0x23 +#define PROPEL_PAYLOAD_SIZE 16 +#define PROPEL_SEARCH_PERIOD 50 //*10ms +#define PROPEL_BIND_PERIOD 1500 +#define PROPEL_PACKET_SIZE 14 +#define PROPEL_RF_NUM_CHANNELS 4 +#define PROPEL_ADDRESS_LENGTH 5 +#define PROPEL_DEFAULT_PERIOD 20 + +enum { + PROPEL_BIND1 = 0, + PROPEL_BIND2, + PROPEL_BIND3, + PROPEL_DATA1, +}; + +static uint16_t __attribute__((unused)) PROPEL_checksum() +{ + typedef union { + struct { + uint8_t h:1; + uint8_t g:1; + uint8_t f:1; + uint8_t e:1; + uint8_t d:1; + uint8_t c:1; + uint8_t b:1; + uint8_t a:1; + } bits; + uint8_t byte:8; + } byte_bits_t; + + uint8_t sum = packet[0]; + for (uint8_t i = 1; i < PROPEL_PACKET_SIZE - 2; i++) + sum += packet[i]; + + byte_bits_t in = { .byte = sum }; + byte_bits_t out = { .byte = sum }; + out.byte ^= 0x0a; + out.bits.d = !(in.bits.d ^ in.bits.h); + out.bits.c = (!in.bits.c && !in.bits.d && in.bits.g) + || (in.bits.c && !in.bits.d && !in.bits.g) + || (!in.bits.c && in.bits.g && !in.bits.h) + || (in.bits.c && !in.bits.g && !in.bits.h) + || (in.bits.c && in.bits.d && in.bits.g && in.bits.h) + || (!in.bits.c && in.bits.d && !in.bits.g && in.bits.h); + out.bits.b = (!in.bits.b && !in.bits.c && !in.bits.d) + || (in.bits.b && in.bits.c && in.bits.g) + || (!in.bits.b && !in.bits.c && !in.bits.g) + || (!in.bits.b && !in.bits.d && !in.bits.g) + || (!in.bits.b && !in.bits.c && !in.bits.h) + || (!in.bits.b && !in.bits.g && !in.bits.h) + || (in.bits.b && in.bits.c && in.bits.d && in.bits.h) + || (in.bits.b && in.bits.d && in.bits.g && in.bits.h); + out.bits.a = (in.bits.a && !in.bits.b) + || (in.bits.a && !in.bits.c && !in.bits.d) + || (in.bits.a && !in.bits.c && !in.bits.g) + || (in.bits.a && !in.bits.d && !in.bits.g) + || (in.bits.a && !in.bits.c && !in.bits.h) + || (in.bits.a && !in.bits.g && !in.bits.h) + || (!in.bits.a && in.bits.b && in.bits.c && in.bits.g) + || (!in.bits.a && in.bits.b && in.bits.c && in.bits.d && in.bits.h) + || (!in.bits.a && in.bits.b && in.bits.d && in.bits.g && in.bits.h); + + return (sum << 8) | (out.byte & 0xff); +} + +static void __attribute__((unused)) PROPEL_bind_packet(bool valid_rx_id) +{ + memset(packet, 0, PROPEL_PACKET_SIZE); + + packet[0] = 0xD0; + memcpy(&packet[1], rx_tx_addr, 4); // only 4 bytes sent of 5-byte address + if (valid_rx_id) memcpy(&packet[5], rx_id, 4); + packet[9] = rf_ch_num; // hopping table to be used when switching to normal mode + packet[11] = 0x05; // unknown, 0x01 on TX2?? + + uint16_t check = PROPEL_checksum(); + packet[12] = check >> 8; + packet[13] = check & 0xff; + + NRF24L01_WriteReg(NRF24L01_07_STATUS, (_BV(NRF24L01_07_RX_DR) | _BV(NRF24L01_07_TX_DS) | _BV(NRF24L01_07_MAX_RT))); + NRF24L01_FlushTx(); + NRF24L01_FlushRx(); + NRF24L01_WritePayload(packet, PROPEL_PACKET_SIZE); +} + +static void __attribute__((unused)) PROPEL_data_packet() +{ + memset(packet, 0, PROPEL_PACKET_SIZE); + + packet[0] = 0xC0; + packet[1] = convert_channel_16b_limit(THROTTLE, 0x2f, 0xcf); + packet[2] = convert_channel_16b_limit(RUDDER , 0xcf, 0x2f); + packet[3] = convert_channel_16b_limit(ELEVATOR, 0x2f, 0xcf); + packet[4] = convert_channel_16b_limit(AILERON , 0xcf, 0x2f); + packet[5] = 0x40; //might be trims but unsused + packet[6] = 0x40; //might be trims but unsused + packet[7] = 0x40; //might be trims but unsused + packet[8] = 0x40; //might be trims but unsused + if (bind_phase) + {//need to send a couple of default packets after bind + bind_phase--; + packet[10] = 0x80; // LEDs + } + else + { + packet[9] = 0x02 // Always fast speed, slow=0x00, medium=0x01, fast=0x02, 0x03=flight training mode + | GET_FLAG( CH14_SW, 0x03) // Flight training mode + | GET_FLAG( CH10_SW, 0x04) // Calibrate + | GET_FLAG( CH12_SW, 0x08) // Take off + | GET_FLAG( CH8_SW, 0x10) // Fire + | GET_FLAG( CH11_SW, 0x20) // Altitude hold=0x20 + | GET_FLAG( CH6_SW, 0x40) // Roll CW + | GET_FLAG( CH7_SW, 0x80); // Roll CCW + packet[10] = GET_FLAG( CH13_SW, 0x20) // Land + | GET_FLAG( CH9_SW, 0x40) // Weapon system activted=0x40 + | GET_FLAG(!CH5_SW, 0x80); // LEDs + } + packet[11] = 5; // unknown, 0x01 on TX2?? + + uint16_t check = PROPEL_checksum(); + packet[12] = check >> 8; + packet[13] = check & 0xff; + + NRF24L01_WriteReg(NRF24L01_05_RF_CH, hopping_frequency[hopping_frequency_no++]); + hopping_frequency_no &= 0x03; + NRF24L01_SetPower(); + NRF24L01_WriteReg(NRF24L01_07_STATUS, (_BV(NRF24L01_07_RX_DR) | _BV(NRF24L01_07_TX_DS) | _BV(NRF24L01_07_MAX_RT))); + NRF24L01_FlushTx(); + NRF24L01_WritePayload(packet, PROPEL_PACKET_SIZE); +} + +static void __attribute__((unused)) PROPEL_init() +{ + NRF24L01_Initialize(); + NRF24L01_WriteReg(NRF24L01_00_CONFIG, 0x7f); + NRF24L01_WriteReg(NRF24L01_01_EN_AA, 0x3f); // AA on all pipes + NRF24L01_WriteReg(NRF24L01_02_EN_RXADDR, 0x3f); // Enable all pipes + NRF24L01_WriteReg(NRF24L01_03_SETUP_AW, 0x03); // 5-byte address + NRF24L01_WriteReg(NRF24L01_04_SETUP_RETR, 0x36); // retransmit 1ms, 6 times + NRF24L01_SetBitrate(NRF24L01_BR_1M); // 1Mbps + NRF24L01_SetPower(); + NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x07); // ?? match protocol capture + NRF24L01_WriteRegisterMulti(NRF24L01_0A_RX_ADDR_P0, (uint8_t *)"\x99\x77\x55\x33\x11", PROPEL_ADDRESS_LENGTH); //Bind address + NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, (uint8_t *)"\x99\x77\x55\x33\x11", PROPEL_ADDRESS_LENGTH); //Bind address + NRF24L01_WriteReg(NRF24L01_05_RF_CH, PROPEL_BIND_RF_CHANNEL); + NRF24L01_Activate(0x73); // Activate feature register + NRF24L01_WriteReg(NRF24L01_1C_DYNPD, 0x3f); // Enable dynamic payload length + NRF24L01_WriteReg(NRF24L01_1D_FEATURE, 0x07); // Enable all features + // Beken 2425 register bank 1 initialized here in stock tx capture + // Hopefully won't matter for nRF compatibility + NRF24L01_FlushTx(); + NRF24L01_SetTxRxMode(TX_EN); +} + +const uint8_t PROGMEM PROPEL_hopping []= { 0x47,0x36,0x27,0x44,0x33,0x0D,0x3C,0x2E,0x1B,0x39,0x2A,0x18 }; +static void __attribute__((unused)) PROPEL_initialize_txid() +{ + //address last byte + rx_tx_addr[4]=0x11; + + //random hopping channel table + rf_ch_num=random(0xfefefefe)&0x03; + for(uint8_t i=0; i<3; i++) + hopping_frequency[i]=pgm_read_byte_near( &PROPEL_hopping[i + 3*rf_ch_num] ); + hopping_frequency[3]=0x23; + +#ifdef PROPEL_FORCE_ID + if(RX_num&1) + memcpy(rx_tx_addr, (uint8_t *)"\x73\xd3\x31\x30\x11", PROPEL_ADDRESS_LENGTH); //TX1: 73 d3 31 30 11 + else + memcpy(rx_tx_addr, (uint8_t *)"\x94\xc5\x31\x30\x11", PROPEL_ADDRESS_LENGTH); //TX2: 94 c5 31 30 11 + rf_ch_num = 0x03; //TX1 + memcpy(hopping_frequency,(uint8_t *)"\x39\x2A\x18\x23",PROPEL_RF_NUM_CHANNELS); //TX1: 57,42,24,35 + rf_ch_num = 0x00; //TX2 + memcpy(hopping_frequency,(uint8_t *)"\x47\x36\x27\x23",PROPEL_RF_NUM_CHANNELS); //TX2: 71,54,39,35 + rf_ch_num = 0x01; // Manual search + memcpy(hopping_frequency,(uint8_t *)"\x44\x33\x0D\x23",PROPEL_RF_NUM_CHANNELS); //Manual: 68,51,13,35 + rf_ch_num = 0x02; // Manual search + memcpy(hopping_frequency,(uint8_t *)"\x3C\x2E\x1B\x23",PROPEL_RF_NUM_CHANNELS); //Manual: 60,46,27,35 +#endif +} + +uint16_t PROPEL_callback() +{ + uint8_t status; + + switch (phase) + { + case PROPEL_BIND1: + PROPEL_bind_packet(false); //rx_id unknown + phase++; //BIND2 + return PROPEL_BIND_PERIOD; + + case PROPEL_BIND2: + status=NRF24L01_ReadReg(NRF24L01_07_STATUS); + if (status & _BV(NRF24L01_07_MAX_RT)) + {// Max retry (6) reached + phase = PROPEL_BIND1; + return PROPEL_BIND_PERIOD; + } + if (!(_BV(NRF24L01_07_RX_DR) & status)) + return PROPEL_BIND_PERIOD; // nothing received + // received frame, got rx_id, save it + NRF24L01_ReadPayload(packet_in, PROPEL_PACKET_SIZE); + memcpy(rx_id, &packet_in[1], 4); + PROPEL_bind_packet(true); //send bind packet with rx_id + phase++; //BIND3 + break; + + case PROPEL_BIND3: + if (_BV(NRF24L01_07_RX_DR) & NRF24L01_ReadReg(NRF24L01_07_STATUS)) + { + NRF24L01_ReadPayload(packet_in, PROPEL_PACKET_SIZE); + if (packet_in[0] == 0xa3 && memcmp(&packet_in[1],rx_id,4)==0) + {//confirmation from the model + phase++; //PROPEL_DATA1 + bind_phase=PROPEL_DEFAULT_PERIOD; + packet_count=0; + BIND_DONE; + break; + } + } + NRF24L01_WriteRegisterMulti(NRF24L01_0A_RX_ADDR_P0, rx_tx_addr, PROPEL_ADDRESS_LENGTH); + NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, rx_tx_addr, PROPEL_ADDRESS_LENGTH); + PROPEL_bind_packet(true); //send bind packet with rx_id + break; + + case PROPEL_DATA1: + if (_BV(NRF24L01_07_RX_DR) & NRF24L01_ReadReg(NRF24L01_07_STATUS)) + {// data received from the model + NRF24L01_ReadPayload(packet_in, PROPEL_PACKET_SIZE); + if (packet_in[0] == 0xa3 && memcmp(&packet_in[1],rx_id,4)==0) + { + telemetry_counter++; //LQI + v_lipo1=packet[5]; //number of life left? + if(telemetry_lost==0) + telemetry_link=1; + } + } + PROPEL_data_packet(); + packet_count++; + if(packet_count>=100) + {//LQI calculation + packet_count=0; + TX_LQI=telemetry_counter; + RX_RSSI=telemetry_counter; + telemetry_counter = 0; + telemetry_lost=0; + } + break; + } + return PROPEL_PACKET_PERIOD; +} + +uint16_t initPROPEL() +{ + BIND_IN_PROGRESS; // autobind protocol + PROPEL_initialize_txid(); + PROPEL_init(); + hopping_frequency_no = 0; + phase=PROPEL_BIND1; + return PROPEL_INITIAL_WAIT; +} + +#endif +// equations for checksum check byte from truth table +// (1) z = a && !b +// || a && !c && !d +// || a && !c && !g +// || a && !d && !g +// || a && !c && !h +// || a && !g && !h +// || !a && b && c && g +// || !a && b && c && d && h +// || !a && b && d && g && h; +// +// (2) y = !b && !c && !d +// || b && c && g +// || !b && !c && !g +// || !b && !d && !g +// || !b && !c && !h +// || !b && !g && !h +// || b && c && d && h +// || b && d && g && h; +// +// (3) x = !c && !d && g +// || c && !d && !g +// || !c && g && !h +// || c && !g && !h +// || c && d && g && h +// || !c && d && !g && h; +// +// (4) w = d && h +// || !d && !h; +// +// (5) v = !e; +// +// (6) u = f; +// +// (7) t = !g; +// +// (8) s = h; \ No newline at end of file diff --git a/Multiprotocol/Telemetry.ino b/Multiprotocol/Telemetry.ino index 4d4bb39..3fa4b86 100644 --- a/Multiprotocol/Telemetry.ino +++ b/Multiprotocol/Telemetry.ino @@ -514,7 +514,7 @@ void frsky_link_frame() telemetry_link |= 2 ; // Send hub if available } else - {//PROTO_HUBSAN, PROTO_AFHDS2A, PROTO_BAYANG, PROTO_NCC1701, PROTO_CABELL, PROTO_HITEC, PROTO_BUGS, PROTO_BUGSMINI, PROTO_FRSKYX, PROTO_FRSKYX2 + {//PROTO_HUBSAN, PROTO_AFHDS2A, PROTO_BAYANG, PROTO_NCC1701, PROTO_CABELL, PROTO_HITEC, PROTO_BUGS, PROTO_BUGSMINI, PROTO_FRSKYX, PROTO_FRSKYX2, PROTO_PROPEL frame[1] = v_lipo1; frame[2] = v_lipo2; frame[3] = RX_RSSI; @@ -935,7 +935,7 @@ void TelemetryUpdate() #endif if( telemetry_link & 1 ) - { // FrSkyD + Hubsan + AFHDS2A + Bayang + Cabell + Hitec + Bugs + BugsMini + NCC1701 + { // FrSkyD + Hubsan + AFHDS2A + Bayang + Cabell + Hitec + Bugs + BugsMini + NCC1701 + PROPEL // FrSkyX telemetry if in PPM frsky_link_frame(); return; diff --git a/Multiprotocol/Validate.h b/Multiprotocol/Validate.h index d68f744..383baec 100644 --- a/Multiprotocol/Validate.h +++ b/Multiprotocol/Validate.h @@ -250,6 +250,7 @@ #undef BAYANG_RX_NRF24L01_INO #undef TIGER_NRF24L01_INO #undef XK_NRF24L01_INO + #undef PROPEL_NRF24L01_INO #endif #if not defined(STM32_BOARD) #undef SX1276_INSTALLED diff --git a/Multiprotocol/_Config.h b/Multiprotocol/_Config.h index 3a9cda6..1815d58 100644 --- a/Multiprotocol/_Config.h +++ b/Multiprotocol/_Config.h @@ -212,6 +212,7 @@ #define MT99XX_NRF24L01_INO #define NCC1701_NRF24L01_INO #define POTENSIC_NRF24L01_INO +#define PROPEL_NRF24L01_INO #define Q303_NRF24L01_INO #define SHENQI_NRF24L01_INO #define SLT_NRF24L01_INO @@ -638,6 +639,8 @@ const PPM_Parameters PPM_prot[14*NBR_BANKS]= { NONE PROTO_POTENSIC NONE + PROTO_PROPEL + NONE PROTO_Q2X2 Q222 Q242 diff --git a/Protocols_Details.md b/Protocols_Details.md index 7a790e2..be727c5 100644 --- a/Protocols_Details.md +++ b/Protocols_Details.md @@ -112,6 +112,7 @@ CFlie|38|CFlie||||||||NRF24L01| [OpenLRS](Protocols_Details.md#OpenLRS---27)|27|||||||||None| [Pelikan](Protocols_Details.md#Pelikan---60)|60|||||||||A7105| [Potensic](Protocols_Details.md#Potensic---51)|51|A20||||||||NRF24L01|XN297 +[PROPEL](Protocols_Details.md#PROPEL---66)|66|74-Z||||||||NRF24L01| [Q2X2](Protocols_Details.md#Q2X2---29)|29|Q222|Q242|Q282||||||NRF24L01| [Q303](Protocols_Details.md#Q303---31)|31|Q303|CX35|CX10D|CX10WD|||||NRF24L01|XN297 [Redpine](Protocols_Details.md#Redpine---50)|50|FAST|SLOW|||||||NRF24L01| @@ -1107,7 +1108,7 @@ CH1|CH2|CH3|CH4|CH5 A|E|T|R|Warp ## Potensic - *51* -Models: Potensic A20 +Model: Potensic A20 CH1|CH2|CH3|CH4|CH5|CH6|CH7|CH8 ---|---|---|---|---|---|---|--- @@ -1121,6 +1122,17 @@ MODE: Beginner -100%, Medium 0%, Advanced +100% HEADLESS: Off -100%, On +100% +## PROPEL - *66* +Model: PROPEL 74-Z Speeder Bike + +Autobind protocol + +Telemetry: RSSI is equal to TX_LQI which indicates how well the TX receives the RX (0-100%). A1 voltage should indicate the numbers of life remaining (not tested). + +CH1|CH2|CH3|CH4|CH5|CH6|CH7|CH8|CH9|CH10|CH11|CH12|CH13|CH14 +---|---|---|---|---|---|---|---|---|----|----|----|----|---- +A|E|T|R|LEDs|RollCW|RollCCW|Fire|Weapons|Calib|Alt_Hold|Take_off|Land|Training + ## Q2X2 - *29* ### Sub_protocol Q222 - *0* Models: Q222 v1 and V686 v2 From 08eee344460049a526139b8645497c58afcd4728 Mon Sep 17 00:00:00 2001 From: Pascal Langer Date: Sun, 5 Apr 2020 09:39:33 +0200 Subject: [PATCH 22/27] Protocol PROPEL: enhanced telemetry --- Multiprotocol/Multiprotocol.h | 2 +- Multiprotocol/Propel_nrf24l01.ino | 3 ++- Protocols_Details.md | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Multiprotocol/Multiprotocol.h b/Multiprotocol/Multiprotocol.h index ab072b2..e44ee9b 100644 --- a/Multiprotocol/Multiprotocol.h +++ b/Multiprotocol/Multiprotocol.h @@ -19,7 +19,7 @@ #define VERSION_MAJOR 1 #define VERSION_MINOR 3 #define VERSION_REVISION 0 -#define VERSION_PATCH_LEVEL 80 +#define VERSION_PATCH_LEVEL 81 //****************** // Protocols diff --git a/Multiprotocol/Propel_nrf24l01.ino b/Multiprotocol/Propel_nrf24l01.ino index 3d3b8ae..2b59805 100644 --- a/Multiprotocol/Propel_nrf24l01.ino +++ b/Multiprotocol/Propel_nrf24l01.ino @@ -255,10 +255,11 @@ uint16_t PROPEL_callback() if (_BV(NRF24L01_07_RX_DR) & NRF24L01_ReadReg(NRF24L01_07_STATUS)) {// data received from the model NRF24L01_ReadPayload(packet_in, PROPEL_PACKET_SIZE); - if (packet_in[0] == 0xa3 && memcmp(&packet_in[1],rx_id,4)==0) + if (packet_in[0] == 0xa3 && memcmp(&packet_in[1],rx_id,3)==0) { telemetry_counter++; //LQI v_lipo1=packet[5]; //number of life left? + v_lipo2=packet[4]; //bit mask: 0x80=flying, 0x08=taking off, 0x04=landing, 0x00=landed/crashed if(telemetry_lost==0) telemetry_link=1; } diff --git a/Protocols_Details.md b/Protocols_Details.md index be727c5..f746b90 100644 --- a/Protocols_Details.md +++ b/Protocols_Details.md @@ -1127,7 +1127,7 @@ Model: PROPEL 74-Z Speeder Bike Autobind protocol -Telemetry: RSSI is equal to TX_LQI which indicates how well the TX receives the RX (0-100%). A1 voltage should indicate the numbers of life remaining (not tested). +Telemetry: RSSI is equal to TX_LQI which indicates how well the TX receives the RX (0-100%). A1 voltage should indicate the numbers of life remaining (not tested). A2 is giving the model status using a bit mask: 0x80=flying, 0x08=taking off, 0x04=landing, 0x00=landed/crashed CH1|CH2|CH3|CH4|CH5|CH6|CH7|CH8|CH9|CH10|CH11|CH12|CH13|CH14 ---|---|---|---|---|---|---|---|---|----|----|----|----|---- From 8af985a2cbe5651977667c2cef15006f14723382 Mon Sep 17 00:00:00 2001 From: Pascal Langer Date: Sun, 5 Apr 2020 10:44:09 +0200 Subject: [PATCH 23/27] FrSkyRX: check additional ID and use RX num --- Multiprotocol/FrSky_Rx_cc2500.ino | 13 +++++++------ Multiprotocol/Multiprotocol.h | 2 +- Protocols_Details.md | 8 +++++--- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/Multiprotocol/FrSky_Rx_cc2500.ino b/Multiprotocol/FrSky_Rx_cc2500.ino index 8bf3c01..ce73302 100644 --- a/Multiprotocol/FrSky_Rx_cc2500.ino +++ b/Multiprotocol/FrSky_Rx_cc2500.ino @@ -313,6 +313,7 @@ uint16_t FrSky_Rx_callback() if(packet[1] == 0x03 && packet[2] == 0x01 && frskyx_rx_check_crc()) { rx_tx_addr[0] = packet[3]; // TXID rx_tx_addr[1] = packet[4]; // TXID + rx_tx_addr[2] = packet[11]; // TXID frsky_rx_finetune = -127; CC2500_WriteReg(CC2500_0C_FSCTRL0, frsky_rx_finetune); phase = FRSKY_RX_TUNE_LOW; @@ -330,7 +331,7 @@ uint16_t FrSky_Rx_callback() case FRSKY_RX_TUNE_LOW: if (len >= packet_length) { CC2500_ReadData(packet, packet_length); - if(packet[1] == 0x03 && packet[2] == 0x01 && frskyx_rx_check_crc() && packet[3] == rx_tx_addr[0] && packet[4] == rx_tx_addr[1]) { + if(packet[1] == 0x03 && packet[2] == 0x01 && frskyx_rx_check_crc() && packet[3] == rx_tx_addr[0] && packet[4] == rx_tx_addr[1] && (frsky_rx_format == FRSKY_RX_D8 || packet[11] == rx_tx_addr[2])) { tune_low = frsky_rx_finetune; frsky_rx_finetune = 127; CC2500_WriteReg(CC2500_0C_FSCTRL0, frsky_rx_finetune); @@ -347,7 +348,7 @@ uint16_t FrSky_Rx_callback() case FRSKY_RX_TUNE_HIGH: if (len >= packet_length) { CC2500_ReadData(packet, packet_length); - if(packet[1] == 0x03 && packet[2] == 0x01 && frskyx_rx_check_crc() && packet[3] == rx_tx_addr[0] && packet[4] == rx_tx_addr[1]) { + if(packet[1] == 0x03 && packet[2] == 0x01 && frskyx_rx_check_crc() && packet[3] == rx_tx_addr[0] && packet[4] == rx_tx_addr[1] && (frsky_rx_format == FRSKY_RX_D8 || packet[11] == rx_tx_addr[2])) { tune_high = frsky_rx_finetune; frsky_rx_finetune = (tune_low + tune_high) / 2; CC2500_WriteReg(CC2500_0C_FSCTRL0, (int8_t)frsky_rx_finetune); @@ -367,14 +368,13 @@ uint16_t FrSky_Rx_callback() case FRSKY_RX_BIND: if(len >= packet_length) { CC2500_ReadData(packet, packet_length); - if(packet[1] == 0x03 && packet[2] == 0x01 && frskyx_rx_check_crc() && packet[3] == rx_tx_addr[0] && packet[4] == rx_tx_addr[1] && packet[5] <= 0x2D) { + if(packet[1] == 0x03 && packet[2] == 0x01 && frskyx_rx_check_crc() && packet[3] == rx_tx_addr[0] && packet[4] == rx_tx_addr[1] && (frsky_rx_format == FRSKY_RX_D8 || packet[11] == rx_tx_addr[2]) && packet[5] <= 0x2D) { for (ch = 0; ch < 5; ch++) hopping_frequency[packet[5]+ch] = packet[6+ch]; state |= 1 << (packet[5] / 5); if (state == 0x3ff) { debug("Bind complete: "); frsky_rx_calibrate(); - rx_tx_addr[2] = packet[12]; // RX # (D16) CC2500_WriteReg(CC2500_18_MCSM0, 0x08); // FS_AUTOCAL = manual CC2500_WriteReg(CC2500_09_ADDR, rx_tx_addr[0]); // set address CC2500_WriteReg(CC2500_07_PKTCTRL1, 0x05); // check address @@ -389,7 +389,8 @@ uint16_t FrSky_Rx_callback() eeprom_write_byte((EE_ADDR)temp++, rx_tx_addr[1]); debug("addr[1]=%02X, ", rx_tx_addr[1]); eeprom_write_byte((EE_ADDR)temp++, rx_tx_addr[2]); - debug("rx_num=%02X, ", rx_tx_addr[2]); + debug("addr[2]=%02X, ", rx_tx_addr[2]); + debug("rx_num=%02X, ", packet[12]); // RX # (D16) eeprom_write_byte((EE_ADDR)temp++, frsky_rx_finetune); debugln("tune=%d", (int8_t)frsky_rx_finetune); for (ch = 0; ch < 47; ch++) @@ -408,7 +409,7 @@ uint16_t FrSky_Rx_callback() case FRSKY_RX_DATA: if (len >= packet_length) { CC2500_ReadData(packet, packet_length); - if (packet[1] == rx_tx_addr[0] && packet[2] == rx_tx_addr[1] && frskyx_rx_check_crc() && (frsky_rx_format == FRSKY_RX_D8 || packet[6] == rx_tx_addr[2])) { + if (packet[1] == rx_tx_addr[0] && packet[2] == rx_tx_addr[1] && frskyx_rx_check_crc() && (frsky_rx_format == FRSKY_RX_D8 || (packet[6] == RX_num && packet[3] == rx_tx_addr[2])) { RX_RSSI = packet[packet_length-2]; if(RX_RSSI >= 128) RX_RSSI -= 128; diff --git a/Multiprotocol/Multiprotocol.h b/Multiprotocol/Multiprotocol.h index e44ee9b..6152013 100644 --- a/Multiprotocol/Multiprotocol.h +++ b/Multiprotocol/Multiprotocol.h @@ -19,7 +19,7 @@ #define VERSION_MAJOR 1 #define VERSION_MINOR 3 #define VERSION_REVISION 0 -#define VERSION_PATCH_LEVEL 81 +#define VERSION_PATCH_LEVEL 82 //****************** // Protocols diff --git a/Protocols_Details.md b/Protocols_Details.md index f746b90..034e78a 100644 --- a/Protocols_Details.md +++ b/Protocols_Details.md @@ -92,7 +92,7 @@ CFlie|38|CFlie||||||||NRF24L01| [FrskyV](Protocols_Details.md#FRSKYV---25)|25|FrskyV||||||||CC2500| [FrskyX](Protocols_Details.md#FRSKYX---15)|15|CH_16|CH_8|EU_16|EU_8|||||CC2500| [FrskyX2](Protocols_Details.md#FRSKYX2---64)|64|CH_16|CH_8|EU_16|EU_8|||||CC2500| -[FrskyX_RX](Protocols_Details.md#FRSKYX_RX---55)|55|||||||||CC2500| +[Frsky_RX](Protocols_Details.md#FRSKY_RX---55)|55|||||||||CC2500| [FX816](Protocols_Details.md#FX816---58)|28|FX816|P38|||||||NRF24L01| [FY326](Protocols_Details.md#FY326---20)|20|FY326|FY319|||||||NRF24L01| [GD00X](Protocols_Details.md#GD00X---47)|47|GD_V1*|GD_V2*|||||||NRF24L01| @@ -375,8 +375,8 @@ CH1|CH2|CH3|CH4|CH5|CH6|CH7|CH8 ## FRSKYX2 - *64* Same as FrSkyX but for v2.1.0. -## FRSKYX_RX - *55* -The FrSkyX receiver protocol enables master/slave trainning, separate access from 2 different radios to the same model,... +## FRSKY_RX - *55* +The FrSky receiver protocol enables master/slave trainning, separate access from 2 different radios to the same model,... Auto selection of FrSkyD and FrSkyX v1.xxx at bind time. @@ -384,6 +384,8 @@ Available in OpenTX 2.3.3, Trainer Mode Master/Multi Extended limits supported +For **FrSkyX, RX num must match on the master and slave**. This enables a multi student configuration for example. + Option for this protocol corresponds to fine frequency tuning. If the value is equal to 0, the RX will auto tune otherwise it will use the indicated value. This value is different for each Module and **must** be accurate otherwise the link will not be stable. From 7e461344a816888863ea883a6cda74c969bf5e8e Mon Sep 17 00:00:00 2001 From: Pascal Langer Date: Sun, 5 Apr 2020 10:46:26 +0200 Subject: [PATCH 24/27] Update Protocols_Details.md --- Protocols_Details.md | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/Protocols_Details.md b/Protocols_Details.md index 034e78a..bf55e99 100644 --- a/Protocols_Details.md +++ b/Protocols_Details.md @@ -378,7 +378,7 @@ Same as FrSkyX but for v2.1.0. ## FRSKY_RX - *55* The FrSky receiver protocol enables master/slave trainning, separate access from 2 different radios to the same model,... -Auto selection of FrSkyD and FrSkyX v1.xxx at bind time. +Auto selection of FrSkyD and FrSkyX v1.xxx FCC/LBT at bind time. Available in OpenTX 2.3.3, Trainer Mode Master/Multi @@ -393,20 +393,6 @@ Check the [Frequency Tuning page](/docs/Frequency_Tuning.md) to determine it. Low power: enable/disable the LNA stage on the RF component to use depending on the distance with the TX. -### Sub_protocol FCC - *0* -FCC protocol 8 or 16 channels. - -CH1|CH2|CH3|CH4|CH5|CH6|CH7|CH8|CH9|CH10|CH11|CH12|CH13|CH14|CH15|CH16 ----|---|---|---|---|---|---|---|---|----|----|----|----|----|----|---- -CH1|CH2|CH3|CH4|CH5|CH6|CH7|CH8|CH9|CH10|CH11|CH12|CH13|CH14|CH15|CH16 - -### Sub_protocol EU_LBT - *1* -EU_LBT protocol 8 or 16 channels. - -CH1|CH2|CH3|CH4|CH5|CH6|CH7|CH8|CH9|CH10|CH11|CH12|CH13|CH14|CH15|CH16 ----|---|---|---|---|---|---|---|---|----|----|----|----|----|----|---- -CH1|CH2|CH3|CH4|CH5|CH6|CH7|CH8|CH9|CH10|CH11|CH12|CH13|CH14|CH15|CH16 - ## HITEC - *39* Models: OPTIMA, MINIMA and MICRO receivers. From 272d2be3aeb8c9ac15c258fbc07d06e28554d10e Mon Sep 17 00:00:00 2001 From: Pascal Langer Date: Sun, 5 Apr 2020 16:05:51 +0200 Subject: [PATCH 25/27] Update FrSky_Rx_cc2500.ino --- Multiprotocol/FrSky_Rx_cc2500.ino | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Multiprotocol/FrSky_Rx_cc2500.ino b/Multiprotocol/FrSky_Rx_cc2500.ino index ce73302..9304f33 100644 --- a/Multiprotocol/FrSky_Rx_cc2500.ino +++ b/Multiprotocol/FrSky_Rx_cc2500.ino @@ -19,6 +19,7 @@ #define FRSKY_RX_D16FCC_LENGTH 32 #define FRSKY_RX_D16LBT_LENGTH 35 + #define FRSKY2_RX_D16_LENGTH 32 #define FRSKY_RX_D8_LENGTH 20 #define FRSKY_RX_FORMATS 3 @@ -409,7 +410,7 @@ uint16_t FrSky_Rx_callback() case FRSKY_RX_DATA: if (len >= packet_length) { CC2500_ReadData(packet, packet_length); - if (packet[1] == rx_tx_addr[0] && packet[2] == rx_tx_addr[1] && frskyx_rx_check_crc() && (frsky_rx_format == FRSKY_RX_D8 || (packet[6] == RX_num && packet[3] == rx_tx_addr[2])) { + if (packet[1] == rx_tx_addr[0] && packet[2] == rx_tx_addr[1] && frskyx_rx_check_crc() && (frsky_rx_format == FRSKY_RX_D8 || (packet[6] == RX_num && packet[3] == rx_tx_addr[2]))) { RX_RSSI = packet[packet_length-2]; if(RX_RSSI >= 128) RX_RSSI -= 128; From 3f652fa06c0ce1309a8d11697719366bdf58b698 Mon Sep 17 00:00:00 2001 From: Pascal Langer Date: Tue, 7 Apr 2020 01:43:05 +0200 Subject: [PATCH 26/27] FrSkyX: improve SPort to RX code --- Multiprotocol/Multiprotocol.h | 2 +- Multiprotocol/Multiprotocol.ino | 56 +++++++++++++++++++-------------- 2 files changed, 33 insertions(+), 25 deletions(-) diff --git a/Multiprotocol/Multiprotocol.h b/Multiprotocol/Multiprotocol.h index 6152013..11d0c5c 100644 --- a/Multiprotocol/Multiprotocol.h +++ b/Multiprotocol/Multiprotocol.h @@ -19,7 +19,7 @@ #define VERSION_MAJOR 1 #define VERSION_MINOR 3 #define VERSION_REVISION 0 -#define VERSION_PATCH_LEVEL 82 +#define VERSION_PATCH_LEVEL 83 //****************** // Protocols diff --git a/Multiprotocol/Multiprotocol.ino b/Multiprotocol/Multiprotocol.ino index c557464..2e9abb6 100644 --- a/Multiprotocol/Multiprotocol.ino +++ b/Multiprotocol/Multiprotocol.ino @@ -1792,33 +1792,41 @@ void update_serial_data() #define BYTE_STUFF 0x7D #define STUFF_MASK 0x20 //debug("SPort_in: "); - SportData[SportTail]=0x7E; - SportTail = (SportTail+1) & (MAX_SPORT_BUFFER-1); - SportData[SportTail]=rx_ok_buff[27]&0x1F; - SportTail = (SportTail+1) & (MAX_SPORT_BUFFER-1); + boolean sport_valid=false; for(uint8_t i=28;i<28+7;i++) + if(rx_ok_buff[i]!=0) sport_valid=true; //Check that the payload is not full of 0 + if(rx_ok_buff[27]&0x1F > 0x1B) //Check 1st byte validity + sport_valid=false; + if(sport_valid) { - if(rx_ok_buff[i]==BYTE_STUFF) - {//stuff - SportData[SportTail]=BYTE_STUFF; - SportTail = (SportTail+1) & (MAX_SPORT_BUFFER-1); - SportData[SportTail]=rx_ok_buff[i]^STUFF_MASK; - } - else - SportData[SportTail]=rx_ok_buff[i]; - //debug("%02X ",SportData[SportTail]); + SportData[SportTail]=0x7E; SportTail = (SportTail+1) & (MAX_SPORT_BUFFER-1); - } - uint8_t used = SportTail; - if ( SportHead > SportTail ) - used += MAX_SPORT_BUFFER - SportHead ; - else - used -= SportHead ; - if ( used >= MAX_SPORT_BUFFER-(MAX_SPORT_BUFFER>>2) ) - { - DATA_BUFFER_LOW_on; - SEND_MULTI_STATUS_on; //Send Multi Status ASAP to inform the TX - debugln("Low buf=%d,h=%d,t=%d",used,SportHead,SportTail); + SportData[SportTail]=rx_ok_buff[27]&0x1F; + SportTail = (SportTail+1) & (MAX_SPORT_BUFFER-1); + for(uint8_t i=28;i<28+7;i++) + { + if( (rx_ok_buff[i]==BYTE_STUFF) || (rx_ok_buff[i]==0x7E) ) + {//stuff + SportData[SportTail]=BYTE_STUFF; + SportTail = (SportTail+1) & (MAX_SPORT_BUFFER-1); + SportData[SportTail]=rx_ok_buff[i]^STUFF_MASK; + } + else + SportData[SportTail]=rx_ok_buff[i]; + //debug("%02X ",SportData[SportTail]); + SportTail = (SportTail+1) & (MAX_SPORT_BUFFER-1); + } + uint8_t used = SportTail; + if ( SportHead > SportTail ) + used += MAX_SPORT_BUFFER - SportHead ; + else + used -= SportHead ; + if ( used >= MAX_SPORT_BUFFER-(MAX_SPORT_BUFFER>>2) ) + { + DATA_BUFFER_LOW_on; + SEND_MULTI_STATUS_on; //Send Multi Status ASAP to inform the TX + debugln("Low buf=%d,h=%d,t=%d",used,SportHead,SportTail); + } } } #endif //SPORT_SEND From 4039cbf8af2416cbd1a937645b71b2d631ac3dd7 Mon Sep 17 00:00:00 2001 From: Pascal Langer Date: Tue, 7 Apr 2020 10:55:54 +0200 Subject: [PATCH 27/27] Small corrections --- Multiprotocol/Multiprotocol.h | 2 +- Multiprotocol/Multiprotocol.ino | 2 +- Multiprotocol/NRF24l01_SPI.ino | 4 ++++ Protocols_Details.md | 19 +++++++++++++++++++ 4 files changed, 25 insertions(+), 2 deletions(-) diff --git a/Multiprotocol/Multiprotocol.h b/Multiprotocol/Multiprotocol.h index 11d0c5c..c6cbe87 100644 --- a/Multiprotocol/Multiprotocol.h +++ b/Multiprotocol/Multiprotocol.h @@ -19,7 +19,7 @@ #define VERSION_MAJOR 1 #define VERSION_MINOR 3 #define VERSION_REVISION 0 -#define VERSION_PATCH_LEVEL 83 +#define VERSION_PATCH_LEVEL 84 //****************** // Protocols diff --git a/Multiprotocol/Multiprotocol.ino b/Multiprotocol/Multiprotocol.ino index 2e9abb6..3e8da97 100644 --- a/Multiprotocol/Multiprotocol.ino +++ b/Multiprotocol/Multiprotocol.ino @@ -1795,7 +1795,7 @@ void update_serial_data() boolean sport_valid=false; for(uint8_t i=28;i<28+7;i++) if(rx_ok_buff[i]!=0) sport_valid=true; //Check that the payload is not full of 0 - if(rx_ok_buff[27]&0x1F > 0x1B) //Check 1st byte validity + if((rx_ok_buff[27]&0x1F) > 0x1B) //Check 1st byte validity sport_valid=false; if(sport_valid) { diff --git a/Multiprotocol/NRF24l01_SPI.ino b/Multiprotocol/NRF24l01_SPI.ino index 3911a77..df0e31b 100644 --- a/Multiprotocol/NRF24l01_SPI.ino +++ b/Multiprotocol/NRF24l01_SPI.ino @@ -172,6 +172,10 @@ void NRF24L01_SetPower() if(prev_power != power) { rf_setup = (rf_setup & 0xF9) | (power << 1); + if(power==3) + rf_setup |=0x01; // Si24r01 full power, unused bit for NRF + else + rf_setup &=0xFE; NRF24L01_WriteReg(NRF24L01_06_RF_SETUP, rf_setup); prev_power=power; } diff --git a/Protocols_Details.md b/Protocols_Details.md index bf55e99..b727cf1 100644 --- a/Protocols_Details.md +++ b/Protocols_Details.md @@ -128,6 +128,7 @@ CFlie|38|CFlie||||||||NRF24L01| [V911S](Protocols_Details.md#V911S---46)|46|V911S*|E119*|||||||NRF24L01|XN297 [WFly](Protocols_Details.md#WFLY---40)|40|WFLY||||||||CYRF6936| [WK2x01](Protocols_Details.md#WK2X01---30)|30|WK2801|WK2401|W6_5_1|W6_6_1|W6_HEL|W6_HEL_I|||CYRF6936| +[XK](Protocols_Details.md#XK---62)|62|XK|X450|X420||||||NRF24L01|XN297 [YD717](Protocols_Details.md#YD717---8)|8|YD717|SKYWLKR|SYMAX4|XINXUN|NIHUI||||NRF24L01| [ZSX](Protocols_Details.md#ZSX---52)|52|280||||||||NRF24L01|XN297 * "*" Sub Protocols designated by * suffix are using a XN297L@250kbps which will be emulated by default with the NRF24L01. If option (freq tune) is diffrent from 0, the CC2500 module (if installed) will be used instead. Each specific sub protocol has a more detailed explanation. @@ -1332,6 +1333,24 @@ Models: WLtoys V911S, XK A110 ### Sub_protocol E119 - *1* Models: Eachine E119 +## XK - *62* + +CH1|CH2|CH3|CH4|CH5|CH6|CH7|CH8|CH9|CH10 +---|---|---|---|---|---|---|---|---|---- +A|E|T|R|Flight_modes|Take_off|Emerg stop|3D/6G|Picture|Video + +Flight_modes: -100%=M-Mode, 0%=6G-Mode, +100%=V-Mode. CH6-CH10 are mementary switches. + +### Sub_protocol X450 - *0* +Models: XK X450 (TX=X8) + +This protocol is known to be problematic because it's using the xn297L emulation with a transmission speed of 250kbps therefore it doesn't work very well with every modules, this is an hardware issue with the accuracy of the components. + +If the model does not respond well to inputs or hard to bind, you can try to switch the emulation from the default NRF24L01 RF component to the CC2500 by using an option value (freq tuning) different from 0. Option in this case is used for fine frequency tuning like any CC2500 protocols so check the [Frequency Tuning page](/docs/Frequency_Tuning.md). + +### Sub_protocol X420 - *1* +Models: XK X420/X520 (TX=X4) + ## YD717 - *8* Autobind protocol