2 * This file is part of u360gts, aka amv-open360tracker 32bits:
3 * https://github.com/raul-ortega/amv-open360tracker-32bits
5 * The code below is an adaptation by Raúl Ortega of the original code written by Samuel Brucksch:
6 * https://github.com/SamuelBrucksch/open360tracker
8 * u360gts is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
13 * u360gts is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with u360gts. If not, see <http://www.gnu.org/licenses/>.
24 #include "telemetry.h"
26 void processFrskyPacket(uint8_t *packet
);
27 void parseTelemHubByte(uint8_t c
);
28 int32_t coordToLong(int8_t neg
, uint16_t bp
, uint16_t ap
);
30 #define START_STOP 0x7e
31 #define BYTESTUFF 0x7d
32 #define STUFF_MASK 0x20
34 // FrSky PRIM IDs (1 byte)
35 #define DATA_FRAME 0x10
37 // FrSky old DATA IDs (1 byte)
38 #define GPS_ALT_BP_ID 0x01
39 #define GPS_ALT_AP_ID 0x09
40 #define BARO_ALT_BP_ID 0x10
41 #define BARO_ALT_AP_ID 0x21
42 #define GPS_LONG_BP_ID 0x12
43 #define GPS_LONG_AP_ID 0x1A
44 #define GPS_LAT_BP_ID 0x13
45 #define GPS_LAT_AP_ID 0x1B
46 #define GPS_LONG_EW_ID 0x22
47 #define GPS_LAT_NS_ID 0x23
48 #define FRSKY_LAST_ID 0x3F
49 //used for sats and fix type
52 // FrSky Passthrough DATA IDs from APM
53 #define GPS_STATUS 0x5002
54 #define AIRCRAFT_HOME 0X5004
55 #define bitmask(X,Y) (((1 << X) -1) << Y)
57 // FrSky new DATA IDs (2 bytes)
58 #define ALT_FIRST_ID 0x0100
59 #define ALT_LAST_ID 0x010f
60 #define T2_FIRST_ID 0x0410
61 #define T2_LAST_ID 0x041f
62 #define GPS_LONG_LATI_FIRST_ID 0x0800
63 #define GPS_LONG_LATI_LAST_ID 0x080f
64 #define GPS_ALT_FIRST_ID 0x0820
65 #define GPS_ALT_LAST_ID 0x082f
66 #define GPS_SPEED_FIRST_ID 0x0830
67 #define GPS_SPEED_LAST_ID 0x083f
68 #define GPS_COURS_FIRST_ID 0x0840
69 #define GPS_COURS_LAST_ID 0x084f
71 #define TELEMETRY_INIT 0
72 #define TELEMETRY_OK 1
73 #define TELEMETRY_KO 2
75 #define FRSKY_RX_PACKET_SIZE 9
77 #define FRSKYX_LATLON_DIVIDER 1000000
79 uint8_t frskyx_RxBuffer
[FRSKY_RX_PACKET_SIZE
];
80 uint8_t telemetryState
= TELEMETRY_INIT
;
90 //lat lon in decimal degree dd.ddddd
96 int32_t frskyx_setLat() {
97 int32_t value
= coordToLong(NS
== 'N' ? 1 : -1, lat_bp
, lat_ap
);
102 int32_t frskyx_setLon() {
103 int32_t value
= coordToLong(EW
== 'E' ? 1 : -1, lon_bp
, lon_ap
);
108 int32_t coordToLong(int8_t neg
, uint16_t bp
, uint16_t ap
) {
109 uint32_t first
= ((uint32_t)bp
/ 100) * FRSKYX_LATLON_DIVIDER
;
110 uint32_t second
= ((((uint32_t)bp
% 100) * FRSKYX_LATLON_DIVIDER
) + ((uint32_t)ap
* 100)) / 60;
111 return ((int32_t)(first
+ second
) * (uint32_t)neg
);
114 void processHubPacket(uint8_t id
, uint16_t value
)
116 if (id
> FRSKY_LAST_ID
)
143 if(telemetry_provider
== TELEMETRY_PROVIDER_DIY_GPS
){
146 } else if (telemetry_provider
== TELEMETRY_PROVIDER_INAV
){
152 if ((NS
== 'N' || NS
== 'S') && (EW
== 'E' || EW
== 'W')) {
153 telemetry_lat
= frskyx_setLat();
154 telemetry_lon
= frskyx_setLon();
155 telemetry_sats
= sats
;
160 bool checkSportPacket(uint8_t *packet
)
163 for (int i
= 1; i
< FRSKY_RX_PACKET_SIZE
; i
++) {
164 crc
+= packet
[i
]; //0-1FF
165 crc
+= crc
>> 8; //0-100
167 crc
+= crc
>> 8; //0-0FF
170 return (crc
== 0x00ff);
173 #define SPORT_DATA_U8(packet) (packet[4])
174 #define SPORT_DATA_S32(packet) (*((int32_t *)(packet+4)))
175 #define SPORT_DATA_U32(packet) (*((uint32_t *)(packet+4)))
176 #define HUB_DATA_U16(packet) (*((uint16_t *)(packet+4)))
178 void processSportPacket(uint8_t *packet
)
180 uint8_t prim
= packet
[1];
181 uint16_t appId
= *((uint16_t *)(packet
+ 2));
183 if (!checkSportPacket(packet
)){
184 telemetry_failed_cs
++;
188 if(appId
== GPS_STATUS
) telemetry_provider
= TELEMETRY_PROVIDER_APM10
;
193 if ((appId
>> 8) == 0) {
195 uint8_t id
= (uint8_t)appId
;
196 uint16_t value
= HUB_DATA_U16(packet
);
197 processHubPacket(id
, value
);
199 else if (appId
>= T2_FIRST_ID
&& appId
<= T2_LAST_ID
) {
200 if(telemetry_provider
== TELEMETRY_PROVIDER_DIY_GPS
){
201 sats
= SPORT_DATA_S32(packet
) / 10;
202 //fix = SPORT_DATA_S32(packet) % 10;
203 } else if (telemetry_provider
== TELEMETRY_PROVIDER_INAV
){
204 sats
= SPORT_DATA_S32(packet
) % 100;
206 //we assume that other systems just send the sats over temp2 and fix over temp1
207 sats
= SPORT_DATA_S32(packet
);
210 else if (appId
>= GPS_SPEED_FIRST_ID
&& appId
<= GPS_SPEED_LAST_ID
) {
211 //frskyData.hub.gpsSpeed_bp = (uint16_t) (SPORT_DATA_U32(packet) / 1000);
213 else if (appId
>= GPS_COURS_FIRST_ID
&& appId
<= GPS_COURS_LAST_ID
) {
214 //uint32_t course = SPORT_DATA_U32(packet)/100;
216 else if (appId
>= GPS_ALT_FIRST_ID
&& appId
<= GPS_ALT_LAST_ID
) {
217 alt
= SPORT_DATA_S32(packet
) / 100;
221 else if (appId
>= GPS_LONG_LATI_FIRST_ID
&& appId
<= GPS_LONG_LATI_LAST_ID
) {
222 uint32_t gps_long_lati_data
= SPORT_DATA_U32(packet
);
223 uint32_t gps_long_lati_b1w
, gps_long_lati_a1w
;
224 gps_long_lati_b1w
= (gps_long_lati_data
& 0x3fffffff) / 10000;
225 gps_long_lati_a1w
= (gps_long_lati_data
& 0x3fffffff) % 10000;
227 switch ((gps_long_lati_data
& 0xc0000000) >> 30) {
229 lat_bp
= (gps_long_lati_b1w
/ 60 * 100) + (gps_long_lati_b1w
% 60);
230 lat_ap
= gps_long_lati_a1w
;
234 lat_bp
= (gps_long_lati_b1w
/ 60 * 100) + (gps_long_lati_b1w
% 60);
235 lat_ap
= gps_long_lati_a1w
;
239 lon_bp
= (gps_long_lati_b1w
/ 60 * 100) + (gps_long_lati_b1w
% 60);
240 lon_ap
= gps_long_lati_a1w
;
244 lon_bp
= (gps_long_lati_b1w
/ 60 * 100) + (gps_long_lati_b1w
% 60);
245 lon_ap
= gps_long_lati_a1w
;
249 if ((NS
== 'N' || NS
== 'S') && (EW
== 'E' || EW
== 'W')) {
250 telemetry_lat
= frskyx_setLat();
251 telemetry_lon
= frskyx_setLon();
252 telemetry_sats
= sats
;
257 else if(appId
== GPS_STATUS
&& telemetry_provider
== TELEMETRY_PROVIDER_APM10
){
258 sats
= (uint8_t) (SPORT_DATA_U32(packet
) & bitmask(4,0));
259 //telemetry.gpsAlt = bit32.extract(VALUE,24,7) * (10^bit32.extract(VALUE,22,2)) * (bit32.extract(VALUE,31,1) == 1 and -1 or 1) -- dm
260 uint16_t msl_dc
, msl_x
, msl_sgn
;
261 msl_dc
= (SPORT_DATA_U32(packet
) & bitmask(7,24)) >> 24;
262 msl_x
= (SPORT_DATA_U32(packet
) & bitmask(2,22)) >> 22;
263 msl_sgn
= (SPORT_DATA_U32(packet
) & bitmask(1,31)) >> 31;
264 telemetry_alt
= (int16_t) (msl_dc
* pow(10,msl_x
) * 0.1 * (msl_sgn
== 1?-1:1));
271 // Receive buffer state machine state enum
272 enum FrSkyDataState
{
278 void frskyx_encodeTargetData(uint8_t data
) {
279 static uint8_t numPktBytes
= 0;
280 static uint8_t dataState
= STATE_DATA_IDLE
;
282 if (data
== START_STOP
) {
283 dataState
= STATE_DATA_IN_FRAME
;
289 frskyx_RxBuffer
[numPktBytes
++] = data
^ STUFF_MASK
;
290 dataState
= STATE_DATA_IN_FRAME
;
293 case STATE_DATA_IN_FRAME
:
294 if (data
== BYTESTUFF
)
295 dataState
= STATE_DATA_XOR
; // XOR next byte
297 frskyx_RxBuffer
[numPktBytes
++] = data
;
302 if (numPktBytes
== FRSKY_RX_PACKET_SIZE
) {
303 processSportPacket(frskyx_RxBuffer
);
304 dataState
= STATE_DATA_IDLE
;