* Initial release for SX1302 CoreCell Reference Design.
This commit is contained in:
Michael Coracin 2019-07-12 15:40:13 +02:00
parent c7a5171a47
commit 4c61c5d48e
79 changed files with 30157 additions and 0 deletions

44
Makefile Normal file
View File

@ -0,0 +1,44 @@
### Environment constants
ARCH ?=
CROSS_COMPILE ?=
export
### general build targets
.PHONY: all clean install install_conf libtools libloragw packet_forwarder util_net_downlink util_chip_id
all: libtools libloragw packet_forwarder util_net_downlink util_chip_id
libtools:
$(MAKE) all -e -C $@
libloragw: libtools
$(MAKE) all -e -C $@
packet_forwarder: libloragw
$(MAKE) all -e -C $@
util_net_downlink: libtools
$(MAKE) all -e -C $@
util_chip_id: libloragw
$(MAKE) all -e -C $@
clean:
$(MAKE) clean -e -C libtools
$(MAKE) clean -e -C libloragw
$(MAKE) clean -e -C packet_forwarder
$(MAKE) clean -e -C util_net_downlink
$(MAKE) clean -e -C util_chip_id
install:
$(MAKE) install -e -C libloragw
$(MAKE) install -e -C packet_forwarder
$(MAKE) install -e -C util_net_downlink
$(MAKE) install -e -C util_chip_id
install_conf:
$(MAKE) install_conf -e -C packet_forwarder
### EOF

1
VERSION Normal file
View File

@ -0,0 +1 @@
1.0.0

126
libloragw/Makefile Normal file
View File

@ -0,0 +1,126 @@
### get external defined data
LIBLORAGW_VERSION := `cat ../VERSION`
include library.cfg
include ../target.cfg
### constant symbols
ARCH ?=
CROSS_COMPILE ?=
CC := $(CROSS_COMPILE)gcc
AR := $(CROSS_COMPILE)ar
CFLAGS := -O2 -Wall -Wextra -std=c99 -Iinc -I. -I../libtools/inc
OBJDIR = obj
INCLUDES = $(wildcard inc/*.h) $(wildcard ../libtools/inc/*.h)
### linking options
LIBS := -lloragw -ltinymt32 -lrt -lm
### general build targets
all: libloragw.a test_loragw_spi test_loragw_i2c test_loragw_reg test_loragw_hal_tx test_loragw_hal_rx test_loragw_cal test_loragw_capture_ram test_loragw_spi_sx1250 test_loragw_counter test_loragw_gps
clean:
rm -f libloragw.a
rm -f test_loragw_*
rm -f $(OBJDIR)/*.o
rm -f inc/config.h
install:
ifneq ($(strip $(TARGET_IP)),)
ifneq ($(strip $(TARGET_DIR)),)
ifneq ($(strip $(TARGET_USR)),)
@echo "---- Copying libloragw files to $(TARGET_IP):$(TARGET_DIR)"
@ssh $(TARGET_USR)@$(TARGET_IP) "mkdir -p $(TARGET_DIR)"
@scp test_loragw_* $(TARGET_USR)@$(TARGET_IP):$(TARGET_DIR)
@scp ../tools/reset_lgw.sh $(TARGET_USR)@$(TARGET_IP):$(TARGET_DIR)
else
@echo "ERROR: TARGET_USR is not configured in target.cfg"
endif
else
@echo "ERROR: TARGET_DIR is not configured in target.cfg"
endif
else
@echo "ERROR: TARGET_IP is not configured in target.cfg"
endif
### transpose library.cfg into a C header file : config.h
inc/config.h: ../VERSION library.cfg
@echo "*** Checking libloragw library configuration ***"
@rm -f $@
#File initialization
@echo "#ifndef _LORAGW_CONFIGURATION_H" >> $@
@echo "#define _LORAGW_CONFIGURATION_H" >> $@
# Release version
@echo "Release version : $(LIBLORAGW_VERSION)"
@echo " #define LIBLORAGW_VERSION "\"$(LIBLORAGW_VERSION)\""" >> $@
# Debug options
@echo " #define DEBUG_AUX $(DEBUG_AUX)" >> $@
@echo " #define DEBUG_SPI $(DEBUG_SPI)" >> $@
@echo " #define DEBUG_I2C $(DEBUG_SPI)" >> $@
@echo " #define DEBUG_REG $(DEBUG_REG)" >> $@
@echo " #define DEBUG_HAL $(DEBUG_HAL)" >> $@
@echo " #define DEBUG_GPS $(DEBUG_GPS)" >> $@
@echo " #define DEBUG_GPIO $(DEBUG_GPIO)" >> $@
@echo " #define DEBUG_LBT $(DEBUG_LBT)" >> $@
@echo " #define DEBUG_RAD $(DEBUG_RAD)" >> $@
@echo " #define DEBUG_CAL $(DEBUG_CAL)" >> $@
@echo " #define DEBUG_SX1302 $(DEBUG_SX1302)" >> $@
# Configuration options
@echo " #define BYPASS_FW_INIT $(BYPASS_FW_INIT)" >> $@
@echo " #define FPGA_BOARD_16_CH $(FPGA_BOARD_16_CH)" >> $@
# end of file
@echo "#endif" >> $@
@echo "*** Configuration seems ok ***"
### library module target
$(OBJDIR):
mkdir -p $(OBJDIR)
$(OBJDIR)/%.o: src/%.c $(INCLUDES) inc/config.h | $(OBJDIR)
$(CC) -c $(CFLAGS) $< -o $@
### static library
libloragw.a: $(OBJDIR)/loragw_spi.o $(OBJDIR)/loragw_i2c.o $(OBJDIR)/loragw_aux.o $(OBJDIR)/loragw_reg.o $(OBJDIR)/loragw_sx1250.o $(OBJDIR)/loragw_sx125x.o $(OBJDIR)/loragw_sx1302.o $(OBJDIR)/loragw_cal.o $(OBJDIR)/loragw_debug.o $(OBJDIR)/loragw_hal.o $(OBJDIR)/loragw_stts751.o $(OBJDIR)/loragw_gps.o $(OBJDIR)/loragw_sx1302_timestamp.o $(OBJDIR)/loragw_sx1302_rx.o
$(AR) rcs $@ $^
### test programs
test_loragw_spi: tst/test_loragw_spi.c libloragw.a
$(CC) $(CFLAGS) -L. -L../libtools $< -o $@ $(LIBS)
test_loragw_i2c: tst/test_loragw_i2c.c libloragw.a
$(CC) $(CFLAGS) -L. -L../libtools $< -o $@ $(LIBS)
test_loragw_reg: tst/test_loragw_reg.c libloragw.a
$(CC) $(CFLAGS) -L. -L../libtools $< -o $@ $(LIBS)
test_loragw_hal_tx: tst/test_loragw_hal_tx.c libloragw.a
$(CC) $(CFLAGS) -L. -L../libtools $< -o $@ $(LIBS)
test_loragw_hal_rx: tst/test_loragw_hal_rx.c libloragw.a
$(CC) $(CFLAGS) -L. -L../libtools $< -o $@ $(LIBS)
test_loragw_capture_ram: tst/test_loragw_capture_ram.c libloragw.a
$(CC) $(CFLAGS) -L. -L../libtools $< -o $@ $(LIBS)
test_loragw_cal: tst/test_loragw_cal.c libloragw.a
$(CC) $(CFLAGS) -L. -L../libtools $< -o $@ $(LIBS)
test_loragw_spi_sx1250: tst/test_loragw_spi_sx1250.c libloragw.a
$(CC) $(CFLAGS) -L. -L../libtools $< -o $@ $(LIBS)
test_loragw_counter: tst/test_loragw_counter.c libloragw.a
$(CC) $(CFLAGS) -L. -L../libtools $< -o $@ $(LIBS)
test_loragw_gps: tst/test_loragw_gps.c libloragw.a
$(CC) $(CFLAGS) -L. -L../libtools $< -o $@ $(LIBS)
### EOF

View File

@ -0,0 +1,84 @@
/*
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(C)2019 Semtech
Description:
SX1302 AGC parameters definition.
License: Revised BSD License, see LICENSE.TXT file include in the project
*/
#ifndef _LORAGW_AGC_PARAMS_H
#define _LORAGW_AGC_PARAMS_H
/* -------------------------------------------------------------------------- */
/* --- PRIVATE TYPES -------------------------------------------------------- */
struct agc_gain_params_s {
uint8_t ana_min;
uint8_t ana_max;
uint8_t ana_thresh_l;
uint8_t ana_thresh_h;
uint8_t dec_attn_min;
uint8_t dec_attn_max;
uint8_t dec_thresh_l;
uint8_t dec_thresh_h1;
uint8_t dec_thresh_h2;
uint8_t chan_attn_min;
uint8_t chan_attn_max;
uint8_t chan_thresh_l;
uint8_t chan_thresh_h;
uint8_t deviceSel; /* sx1250 only */
uint8_t hpMax; /* sx1250 only */
uint8_t paDutyCycle; /* sx1250 only */
};
/* -------------------------------------------------------------------------- */
/* --- PRIVATE CONSTANTS ---------------------------------------------------- */
const struct agc_gain_params_s agc_params_sx1250 = {
.ana_min = 1,
.ana_max = 13,
.ana_thresh_l = 3,
.ana_thresh_h = 12,
.dec_attn_min = 4,
.dec_attn_max = 15,
.dec_thresh_l = 40,
.dec_thresh_h1 = 80,
.dec_thresh_h2 = 90,
.chan_attn_min = 4,
.chan_attn_max = 14,
.chan_thresh_l = 52,
.chan_thresh_h = 132,
.deviceSel = 0,
.hpMax = 7,
.paDutyCycle = 4
};
const struct agc_gain_params_s agc_params_sx125x = {
.ana_min = 0,
.ana_max = 9,
.ana_thresh_l = 16,
.ana_thresh_h = 35,
.dec_attn_min = 7,
.dec_attn_max = 11,
.dec_thresh_l = 45,
.dec_thresh_h1 = 100,
.dec_thresh_h2 = 115,
.chan_attn_min = 4,
.chan_attn_max = 14,
.chan_thresh_l = 52,
.chan_thresh_h = 132,
.deviceSel = 0,
.hpMax = 0,
.paDutyCycle = 0
};
#endif
/* --- EOF ------------------------------------------------------------------ */

View File

@ -0,0 +1,47 @@
/*
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(C)2019 Semtech
Description:
LoRa concentrator HAL common auxiliary functions
License: Revised BSD License, see LICENSE.TXT file include in the project
*/
#ifndef _LORAGW_AUX_H
#define _LORAGW_AUX_H
/* -------------------------------------------------------------------------- */
/* --- DEPENDANCIES --------------------------------------------------------- */
#include "config.h" /* library configuration options (dynamically generated) */
/* -------------------------------------------------------------------------- */
/* --- PUBLIC MACROS -------------------------------------------------------- */
/**
@brief Get a particular bit value from a byte
@param b [in] Any byte from which we want a bit value
@param p [in] Position of the bit in the byte [0..7]
@param n [in] Number of bits we want to get
@return The value corresponding the requested bits
*/
#define TAKE_N_BITS_FROM(b, p, n) (((b) >> (p)) & ((1 << (n)) - 1))
/* -------------------------------------------------------------------------- */
/* --- PUBLIC FUNCTIONS PROTOTYPES ------------------------------------------ */
/**
@brief Wait for a certain time (millisecond accuracy)
@param t number of milliseconds to wait.
*/
void wait_ms(unsigned long t);
#endif
/* --- EOF ------------------------------------------------------------------ */

View File

@ -0,0 +1,59 @@
/*
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(C)2019 Semtech
Description:
LoRa concentrator radio calibration functions
License: Revised BSD License, see LICENSE.TXT file include in the project
*/
#ifndef _LORAGW_CAL_H
#define _LORAGW_CAL_H
/* -------------------------------------------------------------------------- */
/* --- DEPENDANCIES --------------------------------------------------------- */
#include <stdint.h> /* C99 types*/
#include "config.h" /* library configuration options (dynamically generated) */
/* -------------------------------------------------------------------------- */
/* --- PUBLIC CONSTANTS ----------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* --- PUBLIC MACROS -------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* --- PUBLIC TYPES --------------------------------------------------------- */
struct lgw_sx125x_cal_rx_result_s {
int8_t amp;
int8_t phi;
uint16_t rej;
uint16_t rej_init;
uint16_t snr;
};
struct lgw_sx125x_cal_tx_result_s {
uint8_t dac_gain;
uint8_t mix_gain;
int8_t offset_i;
int8_t offset_q;
uint16_t rej;
uint16_t sig;
};
/* -------------------------------------------------------------------------- */
/* --- PUBLIC FUNCTIONS PROTOTYPES ------------------------------------------ */
int sx1302_cal_start(uint8_t version, struct lgw_conf_rxrf_s * rf_chain_cfg, struct lgw_tx_gain_lut_s * txgain_lut);
#endif
/* --- EOF ------------------------------------------------------------------ */

View File

@ -0,0 +1,74 @@
/*
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(C)2019 Semtech
Description:
LoRa concentrator HAL debug functions
License: Revised BSD License, see LICENSE.TXT file include in the project
*/
#ifndef _LORAGW_DBG_H
#define _LORAGW_DBG_H
/* -------------------------------------------------------------------------- */
/* --- DEPENDANCIES --------------------------------------------------------- */
#include "config.h" /* library configuration options (dynamically generated) */
/* -------------------------------------------------------------------------- */
/* --- PUBLIC MACROS -------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* --- PUBLIC FUNCTIONS PROTOTYPES ------------------------------------------ */
/**
@brief
@param
*/
void dbg_log_buffer_to_file(FILE * file, uint8_t * buffer, uint16_t size);
/**
@brief
@param
*/
void dbg_log_payload_diff_to_file(FILE * file, uint8_t * buffer1, uint8_t * buffer2, uint16_t size);
/**
@brief
@param
*/
void dbg_init_random(void);
/**
@brief
@param
*/
void dbg_generate_random_payload(uint32_t pkt_cnt, uint8_t * buffer_expected, uint8_t size);
/**
@brief
@param
*/
int dbg_check_payload(struct lgw_conf_debug_s * context, FILE * file, uint8_t * payload_received, uint8_t size, uint8_t ref_payload_idx, uint8_t sf);
/**
@brief
@param
*/
void dbg_init_gpio(void);
/**
@brief
@param
*/
void dbg_toggle_gpio(void);
#endif
/* --- EOF ------------------------------------------------------------------ */

234
libloragw/inc/loragw_gps.h Normal file
View File

@ -0,0 +1,234 @@
/*
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(C)2019 Semtech
Description:
Library of functions to manage a GNSS module (typically GPS) for accurate
timestamping of packets and synchronisation of gateways.
A limited set of module brands/models are supported.
License: Revised BSD License, see LICENSE.TXT file include in the project
*/
#ifndef _LORAGW_GPS_H
#define _LORAGW_GPS_H
/* -------------------------------------------------------------------------- */
/* --- DEPENDANCIES --------------------------------------------------------- */
#define _GNU_SOURCE
#include <stdint.h> /* C99 types */
#include <time.h> /* time library */
#include <termios.h> /* speed_t */
#include <unistd.h> /* ssize_t */
#include "config.h" /* library configuration options (dynamically generated) */
/* -------------------------------------------------------------------------- */
/* --- PUBLIC TYPES --------------------------------------------------------- */
/**
@struct coord_s
@brief Time solution required for timestamp to absolute time conversion
*/
struct tref {
time_t systime; /*!> system time when solution was calculated */
uint32_t count_us; /*!> reference concentrator internal timestamp */
struct timespec utc; /*!> reference UTC time (from GPS/NMEA) */
struct timespec gps; /*!> reference GPS time (since 01.Jan.1980) */
double xtal_err; /*!> raw clock error (eg. <1 'slow' XTAL) */
};
/**
@struct coord_s
@brief Geodesic coordinates
*/
struct coord_s {
double lat; /*!> latitude [-90,90] (North +, South -) */
double lon; /*!> longitude [-180,180] (East +, West -)*/
short alt; /*!> altitude in meters (WGS 84 geoid ref.) */
};
/**
@enum gps_msg
@brief Type of GPS (and other GNSS) sentences
*/
enum gps_msg {
UNKNOWN, /*!> neutral value */
IGNORED, /*!> frame was not parsed by the system */
INVALID, /*!> system try to parse frame but failed */
INCOMPLETE, /*!> frame parsed was missing bytes */
/* NMEA messages of interest */
NMEA_RMC, /*!> Recommended Minimum data (time + date) */
NMEA_GGA, /*!> Global positioning system fix data (pos + alt) */
NMEA_GNS, /*!> GNSS fix data (pos + alt, sat number) */
NMEA_ZDA, /*!> Time and Date */
/* NMEA message useful for time reference quality assessment */
NMEA_GBS, /*!> GNSS Satellite Fault Detection */
NMEA_GST, /*!> GNSS Pseudo Range Error Statistics */
NMEA_GSA, /*!> GNSS DOP and Active Satellites (sat number) */
NMEA_GSV, /*!> GNSS Satellites in View (sat SNR) */
/* Misc. NMEA messages */
NMEA_GLL, /*!> Latitude and longitude, with time fix and status */
NMEA_TXT, /*!> Text Transmission */
NMEA_VTG, /*!> Course over ground and Ground speed */
/* uBlox proprietary NMEA messages of interest */
UBX_NAV_TIMEGPS, /*!> GPS Time Solution */
UBX_NAV_TIMEUTC /*!> UTC Time Solution */
};
/* -------------------------------------------------------------------------- */
/* --- PUBLIC CONSTANTS ----------------------------------------------------- */
#define LGW_GPS_SUCCESS 0
#define LGW_GPS_ERROR -1
#define LGW_GPS_MIN_MSG_SIZE (8)
#define LGW_GPS_UBX_SYNC_CHAR (0xB5)
#define LGW_GPS_NMEA_SYNC_CHAR (0x24)
/* -------------------------------------------------------------------------- */
/* --- PUBLIC FUNCTIONS PROTOTYPES ------------------------------------------ */
/**
@brief Configure a GPS module
@param tty_path path to the TTY connected to the GPS
@param gps_familly parameter (eg. ubx6 for uBlox gen.6)
@param target_brate target baudrate for communication (0 keeps default target baudrate)
@param fd_ptr pointer to a variable to receive file descriptor on GPS tty
@return success if the function was able to connect and configure a GPS module
*/
int lgw_gps_enable(char* tty_path, char* gps_familly, speed_t target_brate, int* fd_ptr);
/**
@brief Restore GPS serial configuration and close serial device
@param fd file descriptor on GPS tty
@return success if the function was able to complete
*/
int lgw_gps_disable(int fd);
/**
@brief Parse messages coming from the GPS system (or other GNSS)
@param serial_buff pointer to the string to be parsed
@param buff_size maximum string lengths for NMEA parsing (incl. null char)
@return type of frame parsed
The RAW NMEA sentences are parsed to a global set of variables shared with the
lgw_gps_get function.
If the lgw_parse_nmea and lgw_gps_get are used in different threads, a mutex
lock must be acquired before calling either function.
*/
enum gps_msg lgw_parse_nmea(const char* serial_buff, int buff_size);
/**
@brief Parse Ublox proprietary messages coming from the GPS system
@param serial_buff pointer to the string to be parsed
@param buff_size maximum string lengths for UBX parsing (incl. null char)
@param msg_size number of bytes parsed as UBX message if found
@return type of frame parsed
The RAW UBX sentences are parsed to a global set of variables shared with the
lgw_gps_get function.
If the lgw_parse_ubx and lgw_gps_get are used in different threads, a mutex
lock must be acquired before calling either function.
*/
enum gps_msg lgw_parse_ubx(const char* serial_buff, size_t buff_size, size_t *msg_size);
/**
@brief Get the GPS solution (space & time) for the concentrator
@param utc pointer to store UTC time, with ns precision (NULL to ignore)
@param gps_time pointer to store GPS time, with ns precision (NULL to ignore)
@param loc pointer to store coordinates (NULL to ignore)
@param err pointer to store coordinates standard deviation (NULL to ignore)
@return success if the chosen elements could be returned
This function read the global variables generated by the NMEA/UBX parsing
functions lgw_parse_nmea/lgw_parse_ubx. It returns time and location data in a
format that is exploitable by other functions in that library sub-module.
If the lgw_parse_nmea/lgw_parse_ubx and lgw_gps_get are used in different
threads, a mutex lock must be acquired before calling either function.
*/
int lgw_gps_get(struct timespec *utc, struct timespec *gps_time, struct coord_s *loc, struct coord_s *err);
/**
@brief Get time and position information from the serial GPS last message received
@param utc UTC time, with ns precision (leap seconds are ignored)
@param gps_time timestamp of last time pulse from the GPS module converted to the UNIX epoch
(leap seconds are ignored)
@param loc location information
@param err location error estimate if supported
@return success if timestamp was read and time reference could be refreshed
Set systime to 0 in ref to trigger initial synchronization.
*/
int lgw_gps_sync(struct tref *ref, uint32_t count_us, struct timespec utc, struct timespec gps_time);
/**
@brief Convert concentrator timestamp counter value to UTC time
@param ref time reference structure required for time conversion
@param count_us internal timestamp counter of the LoRa concentrator
@param utc pointer to store UTC time, with ns precision (leap seconds ignored)
@return success if the function was able to convert timestamp to UTC
This function is typically used when a packet is received to transform the
internal counter-based timestamp in an absolute timestamp with an accuracy in
the order of a couple microseconds (ns resolution).
*/
int lgw_cnt2utc(struct tref ref, uint32_t count_us, struct timespec* utc);
/**
@brief Convert UTC time to concentrator timestamp counter value
@param ref time reference structure required for time conversion
@param utc UTC time, with ns precision (leap seconds are ignored)
@param count_us pointer to store internal timestamp counter of LoRa concentrator
@return success if the function was able to convert UTC to timestamp
This function is typically used when a packet must be sent at an accurate time
(eg. to send a piggy-back response after receiving a packet from a node) to
transform an absolute UTC time into a matching internal concentrator timestamp.
*/
int lgw_utc2cnt(struct tref ref,struct timespec utc, uint32_t* count_us);
/**
@brief Convert concentrator timestamp counter value to GPS time
@param ref time reference structure required for time conversion
@param count_us internal timestamp counter of the LoRa concentrator
@param gps_time pointer to store GPS time, with ns precision (leap seconds ignored)
@return success if the function was able to convert timestamp to GPS time
This function is typically used when a packet is received to transform the
internal counter-based timestamp in an absolute timestamp with an accuracy in
the order of a millisecond.
*/
int lgw_cnt2gps(struct tref ref, uint32_t count_us, struct timespec* gps_time);
/**
@brief Convert GPS time to concentrator timestamp counter value
@param ref time reference structure required for time conversion
@param gps_time GPS time, with ns precision (leap seconds are ignored)
@param count_us pointer to store internal timestamp counter of LoRa concentrator
@return success if the function was able to convert GPS time to timestamp
This function is typically used when a packet must be sent at an accurate time
(eg. to send a piggy-back response after receiving a packet from a node) to
transform an absolute GPS time into a matching internal concentrator timestamp.
*/
int lgw_gps2cnt(struct tref ref, struct timespec gps_time, uint32_t* count_us);
#endif
/* --- EOF ------------------------------------------------------------------ */

463
libloragw/inc/loragw_hal.h Normal file
View File

@ -0,0 +1,463 @@
/*
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(C)2019 Semtech
Description:
LoRa concentrator Hardware Abstraction Layer
License: Revised BSD License, see LICENSE.TXT file include in the project
*/
#ifndef _LORAGW_HAL_H
#define _LORAGW_HAL_H
/* -------------------------------------------------------------------------- */
/* --- DEPENDANCIES --------------------------------------------------------- */
#include <stdint.h> /* C99 types */
#include <stdbool.h> /* bool type */
#include "config.h" /* library configuration options (dynamically generated) */
/* -------------------------------------------------------------------------- */
/* --- PUBLIC MACROS -------------------------------------------------------- */
#define IS_LORA_BW(bw) ((bw == BW_125KHZ) || (bw == BW_250KHZ) || (bw == BW_500KHZ))
#define IS_LORA_DR(dr) ((dr == DR_LORA_SF5) || (dr == DR_LORA_SF6) || (dr == DR_LORA_SF7) || (dr == DR_LORA_SF8) || (dr == DR_LORA_SF9) || (dr == DR_LORA_SF10) || (dr == DR_LORA_SF11) || (dr == DR_LORA_SF12))
#define IS_LORA_CR(cr) ((cr == CR_LORA_4_5) || (cr == CR_LORA_4_6) || (cr == CR_LORA_4_7) || (cr == CR_LORA_4_8))
#define IS_FSK_BW(bw) ((bw >= 1) && (bw <= 7))
#define IS_FSK_DR(dr) ((dr >= DR_FSK_MIN) && (dr <= DR_FSK_MAX))
#define IS_TX_MODE(mode) ((mode == IMMEDIATE) || (mode == TIMESTAMPED) || (mode == ON_GPS))
/* -------------------------------------------------------------------------- */
/* --- PUBLIC CONSTANTS ----------------------------------------------------- */
/* return status code */
#define LGW_HAL_SUCCESS 0
#define LGW_HAL_ERROR -1
#define LGW_LBT_ISSUE 1
/* radio-specific parameters */
#define LGW_XTAL_FREQU 32000000 /* frequency of the RF reference oscillator */
#define LGW_RF_CHAIN_NB 2 /* number of RF chains */
#define LGW_RF_RX_BANDWIDTH {1000000, 1000000} /* bandwidth of the radios */
/* concentrator chipset-specific parameters */
/* to use array parameters, declare a local const and use 'if_chain' as index */
#define LGW_IF_CHAIN_NB 10 /* number of IF+modem RX chains */
#define LGW_REF_BW 125000 /* typical bandwidth of data channel */
#define LGW_MULTI_NB 8 /* number of LoRa 'multi SF' chains */
/* values available for the 'modulation' parameters */
/* NOTE: arbitrary values */
#define MOD_UNDEFINED 0
#define MOD_CW 0x08
#define MOD_LORA 0x10
#define MOD_FSK 0x20
/* values available for the 'bandwidth' parameters (LoRa & FSK) */
/* NOTE: directly encode FSK RX bandwidth, do not change */
#define BW_UNDEFINED 0
#define BW_500KHZ 0x06
#define BW_250KHZ 0x05
#define BW_125KHZ 0x04
/* values available for the 'datarate' parameters */
/* NOTE: LoRa values used directly to code SF bitmask in 'multi' modem, do not change */
#define DR_UNDEFINED 0
#define DR_LORA_SF5 5
#define DR_LORA_SF6 6
#define DR_LORA_SF7 7
#define DR_LORA_SF8 8
#define DR_LORA_SF9 9
#define DR_LORA_SF10 10
#define DR_LORA_SF11 11
#define DR_LORA_SF12 12
/* NOTE: for FSK directly use baudrate between 500 bauds and 250 kbauds */
#define DR_FSK_MIN 500
#define DR_FSK_MAX 250000
/* values available for the 'coderate' parameters (LoRa only) */
/* NOTE: arbitrary values */
#define CR_UNDEFINED 0
#define CR_LORA_4_5 0x01
#define CR_LORA_4_6 0x02
#define CR_LORA_4_7 0x03
#define CR_LORA_4_8 0x04
/* values available for the 'status' parameter */
/* NOTE: values according to hardware specification */
#define STAT_UNDEFINED 0x00
#define STAT_NO_CRC 0x01
#define STAT_CRC_BAD 0x11
#define STAT_CRC_OK 0x10
/* values available for the 'tx_mode' parameter */
#define IMMEDIATE 0
#define TIMESTAMPED 1
#define ON_GPS 2
/* values available for 'select' in the status function */
#define TX_STATUS 1
#define RX_STATUS 2
/* status code for TX_STATUS */
/* NOTE: arbitrary values */
#define TX_STATUS_UNKNOWN 0
#define TX_OFF 1 /* TX modem disabled, it will ignore commands */
#define TX_FREE 2 /* TX modem is free, ready to receive a command */
#define TX_SCHEDULED 3 /* TX modem is loaded, ready to send the packet after an event and/or delay */
#define TX_EMITTING 4 /* TX modem is emitting */
/* status code for RX_STATUS */
/* NOTE: arbitrary values */
#define RX_STATUS_UNKNOWN 0
#define RX_OFF 1 /* RX modem is disabled, it will ignore commands */
#define RX_ON 2 /* RX modem is receiving */
#define RX_SUSPENDED 3 /* RX is suspended while a TX is ongoing */
/* Maximum size of Tx gain LUT */
#define TX_GAIN_LUT_SIZE_MAX 16
/* -------------------------------------------------------------------------- */
/* --- PUBLIC TYPES --------------------------------------------------------- */
/**
@enum lgw_radio_type_t
@brief Radio types that can be found on the LoRa Gateway
*/
typedef enum {
LGW_RADIO_TYPE_NONE,
LGW_RADIO_TYPE_SX1255,
LGW_RADIO_TYPE_SX1257,
LGW_RADIO_TYPE_SX1272,
LGW_RADIO_TYPE_SX1276,
LGW_RADIO_TYPE_SX1250
} lgw_radio_type_t;
/**
@struct lgw_conf_board_s
@brief Configuration structure for board specificities
*/
struct lgw_conf_board_s {
bool lorawan_public; /*!> Enable ONLY for *public* networks using the LoRa MAC protocol */
uint8_t clksrc; /*!> Index of RF chain which provides clock to concentrator */
bool full_duplex; /*!> Indicates if the gateway operates in full duplex mode or not */
char spidev_path[64];/*!> Path to access the SPI device to connect to the SX1302 */
};
/**
@struct lgw_rssi_tcomp_s
@brief Structure containing all coefficients necessary to compute the offset to be applied on RSSI for current temperature
*/
struct lgw_rssi_tcomp_s {
float coeff_a;
float coeff_b;
float coeff_c;
float coeff_d;
float coeff_e;
};
/**
@struct lgw_conf_rxrf_s
@brief Configuration structure for a RF chain
*/
struct lgw_conf_rxrf_s {
bool enable; /*!> enable or disable that RF chain */
uint32_t freq_hz; /*!> center frequency of the radio in Hz */
float rssi_offset; /*!> Board-specific RSSI correction factor */
struct lgw_rssi_tcomp_s rssi_tcomp; /*!> Board-specific RSSI temperature compensation coefficients */
lgw_radio_type_t type; /*!> Radio type for that RF chain (SX1255, SX1257....) */
bool tx_enable; /*!> enable or disable TX on that RF chain */
};
/**
@struct lgw_conf_rxif_s
@brief Configuration structure for an IF chain
*/
struct lgw_conf_rxif_s {
bool enable; /*!> enable or disable that IF chain */
uint8_t rf_chain; /*!> to which RF chain is that IF chain associated */
int32_t freq_hz; /*!> center frequ of the IF chain, relative to RF chain frequency */
uint8_t bandwidth; /*!> RX bandwidth, 0 for default */
uint32_t datarate; /*!> RX datarate, 0 for default */
uint8_t sync_word_size; /*!> size of FSK sync word (number of bytes, 0 for default) */
uint64_t sync_word; /*!> FSK sync word (ALIGN RIGHT, eg. 0xC194C1) */
bool implicit_hdr; /*!> LoRa Service implicit header */
uint8_t implicit_payload_length; /*!> LoRa Service implicit header payload length (number of bytes, 0 for default) */
bool implicit_crc_en; /*!> LoRa Service implicit header CRC enable */
uint8_t implicit_coderate; /*!> LoRa Service implicit header coding rate */
};
/**
@struct lgw_pkt_rx_s
@brief Structure containing the metadata of a packet that was received and a pointer to the payload
*/
struct lgw_pkt_rx_s {
uint32_t freq_hz; /*!> central frequency of the IF chain */
int32_t freq_offset;
uint8_t if_chain; /*!> by which IF chain was packet received */
uint8_t status; /*!> status of the received packet */
uint32_t count_us; /*!> internal concentrator counter for timestamping, 1 microsecond resolution */
uint8_t rf_chain; /*!> through which RF chain the packet was received */
uint8_t modem_id;
uint8_t modulation; /*!> modulation used by the packet */
uint8_t bandwidth; /*!> modulation bandwidth (LoRa only) */
uint32_t datarate; /*!> RX datarate of the packet (SF for LoRa) */
uint8_t coderate; /*!> error-correcting code of the packet (LoRa only) */
float rssic; /*!> average RSSI of the channel in dB */
float rssis; /*!> average RSSI of the signal in dB */
float snr; /*!> average packet SNR, in dB (LoRa only) */
float snr_min; /*!> minimum packet SNR, in dB (LoRa only) */
float snr_max; /*!> maximum packet SNR, in dB (LoRa only) */
uint16_t crc; /*!> CRC that was received in the payload */
uint16_t size; /*!> payload size in bytes */
uint8_t payload[256]; /*!> buffer containing the payload */
};
/**
@struct lgw_pkt_tx_s
@brief Structure containing the configuration of a packet to send and a pointer to the payload
*/
struct lgw_pkt_tx_s {
uint32_t freq_hz; /*!> center frequency of TX */
uint8_t tx_mode; /*!> select on what event/time the TX is triggered */
uint32_t count_us; /*!> timestamp or delay in microseconds for TX trigger */
uint8_t rf_chain; /*!> through which RF chain will the packet be sent */
int8_t rf_power; /*!> TX power, in dBm */
uint8_t modulation; /*!> modulation to use for the packet */
int8_t freq_offset; /*!> frequency offset from Radio Tx frequency (CW mode) */
uint8_t bandwidth; /*!> modulation bandwidth (LoRa only) */
uint32_t datarate; /*!> TX datarate (baudrate for FSK, SF for LoRa) */
uint8_t coderate; /*!> error-correcting code of the packet (LoRa only) */
bool invert_pol; /*!> invert signal polarity, for orthogonal downlinks (LoRa only) */
uint8_t f_dev; /*!> frequency deviation, in kHz (FSK only) */
uint16_t preamble; /*!> set the preamble length, 0 for default */
bool no_crc; /*!> if true, do not send a CRC in the packet */
bool no_header; /*!> if true, enable implicit header mode (LoRa), fixed length (FSK) */
uint16_t size; /*!> payload size in bytes */
uint8_t payload[256]; /*!> buffer containing the payload */
};
/**
@struct lgw_tx_gain_s
@brief Structure containing all gains of Tx chain
*/
struct lgw_tx_gain_s {
int8_t rf_power; /*!> measured TX power at the board connector, in dBm */
uint8_t dig_gain; /*!> (sx125x) 2 bits: control of the digital gain of SX1302 */
uint8_t pa_gain; /*!> (sx125x) 2 bits: control of the external PA (SX1302 I/O)
(sx1250) 1 bits: enable/disable the external PA (SX1302 I/O) */
uint8_t dac_gain; /*!> (sx125x) 2 bits: control of the radio DAC */
uint8_t mix_gain; /*!> (sx125x) 4 bits: control of the radio mixer */
int8_t offset_i; /*!> (sx125x) calibrated I offset */
int8_t offset_q; /*!> (sx125x) calibrated Q offset */
uint8_t pwr_idx; /*!> (sx1250) 6 bits: control the radio power index to be used for configuration */
};
/**
@struct lgw_tx_gain_lut_s
@brief Structure defining the Tx gain LUT
*/
struct lgw_tx_gain_lut_s {
struct lgw_tx_gain_s lut[TX_GAIN_LUT_SIZE_MAX]; /*!> Array of Tx gain struct */
uint8_t size; /*!> Number of LUT indexes */
};
/**
@struct lgw_conf_debug_s
@brief Configuration structure for debug
*/
struct conf_ref_payload_s {
uint32_t id;
uint8_t payload[255];
uint32_t prev_cnt;
};
struct lgw_conf_debug_s {
uint8_t nb_ref_payload;
struct conf_ref_payload_s ref_payload[16];
char log_file_name[128];
};
/**
@struct lgw_conf_debug_s
@brief Configuration structure for debug
*/
struct lgw_conf_timestamp_s {
bool enable_precision_ts;
uint8_t max_ts_metrics;
uint8_t nb_symbols;
};
/**
@struct lgw_context_s
@brief Configuration context shared across modules
*/
typedef struct lgw_context_s {
/* Global context */
bool is_started;
struct lgw_conf_board_s board_cfg;
/* RX context */
struct lgw_conf_rxrf_s rf_chain_cfg[LGW_RF_CHAIN_NB];
struct lgw_conf_rxif_s if_chain_cfg[LGW_IF_CHAIN_NB];
struct lgw_conf_rxif_s lora_service_cfg; /* LoRa service channel config parameters */
struct lgw_conf_rxif_s fsk_cfg; /* FSK channel config parameters */
/* TX context */
struct lgw_tx_gain_lut_s tx_gain_lut[LGW_RF_CHAIN_NB];
/* Misc */
struct lgw_conf_timestamp_s timestamp_cfg;
/* Debug */
struct lgw_conf_debug_s debug_cfg;
} lgw_context_t;
/* -------------------------------------------------------------------------- */
/* --- PUBLIC FUNCTIONS PROTOTYPES ------------------------------------------ */
/**
@brief Configure the gateway board
@param conf structure containing the configuration parameters
@return LGW_HAL_ERROR id the operation failed, LGW_HAL_SUCCESS else
*/
int lgw_board_setconf(struct lgw_conf_board_s * conf);
/**
@brief Configure an RF chain (must configure before start)
@param rf_chain number of the RF chain to configure [0, LGW_RF_CHAIN_NB - 1]
@param conf structure containing the configuration parameters
@return LGW_HAL_ERROR id the operation failed, LGW_HAL_SUCCESS else
*/
int lgw_rxrf_setconf(uint8_t rf_chain, struct lgw_conf_rxrf_s * conf);
/**
@brief Configure an IF chain + modem (must configure before start)
@param if_chain number of the IF chain + modem to configure [0, LGW_IF_CHAIN_NB - 1]
@param conf structure containing the configuration parameters
@return LGW_HAL_ERROR id the operation failed, LGW_HAL_SUCCESS else
*/
int lgw_rxif_setconf(uint8_t if_chain, struct lgw_conf_rxif_s * conf);
/**
@brief Configure the Tx gain LUT
@param pointer to structure defining the LUT
@return LGW_HAL_ERROR id the operation failed, LGW_HAL_SUCCESS else
*/
int lgw_txgain_setconf(uint8_t rf_chain, struct lgw_tx_gain_lut_s * conf);
/**
@brief Configure the precision timestamp
@param pointer to structure defining the config to be applied
@return LGW_HAL_ERROR id the operation failed, LGW_HAL_SUCCESS else
*/
int lgw_timestamp_setconf(struct lgw_conf_timestamp_s * conf);
/**
@brief Configure the debug context
@param pointer to structure defining the config to be applied
@return LGW_HAL_ERROR id the operation failed, LGW_HAL_SUCCESS else
*/
int lgw_debug_setconf(struct lgw_conf_debug_s * conf);
/**
@brief Connect to the LoRa concentrator, reset it and configure it according to previously set parameters
@return LGW_HAL_ERROR id the operation failed, LGW_HAL_SUCCESS else
*/
int lgw_start(void);
/**
@brief Stop the LoRa concentrator and disconnect it
@return LGW_HAL_ERROR id the operation failed, LGW_HAL_SUCCESS else
*/
int lgw_stop(void);
/**
@brief A non-blocking function that will fetch up to 'max_pkt' packets from the LoRa concentrator FIFO and data buffer
@param max_pkt maximum number of packet that must be retrieved (equal to the size of the array of struct)
@param pkt_data pointer to an array of struct that will receive the packet metadata and payload pointers
@return LGW_HAL_ERROR id the operation failed, else the number of packets retrieved
*/
int lgw_receive(uint8_t max_pkt, struct lgw_pkt_rx_s * pkt_data);
/**
@brief Schedule a packet to be send immediately or after a delay depending on tx_mode
@param pkt_data structure containing the data and metadata for the packet to send
@return LGW_HAL_ERROR id the operation failed, LGW_HAL_SUCCESS else
/!\ When sending a packet, there is a delay (approx 1.5ms) for the analog
circuitry to start and be stable. This delay is adjusted by the HAL depending
on the board version (lgw_i_tx_start_delay_us).
In 'timestamp' mode, this is transparent: the modem is started
lgw_i_tx_start_delay_us microseconds before the user-set timestamp value is
reached, the preamble of the packet start right when the internal timestamp
counter reach target value.
In 'immediate' mode, the packet is emitted as soon as possible: transferring the
packet (and its parameters) from the host to the concentrator takes some time,
then there is the lgw_i_tx_start_delay_us, then the packet is emitted.
In 'triggered' mode (aka PPS/GPS mode), the packet, typically a beacon, is
emitted lgw_i_tx_start_delay_us microsenconds after a rising edge of the
trigger signal. Because there is no way to anticipate the triggering event and
start the analog circuitry beforehand, that delay must be taken into account in
the protocol.
*/
int lgw_send(struct lgw_pkt_tx_s * pkt_data);
/**
@brief Give the the status of different part of the LoRa concentrator
@param select is used to select what status we want to know
@param code is used to return the status code
@return LGW_HAL_ERROR id the operation failed, LGW_HAL_SUCCESS else
*/
int lgw_status(uint8_t rf_chain, uint8_t select, uint8_t * code);
/**
@brief Abort a currently scheduled or ongoing TX
@return LGW_HAL_ERROR id the operation failed, LGW_HAL_SUCCESS else
*/
int lgw_abort_tx(uint8_t rf_chain);
/**
@brief Return value of internal counter when latest event (eg GPS pulse) was captured
@param trig_cnt_us pointer to receive timestamp value
@return LGW_HAL_ERROR id the operation failed, LGW_HAL_SUCCESS else
*/
int lgw_get_trigcnt(uint32_t * trig_cnt_us);
/**
@brief Return instateneous value of internal counter
@param inst_cnt_us pointer to receive timestamp value
@return LGW_HAL_ERROR id the operation failed, LGW_HAL_SUCCESS else
*/
int lgw_get_instcnt(uint32_t * inst_cnt_us);
/**
@brief Return the LoRa concentrator EUI
@param eui pointer to receive eui
@return LGW_HAL_ERROR id the operation failed, LGW_HAL_SUCCESS else
*/
int lgw_get_eui(uint64_t * eui);
/**
@brief Allow user to check the version/options of the library once compiled
@return pointer on a human-readable null terminated string
*/
const char* lgw_version_info(void);
/**
@brief Return time on air of given packet, in milliseconds
@param packet is a pointer to the packet structure
@return the packet time on air in milliseconds
*/
uint32_t lgw_time_on_air(struct lgw_pkt_tx_s * packet);
#endif
/* --- EOF ------------------------------------------------------------------ */

View File

@ -0,0 +1,75 @@
/*
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(C)2019 Semtech
Description:
Host specific functions to address the LoRa concentrator I2C peripherals.
License: Revised BSD License, see LICENSE.TXT file include in the project
*/
#ifndef _LORAGW_I2C_H
#define _LORAGW_I2C_H
/* -------------------------------------------------------------------------- */
/* --- DEPENDANCIES --------------------------------------------------------- */
#include <stdint.h> /* C99 types*/
#include "config.h" /* library configuration options (dynamically generated) */
/* -------------------------------------------------------------------------- */
/* --- PUBLIC CONSTANTS ----------------------------------------------------- */
#define LGW_I2C_SUCCESS 0
#define LGW_I2C_ERROR -1
#define I2C_DEVICE "/dev/i2c-1"
/* -------------------------------------------------------------------------- */
/* --- PUBLIC FUNCTIONS PROTOTYPES ------------------------------------------ */
/**
@brief Open I2C port
@param path Path to the I2C device driver (absolute or relative)
@param device_addr Address of the device
@param i2c_fd Pointer to receive I2C port file descriptor index
@return 0 if I2C port was open successfully, -1 else
*/
int i2c_linuxdev_open(const char *path, uint8_t device_addr, int *i2c_fd);
/**
@brief Close I2C port
@param i2c_fd I2C port file descriptor index
@return 0 if I2C port was closed successfully, -1 else
*/
int i2c_linuxdev_close(int i2c_fd);
/**
@brief Read data from an I2C port
@param i2c_fd I2C port file descriptor index
@param device_addr I2C device address
@param reg_addr Address of the register to be read
@param data Pointer to a buffer to store read data
@return 0 if I2C data read is successful, -1 else
*/
int i2c_linuxdev_read(int i2c_fd, uint8_t device_addr, uint8_t reg_addr, uint8_t *data);
/**
@brief Write data to an I2C port
@param i2c_fd I2C port file descriptor index
@param device_addr I2C device address
@param reg_addr Address of the register to write to
@param data byte to write in the register
@return 0 if I2C data write is successful, -1 else
*/
int i2c_linuxdev_write(int i2c_fd, uint8_t device_addr, uint8_t reg_addr, uint8_t data);
#endif
/* --- EOF ------------------------------------------------------------------ */

1493
libloragw/inc/loragw_reg.h Normal file

File diff suppressed because it is too large Load Diff

102
libloragw/inc/loragw_spi.h Normal file
View File

@ -0,0 +1,102 @@
/*
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(C)2019 Semtech
Description:
Host specific functions to address the LoRa concentrator registers through
a SPI interface.
Single-byte read/write and burst read/write.
Could be used with multiple SPI ports in parallel (explicit file descriptor)
License: Revised BSD License, see LICENSE.TXT file include in the project
*/
#ifndef _LORAGW_SPI_H
#define _LORAGW_SPI_H
/* -------------------------------------------------------------------------- */
/* --- DEPENDANCIES --------------------------------------------------------- */
#include <stdint.h> /* C99 types*/
#include "config.h" /* library configuration options (dynamically generated) */
/* -------------------------------------------------------------------------- */
/* --- PUBLIC CONSTANTS ----------------------------------------------------- */
#define LGW_SPI_SUCCESS 0
#define LGW_SPI_ERROR -1
#define LGW_BURST_CHUNK 1024
#define SPI_SPEED 2000000
#define LGW_SPI_MUX_TARGET_SX1302 0x00
#define LGW_SPI_MUX_TARGET_RADIOA 0x01
#define LGW_SPI_MUX_TARGET_RADIOB 0x02
/* -------------------------------------------------------------------------- */
/* --- PUBLIC FUNCTIONS PROTOTYPES ------------------------------------------ */
/**
@brief LoRa concentrator SPI setup (configure I/O and peripherals)
@param spidev_path path to the SPI device to be used to connect to the SX1302
@param spi_target_ptr pointer on a generic pointer to SPI target (implementation dependant)
@return status of register operation (LGW_SPI_SUCCESS/LGW_SPI_ERROR)
*/
int lgw_spi_open(const char * spidev_path, void **spi_target_ptr);
/**
@brief LoRa concentrator SPI close
@param spi_target generic pointer to SPI target (implementation dependant)
@return status of register operation (LGW_SPI_SUCCESS/LGW_SPI_ERROR)
*/
int lgw_spi_close(void *spi_target);
/**
@brief LoRa concentrator SPI single-byte write
@param spi_target generic pointer to SPI target (implementation dependant)
@param address 7-bit register address
@param data data byte to write
@return status of register operation (LGW_SPI_SUCCESS/LGW_SPI_ERROR)
*/
int lgw_spi_w(void *spi_target, uint8_t spi_mux_target, uint16_t address, uint8_t data);
/**
@brief LoRa concentrator SPI single-byte read
@param spi_target generic pointer to SPI target (implementation dependant)
@param address 7-bit register address
@param data data byte to write
@return status of register operation (LGW_SPI_SUCCESS/LGW_SPI_ERROR)
*/
int lgw_spi_r(void *spi_target, uint8_t spi_mux_target, uint16_t address, uint8_t *data);
/**
@brief LoRa concentrator SPI burst (multiple-byte) write
@param spi_target generic pointer to SPI target (implementation dependant)
@param address 7-bit register address
@param data pointer to byte array that will be sent to the LoRa concentrator
@param size size of the transfer, in byte(s)
@return status of register operation (LGW_SPI_SUCCESS/LGW_SPI_ERROR)
*/
int lgw_spi_wb(void *spi_target, uint8_t spi_mux_target, uint16_t address, const uint8_t *data, uint16_t size);
/**
@brief LoRa concentrator SPI burst (multiple-byte) read
@param spi_target generic pointer to SPI target (implementation dependant)
@param address 7-bit register address
@param data pointer to byte array that will be written from the LoRa concentrator
@param size size of the transfer, in byte(s)
@return status of register operation (LGW_SPI_SUCCESS/LGW_SPI_ERROR)
*/
int lgw_spi_rb(void *spi_target, uint8_t spi_mux_target, uint16_t address, uint8_t *data, uint16_t size);
#endif
/* --- EOF ------------------------------------------------------------------ */

View File

@ -0,0 +1,57 @@
/*
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(C)2019 Semtech
Description:
Basic driver for ST ts751 temperature sensor
License: Revised BSD License, see LICENSE.TXT file include in the project
*/
#ifndef _LORAGW_STTS751_H
#define _LORAGW_STTS751_H
/* -------------------------------------------------------------------------- */
/* --- DEPENDANCIES --------------------------------------------------------- */
#include <stdint.h> /* C99 types */
#include <stdbool.h> /* bool type */
#include "config.h" /* library configuration options (dynamically generated) */
/* -------------------------------------------------------------------------- */
/* --- INTERNAL SHARED TYPES ------------------------------------------------ */
/* -------------------------------------------------------------------------- */
/* --- INTERNAL SHARED FUNCTIONS -------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* --- PUBLIC CONSTANTS ----------------------------------------------------- */
#define I2C_PORT_TEMP_SENSOR 0x39
/* -------------------------------------------------------------------------- */
/* --- PUBLIC FUNCTIONS ----------------------------------------------------- */
/**
@brief TODO
@param TODO
@return TODO
*/
int lgw_stts751_configure(void);
/**
@brief TODO
@param TODO
@return TODO
*/
int lgw_stts751_get_temperature(float * temperature);
#endif
/* --- EOF ------------------------------------------------------------------ */

View File

@ -0,0 +1,101 @@
/*
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(C)2019 Semtech
Description:
Functions used to handle LoRa concentrator SX1250 radios.
License: Revised BSD License, see LICENSE.TXT file include in the project
*/
#ifndef _LORAGW_SX1250_H
#define _LORAGW_SX1250_H
/* -------------------------------------------------------------------------- */
/* --- DEPENDANCIES --------------------------------------------------------- */
#include <stdint.h> /* C99 types*/
#include "config.h" /* library configuration options (dynamically generated) */
/* -------------------------------------------------------------------------- */
/* --- PUBLIC MACROS -------------------------------------------------------- */
#define SX1250_FREQ_TO_REG(f) (uint32_t)((uint64_t)f * (1 << 25) / 32000000U)
/* -------------------------------------------------------------------------- */
/* --- PUBLIC CONSTANTS ----------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* --- PUBLIC TYPES --------------------------------------------------------- */
typedef enum {
CALIBRATE_IMAGE = 0x98,
CLR_IRQ_STATUS = 0x02,
STOP_TIMER_ON_PREAMBLE = 0x9F,
SET_RFSWITCHMODE = 0x9D,
GET_IRQ_STATUS = 0x12,
GET_RX_BUFFER_STATUS = 0x13,
GET_PACKET_STATUS = 0x14,
READ_BUFFER = 0x1E,
READ_REGISTER = 0x1D,
SET_DIO_IRQ_PARAMS = 0x08,
SET_MODULATION_PARAMS = 0x8B,
SET_PA_CONFIG = 0x95,
SET_PACKET_PARAMS = 0x8C,
SET_PACKET_TYPE = 0x8A,
SET_RF_FREQUENCY = 0x86,
SET_BUFFER_BASE_ADDRESS = 0x8F,
SET_SLEEP = 0x84,
SET_STANDBY = 0x80,
SET_RX = 0x82,
SET_TX = 0x83,
SET_TX_PARAMS = 0x8E,
WRITE_BUFFER = 0x0E,
WRITE_REGISTER = 0x0D,
SET_TXCONTINUOUSWAVE = 0xD1,
SET_TXCONTINUOUSPREAMBLE= 0xD2,
GET_STATUS = 0xC0,
SET_REGULATORMODE = 0x96,
SET_FS = 0xC1,
GET_DEVICE_ERRORS = 0x17
} sx1250_op_code_t;
typedef enum {
STDBY_RC = 0x00,
STDBY_XOSC = 0x01
} sx1250_standby_modes_t;
typedef enum {
PACKET_TYPE_GFSK = 0x00,
PACKET_TYPE_LORA = 0x01
} sx1250_packet_type_t;
typedef enum {
SET_RAMP_10U = 0x00,
SET_RAMP_20U = 0x01,
SET_RAMP_40U = 0x02,
SET_RAMP_80U = 0x03,
SET_RAMP_200U = 0x04,
SET_RAMP_800U = 0x05,
SET_RAMP_1700U = 0x06,
SET_RAMP_3400U = 0x07
} sx1250_ramp_time_t;
/* -------------------------------------------------------------------------- */
/* --- PUBLIC FUNCTIONS PROTOTYPES ------------------------------------------ */
int sx1250_write_command(uint8_t rf_chain, sx1250_op_code_t op_code, uint8_t *data, uint16_t size);
int sx1250_read_command(uint8_t rf_chain, sx1250_op_code_t op_code, uint8_t *data, uint16_t size);
int sx1250_calibrate(uint8_t rf_chain, uint32_t freq_hz);
int sx1250_setup(uint8_t rf_chain, uint32_t freq_hz);
#endif
/* --- EOF ------------------------------------------------------------------ */

View File

@ -0,0 +1,148 @@
/*
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(C)2019 Semtech
Description:
Functions used to handle LoRa concentrator SX1255/SX1257 radios.
License: Revised BSD License, see LICENSE.TXT file include in the project
*/
#ifndef _LORAGW_SX125X_H
#define _LORAGW_SX125X_H
/* -------------------------------------------------------------------------- */
/* --- DEPENDANCIES --------------------------------------------------------- */
#include <stdint.h> /* C99 types */
#include <stdbool.h> /* bool type */
/* -------------------------------------------------------------------------- */
/* --- INTERNAL SHARED TYPES ------------------------------------------------ */
struct radio_reg_s
{
uint8_t addr; /* base address of the register */
uint8_t offs; /* position of the register LSB (between 0 to 7) */
uint8_t leng; /* number of bits in the register */
};
/* -------------------------------------------------------------------------- */
/* --- PUBLIC MACROS -------------------------------------------------------- */
#define SX1257_FREQ_TO_REG(f) (uint32_t)((uint64_t)f * (1 << 19) / 32000000U)
#define SX1255_FREQ_TO_REG(f) (uint32_t)((uint64_t)f * (1 << 20) / 32000000U)
/* -------------------------------------------------------------------------- */
/* --- PUBLIC CONSTANTS ----------------------------------------------------- */
#define LGW_REG_SUCCESS 0
#define LGW_REG_ERROR -1
#define SX125x_32MHz_FRAC 15625 /* irreductible fraction for PLL register caculation */
#define SX125x_TX_DAC_CLK_SEL 0 /* 0:int, 1:ext */
#define SX125x_TX_DAC_GAIN 2 /* 3:0, 2:-3, 1:-6, 0:-9 dBFS (default 2) */
#define SX125x_TX_MIX_GAIN 14 /* -38 + 2*TxMixGain dB (default 14) */
#define SX125x_TX_PLL_BW 1 /* 0:75, 1:150, 2:225, 3:300 kHz (default 3) */
#define SX125x_TX_ANA_BW 0 /* 17.5 / 2*(41-TxAnaBw) MHz (default 0) */
#define SX125x_TX_DAC_BW 5 /* 24 + 8*TxDacBw Nb FIR taps (default 2) */
#define SX125x_RX_LNA_GAIN 1 /* 1 to 6, 1 highest gain */
#define SX125x_RX_BB_GAIN 15 /* 0 to 15 , 15 highest gain */
#define SX125x_LNA_ZIN 0 /* 0:50, 1:200 Ohms (default 1) */
#define SX125x_RX_ADC_BW 7 /* 0 to 7, 2:100<BW<200, 5:200<BW<400,7:400<BW kHz SSB (default 7) */
#define SX125x_RX_ADC_TRIM 6 /* 0 to 7, 6 for 32MHz ref, 5 for 36MHz ref */
#define SX125x_RX_BB_BW 0 /* 0:750, 1:500, 2:375; 3:250 kHz SSB (default 1, max 3) */
#define SX125x_RX_PLL_BW 0 /* 0:75, 1:150, 2:225, 3:300 kHz (default 3, max 3) */
#define SX125x_ADC_TEMP 0 /* ADC temperature measurement mode (default 0) */
#define SX125x_XOSC_GM_STARTUP 13 /* (default 13) */
#define SX125x_XOSC_DISABLE 2 /* Disable of Xtal Oscillator blocks bit0:regulator, bit1:core(gm), bit2:amplifier */
typedef enum {
SX125x_REG_MODE = 0,
SX125x_REG_MODE__PA_DRIVER_EN = 1,
SX125x_REG_MODE__TX_EN = 2,
SX125x_REG_MODE__RX_EN = 3,
SX125x_REG_MODE__STANDBY_EN = 4,
SX125x_REG_FRF_RX_MSB = 5,
SX125x_REG_FRF_RX_MID = 6,
SX125x_REG_FRF_RX_LSB = 7,
SX125x_REG_FRF_TX_MSB = 8,
SX125x_REG_FRF_TX_MID = 9,
SX125x_REG_FRF_TX_LSB = 10,
SX125x_REG_VERSION = 11,
SX125x_REG_TX_GAIN = 12,
SX125x_REG_TX_GAIN__DAC_GAIN = 13,
SX125x_REG_TX_GAIN__MIX_GAIN = 14,
SX125x_REG_TX_BW = 15,
SX125x_REG_TX_BW__PLL_BW = 16,
SX125x_REG_TX_BW__ANA_BW = 17,
SX125x_REG_TX_DAC_BW = 18,
SX125x_REG_RX_ANA_GAIN = 19,
SX125x_REG_RX_ANA_GAIN__LNA_GAIN = 20,
SX125x_REG_RX_ANA_GAIN__BB_GAIN = 21,
SX125x_REG_RX_ANA_GAIN__LNA_ZIN = 22,
SX125x_REG_RX_BW = 23,
SX125x_REG_RX_BW__ADC_BW = 24,
SX125x_REG_RX_BW__ADC_TRIM = 25,
SX125x_REG_RX_BW__BB_BW = 26,
SX125x_REG_RX_PLL_BW = 27,
SX125x_REG_RX_PLL_BW__PLL_BW = 28,
SX125x_REG_RX_PLL_BW__ADC_TEMP_EN = 29,
SX125x_REG_DIO_MAPPING = 30,
SX125x_REG_DIO_MAPPING__DIO_0_MAPPING = 31,
SX125x_REG_DIO_MAPPING__DIO_1_MAPPING = 32,
SX125x_REG_DIO_MAPPING__DIO_2_MAPPING = 33,
SX125x_REG_DIO_MAPPING__DIO_3_MAPPING = 34,
SX125x_REG_CLK_SELECT = 35,
SX125x_REG_CLK_SELECT__DIG_LOOPBACK_EN = 36,
SX125x_REG_CLK_SELECT__RF_LOOPBACK_EN = 37,
SX125x_REG_CLK_SELECT__CLK_OUT = 38,
SX125x_REG_CLK_SELECT__DAC_CLK_SELECT = 39,
SX125x_REG_MODE_STATUS = 40,
SX125x_REG_MODE_STATUS__LOW_BAT_EN = 41,
SX125x_REG_MODE_STATUS__RX_PLL_LOCKED = 42,
SX125x_REG_MODE_STATUS__TX_PLL_LOCKED = 43,
SX125x_REG_LOW_BAT_THRESH = 44,
SX125x_REG_SX1257_XOSC_TEST = 45,
SX125x_REG_SX1257_XOSC_TEST__DISABLE = 46,
SX125x_REG_SX1257_XOSC_TEST__GM_STARTUP = 47,
SX125x_REG_SX1255_XOSC_TEST = 48,
SX125x_REG_SX1255_XOSC_TEST__DISABLE = 49,
SX125x_REG_SX1255_XOSC_TEST__GM_STARTUP = 50
}
radio_reg_t;
#define RADIO_TOTALREGS 51
/* -------------------------------------------------------------------------- */
/* --- PUBLIC CONSTANTS ----------------------------------------------------- */
/*
SX1257 frequency setting :
F_register(24bit) = F_rf (Hz) / F_step(Hz)
= F_rf (Hz) * 2^19 / F_xtal(Hz)
= F_rf (Hz) * 2^19 / 32e6
= F_rf (Hz) * 256/15625
SX1255 frequency setting :
F_register(24bit) = F_rf (Hz) / F_step(Hz)
= F_rf (Hz) * 2^20 / F_xtal(Hz)
= F_rf (Hz) * 2^20 / 32e6
= F_rf (Hz) * 512/15625
*/
/* -------------------------------------------------------------------------- */
/* --- PUBLIC FUNCTIONS PROTOTYPES ------------------------------------------ */
int sx125x_setup(uint8_t rf_chain, uint8_t rf_clkout, bool rf_enable, uint8_t rf_radio_type, uint32_t freq_hz);
int lgw_sx125x_reg_w(radio_reg_t idx, uint8_t data, uint8_t rf_chain);
int lgw_sx125x_reg_r(radio_reg_t idx, uint8_t *data, uint8_t rf_chain);
#endif
/* --- EOF ------------------------------------------------------------------ */

View File

@ -0,0 +1,437 @@
/*
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(C)2019 Semtech
Description:
SX1302 Hardware Abstraction Layer entry functions.
License: Revised BSD License, see LICENSE.TXT file include in the project
*/
#ifndef _LORAGW_SX1302_H
#define _LORAGW_SX1302_H
/* -------------------------------------------------------------------------- */
/* --- DEPENDANCIES --------------------------------------------------------- */
#include <stdint.h> /* C99 types*/
#include "config.h" /* library configuration options (dynamically generated) */
/* -------------------------------------------------------------------------- */
/* --- PUBLIC CONSTANTS ----------------------------------------------------- */
/* Default values */
#define SX1302_AGC_RADIO_GAIN_AUTO 0xFF
#define TX_START_DELAY_DEFAULT 1500 /* Calibrated value for 500KHz BW */
/* type of if_chain + modem */
#define IF_UNDEFINED 0
#define IF_LORA_STD 0x10 /* if + standard single-SF LoRa modem */
#define IF_LORA_MULTI 0x11 /* if + LoRa receiver with multi-SF capability */
#define IF_FSK_STD 0x20 /* if + standard FSK modem */
/* -------------------------------------------------------------------------- */
/* --- PUBLIC MACROS -------------------------------------------------------- */
#define REG_SELECT(rf_chain, a, b) ((rf_chain == 0) ? a : b)
#define SET_PPM_ON(bw,dr) (((bw == BW_125KHZ) && ((dr == DR_LORA_SF11) || (dr == DR_LORA_SF12))) || ((bw == BW_250KHZ) && (dr == DR_LORA_SF12)))
/* -------------------------------------------------------------------------- */
/* --- PUBLIC TYPES --------------------------------------------------------- */
/**
@struct sx1302_if_cfg_t
@brief TODO
*/
typedef struct {
bool if_enable;
bool if_rf_chain; /* for each IF, 0 -> radio A, 1 -> radio B */
int32_t if_freq; /* relative to radio frequency, +/- in Hz */
} sx1302_if_cfg_t;
/**
@struct sx1302_lora_service_cfg_t
@brief TODO
*/
typedef struct {
uint8_t lora_rx_bw; /* bandwidth setting for LoRa standalone modem */
uint8_t lora_rx_sf; /* spreading factor setting for LoRa standalone modem */
bool lora_rx_implicit_hdr; /* implicit header setting for LoRa standalone modem */
uint8_t lora_rx_implicit_length; /* implicit header payload length setting for LoRa standalone modem */
bool lora_rx_implicit_crc_en; /* implicit header payload crc enable setting for LoRa standalone modem */
uint8_t lora_rx_implicit_coderate; /* implicit header payload coderate setting for LoRa standalone modem */
} sx1302_lora_service_cfg_t;
/**
@struct sx1302_fsk_cfg_t
@brief TODO
*/
typedef struct {
uint8_t fsk_rx_bw; /* bandwidth setting of FSK modem */
uint32_t fsk_rx_dr; /* FSK modem datarate in bauds */
uint8_t fsk_sync_word_size; /* default number of bytes for FSK sync word */
uint64_t fsk_sync_word; /* default FSK sync word (ALIGNED RIGHT, MSbit first) */
} sx1302_fsk_cfg_t;
/* -------------------------------------------------------------------------- */
/* --- PUBLIC FUNCTIONS PROTOTYPES ------------------------------------------ */
/**
@brief TODO
@param TODO
@return TODO
*/
void sx1302_init(struct lgw_conf_timestamp_s *conf);
/**
@brief Get the SX1302 unique identifier
@param eui pointerto the memory holding the concentrator EUI
@return LGW_REG_SUCCESS if no error, LGW_REG_ERROR otherwise
*/
int sx1302_get_eui(uint64_t * eui);
/**
@brief Check AGC & ARB MCUs parity error, and update timestamp counter wraping status
@brief This function needs to be called regularly (every few seconds) by the upper layer
@param N/A
@return LGW_REG_SUCCESS if no error, LGW_REG_ERROR otherwise
*/
int sx1302_update(void);
/**
@brief Select the clock source radio
@param rf_chain The RF chain index from which to get the clock source
@return LGW_REG_SUCCESS if success, LGW_REG_ERROR otherwise
*/
int sx1302_radio_clock_select(uint8_t rf_chain);
/**
@brief Apply the radio reset sequence to the required RF chain index
@param rf_chain The RF chain index of the radio to be reset
@param type The type of radio to be reset
@return LGW_REG_SUCCESS if success, LGW_REG_ERROR otherwise
*/
int sx1302_radio_reset(uint8_t rf_chain, lgw_radio_type_t type);
/**
@brief Configure the radio type for the given RF chain
@param rf_chain The RF chain index to be configured
@param type The type of radio to be set for the given RF chain
@return LGW_REG_SUCCESS if success, LGW_REG_ERROR otherwise
*/
int sx1302_radio_set_mode(uint8_t rf_chain, lgw_radio_type_t type);
/**
@brief Give/Release control over the radios to/from the Host
@param host_ctrl Set to true to give control to the host, false otherwise
@return LGW_REG_SUCCESS if success, LGW_REG_ERROR otherwise
*/
int sx1302_radio_host_ctrl(bool host_ctrl);
/**
@brief Perform the radio calibration sequence and fill the TX gain LUT with calibration offsets
@param context_rf_chain The RF chains array from which to get RF chains current configuration
@param clksrc The RF chain index which provides the clock source
@param txgain_lut A pointer to the TX gain LUT to be filled
@return LGW_REG_SUCCESS if success, LGW_REG_ERROR otherwise
*/
int sx1302_radio_calibrate(struct lgw_conf_rxrf_s * context_rf_chain, uint8_t clksrc, struct lgw_tx_gain_lut_s * txgain_lut);
/**
@brief Configure the PA and LNA LUTs
@param N/A
@return LGW_REG_SUCCESS if success, LGW_REG_ERROR otherwise
*/
int sx1302_pa_lna_lut_configure(void);
/**
@brief Configure the Radio Front-End stage of the SX1302
@param N/A
@return LGW_REG_SUCCESS if success, LGW_REG_ERROR otherwise
*/
int sx1302_radio_fe_configure(void);
/**
@brief TODO
@param TODO
@return TODO
*/
uint8_t sx1302_get_ifmod_config(uint8_t if_chain);
/**
@brief Configure the channelizer stage of the SX1302
@param if_cfg A pointer to the channels configuration
@param fix_gain Set to true to force the channelizer to a fixed gain, false to let the AGC controlling it
@return LGW_REG_SUCCESS if success, LGW_REG_ERROR otherwise
*/
int sx1302_channelizer_configure(struct lgw_conf_rxif_s * if_cfg, bool fix_gain);
/**
@brief Configure the correlator stage of the SX1302 LoRa multi-SF modems
@param N/A
@return LGW_REG_SUCCESS if success, LGW_REG_ERROR otherwise
*/
int sx1302_lora_correlator_configure(void);
/**
@brief Configure the correlator stage of the SX1302 LoRa single-SF modem
@param cfg A pointer to the channel configuration
@return LGW_REG_SUCCESS if success, LGW_REG_ERROR otherwise
*/
int sx1302_lora_service_correlator_configure(struct lgw_conf_rxif_s * cfg);
/**
@brief Configure the syncword to be used by LoRa modems (public:0x34, private:0x12)
@param public Set to true to use the "public" syncword, false to use the private one
@param lora_service_sf The spreading factor configured for the single-SF LoRa modem
@return LGW_REG_SUCCESS if success, LGW_REG_ERROR otherwise
*/
int sx1302_lora_syncword(bool public, uint8_t lora_service_sf);
/**
@brief Configure the LoRa multi-SF modems
@param radio_freq_hz The center frequency of the RF chain (0 or 1)
@return LGW_REG_SUCCESS if success, LGW_REG_ERROR otherwise
*/
int sx1302_lora_modem_configure(uint32_t radio_freq_hz);
/**
@brief Configure the LoRa single-SF modem
@param cfg A pointer to the channel configuration
@param radio_freq_hz The center frequency of the RF chain (0 or 1)
@return LGW_REG_SUCCESS if success, LGW_REG_ERROR otherwise
*/
int sx1302_lora_service_modem_configure(struct lgw_conf_rxif_s * cfg, uint32_t radio_freq_hz);
/**
@brief Configure the FSK modem
@param cfg A pointer to the channel configuration
@return LGW_REG_SUCCESS if success, LGW_REG_ERROR otherwise
*/
int sx1302_fsk_configure(struct lgw_conf_rxif_s * cfg);
/**
@brief Enable the modems
@param N/A
@return LGW_REG_SUCCESS if success, LGW_REG_ERROR otherwise
*/
int sx1302_modem_enable(void);
/**
@brief Enable/Disable the GPS to allow PPS trigger and counter sampling
@param enbale Set to true to enable, false otherwise
@return LGW_REG_SUCCESS if success, LGW_REG_ERROR otherwise
*/
int sx1302_gps_enable(bool enable);
/**
@brief Get the current SX1302 internal counter value
@param pps True for getting the counter value at last PPS
@return the counter value in mciroseconds (32-bits)
*/
uint32_t sx1302_timestamp_counter(bool pps);
/**
@brief TODO
@param TODO
@return TODO
*/
int sx1302_agc_load_firmware(const uint8_t *firmware);
/**
@brief TODO
@param TODO
@return TODO
*/
int sx1302_agc_status(uint8_t* status);
/**
@brief TODO
@param TODO
@return TODO
*/
int sx1302_agc_wait_status(uint8_t status);
/**
@brief TODO
@param TODO
@return TODO
*/
int sx1302_agc_mailbox_read(uint8_t mailbox, uint8_t* value);
/**
@brief TODO
@param TODO
@return TODO
*/
int sx1302_agc_mailbox_write(uint8_t mailbox, uint8_t value);
/**
@brief TODO
@param TODO
@return TODO
*/
int sx1302_agc_start(uint8_t version, lgw_radio_type_t radio_type, uint8_t ana_gain, uint8_t dec_gain, uint8_t fdd_mode);
/**
@brief TODO
@param TODO
@return TODO
*/
int sx1302_arb_load_firmware(const uint8_t *firmware);
/**
@brief TODO
@param TODO
@return TODO
*/
int sx1302_arb_status(uint8_t* status);
/**
@brief TODO
@param TODO
@return TODO
*/
int sx1302_arb_wait_status(uint8_t status);
/**
@brief TODO
@param TODO
@return TODO
*/
int sx1302_arb_debug_read(uint8_t reg_id, uint8_t* value);
/**
@brief TODO
@param TODO
@return TODO
*/
int sx1302_arb_debug_write(uint8_t reg_id, uint8_t value);
/**
@brief TODO
@param TODO
@return TODO
*/
int sx1302_arb_start(uint8_t version);
/**
@brief TODO
@param TODO
@return TODO
*/
uint8_t sx1302_arb_get_debug_stats_detect(uint8_t channel);
/**
@brief TODO
@param TODO
@return TODO
*/
uint8_t sx1302_arb_get_debug_stats_alloc(uint8_t channel);
/**
@brief TODO
@param TODO
@return TODO
*/
void sx1302_arb_print_debug_stats(void);
/**
@brief TODO
@param TODO
@return TODO
*/
uint16_t sx1302_lora_payload_crc(const uint8_t * data, uint8_t size);
/**
@brief TODO
@param TODO
@return TODO
*/
uint16_t sx1302_rx_buffer_read_ptr_addr(void);
/**
@brief TODO
@param TODO
@return TODO
*/
uint16_t sx1302_rx_buffer_write_ptr_addr(void);
/**
@brief Check if any data to be fetched from the SX1302 RX buffer and fetch it if any.
@param nb_bytes A pointer to allocated memory to hold the number of bytes fetched
@return LGW_REG_SUCCESS if success, LGW_REG_ERROR otherwise
*/
int sx1302_fetch(uint16_t * nb_bytes);
/**
@brief Parse and return the next packet available in the fetched RX buffer.
@param context Gateway configuration context
@param p The structure to get the packet parsed
@return LGW_REG_SUCCESS if a packet could be parsed, LGW_REG_ERROR otherwise
*/
int sx1302_parse(lgw_context_t * context, struct lgw_pkt_rx_s * p);
/**
@brief Configure the delay to be applied by the SX1302 for TX to start
@param rf_chain The RF chain index to be configured
@param radio_type The type of radio for this RF chain
@param modulation The modulation used for the TX
@param bandwidth The bandwidth used for the TX
@return LGW_REG_SUCCESS if success, LGW_REG_ERROR otherwise
*/
int sx1302_tx_set_start_delay(uint8_t rf_chain, lgw_radio_type_t radio_type, uint8_t modulation, uint8_t bandwidth);
/**
@brief Compute the offset to be applied on RSSI for temperature compensation
@param context a pointer to the memory that holds the current temp comp context
@param temperature the temperature for which to compute the offset to be applied
@return the offset to be applied to RSSI
*/
float sx1302_rssi_get_temperature_offset(struct lgw_rssi_tcomp_s * context, float temperature);
/**
@brief Get current TX status of the SX1302
@param rf_chain the TX chain we want to get the status from
@return current status
*/
uint8_t sx1302_tx_status(uint8_t rf_chain);
/**
@brief Get current RX status of the SX1302
@param rf_chain the RX chain we want to get the status from
@return current status
@note NOT IMPLEMENTED
*/
uint8_t sx1302_rx_status(uint8_t rf_chain);
/**
@brief Abort current transmit
@param rf_chain the TX chain on which we want to abort transmit
@return LGW_REG_SUCCESS if success, LGW_REG_ERROR otherwise
*/
int sx1302_tx_abort(uint8_t rf_chain);
/**
@brief TODO
@param TODO
@return TODO
*/
int sx1302_tx_configure(lgw_radio_type_t radio_type);
/**
@brief TODO
@param TODO
@return TODO
*/
int sx1302_send(lgw_radio_type_t radio_type, struct lgw_tx_gain_lut_s * tx_lut, bool lwan_public, struct lgw_conf_rxif_s * context_fsk, struct lgw_pkt_tx_s * pkt_data);
#endif
/* --- EOF ------------------------------------------------------------------ */

View File

@ -0,0 +1,134 @@
/*
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(C)2019 Semtech
Description:
SX1302 RX buffer Hardware Abstraction Layer
License: Revised BSD License, see LICENSE.TXT file include in the project
*/
#ifndef _LORAGW_SX1302_RX_H
#define _LORAGW_SX1302_RX_H
/* -------------------------------------------------------------------------- */
/* --- DEPENDANCIES --------------------------------------------------------- */
#include <stdint.h> /* C99 types*/
#include "config.h" /* library configuration options (dynamically generated) */
/* -------------------------------------------------------------------------- */
/* --- PUBLIC CONSTANTS ----------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* --- PUBLIC MACROS -------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* --- PUBLIC TYPES --------------------------------------------------------- */
/**
@struct rx_packet_s
@brief packet structure as contained in the sx1302 RX packet engine
*/
typedef struct rx_packet_s {
uint8_t rxbytenb_modem;
uint8_t rx_channel_in;
bool crc_en;
uint8_t coding_rate; /* LoRa only */
uint8_t rx_rate_sf; /* LoRa only */
uint8_t modem_id;
int32_t frequency_offset_error; /* LoRa only */
uint8_t payload[255];
bool payload_crc_error;
bool sync_error; /* LoRa only */
bool header_error; /* LoRa only */
bool timing_set; /* LoRa only */
int8_t snr_average; /* LoRa only */
uint8_t rssi_chan_avg;
uint8_t rssi_signal_avg; /* LoRa only */
uint8_t rssi_chan_max_neg_delta;
uint8_t rssi_chan_max_pos_delta;
uint8_t rssi_sig_max_neg_delta; /* LoRa only */
uint8_t rssi_sig_max_pos_delta; /* LoRa only */
uint32_t timestamp_cnt;
uint16_t rx_crc16_value; /* LoRa only */
uint8_t num_ts_metrics_stored; /* LoRa only */
uint8_t timestamp_avg[255]; /* LoRa only */
uint8_t timestamp_stddev[255]; /* LoRa only */
uint8_t packet_checksum;
} rx_packet_t;
/**
@struct rx_buffer_s
@brief buffer to hold the data fetched from the sx1302 RX buffer
*/
typedef struct rx_buffer_s {
uint8_t buffer[4096]; /*!> byte array to hald the data fetched from the RX buffer */
uint16_t buffer_size; /*!> The number of bytes currently stored in the buffer */
int buffer_index; /*!> Current parsing index in the buffer */
} rx_buffer_t;
/* -------------------------------------------------------------------------- */
/* --- PUBLIC FUNCTIONS PROTOTYPES ------------------------------------------ */
/**
@brief TODO
@param TODO
@return TODO
*/
int rx_buffer_new(rx_buffer_t * self);
/**
@brief TODO
@param TODO
@return TODO
*/
int rx_buffer_del(rx_buffer_t * self);
/**
@brief TODO
@param TODO
@return TODO
*/
int rx_buffer_fetch(rx_buffer_t * self);
/**
@brief TODO
@param TODO
@return TODO
*/
int rx_buffer_pop(rx_buffer_t * self, rx_packet_t * pkt);
/* -------------------------------------------------------------------------- */
/* --- DEBUG FUNCTIONS PROTOTYPES ------------------------------------------- */
/**
@brief TODO
@param TODO
@return TODO
*/
uint16_t rx_buffer_read_ptr_addr(void);
/**
@brief TODO
@param TODO
@return TODO
*/
uint16_t rx_buffer_write_ptr_addr(void);
/**
@brief TODO
@param TODO
@return TODO
*/
void rx_buffer_dump(FILE * file, uint16_t start_addr, uint16_t end_addr);
#endif
/* --- EOF ------------------------------------------------------------------ */

View File

@ -0,0 +1,117 @@
/*
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(C)2019 Semtech
Description:
SX1302 timestamp counter Hardware Abstraction Layer
Handles the conversion of a 32-bits 32MHz counter into a 32-bits 1 MHz counter.
This modules MUST be called regularly by the application to maintain counter
wrapping handling for conversion in 1MHz counter.
Provides function to compute the correction to be applied to the received
timestamp for demodulation processing time.
License: Revised BSD License, see LICENSE.TXT file include in the project
*/
#ifndef _LORAGW_SX1302_TIMESTAMP_H
#define _LORAGW_SX1302_TIMESTAMP_H
/* -------------------------------------------------------------------------- */
/* --- DEPENDANCIES --------------------------------------------------------- */
#include <stdint.h> /* C99 types*/
#include <stdbool.h> /* boolean type */
#include "config.h" /* library configuration options (dynamically generated) */
/* -------------------------------------------------------------------------- */
/* --- PUBLIC CONSTANTS ----------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* --- PUBLIC MACROS -------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* --- PUBLIC TYPES --------------------------------------------------------- */
/**
@struct timestamp_counter_s
@brief context to maintain the internal counters (inst and pps trig) wrapping
*/
typedef struct timestamp_counter_s {
uint32_t counter_us_raw_27bits_inst_prev;
uint32_t counter_us_raw_27bits_pps_prev;
uint8_t counter_us_raw_27bits_inst_wrap;
uint8_t counter_us_raw_27bits_pps_wrap;
} timestamp_counter_t;
/* -------------------------------------------------------------------------- */
/* --- PUBLIC FUNCTIONS ----------------------------------------------------- */
/**
@brief TODO
@param TODO
@return TODO
*/
void timestamp_counter_new(timestamp_counter_t * self);
/**
@brief TODO
@param TODO
@return TODO
*/
void timestamp_counter_delete(timestamp_counter_t * self);
/**
@brief Update the counter wrapping status based on given current counter
@param self Pointer to the counter handler
@param pps Set to true to update the PPS trig counter status
@param cnt Current value of the counter to be used for the update
@return N/A
*/
void timestamp_counter_update(timestamp_counter_t * self, bool pps, uint32_t cnt);
/**
@brief Convert the 27-bits counter given by the SX1302 to a 32-bits counter which wraps on a uint32_t.
@param self Pointer to the counter handler
@param pps Set to true to expand the counter based on the PPS trig wrapping status
@param cnt_us The 27-bits counter to be expanded
@return the 32-bits counter
*/
uint32_t timestamp_counter_expand(timestamp_counter_t * self, bool pps, uint32_t cnt_us);
/**
@brief Reads the SX1302 internal counter register, and return the 32-bits 1 MHz counter
@param self Pointer to the counter handler
@param pps Set to true to expand the counter based on the PPS trig wrapping status
@return the current 32-bits counter
*/
uint32_t timestamp_counter_get(timestamp_counter_t * self, bool pps);
/**
@brief Get the timestamp correction to applied to the packet timestamp
@param ifmod modem type
@param bandwidth modulation bandwidth
@param datarate modulation datarate
@param coderate modulation coding rate
@param crc_en indicates if CRC is enabled or disabled
@param payload_length payload length
@return The correction to be applied to the packet timestamp, in microseconds
*/
uint32_t timestamp_counter_correction(int ifmod, uint8_t bandwidth, uint8_t datarate, uint8_t coderate, uint32_t crc_en, uint16_t payload_length);
/**
@brief TODO
@param TODO
@return TODO
*/
int timestamp_counter_mode(bool enable_precision_ts, uint8_t max_ts_metrics, uint8_t nb_symbols);
#endif
/* --- EOF ------------------------------------------------------------------ */

19
libloragw/library.cfg Normal file
View File

@ -0,0 +1,19 @@
# That file will be included in the Makefile files that have hardware dependencies
### Debug options ###
# Set the DEBUG_* to 1 to activate debug mode in individual modules.
# Warning: that makes the module *very verbose*, do not use for production
DEBUG_AUX= 0
DEBUG_SPI= 0
DEBUG_REG= 0
DEBUG_HAL= 0
DEBUG_LBT= 0
DEBUG_GPS= 0
DEBUG_RAD= 0
DEBUG_CAL= 0
DEBUG_SX1302= 0
### Configuration options ###
BYPASS_FW_INIT = 0
FPGA_BOARD_16_CH = 1

393
libloragw/readme.md Normal file
View File

@ -0,0 +1,393 @@
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(C)2019 Semtech
LoRa concentrator HAL user manual
=================================
## 1. Introduction
The LoRa concentrator Hardware Abstraction Layer is a C library that allow you
to use a Semtech concentrator chip through a reduced number of high level C
functions to configure the hardware, send and receive packets.
The Semtech LoRa concentrator is a digital multi-channel multi-standard packet
radio used to send and receive packets wirelessly using LoRa or FSK modulations.
## 2. Components of the library
The library is composed of the following modules:
* loragw_hal
* loragw_reg
* loragw_spi
* loragw_i2c
* loragw_aux
* loragw_gps
* loragw_sx125x
* loragw_sx1250
* loragw_sx1302
* loragw_sx1302_rx
* loragw_sx1302_timestamp
* loragw_stts751
The library also contains basic test programs to demonstrate code use and check
functionality.
### 2.1. loragw_hal
This is the main module and contains the high level functions to configure and
use the LoRa concentrator:
* lgw_board_setconf, to set the configuration of the concentrator
* lgw_rxrf_setconf, to set the configuration of the radio channels
* lgw_rxif_setconf, to set the configuration of the IF+modem channels
* lgw_txgain_setconf, to set the configuration of the concentrator gain table
* lgw_start, to apply the set configuration to the hardware and start it
* lgw_stop, to stop the hardware
* lgw_receive, to fetch packets if any was received
* lgw_send, to send a single packet (non-blocking, see warning in usage section)
* lgw_status, to check when a packet has effectively been sent
For an standard application, include only this module.
The use of this module is detailed on the usage section.
/!\ When sending a packet, there is a delay (approx 1.5ms) for the analog
circuitry to start and be stable. This delay is adjusted by the HAL depending
on the board version (lgw_i_tx_start_delay_us).
In 'timestamp' mode, this is transparent: the modem is started
lgw_i_tx_start_delay_us microseconds before the user-set timestamp value is
reached, the preamble of the packet start right when the internal timestamp
counter reach target value.
In 'immediate' mode, the packet is emitted as soon as possible: transferring the
packet (and its parameters) from the host to the concentrator takes some time,
then there is the lgw_i_tx_start_delay_us, then the packet is emitted.
In 'triggered' mode (aka PPS/GPS mode), the packet, typically a beacon, is
emitted lgw_i_tx_start_delay_us microsenconds after a rising edge of the
trigger signal. Because there is no way to anticipate the triggering event and
start the analog circuitry beforehand, that delay must be taken into account in
the protocol.
### 2.2. loragw_reg
This module is used to access to the LoRa concentrator registers by name instead
of by address:
* lgw_connect, to initialise and check the connection with the hardware
* lgw_disconnect, to disconnect the hardware
* lgw_reg_r, read a named register
* lgw_reg_w, write a named register
* lgw_reg_rb, read a name register in burst
* lgw_reg_wb, write a named register in burst
This module handles read-only registers protection, multi-byte registers
management, signed registers management, read-modify-write routines for
sub-byte registers and read/write burst fragmentation to respect SPI maximum
burst length constraints.
It make the code much easier to read and to debug.
Moreover, if registers are relocated between different hardware revisions but
keep the same function, the code written using register names can be reused "as
is".
If you need access to all the registers, include this module in your
application.
**/!\ Warning** please be sure to have a good understanding of the LoRa
concentrator inner working before accessing the internal registers directly.
### 2.3. loragw_spi
This module contains the functions to access the LoRa concentrator register
array through the SPI interface:
* lgw_spi_r to read one byte
* lgw_spi_w to write one byte
* lgw_spi_rb to read two bytes or more
* lgw_spi_wb to write two bytes or more
Please *do not* include that module directly into your application.
**/!\ Warning** Accessing the LoRa concentrator register array without the
checks and safety provided by the functions in loragw_reg is not recommended.
### 2.4. loragw_aux
This module contains a single host-dependant function wait_ms to pause for a
defined amount of milliseconds.
The procedure to start and configure the LoRa concentrator hardware contained in
the loragw_hal module requires to wait for several milliseconds at certain
steps, typically to allow for supply voltages or clocks to stabilize after been
switched on.
An accuracy of 1 ms or less is ideal.
If your system does not allow that level of accuracy, make sure that the actual
delay is *longer* that the time specified when the function is called (ie.
wait_ms(X) **MUST NOT** before X milliseconds under any circumstance).
If the minimum delays are not guaranteed during the configuration and start
procedure, the hardware might not work at nominal performance.
Most likely, it will not work at all.
### 2.5. loragw_gps
This module contains functions to synchronize the concentrator internal
counter with an absolute time reference, in our case a GPS satellite receiver.
The internal concentrator counter is used to timestamp incoming packets and to
triggers outgoing packets with a microsecond accuracy.
In some cases, it might be useful to be able to transform that internal
timestamp (that is independent for each concentrator running in a typical
networked system) into an absolute GPS time.
In a typical implementation a GPS specific thread will be called, doing the
following things after opening the serial port:
* blocking reads on the serial port (using system read() function)
* parse UBX messages (using lgw_parse_ubx) to get actual native GPS time
* parse NMEA sentences (using lgw_parse_nmea) to get location and UTC time
Note: the RMC sentence gives UTC time, not native GPS time.
And each time an NAV-TIMEGPS UBX message has been received:
* get the concentrator timestamp (using lgw_get_trigcnt, mutex needed to
protect access to the concentrator)
* get the GPS time contained in the UBX message (using lgw_gps_get)
* call the lgw_gps_sync function (use mutex to protect the time reference that
should be a global shared variable).
Then, in other threads, you can simply used that continuously adjusted time
reference to convert internal timestamps to GPS time (using lgw_cnt2gps) or
the other way around (using lgw_gps2cnt). Inernal concentrator timestamp can
also be converted to/from UTC time using lgw_cnt2utc/lgw_utc2cnt functions.
### 2.6. loragw_sx125x
This module contains functions to handle the configuration of SX1255 and
SX1257 radios.
### 2.7. loragw_sx1250
This module contains functions to handle the configuration of SX1250 radios.
### 2.8. loragw_sx1302
This module contains functions to abstract SX1302 concentrator capabilities.
### 2.9. loragw_sx1302_rx
This module is a sub-module of the loragw_sx1302 module focusing on abstracting
the RX buffer of the SX1302.
### 2.10. loragw_sx1302_timestamp
This module is a sub-module of the loragw_sx1302 module focusing on abstracting
the timestamp counter of the SX1302.
It converts the 32-bits 32MHz internal counter of the SX1302 to a 32-bits 1MHz
counter.
This module needs to be called regularly by upper layers to maintain counter
wrapping when converting from 32MHz to 1MHz.
It also provides function to add correction to the timestamp counter to take
into account the LoRa demodulation processing time.
### 2.11. loragw_stts751
This module contains a very basic driver for the STmicroelectronics ST751
temeprature sensor which is on the CoreCell reference design.
### 2.12. loragw_i2c
This module provides basic function to communicate with I2C devices on the board.
It is used in this project for accessing the temperature sensor.
## 3. Software build process
### 3.1. Details of the software
The library is written following ANSI C conventions but using C99 explicit
length data type for all data exchanges with hardware and for parameters.
The loragw_aux module contains POSIX dependant functions for millisecond
accuracy pause.
For embedded platforms, the function could be rewritten using hardware timers.
### 3.2. Building options
All modules use a fprintf(stderr,...) function to display debug diagnostic
messages if the DEBUG_xxx is set to 1 in library.cfg
### 3.3. Building procedures
For cross-compilation set the ARCH and CROSS_COMPILE variables in the Makefile,
or in your shell environment, with the correct toolchain name and path.
ex:
export PATH=/home/foo/rpi-toolchain/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin:$PATH
export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-
The Makefile in the libloragw directory will parse the library.cfg file and
generate a config.h C header file containing #define options.
Those options enables and disables sections of code in the loragw_xxx.h files
and the *.c source files.
The library.cfg is also used directly to select the proper set of dynamic
libraries to be linked with.
### 3.4. Export
Once build, to use that library on another system, you need to export the
following files :
* libloragw/library.cfg -> root configuration file
* libloragw/libloragw.a -> static library, to be linked with a program
* libloragw/readme.md -> required for license compliance
* libloragw/inc/config.h -> C configuration flags, derived from library.cfg
* libloragw/inc/loragw_*.h -> take only the ones you need (eg. _hal and _gps)
After statically linking the library to your application, only the license
is required to be kept or copied inside your program documentation.
## 4. Hardware dependencies
### 4.1. Hardware revision
The loragw_reg and loragw_hal are written for a specific version on the Semtech
hardware (IP and/or silicon revision).
This code has been written for:
* Semtech SX1302 chip
* Semtech SX1250, SX1257 or SX1255 I/Q transceivers
The library will not work if there is a mismatch between the hardware version
and the library version. You can use the test program test_loragw_reg to check
if the hardware registers match their software declaration.
### 4.2. SPI communication
loragw_spi contains 4 SPI functions (read, write, burst read, burst write) that
are platform-dependant.
The functions must be rewritten depending on the SPI bridge you use:
* SPI master matched to the Linux SPI device driver (provided)
* SPI over USB using FTDI components (not provided)
* native SPI using a microcontroller peripheral (not provided)
You can use the test program test_loragw_spi to check with a logic analyser
that the SPI communication is working
### 4.3. GPS receiver (or other GNSS system)
To use the GPS module of the library, the host must be connected to a GPS
receiver via a serial link (or an equivalent receiver using a different
satellite constellation).
The serial link must appear as a "tty" device in the /dev/ directory, and the
user launching the program must have the proper system rights to read and
write on that device.
Use `chmod a+rw` to allow all users to access that specific tty device, or use
sudo to run all your programs (eg. `sudo ./test_loragw_gps`).
In the current revision, the library only reads data from the serial port,
expecting to receive NMEA frames that are generally sent by GPS receivers as
soon as they are powered up, and UBX messages which are proprietary to u-blox
modules.
The GPS receiver **MUST** send UBX messages shortly after sending a PPS pulse
on to allow internal concentrator timestamps to be converted to absolute GPS time.
If the GPS receiver sends a GGA NMEA sentence, the gateway 3D position will
also be available.
## 5. Usage
### 5.1. Setting the software environment
For a typical application you need to:
* include loragw_hal.h in your program source
* link to the libloragw.a static library during compilation
* link to the librt library due to loragw_aux dependencies (timing functions)
For an application that will also access the concentrator configuration
registers directly (eg. for advanced configuration) you also need to:
* include loragw_reg.h in your program source
### 5.2. Using the software API
To use the HAL in your application, you must follow some basic rules:
* configure the radios path and IF+modem path before starting the radio
* the configuration is only transferred to hardware when you call the *start*
function
* you cannot receive packets until one (or +) radio is enabled AND one (or +)
IF+modem part is enabled AND the concentrator is started
* you cannot send packets until one (or +) radio is enabled AND the concentrator
is started
* you must stop the concentrator before changing the configuration
A typical application flow for using the HAL is the following:
<configure the radios and IF+modems>
<start the LoRa concentrator>
loop {
<fetch packets that were received by the concentrator>
<process, store and/or forward received packets>
<send packets through the concentrator>
}
<stop the concentrator>
**/!\ Warning** The lgw_send function is non-blocking and returns while the
LoRa concentrator is still sending the packet, or even before the packet has
started to be transmitted if the packet is triggered on a future event.
While a packet is emitted, no packet can be received (limitation intrinsic to
most radio frequency systems).
Your application *must* take into account the time it takes to send a packet or
check the status (using lgw_status) before attempting to send another packet.
Trying to send a packet while the previous packet has not finished being send
will result in the previous packet not being sent or being sent only partially
(resulting in a CRC error in the receiver).
### 5.3. Debugging mode
To debug your application, it might help to compile the loragw_hal function
with the debug messages activated (set DEBUG_HAL=1 in library.cfg).
It then send a lot of details, including detailed error messages to *stderr*.
## 6. License
Copyright (c) 2019, SEMTECH S.A.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Semtech corporation nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL SEMTECH S.A. BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*EOF*

View File

@ -0,0 +1,514 @@
static uint8_t agc_firmware_sx1250[8192] = {
0x8A, 0x51, 0xF0, 0x6F, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0x64, 0xC0, 0x80, 0x81, 0x84, 0x0A, 0x04, 0xC6, 0x03, 0x59, 0x00, 0xF4, 0x04, 0xC6,
0x12, 0x28, 0xE5, 0x40, 0xCA, 0x00, 0x61, 0x08, 0xCB, 0x40, 0x62, 0x08, 0xCC, 0x00, 0x63, 0x48,
0xCD, 0x40, 0x4A, 0x70, 0xDC, 0x40, 0x64, 0x08, 0xDD, 0x80, 0x04, 0xF0, 0x32, 0x2F, 0x1D, 0xB0,
0xCA, 0x00, 0x08, 0xF0, 0xCB, 0x40, 0x8B, 0xB0, 0xCC, 0x00, 0x4A, 0x70, 0xCD, 0x81, 0xCE, 0x81,
0xCF, 0xC1, 0xD0, 0x01, 0xD1, 0x41, 0xDC, 0x40, 0x08, 0xF0, 0xDD, 0xC1, 0x6B, 0x67, 0x8A, 0x51,
0x4D, 0x48, 0x83, 0x96, 0xBA, 0x40, 0x83, 0x52, 0x4C, 0x08, 0x83, 0x96, 0xB9, 0x40, 0x83, 0x52,
0x4B, 0x48, 0x83, 0x96, 0xB8, 0x00, 0x83, 0x52, 0x4A, 0x08, 0x83, 0x96, 0xB7, 0x80, 0x1D, 0xB0,
0x83, 0x52, 0xCA, 0x00, 0x08, 0xF0, 0xCB, 0x40, 0x8B, 0xB0, 0xCC, 0x00, 0x4A, 0x70, 0xCD, 0x81,
0xCE, 0x81, 0xCF, 0xC1, 0xD0, 0x01, 0xD1, 0x41, 0xDC, 0x40, 0x08, 0xF0, 0xDD, 0xC1, 0xDD, 0x0A,
0x6B, 0x67, 0x4D, 0x48, 0x83, 0x96, 0xBE, 0x80, 0x83, 0x52, 0x4C, 0x08, 0x83, 0x96, 0xBD, 0x80,
0x83, 0x52, 0x4B, 0x48, 0x83, 0x96, 0xBC, 0x40, 0x83, 0x52, 0x4A, 0x08, 0x83, 0x96, 0xBB, 0x80,
0x08, 0x40, 0xE3, 0x40, 0xC1, 0x70, 0xCA, 0x00, 0x4A, 0x70, 0xDC, 0x40, 0x63, 0x48, 0xDD, 0x80,
0x01, 0xF0, 0x32, 0xE7, 0x8A, 0x51, 0x07, 0x70, 0xE2, 0x00, 0x3A, 0xB0, 0xE1, 0x00, 0xE1, 0xCB,
0x77, 0x28, 0xE2, 0xCB, 0x77, 0x28, 0x00, 0x00, 0x0D, 0x70, 0x83, 0x52, 0x03, 0x53, 0xCA, 0x00,
0x05, 0x30, 0xCB, 0x40, 0x84, 0x30, 0xCC, 0x00, 0x4A, 0x70, 0xCD, 0x81, 0xDC, 0x40, 0x63, 0x48,
0xDD, 0x80, 0x04, 0xF0, 0x32, 0xE7, 0x8A, 0x51, 0x0D, 0x70, 0xCA, 0x00, 0x05, 0x30, 0xCB, 0x40,
0x85, 0x70, 0xCC, 0x00, 0x4A, 0x70, 0xCD, 0x81, 0xDC, 0x40, 0x63, 0x48, 0xDD, 0x80, 0x04, 0xF0,
0x32, 0xE7, 0x8A, 0x51, 0x0D, 0x70, 0xCA, 0x00, 0x05, 0x30, 0xCB, 0x40, 0x82, 0x30, 0xCC, 0x00,
0x4A, 0x70, 0xCD, 0x81, 0xDC, 0x40, 0x63, 0x48, 0xDD, 0x80, 0x04, 0xF0, 0x32, 0xE7, 0x8A, 0x51,
0x0D, 0x70, 0xCA, 0x00, 0x05, 0x30, 0xCB, 0x40, 0x80, 0xF0, 0xCC, 0x00, 0x4A, 0x70, 0xCD, 0x81,
0xDC, 0x40, 0x63, 0x48, 0xDD, 0x80, 0x04, 0xF0, 0x32, 0xE7, 0x8A, 0x51, 0x0D, 0x70, 0xCA, 0x00,
0x05, 0x30, 0xCB, 0x40, 0x83, 0x70, 0xCC, 0x00, 0x4A, 0x70, 0xCD, 0x81, 0xDC, 0x40, 0x63, 0x48,
0xDD, 0x80, 0x04, 0xF0, 0x32, 0xE7, 0x8A, 0x51, 0x63, 0x83, 0x03, 0x9D, 0xC9, 0xA8, 0x9C, 0x91,
0xCA, 0xA8, 0x1C, 0x51, 0x05, 0x30, 0xE1, 0x00, 0xE1, 0xCB, 0xCC, 0xA8, 0x0D, 0x70, 0x83, 0x52,
0x03, 0x53, 0xCA, 0x00, 0x05, 0x30, 0xCB, 0x40, 0x87, 0xB0, 0xCC, 0x00, 0x0B, 0x70, 0xCD, 0x40,
0x4A, 0x70, 0xDC, 0x40, 0x63, 0x48, 0xDD, 0x80, 0x04, 0xF0, 0x32, 0xE7, 0x8A, 0x51, 0x00, 0xB0,
0xE3, 0x88, 0x03, 0x9D, 0xE8, 0xA8, 0x2A, 0xE7, 0x8A, 0x51, 0xC9, 0x00, 0xF3, 0x30, 0xEC, 0xE8,
0x2A, 0xE7, 0x8A, 0x51, 0xC9, 0x00, 0x3F, 0x30, 0xC9, 0x85, 0x49, 0x08, 0xDC, 0x40, 0x00, 0xB0,
0x22, 0xA7, 0x8A, 0x51, 0x63, 0x98, 0xFB, 0x68, 0x00, 0xB0, 0x2A, 0xE7, 0x8A, 0x51, 0xC9, 0x00,
0xFE, 0xF9, 0x01, 0x38, 0x01, 0x29, 0x00, 0xB0, 0x2A, 0xE7, 0x8A, 0x51, 0xC9, 0x00, 0xEF, 0xF9,
0x10, 0x38, 0xC9, 0x00, 0xDC, 0x40, 0x00, 0xB0, 0x22, 0xA7, 0x8A, 0x51, 0x00, 0xB0, 0x2A, 0xE7,
0x8A, 0x51, 0xC9, 0x00, 0xC9, 0x50, 0x49, 0x08, 0xDC, 0x40, 0x00, 0xB0, 0x22, 0xA7, 0x8A, 0x51,
0x00, 0xB0, 0x2A, 0xE7, 0x8A, 0x51, 0xC9, 0x00, 0xC9, 0x92, 0x49, 0x08, 0xDC, 0x40, 0x00, 0xB0,
0x22, 0xA7, 0x8A, 0x51, 0x86, 0x70, 0xCA, 0x00, 0xE3, 0x88, 0x03, 0x9D, 0x2E, 0xE9, 0x83, 0x96,
0x3A, 0x48, 0x83, 0x52, 0xCB, 0x40, 0x83, 0x96, 0x39, 0x48, 0x83, 0x52, 0xCC, 0x00, 0x83, 0x96,
0x38, 0x08, 0x83, 0x52, 0xCD, 0x40, 0x83, 0x96, 0x37, 0x88, 0x3C, 0xE9, 0x83, 0x96, 0x3E, 0x88,
0x83, 0x52, 0xCB, 0x40, 0x83, 0x96, 0x3D, 0x88, 0x83, 0x52, 0xCC, 0x00, 0x83, 0x96, 0x3C, 0x48,
0x83, 0x52, 0xCD, 0x40, 0x83, 0x96, 0x3B, 0x88, 0x83, 0x52, 0xCE, 0x40, 0x4A, 0x70, 0xDC, 0x40,
0x63, 0x48, 0xDD, 0x80, 0x05, 0x30, 0x32, 0xE7, 0x8A, 0x51, 0x0D, 0x70, 0xCA, 0x00, 0x08, 0xF0,
0xCB, 0x40, 0x8F, 0xF0, 0xCC, 0x00, 0x4A, 0x70, 0xCD, 0x81, 0xCE, 0x81, 0xCF, 0xC1, 0xDC, 0x40,
0x63, 0x48, 0xDD, 0x80, 0x06, 0x30, 0x32, 0xE7, 0x8A, 0x51, 0x82, 0x30, 0xCA, 0x00, 0xFF, 0xB0,
0xCB, 0x40, 0xCC, 0x00, 0xCD, 0x40, 0x4A, 0x70, 0xDC, 0x40, 0x63, 0x48, 0xDD, 0x80, 0x04, 0xF0,
0x32, 0xE7, 0x8A, 0x51, 0xC7, 0xF0, 0xE1, 0x00, 0x00, 0x00, 0xE1, 0xCB, 0x64, 0xA9, 0x68, 0xA9,
0x00, 0x00, 0x08, 0x40, 0xEC, 0x40, 0xC1, 0x70, 0xCA, 0x00, 0x4A, 0x70, 0xDC, 0x40, 0x6C, 0x48,
0xDD, 0x80, 0x01, 0xF0, 0x32, 0xE7, 0x8A, 0x51, 0x9F, 0x30, 0xE1, 0x00, 0xE1, 0xCB, 0x76, 0x29,
0x79, 0x29, 0x95, 0xB0, 0x83, 0x52, 0x03, 0x53, 0xCA, 0x00, 0x42, 0xC8, 0xCB, 0x40, 0x41, 0xC8,
0xCC, 0x00, 0x40, 0x88, 0xCD, 0x40, 0x4A, 0x70, 0xCE, 0x81, 0xCE, 0xCA, 0xDC, 0x40, 0x6C, 0x48,
0xDD, 0x80, 0x05, 0x30, 0x32, 0xE7, 0x8A, 0x51, 0xEC, 0x88, 0x03, 0x9D, 0x91, 0xA9, 0x25, 0x70,
0x92, 0xA9, 0x2A, 0x70, 0x2A, 0xE7, 0x8A, 0x51, 0xEB, 0x80, 0xEA, 0x40, 0x3F, 0x30, 0xEA, 0xC5,
0x8E, 0xB0, 0xCA, 0x00, 0x6A, 0x48, 0xCB, 0x40, 0x02, 0xF0, 0xCC, 0x00, 0x4A, 0x70, 0xDC, 0x40,
0x6C, 0x48, 0xDD, 0x80, 0x03, 0x30, 0x32, 0xE7, 0x8A, 0x51, 0x6B, 0x88, 0xE1, 0x00, 0x06, 0x30,
0x03, 0xD0, 0xE1, 0x8C, 0xFF, 0x7E, 0x03, 0x9D, 0xA8, 0xA9, 0x61, 0x08, 0xEA, 0x40, 0x03, 0x30,
0xEA, 0xC5, 0x00, 0xB0, 0xEC, 0x88, 0x03, 0x9D, 0xC1, 0xA9, 0x2A, 0xE7, 0x8A, 0x51, 0xC9, 0x00,
0x6A, 0x48, 0xE1, 0x00, 0x03, 0xD0, 0xE1, 0xCD, 0x03, 0xD0, 0xE1, 0xCD, 0x49, 0x08, 0xF3, 0xB9,
0xCE, 0x29, 0x2A, 0xE7, 0x8A, 0x51, 0xC9, 0x00, 0x6A, 0x48, 0xE1, 0x00, 0x06, 0x30, 0x03, 0xD0,
0xE1, 0xCD, 0xFF, 0x7E, 0x03, 0x9D, 0xC7, 0x29, 0x49, 0x08, 0x3F, 0xB9, 0x61, 0x04, 0xC9, 0x00,
0xDC, 0x40, 0x00, 0xB0, 0x22, 0xA7, 0x8A, 0x51, 0xEC, 0x88, 0x03, 0x9D, 0xE1, 0xE9, 0x27, 0xB0,
0x2A, 0xE7, 0x8A, 0x51, 0xEA, 0x40, 0x28, 0x30, 0x2A, 0xE7, 0x8A, 0x51, 0xEB, 0x80, 0x29, 0x70,
0xEA, 0x29, 0x2C, 0x70, 0x2A, 0xE7, 0x8A, 0x51, 0xEA, 0x40, 0x2D, 0xB0, 0x2A, 0xE7, 0x8A, 0x51,
0xEB, 0x80, 0x2E, 0xB0, 0x2A, 0xE7, 0x8A, 0x51, 0xE3, 0x40, 0x86, 0x70, 0xCA, 0x00, 0x03, 0xD0,
0x6A, 0x8C, 0xCB, 0x40, 0x6B, 0x88, 0xE1, 0x00, 0x03, 0xD0, 0xE1, 0x8C, 0x6A, 0x48, 0xE2, 0x00,
0x06, 0x30, 0x03, 0xD0, 0xE2, 0xCD, 0xFF, 0x7E, 0x03, 0xD0, 0x03, 0x9D, 0xFA, 0x69, 0x62, 0x8D,
0x61, 0x04, 0xCC, 0x00, 0x63, 0x48, 0xE1, 0x00, 0x03, 0xD0, 0xE1, 0x8C, 0x6B, 0x88, 0xE2, 0x00,
0x06, 0x30, 0x03, 0xD0, 0xE2, 0xCD, 0xFF, 0x7E, 0x03, 0xD0, 0x03, 0x9D, 0x0A, 0x6A, 0x62, 0x8D,
0x61, 0x04, 0xCD, 0x40, 0x63, 0x48, 0xE1, 0x00, 0x06, 0x30, 0x03, 0xD0, 0xE1, 0xCD, 0xFF, 0x7E,
0x03, 0xD0, 0x03, 0x9D, 0x16, 0xAA, 0x61, 0x8D, 0xCE, 0x40, 0x4A, 0x70, 0xDC, 0x40, 0x6C, 0x48,
0xDD, 0x80, 0x05, 0x30, 0x32, 0xE7, 0x8A, 0x51, 0xE6, 0x81, 0x02, 0xF0, 0x66, 0x42, 0x03, 0x18,
0x45, 0xAA, 0xC7, 0xF0, 0xE1, 0x00, 0x00, 0x00, 0xE1, 0xCB, 0x2B, 0xEA, 0x2F, 0x2A, 0x00, 0x00,
0x1D, 0xB0, 0x83, 0x52, 0x03, 0x53, 0xCA, 0x00, 0x08, 0xF0, 0xCB, 0x40, 0xC8, 0x70, 0xCC, 0x00,
0x4A, 0x70, 0xCD, 0x81, 0xCE, 0x81, 0xDC, 0x40, 0x6C, 0x48, 0xDD, 0x80, 0x05, 0x30, 0x6B, 0x67,
0x8A, 0x51, 0xCA, 0xDA, 0x45, 0xAA, 0xE6, 0xCA, 0x25, 0xAA, 0x4A, 0x08, 0x20, 0x79, 0x66, 0x44,
0xDC, 0x40, 0x1C, 0x70, 0x22, 0xA7, 0x8A, 0x51, 0x1D, 0xB0, 0xCA, 0x00, 0x08, 0xF0, 0xCB, 0x40,
0xDD, 0x30, 0xCC, 0x00, 0x4A, 0x70, 0xCD, 0x81, 0xCE, 0x81, 0xDC, 0x40, 0x6C, 0x48, 0xDD, 0x80,
0x05, 0x30, 0x6B, 0x67, 0x8A, 0x51, 0x4A, 0x08, 0xDC, 0x40, 0x1B, 0xB0, 0x22, 0xA7, 0x8A, 0x51,
0x4A, 0x08, 0xE9, 0x40, 0x07, 0x70, 0xE9, 0xC5, 0x04, 0xF0, 0x69, 0x42, 0x03, 0x5C, 0x6A, 0xEA,
0x04, 0xF0, 0xE9, 0x40, 0xEC, 0x88, 0x03, 0x9D, 0x74, 0xEA, 0x69, 0x48, 0xDC, 0x40, 0x1D, 0xB0,
0x22, 0xA7, 0x8A, 0x51, 0x26, 0x70, 0x7B, 0x6A, 0x69, 0xCE, 0xF0, 0x39, 0xDC, 0x40, 0x1D, 0xB0,
0x22, 0xA7, 0x8A, 0x51, 0x2B, 0xB0, 0x2A, 0xE7, 0x8A, 0x51, 0xEA, 0x40, 0xEA, 0x9F, 0x39, 0x2B,
0x3D, 0xF0, 0x2A, 0xE7, 0x8A, 0x51, 0xEB, 0x80, 0x3E, 0xF0, 0x2A, 0xE7, 0x8A, 0x51, 0xEA, 0x40,
0x06, 0x30, 0xDC, 0x40, 0x69, 0x48, 0xC6, 0x27, 0x01, 0xBE, 0x84, 0x80, 0x8A, 0x95, 0x00, 0x60,
0x8A, 0x51, 0xE8, 0x00, 0x06, 0x30, 0xDC, 0x40, 0x69, 0x48, 0xC6, 0x27, 0x02, 0xBE, 0x84, 0x80,
0x8A, 0x95, 0x00, 0x60, 0x8A, 0x51, 0xE7, 0x80, 0x6B, 0x88, 0x68, 0x02, 0x03, 0x5C, 0xA8, 0xAA,
0x6B, 0x88, 0x68, 0x46, 0x03, 0x9D, 0xCD, 0x2A, 0x6A, 0x48, 0x67, 0x82, 0x03, 0x18, 0xCD, 0x2A,
0x01, 0xF0, 0x9E, 0x40, 0x1D, 0xB0, 0xCA, 0x00, 0x08, 0xF0, 0xCB, 0x40, 0xC1, 0x70, 0xCC, 0x00,
0x4A, 0x70, 0xCD, 0x81, 0xCE, 0x81, 0xDC, 0x40, 0x6C, 0x48, 0xDD, 0x80, 0x05, 0x30, 0x6B, 0x67,
0x8A, 0x51, 0x4A, 0x08, 0xEB, 0x80, 0x1F, 0x79, 0xE0, 0xB8, 0xEB, 0x80, 0x0D, 0x70, 0xCA, 0x00,
0x08, 0xF0, 0xCB, 0x40, 0xC1, 0x70, 0xCC, 0x00, 0x6B, 0x88, 0xCD, 0x40, 0x4A, 0x70, 0xDC, 0x40,
0x6C, 0x48, 0xDD, 0x80, 0x04, 0xF0, 0x32, 0xE7, 0x8A, 0x51, 0x06, 0x30, 0xDC, 0x40, 0x69, 0x48,
0xC6, 0x27, 0x03, 0xFE, 0x84, 0x80, 0x8A, 0x95, 0x00, 0x60, 0x8A, 0x51, 0xE8, 0x00, 0x06, 0x30,
0xDC, 0x40, 0x69, 0x48, 0xC6, 0x27, 0x04, 0xBE, 0x84, 0x80, 0x8A, 0x95, 0x00, 0x60, 0x8A, 0x51,
0xE7, 0x80, 0x6B, 0x88, 0x68, 0x02, 0x03, 0x5C, 0xED, 0x6A, 0x6B, 0x88, 0x68, 0x46, 0x03, 0x9D,
0xFF, 0x2B, 0x6A, 0x48, 0x67, 0x82, 0x03, 0x18, 0xFF, 0x2B, 0x06, 0x30, 0xDC, 0x40, 0x69, 0x48,
0xC6, 0x27, 0x05, 0xFE, 0x84, 0x80, 0x8A, 0x95, 0x00, 0x60, 0x8A, 0x51, 0xE8, 0x00, 0x06, 0x30,
0xDC, 0x40, 0x69, 0x48, 0xC6, 0x27, 0x06, 0xFE, 0x84, 0x80, 0x8A, 0x95, 0x00, 0x60, 0x8A, 0x51,
0xE7, 0x80, 0x6B, 0x88, 0x68, 0x02, 0x03, 0x5C, 0x0D, 0xEB, 0x6B, 0x88, 0x68, 0x46, 0x03, 0x9D,
0x23, 0xEB, 0x6A, 0x48, 0x67, 0x82, 0x03, 0x18, 0x23, 0xEB, 0x03, 0x30, 0x9E, 0x40, 0x1D, 0xB0,
0xCA, 0x00, 0x08, 0xF0, 0xCB, 0x40, 0xC2, 0x70, 0xCC, 0x00, 0x4A, 0x70, 0xCD, 0x81, 0xCE, 0x81,
0xDC, 0x40, 0x6C, 0x48, 0xDD, 0x80, 0x05, 0x30, 0x6B, 0x67, 0x8A, 0x51, 0x4A, 0x08, 0xEB, 0x80,
0xE1, 0x39, 0x0E, 0xB8, 0xEF, 0xEB, 0x02, 0xF0, 0x9E, 0x40, 0x1D, 0xB0, 0xCA, 0x00, 0x08, 0xF0,
0xCB, 0x40, 0xC2, 0x70, 0xCC, 0x00, 0x4A, 0x70, 0xCD, 0x81, 0xCE, 0x81, 0xDC, 0x40, 0x6C, 0x48,
0xDD, 0x80, 0x05, 0x30, 0x6B, 0x67, 0x8A, 0x51, 0x4A, 0x08, 0xEB, 0x80, 0xE1, 0x39, 0x06, 0x78,
0xEF, 0xEB, 0x07, 0x70, 0xEA, 0xC5, 0x05, 0x30, 0x6A, 0x42, 0x03, 0x18, 0x49, 0xEB, 0x69, 0x48,
0x3C, 0x7E, 0x84, 0x80, 0x8A, 0x95, 0x00, 0x60, 0x8A, 0x51, 0xE4, 0x00, 0x69, 0x48, 0x2D, 0x7E,
0x64, 0xEB, 0x6A, 0x48, 0x05, 0xBA, 0x03, 0x9D, 0x57, 0x6B, 0x69, 0x48, 0x41, 0xFE, 0x84, 0x80,
0x8A, 0x95, 0x00, 0x60, 0x8A, 0x51, 0xE4, 0x00, 0x69, 0x48, 0x32, 0x3E, 0x64, 0xEB, 0x06, 0x30,
0x6A, 0x42, 0x03, 0x5C, 0x69, 0x2B, 0x69, 0x48, 0x46, 0x3E, 0x84, 0x80, 0x8A, 0x95, 0x00, 0x60,
0x8A, 0x51, 0xE4, 0x00, 0x69, 0x48, 0x37, 0xBE, 0x84, 0x80, 0x8A, 0x95, 0x00, 0x60, 0x8A, 0x51,
0xE5, 0x40, 0x0D, 0x70, 0xCA, 0x00, 0x08, 0xF0, 0xCB, 0x40, 0x89, 0x70, 0xCC, 0x00, 0x06, 0x30,
0x6A, 0x42, 0x03, 0x5C, 0x75, 0x6B, 0x09, 0x30, 0x76, 0x6B, 0x0D, 0x70, 0xCD, 0x40, 0x4A, 0x70,
0xDC, 0x40, 0x6C, 0x48, 0xDD, 0x80, 0x04, 0xF0, 0x32, 0xE7, 0x8A, 0x51, 0x06, 0x30, 0x6A, 0x42,
0x03, 0x5C, 0xA5, 0x2B, 0x1D, 0xB0, 0xCA, 0x00, 0x08, 0xF0, 0xCB, 0x40, 0xC2, 0x70, 0xCC, 0x00,
0x4A, 0x70, 0xCD, 0x81, 0xCE, 0x81, 0xDC, 0x40, 0x6C, 0x48, 0xDD, 0x80, 0x05, 0x30, 0x6B, 0x67,
0x8A, 0x51, 0x4A, 0x08, 0xEB, 0x80, 0x9F, 0xB9, 0x20, 0x38, 0xEB, 0x80, 0x0D, 0x70, 0xCA, 0x00,
0x08, 0xF0, 0xCB, 0x40, 0xC2, 0x70, 0xCC, 0x00, 0x6B, 0x88, 0xCD, 0x40, 0x4A, 0x70, 0xDC, 0x40,
0x6C, 0x48, 0xDD, 0x80, 0x04, 0xF0, 0x32, 0xE7, 0x8A, 0x51, 0x64, 0x08, 0x03, 0x59, 0xD4, 0x2B,
0x1D, 0xB0, 0xCA, 0x00, 0x08, 0xF0, 0xCB, 0x40, 0xC1, 0x70, 0xCC, 0x00, 0x4A, 0x70, 0xCD, 0x81,
0xCE, 0x81, 0xDC, 0x40, 0x6C, 0x48, 0xDD, 0x80, 0x05, 0x30, 0x6B, 0x67, 0x8A, 0x51, 0x4A, 0x08,
0xEB, 0x80, 0x64, 0x08, 0xE1, 0x00, 0x05, 0x30, 0x03, 0xD0, 0xE1, 0xCD, 0xFF, 0x7E, 0x03, 0x9D,
0xBC, 0x6B, 0x6B, 0x88, 0x1F, 0x79, 0x61, 0x04, 0xEB, 0x80, 0x0D, 0x70, 0xCA, 0x00, 0x08, 0xF0,
0xCB, 0x40, 0xC1, 0x70, 0xCC, 0x00, 0x6B, 0x88, 0xCD, 0x40, 0x4A, 0x70, 0xDC, 0x40, 0x6C, 0x48,
0xDD, 0x80, 0x04, 0xF0, 0x32, 0xE7, 0x8A, 0x51, 0x65, 0x48, 0x03, 0x59, 0xFF, 0x2B, 0x1D, 0xB0,
0xCA, 0x00, 0x08, 0xF0, 0xCB, 0x40, 0xC2, 0x70, 0xCC, 0x00, 0x4A, 0x70, 0xCD, 0x81, 0xCE, 0x81,
0xDC, 0x40, 0x6C, 0x48, 0xDD, 0x80, 0x05, 0x30, 0x6B, 0x67, 0x8A, 0x51, 0x4A, 0x08, 0xEB, 0x80,
0x65, 0x48, 0xE1, 0x00, 0x03, 0xD0, 0xE1, 0xCD, 0x6B, 0x88, 0xE1, 0x39, 0x61, 0x04, 0xEB, 0x80,
0x0D, 0x70, 0xCA, 0x00, 0x08, 0xF0, 0xCB, 0x40, 0xC2, 0x70, 0xCC, 0x00, 0x6B, 0x88, 0xCD, 0x40,
0x4A, 0x70, 0xDC, 0x40, 0x6C, 0x48, 0xDD, 0x80, 0x04, 0xF0, 0x32, 0xE7, 0x8A, 0x51, 0x6C, 0xCB,
0x07, 0xAC, 0x00, 0xB0, 0x2A, 0xE7, 0x8A, 0x51, 0xC9, 0x00, 0x49, 0x52, 0x0C, 0x6C, 0x00, 0xB0,
0x2A, 0xE7, 0x8A, 0x51, 0xC9, 0x00, 0x49, 0x10, 0x49, 0x08, 0xDC, 0x40, 0x00, 0xB0, 0x22, 0xA7,
0x8A, 0x51, 0x05, 0x30, 0xE1, 0x00, 0xE1, 0xCB, 0x13, 0xAC, 0x00, 0xB0, 0x2A, 0xE7, 0x8A, 0x51,
0xC9, 0x00, 0xFD, 0xF9, 0x02, 0x38, 0xC9, 0x00, 0xDC, 0x40, 0x00, 0xB0, 0x22, 0xA7, 0x8A, 0x51,
0x00, 0xB0, 0x2A, 0xE7, 0x8A, 0x51, 0xC9, 0x00, 0xDF, 0xF9, 0x20, 0x38, 0xC9, 0x00, 0x49, 0x08,
0xDC, 0x40, 0x00, 0xB0, 0x22, 0xA7, 0x8A, 0x51, 0x15, 0x70, 0xE2, 0x00, 0xC6, 0xB0, 0xE1, 0x00,
0xE1, 0xCB, 0x30, 0x6C, 0xE2, 0xCB, 0x30, 0x6C, 0x00, 0x00, 0x0D, 0x70, 0x83, 0x52, 0x03, 0x53,
0xCA, 0x00, 0x05, 0x30, 0xCB, 0x40, 0x84, 0x30, 0xCC, 0x00, 0x4A, 0x70, 0xCD, 0x81, 0xDC, 0x40,
0x6C, 0x48, 0xDD, 0x80, 0x04, 0xF0, 0x32, 0xE7, 0x8A, 0x51, 0x0D, 0x70, 0xCA, 0x00, 0x05, 0x30,
0xCB, 0x40, 0x85, 0x70, 0xCC, 0x00, 0x4A, 0x70, 0xCD, 0x81, 0xDC, 0x40, 0x6C, 0x48, 0xDD, 0x80,
0x04, 0xF0, 0x32, 0xE7, 0x8A, 0x51, 0x0D, 0x70, 0xCA, 0x00, 0x05, 0x30, 0xCB, 0x40, 0x82, 0x30,
0xCC, 0x00, 0x3F, 0x30, 0xCD, 0x40, 0x4A, 0x70, 0xDC, 0x40, 0x6C, 0x48, 0xDD, 0x80, 0x04, 0xF0,
0x32, 0xE7, 0x8A, 0x51, 0x0D, 0x70, 0xCA, 0x00, 0x05, 0x30, 0xCB, 0x40, 0x80, 0xF0, 0xCC, 0x00,
0x3E, 0xF0, 0xCD, 0x40, 0x4A, 0x70, 0xDC, 0x40, 0x6C, 0x48, 0xDD, 0x80, 0x04, 0xF0, 0x32, 0xE7,
0x8A, 0x51, 0x0D, 0x70, 0xCA, 0x00, 0x05, 0x30, 0xCB, 0x40, 0x83, 0x70, 0xCC, 0x00, 0x3E, 0xF0,
0xCD, 0x40, 0x4A, 0x70, 0xDC, 0x40, 0x6C, 0x48, 0xDD, 0x80, 0x04, 0xF0, 0x32, 0xE7, 0x8A, 0x51,
0x6C, 0x83, 0x03, 0x9D, 0x85, 0xAC, 0x9C, 0xD5, 0x86, 0xAC, 0x1C, 0x95, 0x05, 0x30, 0xE1, 0x00,
0xE1, 0xCB, 0x88, 0x6C, 0x0D, 0x70, 0x83, 0x52, 0x03, 0x53, 0xCA, 0x00, 0x05, 0x30, 0xCB, 0x40,
0x87, 0xB0, 0xCC, 0x00, 0x09, 0x30, 0xCD, 0x40, 0x4A, 0x70, 0xDC, 0x40, 0x6C, 0x48, 0xDD, 0x80,
0x04, 0xF0, 0x32, 0xE7, 0x8A, 0x51, 0xD1, 0xB0, 0xCA, 0x00, 0x4A, 0x70, 0xDC, 0x40, 0x6C, 0x48,
0xDD, 0x80, 0x01, 0xF0, 0x32, 0xE7, 0x8A, 0x51, 0x04, 0xF0, 0xE2, 0x00, 0x1C, 0x70, 0xE1, 0x00,
0xE1, 0xCB, 0xA8, 0xAC, 0xE2, 0xCB, 0xA8, 0xAC, 0x00, 0x00, 0x08, 0x40, 0x55, 0xB0, 0x83, 0x52,
0x9B, 0x40, 0xC8, 0x01, 0x02, 0xF0, 0x48, 0xC2, 0x03, 0x18, 0x72, 0x2D, 0x48, 0xC8, 0x01, 0xBE,
0x9B, 0x40, 0x48, 0xC8, 0xB2, 0x7E, 0x84, 0x80, 0x83, 0x93, 0x80, 0x81, 0x48, 0xC8, 0xB2, 0x7E,
0x84, 0x80, 0x80, 0x88, 0x03, 0x9D, 0xEA, 0x2C, 0x3C, 0xB0, 0x2A, 0xE7, 0x8A, 0x51, 0xC9, 0x00,
0x48, 0x18, 0xD6, 0x2C, 0x48, 0xC8, 0xB2, 0x7E, 0x84, 0x80, 0x49, 0x08, 0xDE, 0x80, 0x07, 0x70,
0x03, 0xD0, 0xDE, 0x0C, 0xFF, 0x7E, 0x03, 0x9D, 0xD0, 0xAC, 0xE1, 0xEC, 0x48, 0xC8, 0xB2, 0x7E,
0x84, 0x80, 0x49, 0x08, 0xDE, 0x80, 0x05, 0x30, 0x03, 0xD0, 0xDE, 0x0C, 0xFF, 0x7E, 0x03, 0x9D,
0xDC, 0x2C, 0x5E, 0x88, 0x83, 0x93, 0x80, 0x40, 0x48, 0xC8, 0xB2, 0x7E, 0x84, 0x80, 0x01, 0xF0,
0x80, 0xC5, 0xBE, 0x6C, 0x3E, 0xF0, 0x2A, 0xE7, 0x8A, 0x51, 0xC9, 0x00, 0x49, 0x08, 0x03, 0x59,
0x0A, 0xAD, 0x10, 0xF0, 0x49, 0x02, 0x03, 0x18, 0x0A, 0xAD, 0x48, 0xC8, 0xAE, 0xBE, 0x84, 0x80,
0x49, 0x08, 0x83, 0x93, 0x80, 0x40, 0x48, 0xC8, 0xAE, 0xBE, 0x84, 0x80, 0x00, 0x48, 0xDC, 0x40,
0x1B, 0xB0, 0x22, 0xA7, 0x8A, 0x51, 0x48, 0xC8, 0xAC, 0x7E, 0x84, 0x80, 0x83, 0x93, 0x80, 0x81,
0x80, 0xCA, 0x29, 0xED, 0x49, 0x08, 0xFF, 0x3A, 0x03, 0x9D, 0x16, 0xED, 0x48, 0xC8, 0xAE, 0xBE,
0x84, 0x80, 0x04, 0xF0, 0x83, 0x93, 0x80, 0x40, 0xFF, 0xB0, 0x20, 0x6D, 0x48, 0xC8, 0xAE, 0xBE,
0x84, 0x80, 0x04, 0xF0, 0x83, 0x93, 0x80, 0x40, 0x48, 0xC8, 0xAE, 0xBE, 0x84, 0x80, 0x00, 0x48,
0xDC, 0x40, 0x1B, 0xB0, 0x22, 0xA7, 0x8A, 0x51, 0x48, 0xC8, 0xAC, 0x7E, 0x84, 0x80, 0x83, 0x93,
0x80, 0x81, 0x3F, 0x30, 0x2A, 0xE7, 0x8A, 0x51, 0xC9, 0x00, 0x49, 0x08, 0x03, 0x59, 0x49, 0xED,
0x0E, 0x70, 0x49, 0x02, 0x03, 0x18, 0x49, 0xED, 0x48, 0xC8, 0xA8, 0x3E, 0x84, 0x80, 0x49, 0x08,
0x83, 0x93, 0x80, 0x40, 0x48, 0xC8, 0xA8, 0x3E, 0x84, 0x80, 0x00, 0x48, 0xDC, 0x40, 0x1C, 0x70,
0x22, 0xA7, 0x8A, 0x51, 0x48, 0xC8, 0x23, 0x3E, 0x84, 0x80, 0x83, 0x93, 0x80, 0x81, 0x80, 0xCA,
0x68, 0xED, 0x49, 0x08, 0xFF, 0x3A, 0x03, 0x9D, 0x55, 0x2D, 0x48, 0xC8, 0xA8, 0x3E, 0x84, 0x80,
0x0D, 0x70, 0x83, 0x93, 0x80, 0x40, 0xFF, 0xB0, 0x5F, 0xAD, 0x48, 0xC8, 0xA8, 0x3E, 0x84, 0x80,
0x0D, 0x70, 0x83, 0x93, 0x80, 0x40, 0x48, 0xC8, 0xA8, 0x3E, 0x84, 0x80, 0x00, 0x48, 0xDC, 0x40,
0x1C, 0x70, 0x22, 0xA7, 0x8A, 0x51, 0x48, 0xC8, 0x23, 0x3E, 0x84, 0x80, 0x83, 0x93, 0x80, 0x81,
0x48, 0xC8, 0xB0, 0x3E, 0x84, 0x80, 0x00, 0x48, 0xDC, 0x40, 0x1A, 0x70, 0x22, 0xA7, 0x8A, 0x51,
0xC8, 0x4A, 0xB2, 0xEC, 0x03, 0x30, 0x9B, 0x40, 0x3C, 0xB0, 0x2A, 0xE7, 0x8A, 0x51, 0xC9, 0x00,
0x03, 0xBA, 0x03, 0x9D, 0x74, 0x2D, 0x3E, 0xF0, 0x2A, 0xE7, 0x8A, 0x51, 0xAA, 0x00, 0x3F, 0x30,
0x2A, 0xE7, 0x8A, 0x51, 0xAC, 0x00, 0x2A, 0x02, 0x03, 0x5C, 0x9C, 0x2D, 0x0E, 0x70, 0x2A, 0x02,
0x03, 0x18, 0x9C, 0x2D, 0x2C, 0x08, 0x03, 0x59, 0x9C, 0x2D, 0x2A, 0x08, 0xDC, 0x40, 0x1B, 0xB0,
0x22, 0xA7, 0x8A, 0x51, 0x2C, 0x08, 0xDC, 0x40, 0x1C, 0x70, 0x22, 0xA7, 0x8A, 0x51, 0x2A, 0x08,
0xA9, 0x00, 0x2C, 0x08, 0xAB, 0x40, 0xAA, 0x2D, 0x0D, 0x70, 0xDC, 0x40, 0x1B, 0xB0, 0x22, 0xA7,
0x8A, 0x51, 0x1C, 0x70, 0xDC, 0x81, 0xDC, 0xCA, 0x22, 0xA7, 0x8A, 0x51, 0x0D, 0x70, 0xA9, 0x00,
0xAB, 0x81, 0xAB, 0xCA, 0x04, 0xF0, 0x9B, 0x40, 0x3C, 0xB0, 0x2A, 0xE7, 0x8A, 0x51, 0xC9, 0x00,
0x04, 0x7A, 0x03, 0x9D, 0xAC, 0x2D, 0x3E, 0xF0, 0x2A, 0xE7, 0x8A, 0x51, 0xAE, 0x40, 0x3F, 0x30,
0x2A, 0xE7, 0x8A, 0x51, 0xB0, 0xC0, 0x2E, 0x42, 0x03, 0x5C, 0xD0, 0xED, 0x02, 0xF0, 0x30, 0xC2,
0x03, 0x5C, 0xD0, 0xED, 0x2E, 0x48, 0xDC, 0x40, 0x1B, 0xB0, 0x22, 0xA7, 0x8A, 0x51, 0x30, 0xC8,
0xDC, 0x40, 0x1C, 0x70, 0x22, 0xA7, 0x8A, 0x51, 0x2E, 0x48, 0xAD, 0x40, 0x30, 0xC8, 0xDD, 0xAD,
0x0C, 0x30, 0xDC, 0x40, 0x1B, 0xB0, 0x22, 0xA7, 0x8A, 0x51, 0x03, 0x30, 0xDC, 0x40, 0x1C, 0x70,
0x22, 0xA7, 0x8A, 0x51, 0x0C, 0x30, 0xAD, 0x40, 0x03, 0x30, 0xAF, 0x80, 0x05, 0x30, 0x9B, 0x40,
0x3C, 0xB0, 0x2A, 0xE7, 0x8A, 0x51, 0xC9, 0x00, 0x05, 0xBA, 0x03, 0x9D, 0xE0, 0xED, 0x3E, 0xF0,
0x2A, 0xE7, 0x8A, 0x51, 0xB7, 0x80, 0x3F, 0x30, 0x2A, 0xE7, 0x8A, 0x51, 0xB9, 0x40, 0x37, 0x82,
0x03, 0x5C, 0x07, 0xEE, 0x10, 0xF0, 0x37, 0x82, 0x03, 0x18, 0x07, 0xEE, 0x39, 0x48, 0x03, 0x59,
0x07, 0xEE, 0x37, 0x88, 0xDC, 0x40, 0x1B, 0xB0, 0x22, 0xA7, 0x8A, 0x51, 0x39, 0x48, 0xDC, 0x40,
0x1C, 0x70, 0x22, 0xA7, 0x8A, 0x51, 0x37, 0x88, 0xB6, 0x40, 0x39, 0x48, 0x14, 0xAE, 0x0F, 0xB0,
0xDC, 0x40, 0x1B, 0xB0, 0x22, 0xA7, 0x8A, 0x51, 0x04, 0xF0, 0xDC, 0x40, 0x1C, 0x70, 0x22, 0xA7,
0x8A, 0x51, 0x0F, 0xB0, 0xB6, 0x40, 0x04, 0xF0, 0xB8, 0x00, 0x06, 0x30, 0x9B, 0x40, 0x3C, 0xB0,
0x2A, 0xE7, 0x8A, 0x51, 0xC9, 0x00, 0x06, 0xBA, 0x03, 0x9D, 0x17, 0x2E, 0x3D, 0xF0, 0x2A, 0xE7,
0x8A, 0x51, 0xBD, 0x80, 0x3E, 0xF0, 0x2A, 0xE7, 0x8A, 0x51, 0xBB, 0x80, 0x3F, 0x30, 0x2A, 0xE7,
0x8A, 0x51, 0xBF, 0xC0, 0x3B, 0x88, 0x3D, 0x82, 0x03, 0x5C, 0x4A, 0xEE, 0x3F, 0xC8, 0x3B, 0x82,
0x03, 0x5C, 0x4A, 0xEE, 0x3F, 0xC8, 0x03, 0x59, 0x4A, 0xEE, 0x3D, 0x88, 0xDC, 0x40, 0x1A, 0x70,
0x22, 0xA7, 0x8A, 0x51, 0x3B, 0x88, 0xDC, 0x40, 0x1B, 0xB0, 0x22, 0xA7, 0x8A, 0x51, 0x3F, 0xC8,
0xDC, 0x40, 0x1C, 0x70, 0x22, 0xA7, 0x8A, 0x51, 0x3D, 0x88, 0xBC, 0x40, 0x3B, 0x88, 0xBA, 0x40,
0x3F, 0xC8, 0x5E, 0x6E, 0x5A, 0xB0, 0xDC, 0x40, 0x1A, 0x70, 0x22, 0xA7, 0x8A, 0x51, 0x50, 0x30,
0xDC, 0x40, 0x1B, 0xB0, 0x22, 0xA7, 0x8A, 0x51, 0x28, 0x30, 0xDC, 0x40, 0x1C, 0x70, 0x22, 0xA7,
0x8A, 0x51, 0x5A, 0xB0, 0xBC, 0x40, 0x50, 0x30, 0xBA, 0x40, 0x28, 0x30, 0xBE, 0x80, 0x07, 0x70,
0x9B, 0x40, 0x3C, 0xB0, 0x2A, 0xE7, 0x8A, 0x51, 0xC9, 0x00, 0x07, 0xFA, 0x03, 0x9D, 0x61, 0xEE,
0x3E, 0xF0, 0x2A, 0xE7, 0x8A, 0x51, 0xB1, 0x00, 0x3F, 0x30, 0x2A, 0xE7, 0x8A, 0x51, 0xB3, 0x40,
0x31, 0x02, 0x03, 0x5C, 0x8A, 0xEE, 0x10, 0xF0, 0x31, 0x02, 0x03, 0x18, 0x8A, 0xEE, 0x33, 0x48,
0x03, 0x59, 0x8A, 0xEE, 0x31, 0x08, 0xDC, 0x40, 0x1B, 0xB0, 0x22, 0xA7, 0x8A, 0x51, 0x33, 0x48,
0xDC, 0x40, 0x1C, 0x70, 0x22, 0xA7, 0x8A, 0x51, 0x31, 0x08, 0x83, 0x96, 0xB4, 0x00, 0x83, 0x52,
0x33, 0x48, 0x99, 0x2E, 0x0E, 0x70, 0xDC, 0x40, 0x1B, 0xB0, 0x22, 0xA7, 0x8A, 0x51, 0x04, 0xF0,
0xDC, 0x40, 0x1C, 0x70, 0x22, 0xA7, 0x8A, 0x51, 0x0E, 0x70, 0x83, 0x96, 0xB4, 0x00, 0x04, 0xF0,
0x83, 0x52, 0xB2, 0x00, 0x08, 0xF0, 0x9B, 0x40, 0x3C, 0xB0, 0x2A, 0xE7, 0x8A, 0x51, 0xC9, 0x00,
0x08, 0x7A, 0x03, 0x9D, 0x9C, 0x2E, 0x3E, 0xF0, 0x2A, 0xE7, 0x8A, 0x51, 0xB4, 0x00, 0x3F, 0x30,
0x2A, 0xE7, 0x8A, 0x51, 0xB5, 0x40, 0x34, 0x08, 0x35, 0x42, 0x03, 0x18, 0xC4, 0xEE, 0x02, 0xF0,
0x35, 0x42, 0x03, 0x5C, 0xC4, 0xEE, 0x34, 0x08, 0xDC, 0x40, 0x1B, 0xB0, 0x22, 0xA7, 0x8A, 0x51,
0x35, 0x48, 0xDC, 0x40, 0x1C, 0x70, 0x22, 0xA7, 0x8A, 0x51, 0x34, 0x08, 0x83, 0x96, 0xB5, 0x40,
0x83, 0x52, 0x35, 0x48, 0x83, 0x96, 0xD2, 0x2E, 0x84, 0x30, 0xDC, 0x40, 0x1B, 0xB0, 0x22, 0xA7,
0x8A, 0x51, 0x34, 0x70, 0xDC, 0x40, 0x1C, 0x70, 0x22, 0xA7, 0x8A, 0x51, 0x84, 0x30, 0x83, 0x96,
0xB5, 0x40, 0x34, 0x70, 0xB6, 0x40, 0x09, 0x30, 0x83, 0x52, 0x9B, 0x40, 0x3C, 0xB0, 0x2A, 0xE7,
0x8A, 0x51, 0xC9, 0x00, 0x09, 0xBA, 0x03, 0x9D, 0xD6, 0x6E, 0x3D, 0xF0, 0x2A, 0xE7, 0x8A, 0x51,
0xC2, 0xC0, 0x3E, 0xF0, 0x2A, 0xE7, 0x8A, 0x51, 0xC1, 0xC0, 0x3F, 0x30, 0x2A, 0xE7, 0x8A, 0x51,
0xC0, 0x80, 0x07, 0x70, 0x42, 0xC2, 0x03, 0x18, 0x05, 0xEF, 0x08, 0xF0, 0x41, 0xC2, 0x03, 0x18,
0x05, 0xEF, 0x02, 0xF0, 0x40, 0x82, 0x03, 0x18, 0x05, 0xEF, 0x42, 0xC8, 0xDC, 0x40, 0x1A, 0x70,
0x22, 0xA7, 0x8A, 0x51, 0x41, 0xC8, 0xDC, 0x40, 0x1B, 0xB0, 0x22, 0xA7, 0x8A, 0x51, 0x40, 0x88,
0xDC, 0x40, 0x1C, 0x70, 0x22, 0xA7, 0x8A, 0x51, 0x18, 0xEF, 0x1A, 0x70, 0xDC, 0x81, 0xDC, 0xCA,
0x22, 0xA7, 0x8A, 0x51, 0x02, 0xF0, 0xDC, 0x40, 0x1B, 0xB0, 0x22, 0xA7, 0x8A, 0x51, 0x1C, 0x70,
0xDC, 0x81, 0x22, 0xA7, 0x8A, 0x51, 0x02, 0xF0, 0xC2, 0x01, 0xC2, 0x4A, 0xC1, 0xC0, 0xC0, 0xC1,
0x0A, 0x30, 0x9B, 0x40, 0x3C, 0xB0, 0x2A, 0xE7, 0x8A, 0x51, 0xC9, 0x00, 0x0A, 0xBA, 0x03, 0x59,
0x08, 0x40, 0x1A, 0x2F, 0xDD, 0x80, 0x5C, 0x48, 0x96, 0x00, 0x5D, 0x88, 0x95, 0x00, 0x17, 0x94,
0x17, 0x50, 0x08, 0x40, 0x83, 0x52, 0x03, 0x53, 0xDC, 0x40, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90,
0x0D, 0x08, 0x08, 0x40, 0xDF, 0xC0, 0x5D, 0xD8, 0x50, 0xEF, 0xE0, 0x01, 0x5F, 0xC8, 0x60, 0xC2,
0x03, 0x18, 0x45, 0x2F, 0x60, 0xC8, 0x5C, 0xC7, 0xDE, 0x80, 0x84, 0x80, 0x83, 0x93, 0x00, 0x48,
0x99, 0x00, 0x18, 0x14, 0x18, 0xD0, 0xE0, 0x4A, 0x36, 0x6F, 0x18, 0x55, 0x18, 0x11, 0x83, 0x52,
0x03, 0x53, 0x11, 0x5C, 0x08, 0x40, 0x05, 0x30, 0xDE, 0x80, 0xDE, 0x4B, 0x4D, 0x6F, 0x47, 0x6F,
0xE0, 0x01, 0x5F, 0xC8, 0x60, 0xC2, 0x03, 0x18, 0x60, 0xEF, 0x60, 0xC8, 0x5C, 0xC7, 0xDE, 0x80,
0x84, 0x80, 0x83, 0x93, 0x00, 0x48, 0x9A, 0x00, 0x98, 0x95, 0x98, 0x51, 0xE0, 0x4A, 0x51, 0x2F,
0x98, 0x96, 0x98, 0x52, 0x83, 0x52, 0x03, 0x53, 0x91, 0x9C, 0x08, 0x40, 0x05, 0x30, 0xDE, 0x80,
0xDE, 0x4B, 0x68, 0x2F, 0x62, 0x2F, 0xDF, 0xC0, 0x5D, 0xD8, 0x9A, 0x6F, 0xE0, 0x01, 0x5F, 0xC8,
0x60, 0xC2, 0x03, 0x18, 0x7E, 0xEF, 0x60, 0xC8, 0x5C, 0xC7, 0xDE, 0x80, 0x84, 0x80, 0x83, 0x93,
0x00, 0x48, 0x99, 0x00, 0x18, 0x14, 0x18, 0xD0, 0xE0, 0x4A, 0x6F, 0xEF, 0x18, 0x55, 0x18, 0x11,
0x83, 0x52, 0x03, 0x53, 0x11, 0x5C, 0x8A, 0x2F, 0x2A, 0x70, 0xDE, 0x80, 0xDE, 0x4B, 0x86, 0x2F,
0x00, 0x00, 0x80, 0xAF, 0xE0, 0x01, 0x5F, 0xC8, 0x60, 0xC2, 0x03, 0x18, 0x08, 0x40, 0x60, 0xC8,
0x5C, 0xC7, 0xDE, 0x80, 0x84, 0x80, 0x0F, 0x48, 0x83, 0x93, 0x80, 0x40, 0x98, 0x54, 0x98, 0x10,
0xE0, 0x4A, 0x8B, 0x6F, 0xE0, 0x01, 0x5F, 0xC8, 0x60, 0xC2, 0x03, 0x18, 0xAA, 0x6F, 0x60, 0xC8,
0x5C, 0xC7, 0xDE, 0x80, 0x84, 0x80, 0x83, 0x93, 0x00, 0x48, 0x9A, 0x00, 0x98, 0x95, 0x98, 0x51,
0xE0, 0x4A, 0x9B, 0xAF, 0x98, 0x96, 0x98, 0x52, 0x83, 0x52, 0x03, 0x53, 0x91, 0x9C, 0xB6, 0xAF,
0x2A, 0x70, 0xDE, 0x80, 0xDE, 0x4B, 0xB2, 0x6F, 0x00, 0x00, 0xAC, 0x6F, 0xE0, 0x01, 0x5F, 0xC8,
0x60, 0xC2, 0x03, 0x18, 0x08, 0x40, 0x60, 0xC8, 0x5C, 0xC7, 0xDE, 0x80, 0x84, 0x80, 0x10, 0x88,
0x83, 0x93, 0x80, 0x40, 0x18, 0x56, 0x18, 0x12, 0xE0, 0x4A, 0xB7, 0xEF, 0xDE, 0x80, 0xDD, 0xC1,
0x5C, 0x48, 0x5E, 0xD8, 0xDD, 0x47, 0x03, 0xD0, 0xDC, 0x0D, 0x5C, 0x48, 0xDE, 0x18, 0xDD, 0x47,
0x03, 0xD0, 0xDC, 0x0D, 0x5C, 0x48, 0x5E, 0x19, 0xDD, 0x47, 0x03, 0xD0, 0xDC, 0x0D, 0x5C, 0x48,
0xDE, 0x59, 0xDD, 0x47, 0x03, 0xD0, 0xDC, 0x0D, 0x5C, 0x48, 0x5E, 0x1A, 0xDD, 0x47, 0x03, 0xD0,
0xDC, 0x0D, 0x5C, 0x48, 0xDE, 0x5A, 0xDD, 0x47, 0x03, 0xD0, 0xDC, 0x0D, 0x5C, 0x48, 0x5E, 0x5B,
0xDD, 0x47, 0x03, 0xD0, 0xDC, 0x0D, 0x5C, 0x48, 0xDE, 0x9B, 0xDD, 0x47, 0x5D, 0x88, 0x08, 0x40,
0xEF, 0x01, 0x83, 0x93, 0x21, 0x30, 0x84, 0x80, 0x5C, 0xB0, 0x8A, 0x51, 0x11, 0xE0, 0x8A, 0x51,
0xA0, 0x30, 0x84, 0x80, 0xC2, 0x70, 0x8A, 0x51, 0x11, 0xE0, 0x83, 0x01, 0x8A, 0x95, 0x5F, 0xAB,
0x08, 0xF0, 0x8A, 0xC0, 0x04, 0x88, 0x84, 0x0A, 0x82, 0x47, 0x00, 0xF4, 0x01, 0x34, 0x48, 0x74,
0x02, 0x34, 0x3D, 0x34, 0x03, 0x74, 0x33, 0xF4, 0x00, 0xF4, 0xA4, 0xB4, 0x00, 0xF4, 0xF6, 0x74,
0x03, 0x74, 0x33, 0xF4, 0x00, 0xF4, 0x52, 0xB4, 0x00, 0xF4, 0x52, 0xB4, 0x00, 0xF4, 0xCD, 0x34,
0x00, 0xF4, 0x29, 0xB4, 0x00, 0xF4, 0x29, 0xB4, 0x00, 0xF4, 0x66, 0xF4, 0x00, 0xF4, 0x15, 0xB4,
0x00, 0xF4, 0x15, 0xB4, 0x00, 0xF4, 0x29, 0xB4, 0x7F, 0xB4, 0x34, 0xB4, 0x2F, 0x34, 0x2A, 0xB4,
0x26, 0xB4, 0x21, 0x74, 0x1A, 0xB4, 0x16, 0xB4, 0x0F, 0xF4, 0x09, 0x74, 0x04, 0x34, 0x02, 0x34,
0x01, 0x34, 0x00, 0xF4, 0x00, 0xF4, 0x00, 0xF4, 0x00, 0xF4, 0x03, 0x74, 0x03, 0x74, 0x00, 0xF4,
0x00, 0xF4, 0x03, 0x74, 0x03, 0x74, 0x07, 0xB4, 0x01, 0x34, 0x01, 0x34, 0x01, 0x34, 0x07, 0xB4,
0x07, 0xB4, 0x00, 0xF4, 0x00, 0xF4, 0x07, 0xB4, 0x07, 0xB4, 0x07, 0xB4, 0x00, 0xF4, 0x07, 0xB4,
0x07, 0xB4, 0x07, 0xB4, 0x07, 0xB4, 0x07, 0xB4, 0x07, 0xB4, 0x07, 0xB4, 0x07, 0xB4, 0x07, 0xB4,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xC8, 0x01, 0x02, 0xF0, 0x48, 0xC2,
0x03, 0x18, 0x3D, 0x6B, 0x48, 0xC8, 0xB0, 0x3E, 0x84, 0x80, 0x83, 0x93, 0x80, 0x81, 0x48, 0xC8,
0xAE, 0xBE, 0x84, 0x80, 0x80, 0x81, 0x48, 0xC8, 0xA8, 0x3E, 0x84, 0x80, 0x80, 0x81, 0x48, 0xC8,
0xB2, 0x7E, 0x84, 0x80, 0x80, 0x81, 0x48, 0xC8, 0x23, 0x3E, 0x84, 0x80, 0x80, 0x81, 0x48, 0xC8,
0xAC, 0x7E, 0x84, 0x80, 0x80, 0x81, 0xC8, 0x4A, 0x1E, 0x2B, 0x0D, 0x70, 0xAB, 0x81, 0xAB, 0xCA,
0xA9, 0x00, 0x03, 0x30, 0xAF, 0x80, 0x0C, 0x30, 0xAD, 0x40, 0x04, 0xF0, 0xB8, 0x00, 0x0F, 0xB0,
0xB6, 0x40, 0x28, 0x30, 0xBE, 0x80, 0x50, 0x30, 0xBA, 0x40, 0x5A, 0xB0, 0xBC, 0x40, 0x04, 0xF0,
0xB2, 0x00, 0x0E, 0x70, 0x83, 0x96, 0xB4, 0x00, 0x34, 0x70, 0xB6, 0x40, 0x84, 0x30, 0xB5, 0x40,
0x83, 0x52, 0x02, 0xF0, 0xC2, 0x01, 0xC2, 0x4A, 0xC1, 0xC0, 0xC0, 0xC1, 0x08, 0x40, 0x9B, 0x81,
0x01, 0xF0, 0xA0, 0x80, 0x95, 0x41, 0x96, 0x41, 0x97, 0x81, 0x98, 0x01, 0x99, 0x41, 0x9A, 0x41,
0x9B, 0x81, 0x9C, 0x41, 0x9E, 0x81, 0x55, 0xB0, 0x9E, 0x40, 0x9B, 0x81, 0x1C, 0x70, 0xDC, 0x81,
0xDC, 0xCA, 0x8A, 0x51, 0x22, 0xA7, 0x8A, 0x95, 0x8A, 0x95, 0x1D, 0xE3, 0x8A, 0x95, 0x3D, 0xF0,
0x8A, 0x51, 0x2A, 0xE7, 0x8A, 0x95, 0xC9, 0x00, 0xF7, 0xFA, 0x03, 0x9D, 0x82, 0xAB, 0x83, 0x96,
0xBF, 0x01, 0x85, 0xEB, 0x83, 0x96, 0xBF, 0x01, 0xBF, 0x4A, 0x3F, 0xC8, 0x03, 0x59, 0x8C, 0xEB,
0x8A, 0x51, 0xAE, 0xE4, 0x8A, 0x95, 0x8F, 0x6B, 0x01, 0xF0, 0x83, 0x52, 0x9B, 0x40, 0x8A, 0x51,
0x27, 0x60, 0x8A, 0x95, 0x00, 0xB0, 0x8A, 0x51, 0x2A, 0xE7, 0x8A, 0x95, 0xC9, 0x00, 0xFE, 0xF9,
0x01, 0x38, 0xC9, 0x00, 0xDC, 0x40, 0x00, 0xB0, 0x8A, 0x51, 0x22, 0xA7, 0x8A, 0x95, 0x00, 0xB0,
0x8A, 0x51, 0x2A, 0xE7, 0x8A, 0x95, 0xC9, 0x00, 0xEF, 0xF9, 0x10, 0x38, 0xC9, 0x00, 0x49, 0x08,
0xDC, 0x40, 0x00, 0xB0, 0x8A, 0x51, 0x22, 0xA7, 0x8A, 0x95, 0x1C, 0x54, 0x9C, 0x94, 0x9C, 0xD6,
0x35, 0xB0, 0xED, 0x80, 0xED, 0x4B, 0xB2, 0x2B, 0x83, 0x52, 0x03, 0x53, 0x9C, 0x92, 0x9C, 0x17,
0x35, 0xB0, 0xED, 0x80, 0xED, 0x4B, 0xBA, 0x6B, 0x83, 0x52, 0x03, 0x53, 0x9C, 0xD3, 0xC8, 0x01,
0x02, 0xF0, 0x48, 0xC2, 0x03, 0x18, 0x8D, 0xEC, 0x08, 0xF0, 0xE1, 0x00, 0xAC, 0xB0, 0xE2, 0x00,
0x03, 0x30, 0xE3, 0x40, 0x48, 0xC8, 0xE4, 0x00, 0x0D, 0x70, 0x8A, 0x51, 0x19, 0x20, 0x8A, 0x95,
0x48, 0xC8, 0x23, 0x3E, 0x84, 0x80, 0x83, 0x93, 0x00, 0xCB, 0xE0, 0xEB, 0x48, 0xC8, 0xA8, 0x3E,
0x84, 0x80, 0x00, 0x48, 0xED, 0x80, 0x48, 0xC8, 0x21, 0xFE, 0x84, 0x80, 0x6D, 0x88, 0xE4, 0x2B,
0x48, 0xC8, 0x21, 0xFE, 0x84, 0x80, 0x29, 0x08, 0x80, 0x40, 0x48, 0xC8, 0x21, 0xFE, 0x84, 0x80,
0x00, 0x48, 0x1F, 0xBE, 0x84, 0x80, 0x8A, 0x95, 0x00, 0x60, 0x8A, 0x95, 0xED, 0x80, 0x48, 0xC8,
0x27, 0x7E, 0x84, 0x80, 0x6D, 0x88, 0x80, 0x40, 0x08, 0xF0, 0xE1, 0x00, 0xB6, 0xF0, 0xE2, 0x00,
0x48, 0xC8, 0x21, 0xFE, 0x84, 0x80, 0x00, 0x48, 0x20, 0x38, 0xE3, 0x40, 0x48, 0xC8, 0xE4, 0x00,
0x0D, 0x70, 0x8A, 0x51, 0x19, 0x20, 0x8A, 0x95, 0x48, 0xC8, 0xAC, 0x7E, 0x84, 0x80, 0x83, 0x93,
0x00, 0xCB, 0x14, 0x6C, 0x48, 0xC8, 0xAE, 0xBE, 0x84, 0x80, 0x00, 0x48, 0xED, 0x80, 0x48, 0xC8,
0x5A, 0x7E, 0x84, 0x80, 0x6D, 0x88, 0x18, 0x6C, 0x48, 0xC8, 0x5A, 0x7E, 0x84, 0x80, 0x38, 0x08,
0x80, 0x40, 0x06, 0x30, 0xDC, 0x40, 0x48, 0xC8, 0x5A, 0x7E, 0x84, 0x80, 0x38, 0x08, 0x00, 0x42,
0x8A, 0x51, 0xC6, 0x27, 0x8A, 0x95, 0xED, 0x80, 0x48, 0xC8, 0xAA, 0x7E, 0x84, 0x80, 0x6D, 0x88,
0x83, 0x93, 0x80, 0x40, 0x48, 0xC8, 0xAA, 0x7E, 0x84, 0x80, 0x00, 0x48, 0xED, 0x80, 0x48, 0xC8,
0x27, 0x7E, 0x84, 0x80, 0x00, 0x48, 0x6D, 0x07, 0xEE, 0x80, 0x48, 0xC8, 0x25, 0x3E, 0x84, 0x80,
0x6E, 0x88, 0x80, 0x40, 0x48, 0xC8, 0x25, 0x3E, 0x84, 0x80, 0x7F, 0x70, 0x00, 0x42, 0x03, 0x5C,
0x46, 0xAC, 0x48, 0xC8, 0x25, 0x3E, 0x84, 0x80, 0x7F, 0x70, 0x80, 0x40, 0x48, 0x18, 0x4F, 0x2C,
0x48, 0xC8, 0x25, 0x3E, 0x84, 0x80, 0x00, 0x48, 0xDC, 0x40, 0x03, 0x30, 0x55, 0xEC, 0x48, 0xC8,
0x25, 0x3E, 0x84, 0x80, 0x00, 0x48, 0xDC, 0x40, 0x04, 0xF0, 0x8A, 0x51, 0x22, 0xA7, 0x8A, 0x95,
0x48, 0x18, 0x71, 0xEC, 0x48, 0xC8, 0x5A, 0x7E, 0x84, 0x80, 0x83, 0x93, 0x00, 0x48, 0xED, 0x80,
0x01, 0xF0, 0x03, 0xD0, 0xED, 0x4D, 0xFF, 0x7E, 0x03, 0xD0, 0x03, 0x9D, 0x62, 0xAC, 0x6D, 0x0D,
0x02, 0xBE, 0xC9, 0x00, 0xDC, 0x40, 0x01, 0xF0, 0x8A, 0x51, 0x22, 0xA7, 0x8A, 0x95, 0x01, 0xF0,
0x87, 0xEC, 0x48, 0xC8, 0x5A, 0x7E, 0x84, 0x80, 0x83, 0x93, 0x00, 0x48, 0xED, 0x80, 0x01, 0xF0,
0x03, 0xD0, 0xED, 0x4D, 0xFF, 0x7E, 0x03, 0xD0, 0x03, 0x9D, 0x79, 0x2C, 0x6D, 0x0D, 0x02, 0xBE,
0xC9, 0x00, 0xDC, 0x40, 0x02, 0xF0, 0x8A, 0x51, 0x22, 0xA7, 0x8A, 0x95, 0x02, 0xF0, 0xDC, 0x81,
0x8A, 0x51, 0x22, 0xA7, 0x8A, 0x95, 0xC8, 0x4A, 0xC0, 0xAB, 0xC4, 0x01, 0x08, 0xF0, 0x44, 0xC2,
0x03, 0x18, 0xB0, 0xAC, 0x44, 0xC8, 0xDC, 0x40, 0x05, 0x30, 0x8A, 0x51, 0x22, 0xA7, 0x8A, 0x95,
0x44, 0xC8, 0xA0, 0xFE, 0x84, 0x80, 0x32, 0x08, 0x83, 0x93, 0x80, 0x40, 0x44, 0xC8, 0xA0, 0xFE,
0x84, 0x80, 0x03, 0x14, 0x00, 0xCD, 0xC9, 0x00, 0xDC, 0x40, 0x06, 0x30, 0x8A, 0x51, 0x22, 0xA7,
0x8A, 0x95, 0x06, 0x30, 0xDC, 0x81, 0x8A, 0x51, 0x22, 0xA7, 0x8A, 0x95, 0xC4, 0x4A, 0x8E, 0xEC,
0x34, 0x70, 0x9E, 0x40, 0x83, 0x52, 0x12, 0x18, 0x1C, 0x9C, 0xBC, 0x2C, 0x1C, 0x10, 0x9B, 0xD4,
0x00, 0xB0, 0x8A, 0x51, 0x6A, 0xA1, 0x8A, 0x95, 0x83, 0x52, 0x03, 0x53, 0x12, 0x5C, 0x1C, 0x58,
0xC9, 0xEC, 0x00, 0xB0, 0x8A, 0x51, 0x69, 0x60, 0x8A, 0x95, 0x83, 0x52, 0x03, 0x53, 0x1C, 0x54,
0x9B, 0x90, 0x92, 0x58, 0x9C, 0xDC, 0xD2, 0xEC, 0x9C, 0x50, 0x9B, 0xD4, 0x01, 0xF0, 0x8A, 0x51,
0x6A, 0xA1, 0x8A, 0x95, 0x83, 0x52, 0x03, 0x53, 0x92, 0x9C, 0x9C, 0x98, 0xDF, 0xAC, 0x01, 0xF0,
0x8A, 0x51, 0x69, 0x60, 0x8A, 0x95, 0x83, 0x52, 0x03, 0x53, 0x9C, 0x94, 0x9B, 0x90, 0xC4, 0x01,
0x08, 0xF0, 0x44, 0xC2, 0x03, 0x18, 0x2E, 0x2D, 0x44, 0xC8, 0xDC, 0x40, 0x05, 0x30, 0x8A, 0x51,
0x22, 0xA7, 0x8A, 0x95, 0x24, 0x30, 0x8A, 0x51, 0x2A, 0xE7, 0x8A, 0x95, 0xC5, 0x00, 0x83, 0x96,
0x35, 0x42, 0x03, 0x18, 0x04, 0x6D, 0x83, 0x52, 0x44, 0xC8, 0xA0, 0xFE, 0x84, 0x80, 0x83, 0x96,
0x34, 0x08, 0x83, 0x93, 0x00, 0x42, 0x03, 0x18, 0x04, 0x6D, 0x83, 0x52, 0x44, 0xC8, 0xA0, 0xFE,
0x84, 0x80, 0x00, 0x48, 0x01, 0xBE, 0x16, 0xED, 0x36, 0x48, 0x83, 0x52, 0x45, 0x02, 0x03, 0x18,
0x2C, 0xED, 0x44, 0xC8, 0xA0, 0xFE, 0x84, 0x80, 0x83, 0x93, 0x00, 0x48, 0x32, 0x02, 0x03, 0x18,
0x2C, 0xED, 0x44, 0xC8, 0xA0, 0xFE, 0x84, 0x80, 0x00, 0x48, 0xFF, 0x7E, 0xED, 0x80, 0x44, 0xC8,
0xA0, 0xFE, 0x84, 0x80, 0x6D, 0x88, 0x80, 0x40, 0x44, 0xC8, 0xA0, 0xFE, 0x84, 0x80, 0x03, 0x14,
0x00, 0xCD, 0xC9, 0x00, 0xDC, 0x40, 0x06, 0x30, 0x8A, 0x51, 0x22, 0xA7, 0x8A, 0x95, 0x06, 0x30,
0xDC, 0x81, 0x8A, 0x51, 0x22, 0xA7, 0x8A, 0x95, 0xC4, 0x4A, 0xE0, 0xAC, 0x00, 0xB0, 0x14, 0x59,
0x80, 0xF0, 0x94, 0x99, 0x40, 0x38, 0x14, 0x18, 0x20, 0x38, 0x94, 0x58, 0x10, 0x38, 0x21, 0xC4,
0x9E, 0x40, 0x83, 0x96, 0xC0, 0xC1, 0x83, 0x52, 0x21, 0x30, 0xC8, 0x01, 0x8A, 0x51, 0x2A, 0xE7,
0x8A, 0x95, 0xC6, 0x00, 0x20, 0xF0, 0x8A, 0x51, 0x2A, 0xE7, 0x8A, 0x95, 0xC3, 0x00, 0x94, 0x9C,
0x9B, 0x6D, 0x48, 0xC8, 0x23, 0x3E, 0x84, 0x80, 0x83, 0x93, 0x80, 0x88, 0x03, 0x9D, 0x90, 0xAD,
0x48, 0xC8, 0x21, 0xFE, 0x84, 0x80, 0x00, 0x48, 0x2B, 0x42, 0x03, 0x18, 0x90, 0xAD, 0x48, 0xC8,
0x21, 0xFE, 0x84, 0x80, 0x00, 0x48, 0xFF, 0x7E, 0xED, 0x80, 0x48, 0xC8, 0x21, 0xFE, 0x84, 0x80,
0x6D, 0x88, 0x80, 0x40, 0x08, 0xF0, 0xE1, 0x00, 0xB6, 0xF0, 0xE2, 0x00, 0x48, 0xC8, 0x21, 0xFE,
0x84, 0x80, 0x00, 0x48, 0x20, 0x38, 0xE3, 0x40, 0x48, 0xC8, 0xE4, 0x00, 0x0D, 0x70, 0x8A, 0x51,
0x19, 0x20, 0x8A, 0x95, 0x01, 0xF0, 0xDC, 0x81, 0xDC, 0xCA, 0x8A, 0x51, 0x22, 0xA7, 0x8A, 0x95,
0x01, 0xF0, 0xDC, 0x81, 0x8A, 0x51, 0x22, 0xA7, 0x8A, 0x95, 0x48, 0xC8, 0x21, 0xFE, 0x84, 0x80,
0x83, 0x93, 0x00, 0x48, 0x1F, 0xBE, 0x84, 0x80, 0x8A, 0x95, 0x00, 0x60, 0x8A, 0x95, 0xED, 0x80,
0x48, 0xC8, 0x27, 0x7E, 0x84, 0x80, 0x6D, 0x88, 0x80, 0x40, 0x83, 0x96, 0xC0, 0xC1, 0xC0, 0x0A,
0x83, 0x52, 0x9C, 0x92, 0x1C, 0x96, 0x05, 0x30, 0xED, 0x80, 0xED, 0x4B, 0x95, 0x2D, 0x83, 0x52,
0x03, 0x53, 0x1C, 0x52, 0x71, 0x2E, 0x48, 0xC8, 0xAC, 0x7E, 0x84, 0x80, 0x83, 0x93, 0x80, 0x88,
0x03, 0x9D, 0x15, 0xEE, 0x48, 0xC8, 0x21, 0xFE, 0x84, 0x80, 0x00, 0x48, 0x29, 0x46, 0x03, 0x9D,
0xAB, 0x6D, 0x3A, 0x48, 0xAC, 0x2D, 0x3C, 0x48, 0xC7, 0x40, 0x46, 0x08, 0x47, 0x42, 0x03, 0x18,
0xD0, 0xED, 0x48, 0xC8, 0x5A, 0x7E, 0x84, 0x80, 0x36, 0x48, 0x00, 0x42, 0x03, 0x18, 0xD0, 0xED,
0x48, 0xC8, 0x5A, 0x7E, 0x84, 0x80, 0x00, 0x48, 0x01, 0xBE, 0xED, 0x80, 0x48, 0xC8, 0x5A, 0x7E,
0x84, 0x80, 0x6D, 0x88, 0x80, 0x40, 0x48, 0xC8, 0x5A, 0x7E, 0x84, 0x80, 0x00, 0x48, 0xED, 0x80,
0x01, 0xF0, 0x03, 0xD0, 0xED, 0x4D, 0xFF, 0x7E, 0x03, 0xD0, 0x03, 0x9D, 0xCA, 0x2D, 0xF2, 0x6D,
0x3E, 0x88, 0x46, 0x02, 0x48, 0xC8, 0x03, 0x18, 0x16, 0xEE, 0x5A, 0x7E, 0x84, 0x80, 0x00, 0x48,
0x38, 0x02, 0x48, 0xC8, 0x03, 0x18, 0x16, 0xEE, 0x5A, 0x7E, 0x84, 0x80, 0x00, 0x48, 0xFF, 0x7E,
0xED, 0x80, 0x48, 0xC8, 0x5A, 0x7E, 0x84, 0x80, 0x6D, 0x88, 0x80, 0x40, 0x48, 0xC8, 0x5A, 0x7E,
0x84, 0x80, 0x00, 0x48, 0xED, 0x80, 0x01, 0xF0, 0x03, 0xD0, 0xED, 0x4D, 0xFF, 0x7E, 0x03, 0xD0,
0x03, 0x9D, 0xED, 0xAD, 0x6D, 0x0D, 0x02, 0xBE, 0xC9, 0x00, 0xDC, 0x40, 0x01, 0xF0, 0x8A, 0x51,
0x22, 0xA7, 0x8A, 0x95, 0x01, 0xF0, 0xDC, 0x81, 0x8A, 0x51, 0x22, 0xA7, 0x8A, 0x95, 0x83, 0x96,
0x06, 0x30, 0xC0, 0xC1, 0xC0, 0x0A, 0x83, 0x52, 0xDC, 0x40, 0x48, 0xC8, 0x5A, 0x7E, 0x84, 0x80,
0x38, 0x08, 0x83, 0x93, 0x00, 0x42, 0x8A, 0x51, 0xC6, 0x27, 0x8A, 0x95, 0xED, 0x80, 0x48, 0xC8,
0xAA, 0x7E, 0x84, 0x80, 0x6D, 0x88, 0x83, 0x93, 0x80, 0x40, 0x48, 0xC8, 0x23, 0x3E, 0x84, 0x80,
0x80, 0x88, 0x03, 0x9D, 0x71, 0x2E, 0x43, 0x08, 0x2D, 0x42, 0x03, 0x18, 0x2C, 0xEE, 0x48, 0xC8,
0x21, 0xFE, 0x84, 0x80, 0x00, 0x48, 0x2B, 0x42, 0x03, 0x18, 0x2C, 0xEE, 0x48, 0xC8, 0x21, 0xFE,
0x84, 0x80, 0x00, 0x48, 0xFF, 0x7E, 0x3C, 0x2E, 0x2F, 0x88, 0x43, 0x02, 0x03, 0x18, 0x71, 0x2E,
0x48, 0xC8, 0x21, 0xFE, 0x84, 0x80, 0x29, 0x08, 0x00, 0x42, 0x03, 0x18, 0x71, 0x2E, 0x48, 0xC8,
0x21, 0xFE, 0x84, 0x80, 0x00, 0x48, 0x01, 0xBE, 0xED, 0x80, 0x48, 0xC8, 0x21, 0xFE, 0x84, 0x80,
0x6D, 0x88, 0x80, 0x40, 0x08, 0xF0, 0xE1, 0x00, 0xB6, 0xF0, 0xE2, 0x00, 0x48, 0xC8, 0x21, 0xFE,
0x84, 0x80, 0x00, 0x48, 0x20, 0x38, 0xE3, 0x40, 0x48, 0xC8, 0xE4, 0x00, 0x0D, 0x70, 0x8A, 0x51,
0x19, 0x20, 0x8A, 0x95, 0x01, 0xF0, 0xDC, 0x81, 0xDC, 0xCA, 0x8A, 0x51, 0x22, 0xA7, 0x8A, 0x95,
0x01, 0xF0, 0xDC, 0x81, 0x8A, 0x51, 0x22, 0xA7, 0x8A, 0x95, 0x83, 0x96, 0xC0, 0xC1, 0xC0, 0x0A,
0x83, 0x52, 0x48, 0xC8, 0x21, 0xFE, 0x84, 0x80, 0x83, 0x93, 0x00, 0x48, 0x1F, 0xBE, 0x84, 0x80,
0x8A, 0x95, 0x00, 0x60, 0x8A, 0x95, 0xED, 0x80, 0x48, 0xC8, 0x27, 0x7E, 0x84, 0x80, 0x6D, 0x88,
0x80, 0x40, 0x83, 0x96, 0x40, 0x0B, 0x9B, 0x6E, 0x83, 0x52, 0x48, 0xC8, 0xAA, 0x7E, 0x84, 0x80,
0x83, 0x93, 0x00, 0x48, 0xED, 0x80, 0x48, 0xC8, 0x27, 0x7E, 0x84, 0x80, 0x00, 0x48, 0x6D, 0x07,
0xEE, 0x80, 0x48, 0xC8, 0x25, 0x3E, 0x84, 0x80, 0x6E, 0x88, 0x80, 0x40, 0x48, 0xC8, 0x25, 0x3E,
0x84, 0x80, 0x7F, 0x70, 0x00, 0x42, 0x48, 0xC8, 0x03, 0x5C, 0x93, 0x2E, 0x25, 0x3E, 0x84, 0x80,
0x7F, 0x70, 0x80, 0x40, 0x48, 0xC8, 0x25, 0x3E, 0x84, 0x80, 0x00, 0x48, 0xDC, 0x40, 0x03, 0x30,
0x8A, 0x51, 0x22, 0xA7, 0x8A, 0x95, 0x83, 0x52, 0x23, 0x70, 0xC8, 0x01, 0xC8, 0x4A, 0x8A, 0x51,
0x2A, 0xE7, 0x8A, 0x95, 0xC6, 0x00, 0x22, 0x30, 0x8A, 0x51, 0x2A, 0xE7, 0x8A, 0x95, 0xC3, 0x00,
0x83, 0x96, 0xC1, 0x01, 0x83, 0x52, 0x94, 0xDD, 0xFF, 0x2E, 0x48, 0xC8, 0x23, 0x3E, 0x84, 0x80,
0x83, 0x93, 0x80, 0x88, 0x03, 0x9D, 0xF4, 0x6E, 0x48, 0xC8, 0x21, 0xFE, 0x84, 0x80, 0x00, 0x48,
0x2B, 0x42, 0x03, 0x18, 0xF4, 0x6E, 0x48, 0xC8, 0x21, 0xFE, 0x84, 0x80, 0x00, 0x48, 0xFF, 0x7E,
0xED, 0x80, 0x48, 0xC8, 0x21, 0xFE, 0x84, 0x80, 0x6D, 0x88, 0x80, 0x40, 0x08, 0xF0, 0xE1, 0x00,
0xB6, 0xF0, 0xE2, 0x00, 0x48, 0xC8, 0x21, 0xFE, 0x84, 0x80, 0x00, 0x48, 0x20, 0x38, 0xE3, 0x40,
0x48, 0xC8, 0xE4, 0x00, 0x0D, 0x70, 0x8A, 0x51, 0x19, 0x20, 0x8A, 0x95, 0x02, 0xF0, 0xDC, 0x81,
0xDC, 0xCA, 0x8A, 0x51, 0x22, 0xA7, 0x8A, 0x95, 0x02, 0xF0, 0xDC, 0x81, 0x8A, 0x51, 0x22, 0xA7,
0x8A, 0x95, 0x48, 0xC8, 0x21, 0xFE, 0x84, 0x80, 0x83, 0x93, 0x00, 0x48, 0x1F, 0xBE, 0x84, 0x80,
0x8A, 0x95, 0x00, 0x60, 0x8A, 0x95, 0xED, 0x80, 0x48, 0xC8, 0x27, 0x7E, 0x84, 0x80, 0x6D, 0x88,
0x80, 0x40, 0x83, 0x96, 0xC1, 0x01, 0xC1, 0x4A, 0x83, 0x52, 0x9C, 0xD3, 0x1C, 0xD7, 0x05, 0x30,
0xED, 0x80, 0xED, 0x4B, 0xF9, 0xAE, 0x83, 0x52, 0x03, 0x53, 0x1C, 0x93, 0xD5, 0xAF, 0x48, 0xC8,
0xAC, 0x7E, 0x84, 0x80, 0x83, 0x93, 0x80, 0x88, 0x03, 0x9D, 0x79, 0xAF, 0x48, 0xC8, 0x21, 0xFE,
0x84, 0x80, 0x00, 0x48, 0x29, 0x46, 0x03, 0x9D, 0x0F, 0x6F, 0x3A, 0x48, 0x10, 0xAF, 0x3C, 0x48,
0xC7, 0x40, 0x46, 0x08, 0x47, 0x42, 0x03, 0x18, 0x34, 0x2F, 0x48, 0xC8, 0x5A, 0x7E, 0x84, 0x80,
0x36, 0x48, 0x00, 0x42, 0x03, 0x18, 0x34, 0x2F, 0x48, 0xC8, 0x5A, 0x7E, 0x84, 0x80, 0x00, 0x48,
0x01, 0xBE, 0xED, 0x80, 0x48, 0xC8, 0x5A, 0x7E, 0x84, 0x80, 0x6D, 0x88, 0x80, 0x40, 0x48, 0xC8,
0x5A, 0x7E, 0x84, 0x80, 0x00, 0x48, 0xED, 0x80, 0x01, 0xF0, 0x03, 0xD0, 0xED, 0x4D, 0xFF, 0x7E,
0x03, 0xD0, 0x03, 0x9D, 0x2E, 0x6F, 0x56, 0x6F, 0x3E, 0x88, 0x46, 0x02, 0x48, 0xC8, 0x03, 0x18,
0x7A, 0xAF, 0x5A, 0x7E, 0x84, 0x80, 0x00, 0x48, 0x38, 0x02, 0x48, 0xC8, 0x03, 0x18, 0x7A, 0xAF,
0x5A, 0x7E, 0x84, 0x80, 0x00, 0x48, 0xFF, 0x7E, 0xED, 0x80, 0x48, 0xC8, 0x5A, 0x7E, 0x84, 0x80,
0x6D, 0x88, 0x80, 0x40, 0x48, 0xC8, 0x5A, 0x7E, 0x84, 0x80, 0x00, 0x48, 0xED, 0x80, 0x01, 0xF0,
0x03, 0xD0, 0xED, 0x4D, 0xFF, 0x7E, 0x03, 0xD0, 0x03, 0x9D, 0x51, 0x2F, 0x6D, 0x0D, 0x02, 0xBE,
0xC9, 0x00, 0xDC, 0x40, 0x02, 0xF0, 0x8A, 0x51, 0x22, 0xA7, 0x8A, 0x95, 0x02, 0xF0, 0xDC, 0x81,
0x8A, 0x51, 0x22, 0xA7, 0x8A, 0x95, 0x83, 0x96, 0x06, 0x30, 0xC1, 0x01, 0xC1, 0x4A, 0x83, 0x52,
0xDC, 0x40, 0x48, 0xC8, 0x5A, 0x7E, 0x84, 0x80, 0x38, 0x08, 0x83, 0x93, 0x00, 0x42, 0x8A, 0x51,
0xC6, 0x27, 0x8A, 0x95, 0xED, 0x80, 0x48, 0xC8, 0xAA, 0x7E, 0x84, 0x80, 0x6D, 0x88, 0x83, 0x93,
0x80, 0x40, 0x48, 0xC8, 0x23, 0x3E, 0x84, 0x80, 0x80, 0x88, 0x03, 0x9D, 0xD5, 0xAF, 0x43, 0x08,
0x2D, 0x42, 0x03, 0x18, 0x90, 0xEF, 0x48, 0xC8, 0x21, 0xFE, 0x84, 0x80, 0x00, 0x48, 0x2B, 0x42,
0x03, 0x18, 0x90, 0xEF, 0x48, 0xC8, 0x21, 0xFE, 0x84, 0x80, 0x00, 0x48, 0xFF, 0x7E, 0xA0, 0xEF,
0x2F, 0x88, 0x43, 0x02, 0x03, 0x18, 0xD5, 0xAF, 0x48, 0xC8, 0x21, 0xFE, 0x84, 0x80, 0x29, 0x08,
0x00, 0x42, 0x03, 0x18, 0xD5, 0xAF, 0x48, 0xC8, 0x21, 0xFE, 0x84, 0x80, 0x00, 0x48, 0x01, 0xBE,
0xED, 0x80, 0x48, 0xC8, 0x21, 0xFE, 0x84, 0x80, 0x6D, 0x88, 0x80, 0x40, 0x08, 0xF0, 0xE1, 0x00,
0xB6, 0xF0, 0xE2, 0x00, 0x48, 0xC8, 0x21, 0xFE, 0x84, 0x80, 0x00, 0x48, 0x20, 0x38, 0xE3, 0x40,
0x48, 0xC8, 0xE4, 0x00, 0x0D, 0x70, 0x8A, 0x51, 0x19, 0x20, 0x8A, 0x95, 0x02, 0xF0, 0xDC, 0x81,
0xDC, 0xCA, 0x8A, 0x51, 0x22, 0xA7, 0x8A, 0x95, 0x02, 0xF0, 0xDC, 0x81, 0x8A, 0x51, 0x22, 0xA7,
0x8A, 0x95, 0x83, 0x96, 0xC1, 0x01, 0xC1, 0x4A, 0x83, 0x52, 0x48, 0xC8, 0x21, 0xFE, 0x84, 0x80,
0x83, 0x93, 0x00, 0x48, 0x1F, 0xBE, 0x84, 0x80, 0x8A, 0x95, 0x00, 0x60, 0x8A, 0x95, 0xED, 0x80,
0x48, 0xC8, 0x27, 0x7E, 0x84, 0x80, 0x6D, 0x88, 0x80, 0x40, 0x83, 0x96, 0x41, 0x4B, 0xB2, 0xEC,
0x83, 0x52, 0x48, 0xC8, 0xAA, 0x7E, 0x84, 0x80, 0x83, 0x93, 0x00, 0x48, 0xED, 0x80, 0x48, 0xC8,
0x27, 0x7E, 0x84, 0x80, 0x00, 0x48, 0x6D, 0x07, 0xEE, 0x80, 0x48, 0xC8, 0x25, 0x3E, 0x84, 0x80,
0x6E, 0x88, 0x80, 0x40, 0x48, 0xC8, 0x25, 0x3E, 0x84, 0x80, 0x7F, 0x70, 0x00, 0x42, 0x48, 0xC8,
0x03, 0x5C, 0xF7, 0x2F, 0x25, 0x3E, 0x84, 0x80, 0x7F, 0x70, 0x80, 0x40, 0x48, 0xC8, 0x25, 0x3E,
0x84, 0x80, 0x00, 0x48, 0xDC, 0x40, 0x04, 0xF0, 0x8A, 0x51, 0x22, 0xA7, 0x8A, 0x95, 0xB2, 0xEC
};

View File

@ -0,0 +1,515 @@
static uint8_t agc_firmware_sx125x[8192] = {
0x8A, 0x51, 0xCF, 0xEF, 0x00, 0xB0, 0x8A, 0xC0, 0x04, 0x88, 0x84, 0x0A, 0x82, 0x47, 0x00, 0xF4,
0x0F, 0xF4, 0x0C, 0x74, 0x09, 0x74, 0x09, 0x74, 0x09, 0x74, 0x0C, 0x74, 0x0F, 0xF4, 0x0C, 0x74,
0x0F, 0xF4, 0x0C, 0x74, 0x00, 0xF4, 0x06, 0x74, 0x0C, 0x74, 0x12, 0x74, 0x18, 0x74, 0x1E, 0xF4,
0x24, 0x74, 0x2A, 0xB4, 0x30, 0x74, 0x36, 0xF4, 0x01, 0x34, 0x01, 0x34, 0x01, 0x34, 0x02, 0x34,
0x03, 0x74, 0x04, 0x34, 0x05, 0x74, 0x05, 0x74, 0x06, 0x74, 0x06, 0x74, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0x64, 0xC0, 0x80, 0x81, 0x84, 0x0A,
0x04, 0xC6, 0x03, 0x59, 0x00, 0xF4, 0x04, 0xC6, 0xB6, 0x29, 0xDF, 0xC0, 0xDE, 0xC1, 0x5D, 0x88,
0x5F, 0x18, 0xDE, 0x47, 0x03, 0xD0, 0xDD, 0x4D, 0x5D, 0x88, 0xDF, 0x58, 0xDE, 0x47, 0x03, 0xD0,
0xDD, 0x4D, 0x5D, 0x88, 0x5F, 0x59, 0xDE, 0x47, 0x03, 0xD0, 0xDD, 0x4D, 0x5D, 0x88, 0xDF, 0x99,
0xDE, 0x47, 0x03, 0xD0, 0xDD, 0x4D, 0x5D, 0x88, 0x5F, 0x5A, 0xDE, 0x47, 0x03, 0xD0, 0xDD, 0x4D,
0x5D, 0x88, 0xDF, 0x9A, 0xDE, 0x47, 0x03, 0xD0, 0xDD, 0x4D, 0x5D, 0x88, 0x5F, 0x9B, 0xDE, 0x47,
0x03, 0xD0, 0xDD, 0x4D, 0x5D, 0x88, 0xDF, 0xDB, 0xDE, 0x47, 0x5E, 0x88, 0x08, 0x40, 0xD7, 0xC1,
0x02, 0xF0, 0x57, 0x82, 0x03, 0x18, 0x07, 0xAA, 0x57, 0x88, 0x2D, 0x7E, 0x84, 0x80, 0x83, 0x93,
0x80, 0x81, 0x57, 0x88, 0x2B, 0x7E, 0x84, 0x80, 0x80, 0x81, 0x57, 0x88, 0x25, 0x3E, 0x84, 0x80,
0x80, 0x81, 0x57, 0x88, 0x33, 0x7E, 0x84, 0x80, 0x80, 0x81, 0x57, 0x88, 0xA8, 0x3E, 0x84, 0x80,
0x80, 0x81, 0x57, 0x88, 0x29, 0x3E, 0x84, 0x80, 0x80, 0x81, 0xD7, 0x0A, 0xE8, 0xE9, 0x09, 0x30,
0xB7, 0xC1, 0xB5, 0x40, 0x10, 0xF0, 0xBB, 0x80, 0x23, 0x70, 0xB9, 0x40, 0x07, 0x70, 0xC7, 0x40,
0x0B, 0x70, 0xC5, 0x00, 0x2D, 0xB0, 0xCD, 0x40, 0x64, 0x70, 0xC9, 0x00, 0x73, 0xF0, 0xCB, 0x40,
0x04, 0xF0, 0xBF, 0xC0, 0x0E, 0x70, 0xBD, 0x80, 0x34, 0x70, 0xC3, 0x00, 0x84, 0x30, 0xC1, 0xC0,
0x08, 0x40, 0x01, 0xF0, 0xA0, 0x80, 0x95, 0x41, 0x96, 0x41, 0x97, 0x81, 0x98, 0x01, 0x99, 0x41,
0x9A, 0x41, 0x9B, 0x81, 0x9C, 0x41, 0x9E, 0x81, 0x00, 0xB0, 0xDD, 0x80, 0x5D, 0x88, 0x95, 0x00,
0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08, 0xEF, 0xC0, 0x6F, 0xC8, 0xFE, 0xF9, 0x01, 0x38, 0xEF, 0xC0,
0x6F, 0xC8, 0xDD, 0x80, 0x00, 0xB0, 0xDE, 0x80, 0x5D, 0x88, 0x96, 0x00, 0x5E, 0x88, 0x95, 0x00,
0x17, 0x94, 0x17, 0x50, 0x00, 0xB0, 0xDD, 0x80, 0x5D, 0x88, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90,
0x0D, 0x08, 0xEF, 0xC0, 0x6F, 0xC8, 0xEF, 0xF9, 0x10, 0x38, 0xEF, 0xC0, 0x6F, 0xC8, 0xDD, 0x80,
0x00, 0xB0, 0xDE, 0x80, 0x5D, 0x88, 0x96, 0x00, 0x5E, 0x88, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50,
0x1C, 0x54, 0x9C, 0x94, 0x83, 0x96, 0xAC, 0x41, 0xAD, 0x81, 0x83, 0x52, 0xB3, 0x81, 0xB4, 0x41,
0x9B, 0x81, 0x1C, 0x70, 0xDD, 0xC1, 0xDD, 0x0A, 0xDE, 0x80, 0x5D, 0x88, 0x96, 0x00, 0x5E, 0x88,
0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x8A, 0x51, 0xE7, 0x21, 0x8A, 0x51, 0x3D, 0xF0, 0xDD, 0x80,
0x5D, 0x88, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08, 0xEF, 0xC0, 0x6F, 0xC8, 0xF7, 0xFA,
0x03, 0x9D, 0x7C, 0x2A, 0xCF, 0xC1, 0x7E, 0x6A, 0xCF, 0xC1, 0xCF, 0x0A, 0x4F, 0x88, 0x03, 0x59,
0x85, 0xAA, 0x8A, 0x95, 0xB1, 0xA4, 0x8A, 0x51, 0x87, 0xEA, 0x01, 0xF0, 0x9B, 0x40, 0xD7, 0xC1,
0x02, 0xF0, 0x57, 0x82, 0x03, 0x18, 0x94, 0xEB, 0x57, 0x88, 0xA8, 0x3E, 0x84, 0x80, 0x83, 0x93,
0x00, 0xCB, 0x9C, 0xEA, 0x57, 0x88, 0x25, 0x3E, 0x84, 0x80, 0x00, 0x48, 0xEB, 0x80, 0x57, 0x88,
0x23, 0x3E, 0x84, 0x80, 0x6B, 0x88, 0xA0, 0x6A, 0x57, 0x88, 0x23, 0x3E, 0x84, 0x80, 0x37, 0x88,
0x80, 0x40, 0x57, 0x88, 0x23, 0x3E, 0x84, 0x80, 0x00, 0x48, 0x01, 0xBE, 0x84, 0x80, 0x8A, 0x51,
0x02, 0xA0, 0x8A, 0x51, 0x0F, 0x39, 0xEB, 0x80, 0x03, 0xD0, 0xEB, 0x4D, 0x57, 0x88, 0x23, 0x3E,
0x84, 0x80, 0x00, 0x48, 0x15, 0x3E, 0x84, 0x80, 0x8A, 0x51, 0x02, 0xA0, 0x8A, 0x51, 0x07, 0xF9,
0xEC, 0x40, 0x04, 0xF0, 0x03, 0xD0, 0xEC, 0x0D, 0xFF, 0x7E, 0x03, 0xD0, 0x03, 0x9D, 0xBB, 0x6A,
0x6C, 0xCD, 0x6B, 0x84, 0x01, 0x38, 0xED, 0x80, 0x57, 0x88, 0x21, 0xFE, 0x84, 0x80, 0x6D, 0x88,
0x80, 0x40, 0x8C, 0x70, 0x83, 0x96, 0xB5, 0x40, 0x83, 0x52, 0x57, 0x88, 0x21, 0xFE, 0x84, 0x80,
0x00, 0x48, 0x83, 0x96, 0xB6, 0x40, 0xB5, 0xF0, 0x83, 0x52, 0xDD, 0x80, 0x57, 0x88, 0xDE, 0x80,
0x02, 0xF0, 0x8A, 0x95, 0x15, 0x64, 0x8A, 0x51, 0x57, 0x88, 0x23, 0x3E, 0x84, 0x80, 0x83, 0x93,
0x00, 0x48, 0x0B, 0x3E, 0x84, 0x80, 0x8A, 0x51, 0x02, 0xA0, 0x8A, 0x51, 0xEB, 0x80, 0x57, 0x88,
0x31, 0x3E, 0x84, 0x80, 0x6B, 0x88, 0x80, 0x40, 0x57, 0x88, 0x29, 0x3E, 0x84, 0x80, 0x00, 0xCB,
0xFB, 0xAA, 0x57, 0x88, 0x2B, 0x7E, 0x84, 0x80, 0x00, 0x48, 0xEB, 0x80, 0x57, 0x88, 0x59, 0x7E,
0x84, 0x80, 0x6B, 0x88, 0xFF, 0xEA, 0x57, 0x88, 0x59, 0x7E, 0x84, 0x80, 0x47, 0x48, 0x80, 0x40,
0x06, 0x30, 0xDD, 0x80, 0x57, 0x88, 0x59, 0x7E, 0x84, 0x80, 0x47, 0x48, 0x00, 0x42, 0x8A, 0x51,
0xBD, 0x21, 0x8A, 0x51, 0xEB, 0x80, 0x57, 0x88, 0x27, 0x7E, 0x84, 0x80, 0x6B, 0x88, 0x80, 0x40,
0x57, 0x88, 0x27, 0x7E, 0x84, 0x80, 0x00, 0x48, 0xEB, 0x80, 0x57, 0x88, 0x31, 0x3E, 0x84, 0x80,
0x00, 0x48, 0x6B, 0x07, 0xEC, 0x40, 0x57, 0x88, 0x2F, 0xBE, 0x84, 0x80, 0x6C, 0x48, 0x80, 0x40,
0x57, 0x88, 0x2F, 0xBE, 0x84, 0x80, 0x7F, 0x70, 0x00, 0x42, 0x03, 0x5C, 0x2C, 0xEB, 0x57, 0x88,
0x2F, 0xBE, 0x84, 0x80, 0x7F, 0x70, 0x80, 0x40, 0x57, 0xD8, 0x62, 0xEB, 0x57, 0x88, 0x2F, 0xBE,
0x84, 0x80, 0x00, 0x48, 0xDD, 0x80, 0x03, 0x30, 0xDE, 0x80, 0x5D, 0x88, 0x96, 0x00, 0x5E, 0x88,
0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x57, 0xD8, 0x70, 0xEB, 0x57, 0x88, 0x59, 0x7E, 0x84, 0x80,
0x83, 0x93, 0x00, 0x48, 0xEB, 0x80, 0x01, 0xF0, 0x03, 0xD0, 0xEB, 0x4D, 0xFF, 0x7E, 0x03, 0xD0,
0x03, 0x9D, 0x45, 0xEB, 0x6B, 0x0D, 0x02, 0xBE, 0xEF, 0xC0, 0x6F, 0xC8, 0xDD, 0x80, 0x01, 0xF0,
0xDE, 0x80, 0x5D, 0x88, 0x96, 0x00, 0x5E, 0x88, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x01, 0xF0,
0xDD, 0xC1, 0xDE, 0x80, 0x5D, 0x88, 0x96, 0x00, 0x5E, 0x88, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50,
0xD7, 0x0A, 0x88, 0x6A, 0x57, 0x88, 0x2F, 0xBE, 0x84, 0x80, 0x00, 0x48, 0xDD, 0x80, 0x04, 0xF0,
0xDE, 0x80, 0x5D, 0x88, 0x96, 0x00, 0x5E, 0x88, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x3B, 0x6B,
0x57, 0x88, 0x59, 0x7E, 0x84, 0x80, 0x83, 0x93, 0x00, 0x48, 0xEB, 0x80, 0x01, 0xF0, 0x03, 0xD0,
0xEB, 0x4D, 0xFF, 0x7E, 0x03, 0xD0, 0x03, 0x9D, 0x78, 0x2B, 0x6B, 0x0D, 0x02, 0xBE, 0xEF, 0xC0,
0x6F, 0xC8, 0xDD, 0x80, 0x02, 0xF0, 0xDE, 0x80, 0x5D, 0x88, 0x96, 0x00, 0x5E, 0x88, 0x95, 0x00,
0x17, 0x94, 0x17, 0x50, 0x02, 0xF0, 0xDD, 0xC1, 0xDE, 0x80, 0x5D, 0x88, 0x96, 0x00, 0x5E, 0x88,
0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x60, 0xAB, 0xD1, 0x41, 0x51, 0x08, 0xDD, 0x80, 0x05, 0x30,
0xDE, 0x80, 0x5D, 0x88, 0x96, 0x00, 0x5E, 0x88, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x51, 0x08,
0xA0, 0xFE, 0x84, 0x80, 0x3F, 0xC8, 0x83, 0x93, 0x80, 0x40, 0x51, 0x08, 0xA0, 0xFE, 0x84, 0x80,
0x03, 0x14, 0x00, 0xCD, 0xEF, 0xC0, 0x6F, 0xC8, 0xDD, 0x80, 0x06, 0x30, 0xDE, 0x80, 0x5D, 0x88,
0x96, 0x00, 0x5E, 0x88, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x06, 0x30, 0xDD, 0xC1, 0xDE, 0x80,
0x5D, 0x88, 0x96, 0x00, 0x5E, 0x88, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x08, 0xF0, 0xD1, 0x8A,
0x51, 0x02, 0x03, 0x5C, 0x95, 0x2B, 0x12, 0x18, 0x1C, 0x9C, 0xCC, 0x2B, 0x1C, 0x10, 0x9B, 0xD4,
0x00, 0xB0, 0x8A, 0x95, 0x0F, 0xA2, 0x8A, 0x51, 0x12, 0x5C, 0x1C, 0x58, 0xD5, 0x6B, 0x00, 0xB0,
0x8A, 0x95, 0x75, 0xE1, 0x8A, 0x51, 0x1C, 0x54, 0x9B, 0x90, 0x92, 0x58, 0x9C, 0xDC, 0xDE, 0xAB,
0x9C, 0x50, 0x9B, 0xD4, 0x01, 0xF0, 0x8A, 0x95, 0x0F, 0xA2, 0x8A, 0x51, 0x92, 0x9C, 0x9C, 0x98,
0xE7, 0xAB, 0x01, 0xF0, 0x8A, 0x95, 0x75, 0xE1, 0x8A, 0x51, 0x9C, 0x94, 0x9B, 0x90, 0xD1, 0x41,
0x51, 0x08, 0xDD, 0x80, 0x05, 0x30, 0xDE, 0x80, 0x5D, 0x88, 0x96, 0x00, 0x5E, 0x88, 0x95, 0x00,
0x17, 0x94, 0x17, 0x50, 0x24, 0x30, 0xDD, 0x80, 0x5D, 0x88, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90,
0x0D, 0x08, 0xD2, 0x00, 0x52, 0x08, 0x41, 0xC2, 0x03, 0x18, 0x4D, 0xEC, 0x51, 0x08, 0xA0, 0xFE,
0x84, 0x80, 0x3D, 0x88, 0x83, 0x93, 0x00, 0x42, 0x03, 0x18, 0x4D, 0xEC, 0x51, 0x08, 0xA0, 0xFE,
0x84, 0x80, 0x00, 0x48, 0x01, 0xBE, 0xEB, 0x80, 0x51, 0x08, 0xA0, 0xFE, 0x84, 0x80, 0x6B, 0x88,
0x80, 0x40, 0x51, 0x08, 0xA0, 0xFE, 0x84, 0x80, 0x03, 0x14, 0x00, 0xCD, 0xEF, 0xC0, 0x6F, 0xC8,
0xDD, 0x80, 0x06, 0x30, 0xDE, 0x80, 0x5D, 0x88, 0x96, 0x00, 0x5E, 0x88, 0x95, 0x00, 0x17, 0x94,
0x17, 0x50, 0x06, 0x30, 0xDD, 0xC1, 0xDE, 0x80, 0x5D, 0x88, 0x96, 0x00, 0x5E, 0x88, 0x95, 0x00,
0x17, 0x94, 0x17, 0x50, 0x08, 0xF0, 0xD1, 0x8A, 0x51, 0x02, 0x03, 0x5C, 0xE8, 0x2B, 0x59, 0xCE,
0xF0, 0x39, 0x23, 0x04, 0x9E, 0x40, 0x21, 0x30, 0xD7, 0xC1, 0xDD, 0x80, 0x5D, 0x88, 0x95, 0x00,
0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08, 0xD3, 0x40, 0x20, 0xF0, 0xDD, 0x80, 0x5D, 0x88, 0x95, 0x00,
0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08, 0xD0, 0xC0, 0xD5, 0x81, 0x57, 0x88, 0x29, 0x3E, 0x84, 0x80,
0x83, 0x93, 0x80, 0x88, 0x03, 0x59, 0x7E, 0x6C, 0x0D, 0xED, 0x43, 0x08, 0x52, 0x02, 0x03, 0x18,
0x2A, 0xAC, 0x51, 0x08, 0xA0, 0xFE, 0x84, 0x80, 0x83, 0x93, 0x00, 0x48, 0x3F, 0xC2, 0x03, 0x18,
0x2A, 0xAC, 0x51, 0x08, 0xA0, 0xFE, 0x84, 0x80, 0x00, 0x48, 0xFF, 0x7E, 0xEB, 0x80, 0x51, 0x08,
0xA0, 0xFE, 0x84, 0x80, 0x6B, 0x88, 0x80, 0x40, 0x51, 0x08, 0xA0, 0xFE, 0x84, 0x80, 0x03, 0x14,
0x00, 0xCD, 0xEF, 0xC0, 0x6F, 0xC8, 0xDD, 0x80, 0x06, 0x30, 0xDE, 0x80, 0x5D, 0x88, 0x96, 0x00,
0x5E, 0x88, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x06, 0x30, 0xDD, 0xC1, 0xDE, 0x80, 0x5D, 0x88,
0x96, 0x00, 0x5E, 0x88, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x2A, 0xAC, 0x57, 0x88, 0x23, 0x3E,
0x84, 0x80, 0x00, 0x48, 0x37, 0xC6, 0x03, 0x9D, 0x87, 0xEC, 0x49, 0x08, 0x88, 0x6C, 0x4B, 0x48,
0xD4, 0x00, 0x53, 0x48, 0x54, 0x02, 0x03, 0x18, 0xC2, 0xAC, 0x57, 0x88, 0x59, 0x7E, 0x84, 0x80,
0x45, 0x08, 0x00, 0x42, 0x03, 0x18, 0xC2, 0xAC, 0x57, 0x88, 0x59, 0x7E, 0x84, 0x80, 0x00, 0x48,
0x01, 0xBE, 0xEB, 0x80, 0x57, 0x88, 0x59, 0x7E, 0x84, 0x80, 0x6B, 0x88, 0x80, 0x40, 0x57, 0x88,
0x59, 0x7E, 0x84, 0x80, 0x00, 0x48, 0xEB, 0x80, 0x01, 0xF0, 0x03, 0xD0, 0xEB, 0x4D, 0xFF, 0x7E,
0x03, 0xD0, 0x03, 0x9D, 0xA6, 0xEC, 0x6B, 0x0D, 0x02, 0xBE, 0xEF, 0xC0, 0x6F, 0xC8, 0xDD, 0x80,
0x01, 0xF0, 0xDE, 0x80, 0x5D, 0x88, 0x96, 0x00, 0x5E, 0x88, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50,
0x01, 0xF0, 0xDD, 0xC1, 0xDE, 0x80, 0x5D, 0x88, 0x96, 0x00, 0x5E, 0x88, 0x95, 0x00, 0x17, 0x94,
0x17, 0x50, 0xFA, 0x6C, 0x4D, 0x48, 0x53, 0x42, 0x57, 0x88, 0x03, 0x18, 0x0E, 0xED, 0x59, 0x7E,
0x84, 0x80, 0x00, 0x48, 0x47, 0x42, 0x57, 0x88, 0x03, 0x18, 0x0E, 0xED, 0x59, 0x7E, 0x84, 0x80,
0x00, 0x48, 0xFF, 0x7E, 0xEB, 0x80, 0x57, 0x88, 0x59, 0x7E, 0x84, 0x80, 0x6B, 0x88, 0x80, 0x40,
0x57, 0x88, 0x59, 0x7E, 0x84, 0x80, 0x00, 0x48, 0xEB, 0x80, 0x01, 0xF0, 0x03, 0xD0, 0xEB, 0x4D,
0xFF, 0x7E, 0x03, 0xD0, 0x03, 0x9D, 0xDF, 0xAC, 0x6B, 0x0D, 0x02, 0xBE, 0xEF, 0xC0, 0x6F, 0xC8,
0xDD, 0x80, 0x01, 0xF0, 0xDE, 0x80, 0x5D, 0x88, 0x96, 0x00, 0x5E, 0x88, 0x95, 0x00, 0x17, 0x94,
0x17, 0x50, 0x01, 0xF0, 0xDD, 0xC1, 0xDE, 0x80, 0x5D, 0x88, 0x96, 0x00, 0x5E, 0x88, 0x95, 0x00,
0x17, 0x94, 0x17, 0x50, 0x06, 0x30, 0xD5, 0x81, 0xD5, 0xCA, 0xDD, 0x80, 0x57, 0x88, 0x59, 0x7E,
0x84, 0x80, 0x47, 0x48, 0x83, 0x93, 0x00, 0x42, 0x8A, 0x51, 0xBD, 0x21, 0x8A, 0x51, 0xEB, 0x80,
0x57, 0x88, 0x27, 0x7E, 0x84, 0x80, 0x6B, 0x88, 0x80, 0x40, 0x57, 0x88, 0xA8, 0x3E, 0x84, 0x80,
0x80, 0x88, 0x03, 0x9D, 0xEE, 0xAD, 0x50, 0xC8, 0x39, 0x42, 0x03, 0x18, 0x78, 0x2D, 0x57, 0x88,
0x23, 0x3E, 0x84, 0x80, 0x35, 0x48, 0x00, 0x42, 0x03, 0x18, 0x78, 0x2D, 0x57, 0x88, 0x23, 0x3E,
0x84, 0x80, 0x00, 0x48, 0x01, 0xBE, 0xEB, 0x80, 0x57, 0x88, 0x23, 0x3E, 0x84, 0x80, 0x6B, 0x88,
0x80, 0x40, 0x57, 0x88, 0x23, 0x3E, 0x84, 0x80, 0x00, 0x48, 0x01, 0xBE, 0x84, 0x80, 0x8A, 0x51,
0x02, 0xA0, 0x8A, 0x51, 0x0F, 0x39, 0xEB, 0x80, 0x03, 0xD0, 0xEB, 0x4D, 0x57, 0x88, 0x23, 0x3E,
0x84, 0x80, 0x00, 0x48, 0x15, 0x3E, 0x84, 0x80, 0x8A, 0x51, 0x02, 0xA0, 0x8A, 0x51, 0x07, 0xF9,
0xEC, 0x40, 0x04, 0xF0, 0x03, 0xD0, 0xEC, 0x0D, 0xFF, 0x7E, 0x03, 0xD0, 0x03, 0x9D, 0x43, 0xED,
0x6C, 0xCD, 0x6B, 0x84, 0x01, 0x38, 0xED, 0x80, 0x57, 0x88, 0x21, 0xFE, 0x84, 0x80, 0x6D, 0x88,
0x80, 0x40, 0x8C, 0x70, 0x83, 0x96, 0xB5, 0x40, 0x83, 0x52, 0x57, 0x88, 0x21, 0xFE, 0x84, 0x80,
0x00, 0x48, 0x83, 0x96, 0xB6, 0x40, 0xB5, 0xF0, 0x83, 0x52, 0xDD, 0x80, 0x57, 0x88, 0xDE, 0x80,
0x02, 0xF0, 0x8A, 0x95, 0x15, 0x64, 0x8A, 0x51, 0x01, 0xF0, 0xDD, 0xC1, 0xDD, 0x0A, 0xDE, 0x80,
0x5D, 0x88, 0x96, 0x00, 0x5E, 0x88, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x01, 0xF0, 0xDD, 0xC1,
0xDE, 0x80, 0x5D, 0x88, 0x96, 0x00, 0x5E, 0x88, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0xDC, 0x6D,
0x3B, 0x88, 0x50, 0xC2, 0x03, 0x18, 0xEE, 0xAD, 0x57, 0x88, 0x23, 0x3E, 0x84, 0x80, 0x00, 0x48,
0x37, 0x82, 0x03, 0x18, 0xEE, 0xAD, 0x57, 0x88, 0x23, 0x3E, 0x84, 0x80, 0x00, 0x48, 0xFF, 0x7E,
0xEB, 0x80, 0x57, 0x88, 0x23, 0x3E, 0x84, 0x80, 0x6B, 0x88, 0x80, 0x40, 0x57, 0x88, 0x23, 0x3E,
0x84, 0x80, 0x00, 0x48, 0x01, 0xBE, 0x84, 0x80, 0x8A, 0x51, 0x02, 0xA0, 0x8A, 0x51, 0x0F, 0x39,
0xEB, 0x80, 0x03, 0xD0, 0xEB, 0x4D, 0x57, 0x88, 0x23, 0x3E, 0x84, 0x80, 0x00, 0x48, 0x15, 0x3E,
0x84, 0x80, 0x8A, 0x51, 0x02, 0xA0, 0x8A, 0x51, 0x07, 0xF9, 0xEC, 0x40, 0x04, 0xF0, 0x03, 0xD0,
0xEC, 0x0D, 0xFF, 0x7E, 0x03, 0xD0, 0x03, 0x9D, 0xA8, 0xED, 0x6C, 0xCD, 0x6B, 0x84, 0x01, 0x38,
0xED, 0x80, 0x57, 0x88, 0x21, 0xFE, 0x84, 0x80, 0x6D, 0x88, 0x80, 0x40, 0x8C, 0x70, 0x83, 0x96,
0xB5, 0x40, 0x83, 0x52, 0x57, 0x88, 0x21, 0xFE, 0x84, 0x80, 0x00, 0x48, 0x83, 0x96, 0xB6, 0x40,
0xB5, 0xF0, 0x83, 0x52, 0xDD, 0x80, 0x57, 0x88, 0xDE, 0x80, 0x02, 0xF0, 0x8A, 0x95, 0x15, 0x64,
0x8A, 0x51, 0x01, 0xF0, 0xDD, 0xC1, 0xDD, 0x0A, 0xDE, 0x80, 0x5D, 0x88, 0x96, 0x00, 0x5E, 0x88,
0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x01, 0xF0, 0xDD, 0xC1, 0xDE, 0x80, 0x5D, 0x88, 0x96, 0x00,
0x5E, 0x88, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0xD5, 0x81, 0xD5, 0xCA, 0x57, 0x88, 0x23, 0x3E,
0x84, 0x80, 0x83, 0x93, 0x00, 0x48, 0x0B, 0x3E, 0x84, 0x80, 0x8A, 0x51, 0x02, 0xA0, 0x8A, 0x51,
0xEB, 0x80, 0x57, 0x88, 0x31, 0x3E, 0x84, 0x80, 0x6B, 0x88, 0x80, 0x40, 0x55, 0xCB, 0x19, 0xEE,
0x57, 0x88, 0x27, 0x7E, 0x84, 0x80, 0x00, 0x48, 0xEB, 0x80, 0x57, 0x88, 0x31, 0x3E, 0x84, 0x80,
0x00, 0x48, 0x6B, 0x07, 0xEC, 0x40, 0x57, 0x88, 0x2F, 0xBE, 0x84, 0x80, 0x6C, 0x48, 0x80, 0x40,
0x57, 0x88, 0x2F, 0xBE, 0x84, 0x80, 0x7F, 0x70, 0x00, 0x42, 0x57, 0x88, 0x03, 0x5C, 0x0D, 0xEE,
0x2F, 0xBE, 0x84, 0x80, 0x7F, 0x70, 0x80, 0x40, 0x57, 0x88, 0x2F, 0xBE, 0x84, 0x80, 0x00, 0x48,
0xDD, 0x80, 0x03, 0x30, 0xDE, 0x80, 0x5D, 0x88, 0x96, 0x00, 0x5E, 0x88, 0x95, 0x00, 0x17, 0x94,
0x17, 0x50, 0x23, 0x70, 0xD7, 0xC1, 0xD7, 0x0A, 0xDD, 0x80, 0x5D, 0x88, 0x95, 0x00, 0x97, 0xD4,
0x97, 0x90, 0x0D, 0x08, 0xD3, 0x40, 0x22, 0x30, 0xDD, 0x80, 0x5D, 0x88, 0x95, 0x00, 0x97, 0xD4,
0x97, 0x90, 0x0D, 0x08, 0xD0, 0xC0, 0xD6, 0x81, 0x57, 0x88, 0x29, 0x3E, 0x84, 0x80, 0x83, 0x93,
0x80, 0x88, 0x03, 0x9D, 0xC2, 0xEE, 0x57, 0x88, 0x23, 0x3E, 0x84, 0x80, 0x00, 0x48, 0x37, 0xC6,
0x03, 0x9D, 0x3C, 0x2E, 0x49, 0x08, 0x3D, 0x6E, 0x4B, 0x48, 0xD4, 0x00, 0x53, 0x48, 0x54, 0x02,
0x03, 0x18, 0x77, 0xAE, 0x57, 0x88, 0x59, 0x7E, 0x84, 0x80, 0x45, 0x08, 0x00, 0x42, 0x03, 0x18,
0x77, 0xAE, 0x57, 0x88, 0x59, 0x7E, 0x84, 0x80, 0x00, 0x48, 0x01, 0xBE, 0xEB, 0x80, 0x57, 0x88,
0x59, 0x7E, 0x84, 0x80, 0x6B, 0x88, 0x80, 0x40, 0x57, 0x88, 0x59, 0x7E, 0x84, 0x80, 0x00, 0x48,
0xEB, 0x80, 0x01, 0xF0, 0x03, 0xD0, 0xEB, 0x4D, 0xFF, 0x7E, 0x03, 0xD0, 0x03, 0x9D, 0x5B, 0x6E,
0x6B, 0x0D, 0x02, 0xBE, 0xEF, 0xC0, 0x6F, 0xC8, 0xDD, 0x80, 0x02, 0xF0, 0xDE, 0x80, 0x5D, 0x88,
0x96, 0x00, 0x5E, 0x88, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x02, 0xF0, 0xDD, 0xC1, 0xDE, 0x80,
0x5D, 0x88, 0x96, 0x00, 0x5E, 0x88, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0xAF, 0xAE, 0x4D, 0x48,
0x53, 0x42, 0x57, 0x88, 0x03, 0x18, 0xC3, 0x2E, 0x59, 0x7E, 0x84, 0x80, 0x00, 0x48, 0x47, 0x42,
0x57, 0x88, 0x03, 0x18, 0xC3, 0x2E, 0x59, 0x7E, 0x84, 0x80, 0x00, 0x48, 0xFF, 0x7E, 0xEB, 0x80,
0x57, 0x88, 0x59, 0x7E, 0x84, 0x80, 0x6B, 0x88, 0x80, 0x40, 0x57, 0x88, 0x59, 0x7E, 0x84, 0x80,
0x00, 0x48, 0xEB, 0x80, 0x01, 0xF0, 0x03, 0xD0, 0xEB, 0x4D, 0xFF, 0x7E, 0x03, 0xD0, 0x03, 0x9D,
0x94, 0xEE, 0x6B, 0x0D, 0x02, 0xBE, 0xEF, 0xC0, 0x6F, 0xC8, 0xDD, 0x80, 0x02, 0xF0, 0xDE, 0x80,
0x5D, 0x88, 0x96, 0x00, 0x5E, 0x88, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x02, 0xF0, 0xDD, 0xC1,
0xDE, 0x80, 0x5D, 0x88, 0x96, 0x00, 0x5E, 0x88, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x06, 0x30,
0xD6, 0x81, 0xD6, 0xCA, 0xDD, 0x80, 0x57, 0x88, 0x59, 0x7E, 0x84, 0x80, 0x47, 0x48, 0x83, 0x93,
0x00, 0x42, 0x8A, 0x51, 0xBD, 0x21, 0x8A, 0x51, 0xEB, 0x80, 0x57, 0x88, 0x27, 0x7E, 0x84, 0x80,
0x6B, 0x88, 0x80, 0x40, 0x57, 0x88, 0xA8, 0x3E, 0x84, 0x80, 0x80, 0x88, 0x03, 0x9D, 0xA3, 0x6F,
0x50, 0xC8, 0x39, 0x42, 0x03, 0x18, 0x2D, 0x6F, 0x57, 0x88, 0x23, 0x3E, 0x84, 0x80, 0x35, 0x48,
0x00, 0x42, 0x03, 0x18, 0x2D, 0x6F, 0x57, 0x88, 0x23, 0x3E, 0x84, 0x80, 0x00, 0x48, 0x01, 0xBE,
0xEB, 0x80, 0x57, 0x88, 0x23, 0x3E, 0x84, 0x80, 0x6B, 0x88, 0x80, 0x40, 0x57, 0x88, 0x23, 0x3E,
0x84, 0x80, 0x00, 0x48, 0x01, 0xBE, 0x84, 0x80, 0x8A, 0x51, 0x02, 0xA0, 0x8A, 0x51, 0x0F, 0x39,
0xEB, 0x80, 0x03, 0xD0, 0xEB, 0x4D, 0x57, 0x88, 0x23, 0x3E, 0x84, 0x80, 0x00, 0x48, 0x15, 0x3E,
0x84, 0x80, 0x8A, 0x51, 0x02, 0xA0, 0x8A, 0x51, 0x07, 0xF9, 0xEC, 0x40, 0x04, 0xF0, 0x03, 0xD0,
0xEC, 0x0D, 0xFF, 0x7E, 0x03, 0xD0, 0x03, 0x9D, 0xF8, 0x6E, 0x6C, 0xCD, 0x6B, 0x84, 0x01, 0x38,
0xED, 0x80, 0x57, 0x88, 0x21, 0xFE, 0x84, 0x80, 0x6D, 0x88, 0x80, 0x40, 0x8C, 0x70, 0x83, 0x96,
0xB5, 0x40, 0x83, 0x52, 0x57, 0x88, 0x21, 0xFE, 0x84, 0x80, 0x00, 0x48, 0x83, 0x96, 0xB6, 0x40,
0xB5, 0xF0, 0x83, 0x52, 0xDD, 0x80, 0x57, 0x88, 0xDE, 0x80, 0x02, 0xF0, 0x8A, 0x95, 0x15, 0x64,
0x8A, 0x51, 0x02, 0xF0, 0xDD, 0xC1, 0xDD, 0x0A, 0xDE, 0x80, 0x5D, 0x88, 0x96, 0x00, 0x5E, 0x88,
0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x02, 0xF0, 0xDD, 0xC1, 0xDE, 0x80, 0x5D, 0x88, 0x96, 0x00,
0x5E, 0x88, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x91, 0x2F, 0x3B, 0x88, 0x50, 0xC2, 0x03, 0x18,
0xA3, 0x6F, 0x57, 0x88, 0x23, 0x3E, 0x84, 0x80, 0x00, 0x48, 0x37, 0x82, 0x03, 0x18, 0xA3, 0x6F,
0x57, 0x88, 0x23, 0x3E, 0x84, 0x80, 0x00, 0x48, 0xFF, 0x7E, 0xEB, 0x80, 0x57, 0x88, 0x23, 0x3E,
0x84, 0x80, 0x6B, 0x88, 0x80, 0x40, 0x57, 0x88, 0x23, 0x3E, 0x84, 0x80, 0x00, 0x48, 0x01, 0xBE,
0x84, 0x80, 0x8A, 0x51, 0x02, 0xA0, 0x8A, 0x51, 0x0F, 0x39, 0xEB, 0x80, 0x03, 0xD0, 0xEB, 0x4D,
0x57, 0x88, 0x23, 0x3E, 0x84, 0x80, 0x00, 0x48, 0x15, 0x3E, 0x84, 0x80, 0x8A, 0x51, 0x02, 0xA0,
0x8A, 0x51, 0x07, 0xF9, 0xEC, 0x40, 0x04, 0xF0, 0x03, 0xD0, 0xEC, 0x0D, 0xFF, 0x7E, 0x03, 0xD0,
0x03, 0x9D, 0x5D, 0xAF, 0x6C, 0xCD, 0x6B, 0x84, 0x01, 0x38, 0xED, 0x80, 0x57, 0x88, 0x21, 0xFE,
0x84, 0x80, 0x6D, 0x88, 0x80, 0x40, 0x8C, 0x70, 0x83, 0x96, 0xB5, 0x40, 0x83, 0x52, 0x57, 0x88,
0x21, 0xFE, 0x84, 0x80, 0x00, 0x48, 0x83, 0x96, 0xB6, 0x40, 0xB5, 0xF0, 0x83, 0x52, 0xDD, 0x80,
0x57, 0x88, 0xDE, 0x80, 0x02, 0xF0, 0x8A, 0x95, 0x15, 0x64, 0x8A, 0x51, 0x02, 0xF0, 0xDD, 0xC1,
0xDD, 0x0A, 0xDE, 0x80, 0x5D, 0x88, 0x96, 0x00, 0x5E, 0x88, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50,
0x02, 0xF0, 0xDD, 0xC1, 0xDE, 0x80, 0x5D, 0x88, 0x96, 0x00, 0x5E, 0x88, 0x95, 0x00, 0x17, 0x94,
0x17, 0x50, 0xD6, 0x81, 0xD6, 0xCA, 0x57, 0x88, 0x23, 0x3E, 0x84, 0x80, 0x83, 0x93, 0x00, 0x48,
0x0B, 0x3E, 0x84, 0x80, 0x8A, 0x51, 0x02, 0xA0, 0x8A, 0x51, 0xEB, 0x80, 0x57, 0x88, 0x31, 0x3E,
0x84, 0x80, 0x6B, 0x88, 0x80, 0x40, 0x56, 0xCB, 0xC3, 0x2B, 0x57, 0x88, 0x27, 0x7E, 0x84, 0x80,
0x00, 0x48, 0xEB, 0x80, 0x57, 0x88, 0x31, 0x3E, 0x84, 0x80, 0x00, 0x48, 0x6B, 0x07, 0xEC, 0x40,
0x57, 0x88, 0x2F, 0xBE, 0x84, 0x80, 0x6C, 0x48, 0x80, 0x40, 0x57, 0x88, 0x2F, 0xBE, 0x84, 0x80,
0x7F, 0x70, 0x00, 0x42, 0x57, 0x88, 0x03, 0x5C, 0xC2, 0x2F, 0x2F, 0xBE, 0x84, 0x80, 0x7F, 0x70,
0x80, 0x40, 0x57, 0x88, 0x2F, 0xBE, 0x84, 0x80, 0x00, 0x48, 0xDD, 0x80, 0x04, 0xF0, 0xDE, 0x80,
0x5D, 0x88, 0x96, 0x00, 0x5E, 0x88, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0xC3, 0x2B, 0xEE, 0xC1,
0x83, 0x93, 0x23, 0x70, 0x84, 0x80, 0x5D, 0xF0, 0x8A, 0x51, 0xB5, 0xE1, 0x8A, 0x51, 0xA0, 0x30,
0x84, 0x80, 0xD5, 0xF0, 0x8A, 0x51, 0xB5, 0xE1, 0x83, 0x96, 0x38, 0x70, 0xD5, 0x40, 0x3A, 0xB0,
0xD6, 0x40, 0x3C, 0xB0, 0xD7, 0x80, 0x78, 0xB0, 0xD8, 0x00, 0x7A, 0xF0, 0xD9, 0x40, 0x7C, 0xF0,
0xDA, 0x40, 0x7D, 0x30, 0xDB, 0x80, 0x7F, 0x70, 0xDC, 0x40, 0xB9, 0xF0, 0xDD, 0x80, 0xBA, 0xF0,
0xDE, 0x80, 0xBB, 0x30, 0xDF, 0xC0, 0xFA, 0x30, 0xE0, 0xC0, 0xFB, 0x70, 0xE1, 0x00, 0xFC, 0x30,
0xE2, 0x00, 0xFD, 0x70, 0xE3, 0x40, 0xFF, 0xB0, 0xE4, 0x00, 0x83, 0x01, 0x8A, 0x51, 0x21, 0x6A,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xE4, 0x00, 0x64, 0x9C, 0xDD, 0x69,
0x00, 0xB0, 0xDD, 0x80, 0x5D, 0x88, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08, 0xEF, 0xC0,
0x3F, 0x30, 0xEF, 0x45, 0x6F, 0xC8, 0xDD, 0x80, 0x00, 0xB0, 0xDE, 0x80, 0x5D, 0x88, 0x96, 0x00,
0x5E, 0x88, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0xE4, 0xB0, 0xE2, 0x00, 0x8F, 0x29, 0x90, 0x69,
0xE2, 0xCB, 0x8E, 0xE9, 0x93, 0xE9, 0x00, 0x00, 0x83, 0x52, 0x03, 0x53, 0x64, 0x9C, 0xF2, 0x29,
0x00, 0xB0, 0xDD, 0x80, 0x5D, 0x88, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08, 0xEF, 0xC0,
0x6F, 0xC8, 0xEF, 0xF9, 0x10, 0x38, 0xEF, 0xC0, 0x6F, 0xC8, 0xDD, 0x80, 0x00, 0xB0, 0xDE, 0x80,
0x5D, 0x88, 0x96, 0x00, 0x5E, 0x88, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x80, 0xF0, 0x83, 0x96,
0xB5, 0x40, 0x03, 0x30, 0xB6, 0x40, 0xB5, 0xF0, 0x83, 0x52, 0xDD, 0x80, 0x64, 0x08, 0xDE, 0x80,
0x02, 0xF0, 0x15, 0x64, 0x8A, 0x95, 0x04, 0xF0, 0xE3, 0x40, 0x1C, 0x70, 0xE2, 0x00, 0xE2, 0xCB,
0xBF, 0xA9, 0xE3, 0x0B, 0xBF, 0xA9, 0x00, 0x00, 0x91, 0x70, 0x83, 0x96, 0x03, 0x53, 0xB5, 0x40,
0x83, 0x52, 0x64, 0x08, 0xAE, 0xBE, 0x84, 0x80, 0x83, 0x93, 0x00, 0x48, 0x83, 0x96, 0xB6, 0x40,
0xB5, 0xF0, 0x83, 0x52, 0xDD, 0x80, 0x64, 0x08, 0xDE, 0x80, 0x02, 0xF0, 0x15, 0x64, 0x8A, 0x95,
0x83, 0x96, 0xAE, 0x1C, 0xAF, 0x18, 0x09, 0x6A, 0x0C, 0x6A, 0x00, 0xB0, 0xDD, 0x80, 0x5D, 0x88,
0x95, 0x00, 0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08, 0xEF, 0xC0, 0xF3, 0x30, 0xEF, 0x45, 0x6F, 0xC8,
0xDD, 0x80, 0x00, 0xB0, 0xDE, 0x80, 0x5D, 0x88, 0x96, 0x00, 0x5E, 0x88, 0x95, 0x00, 0x17, 0x94,
0x17, 0x50, 0x8C, 0xA9, 0x00, 0xB0, 0xDD, 0x80, 0x5D, 0x88, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90,
0x0D, 0x08, 0xEF, 0xC0, 0x6F, 0xC8, 0xFE, 0xF9, 0x01, 0x38, 0xEF, 0xC0, 0x6F, 0xC8, 0xDD, 0x80,
0x00, 0xB0, 0xDE, 0x80, 0x5D, 0x88, 0x96, 0x00, 0x5E, 0x88, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50,
0xAE, 0x29, 0x83, 0x52, 0x1B, 0x92, 0x08, 0x40, 0x83, 0x52, 0x1B, 0xD6, 0x08, 0x40, 0xEA, 0x40,
0x6A, 0x98, 0x1A, 0xAA, 0x25, 0x70, 0xDD, 0x80, 0x5D, 0x88, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90,
0x0D, 0x08, 0x21, 0x6A, 0x2A, 0x70, 0xDD, 0x80, 0x5D, 0x88, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90,
0x0D, 0x08, 0xE6, 0x40, 0xE9, 0x40, 0x3F, 0x30, 0xE9, 0xC5, 0x88, 0x30, 0x83, 0x96, 0xB5, 0x40,
0x83, 0x52, 0x69, 0x48, 0x83, 0x96, 0xB6, 0x40, 0xB5, 0xF0, 0x83, 0x52, 0xDD, 0x80, 0x6A, 0x48,
0xDE, 0x80, 0x02, 0xF0, 0x15, 0x64, 0x8A, 0x95, 0x66, 0x48, 0xE3, 0x40, 0x06, 0x30, 0x03, 0xD0,
0xE3, 0xCC, 0xFF, 0x7E, 0x03, 0x9D, 0x37, 0x2A, 0x63, 0x48, 0xE9, 0x40, 0x03, 0x30, 0xE9, 0xC5,
0x6A, 0x98, 0x68, 0xAA, 0x00, 0xB0, 0xDD, 0x80, 0x5D, 0x88, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90,
0x0D, 0x08, 0xEF, 0xC0, 0x69, 0x48, 0xE3, 0x40, 0x03, 0xD0, 0xE3, 0x0D, 0x03, 0xD0, 0xE3, 0x0D,
0x6F, 0xC8, 0xF3, 0xB9, 0x63, 0x44, 0xEF, 0xC0, 0x6F, 0xC8, 0xDD, 0x80, 0x00, 0xB0, 0xDE, 0x80,
0x5D, 0x88, 0x96, 0x00, 0x5E, 0x88, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x6A, 0x98, 0x87, 0xEA,
0x26, 0x70, 0xDD, 0x80, 0x5D, 0x88, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08, 0x8E, 0xEA,
0x00, 0xB0, 0xDD, 0x80, 0x5D, 0x88, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08, 0xEF, 0xC0,
0x69, 0x48, 0xE3, 0x40, 0x06, 0x30, 0x03, 0xD0, 0xE3, 0x0D, 0xFF, 0x7E, 0x03, 0x9D, 0x73, 0x2A,
0x6F, 0xC8, 0x3F, 0xB9, 0x63, 0x44, 0xEF, 0xC0, 0x6F, 0xC8, 0xDD, 0x80, 0x00, 0xB0, 0xDE, 0x80,
0x5D, 0x88, 0x96, 0x00, 0x5E, 0x88, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x5E, 0x2A, 0x2B, 0xB0,
0xDD, 0x80, 0x5D, 0x88, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08, 0xE9, 0x40, 0x07, 0x70,
0xE9, 0xC5, 0x06, 0x30, 0x69, 0x42, 0x03, 0x5C, 0xA7, 0x2A, 0x8A, 0x70, 0x83, 0x96, 0xB5, 0x40,
0x6A, 0xB0, 0xB6, 0x40, 0xB5, 0xF0, 0x83, 0x52, 0xDD, 0x80, 0x6A, 0x48, 0xDE, 0x80, 0x02, 0xF0,
0x15, 0x64, 0x8A, 0x95, 0x8B, 0xB0, 0x83, 0x96, 0xB5, 0x40, 0x04, 0xF0, 0xC0, 0x6A, 0x69, 0x48,
0x05, 0xBA, 0x8A, 0x70, 0x03, 0x9D, 0xB1, 0xEA, 0x83, 0x96, 0xB5, 0x40, 0x20, 0xF0, 0xB6, 0x40,
0xB4, 0xEA, 0x83, 0x96, 0xB5, 0x40, 0xB6, 0x81, 0xB5, 0xF0, 0x83, 0x52, 0xDD, 0x80, 0x6A, 0x48,
0xDE, 0x80, 0x02, 0xF0, 0x15, 0x64, 0x8A, 0x95, 0x8B, 0xB0, 0x83, 0x96, 0xB5, 0x40, 0x05, 0x30,
0xB6, 0x40, 0xB5, 0xF0, 0x83, 0x52, 0xDD, 0x80, 0x6A, 0x48, 0xDE, 0x80, 0x02, 0xF0, 0x15, 0x64,
0x8A, 0x95, 0x6A, 0x98, 0xE3, 0x2A, 0x27, 0xB0, 0xDD, 0x80, 0x5D, 0x88, 0x95, 0x00, 0x97, 0xD4,
0x97, 0x90, 0x0D, 0x08, 0xE5, 0x40, 0x28, 0x30, 0xDD, 0x80, 0x5D, 0x88, 0x95, 0x00, 0x97, 0xD4,
0x97, 0x90, 0x0D, 0x08, 0xE9, 0x40, 0x29, 0x70, 0xDD, 0x80, 0x5D, 0x88, 0x95, 0x00, 0x97, 0xD4,
0x97, 0x90, 0x0D, 0x08, 0xFA, 0x6A, 0x2C, 0x70, 0xDD, 0x80, 0x5D, 0x88, 0x95, 0x00, 0x97, 0xD4,
0x97, 0x90, 0x0D, 0x08, 0xE5, 0x40, 0x2D, 0xB0, 0xDD, 0x80, 0x5D, 0x88, 0x95, 0x00, 0x97, 0xD4,
0x97, 0x90, 0x0D, 0x08, 0xE9, 0x40, 0x2E, 0xB0, 0xDD, 0x80, 0x5D, 0x88, 0x95, 0x00, 0x97, 0xD4,
0x97, 0x90, 0x0D, 0x08, 0xE7, 0x80, 0x69, 0x48, 0xE3, 0x40, 0x07, 0x70, 0x03, 0xD0, 0xE3, 0xCC,
0xFF, 0x7E, 0x03, 0xD0, 0x03, 0x9D, 0xFF, 0xEA, 0x65, 0xCD, 0x63, 0x44, 0xE8, 0x00, 0x84, 0x30,
0x83, 0x96, 0xB5, 0x40, 0x83, 0x52, 0x68, 0x08, 0x83, 0x96, 0xB6, 0x40, 0xB5, 0xF0, 0x83, 0x52,
0xDD, 0x80, 0x6A, 0x48, 0xDE, 0x80, 0x02, 0xF0, 0x15, 0x64, 0x8A, 0x95, 0x67, 0x88, 0xE3, 0x40,
0x07, 0x70, 0x03, 0xD0, 0xE3, 0xCC, 0xFF, 0x7E, 0x03, 0xD0, 0x03, 0x9D, 0x1A, 0xEB, 0x69, 0xCD,
0x63, 0x44, 0xE8, 0x00, 0x85, 0x70, 0x83, 0x96, 0xB5, 0x40, 0x83, 0x52, 0x68, 0x08, 0x83, 0x96,
0xB6, 0x40, 0xB5, 0xF0, 0x83, 0x52, 0xDD, 0x80, 0x6A, 0x48, 0xDE, 0x80, 0x02, 0xF0, 0x15, 0x64,
0x8A, 0x95, 0x03, 0xD0, 0x67, 0x0D, 0xE8, 0x00, 0x86, 0x70, 0x83, 0x96, 0xB5, 0x40, 0x83, 0x52,
0x68, 0x08, 0x83, 0x96, 0xB6, 0x40, 0xB5, 0xF0, 0x83, 0x52, 0xDD, 0x80, 0x6A, 0x48, 0xDE, 0x80,
0x02, 0xF0, 0x15, 0x64, 0x8A, 0x95, 0x6A, 0x48, 0x2D, 0x7E, 0x84, 0x80, 0x83, 0x93, 0x80, 0x88,
0x03, 0x59, 0x4F, 0x6B, 0x6A, 0x48, 0x5B, 0xBE, 0x84, 0x80, 0x03, 0x30, 0x53, 0x2B, 0x6A, 0x48,
0x5B, 0xBE, 0x84, 0x80, 0x01, 0xF0, 0x80, 0x40, 0x80, 0xF0, 0x83, 0x96, 0xB5, 0x40, 0x83, 0x52,
0x6A, 0x48, 0x5B, 0xBE, 0x84, 0x80, 0x00, 0x48, 0x04, 0x38, 0x83, 0x96, 0xB6, 0x40, 0xB5, 0xF0,
0x83, 0x52, 0xDD, 0x80, 0x6A, 0x48, 0xDE, 0x80, 0x02, 0xF0, 0x15, 0x64, 0x8A, 0x95, 0x03, 0x30,
0xE4, 0x00, 0x7D, 0x30, 0xE3, 0x40, 0xE3, 0x0B, 0x6B, 0x6B, 0xE4, 0xCB, 0x6B, 0x6B, 0x83, 0x52,
0x03, 0x53, 0x6A, 0xDC, 0x87, 0x2B, 0x00, 0xB0, 0xDD, 0x80, 0x5D, 0x88, 0x95, 0x00, 0x97, 0xD4,
0x97, 0x90, 0x0D, 0x08, 0xEF, 0xC0, 0x6F, 0x12, 0x6F, 0xC8, 0xDD, 0x80, 0x00, 0xB0, 0xDE, 0x80,
0x5D, 0x88, 0x96, 0x00, 0x5E, 0x88, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x9A, 0x2B, 0x00, 0xB0,
0xDD, 0x80, 0x5D, 0x88, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08, 0xEF, 0xC0, 0x6F, 0xD0,
0x6F, 0xC8, 0xDD, 0x80, 0x00, 0xB0, 0xDE, 0x80, 0x5D, 0x88, 0x96, 0x00, 0x5E, 0x88, 0x95, 0x00,
0x17, 0x94, 0x17, 0x50, 0x05, 0x30, 0xE3, 0x40, 0xE3, 0x0B, 0x9C, 0x2B, 0x83, 0x52, 0x03, 0x53,
0x6A, 0xDC, 0xFC, 0xAB, 0x00, 0xB0, 0xDD, 0x80, 0x5D, 0x88, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90,
0x0D, 0x08, 0xEF, 0xC0, 0x6F, 0xC8, 0xDF, 0xF9, 0x20, 0x38, 0xEF, 0xC0, 0x6F, 0xC8, 0xDD, 0x80,
0x00, 0xB0, 0xDE, 0x80, 0x5D, 0x88, 0x96, 0x00, 0x5E, 0x88, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50,
0x15, 0x70, 0xE4, 0x00, 0xC6, 0xB0, 0xE3, 0x40, 0xE3, 0x0B, 0xBC, 0x6B, 0xE4, 0xCB, 0xBC, 0x6B,
0x00, 0x00, 0x80, 0xF0, 0x83, 0x96, 0x03, 0x53, 0xB5, 0x40, 0x83, 0x52, 0x6A, 0x48, 0x5B, 0xBE,
0x84, 0x80, 0x83, 0x93, 0x00, 0x48, 0x0C, 0x78, 0x83, 0x96, 0xB6, 0x40, 0xB5, 0xF0, 0x83, 0x52,
0xDD, 0x80, 0x6A, 0x48, 0xDE, 0x80, 0x02, 0xF0, 0x15, 0x64, 0x8A, 0x95, 0x04, 0xF0, 0xE4, 0x00,
0x1C, 0x70, 0xE3, 0x40, 0xE3, 0x0B, 0xDA, 0x6B, 0xE4, 0xCB, 0xDA, 0x6B, 0x00, 0x00, 0x11, 0x30,
0x83, 0x96, 0x03, 0x53, 0xB5, 0x40, 0x55, 0xB0, 0xB6, 0x40, 0xB5, 0xF0, 0x83, 0x52, 0xDD, 0x80,
0x6A, 0x48, 0xDE, 0x80, 0x02, 0xF0, 0x4E, 0xA4, 0x8A, 0x95, 0x6A, 0x48, 0xAE, 0xBE, 0x84, 0x80,
0x83, 0x96, 0x35, 0x48, 0x83, 0x93, 0x80, 0x40, 0x83, 0x52, 0x6A, 0x48, 0xAE, 0xBE, 0x84, 0x80,
0x00, 0xDC, 0x13, 0xAC, 0x9B, 0xD2, 0x08, 0x40, 0x00, 0xB0, 0xDD, 0x80, 0x5D, 0x88, 0x95, 0x00,
0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08, 0xEF, 0xC0, 0x6F, 0xC8, 0xFD, 0xF9, 0x02, 0x38, 0xEF, 0xC0,
0x6F, 0xC8, 0xDD, 0x80, 0x00, 0xB0, 0xDE, 0x80, 0x5D, 0x88, 0x96, 0x00, 0x5E, 0x88, 0x95, 0x00,
0x17, 0x94, 0x17, 0x50, 0xB8, 0x2B, 0x9B, 0x16, 0x08, 0x40, 0xE0, 0xC0, 0x5E, 0xD8, 0x33, 0xEC,
0xE1, 0x41, 0x60, 0xC8, 0x61, 0x02, 0x03, 0x18, 0x28, 0x6C, 0x61, 0x08, 0x5D, 0x07, 0xDF, 0xC0,
0x84, 0x80, 0x83, 0x93, 0x00, 0x48, 0x99, 0x00, 0x18, 0x14, 0x18, 0xD0, 0xE1, 0x8A, 0x19, 0xAC,
0x18, 0x55, 0x18, 0x11, 0x83, 0x52, 0x03, 0x53, 0x11, 0x5C, 0x08, 0x40, 0x05, 0x30, 0xDF, 0xC0,
0xDF, 0x8B, 0x30, 0x6C, 0x2A, 0xAC, 0xE1, 0x41, 0x60, 0xC8, 0x61, 0x02, 0x03, 0x18, 0x43, 0xAC,
0x61, 0x08, 0x5D, 0x07, 0xDF, 0xC0, 0x84, 0x80, 0x83, 0x93, 0x00, 0x48, 0x9A, 0x00, 0x98, 0x95,
0x98, 0x51, 0xE1, 0x8A, 0x34, 0xAC, 0x98, 0x96, 0x98, 0x52, 0x83, 0x52, 0x03, 0x53, 0x91, 0x9C,
0x08, 0x40, 0x05, 0x30, 0xDF, 0xC0, 0xDF, 0x8B, 0x4B, 0xEC, 0x45, 0xAC, 0xE0, 0xC0, 0x10, 0xF0,
0x60, 0xC2, 0x03, 0x5C, 0x55, 0xEC, 0x10, 0xF0, 0x56, 0xEC, 0x60, 0xC8, 0xE1, 0x00, 0x5E, 0xD8,
0x85, 0xAC, 0xE2, 0x41, 0x61, 0x08, 0x62, 0x02, 0x03, 0x18, 0x69, 0xEC, 0x62, 0x08, 0x5D, 0x07,
0xDF, 0xC0, 0x84, 0x80, 0x83, 0x93, 0x00, 0x48, 0x99, 0x00, 0x18, 0x14, 0x18, 0xD0, 0xE2, 0x8A,
0x5A, 0xEC, 0x18, 0x55, 0x18, 0x11, 0x83, 0x52, 0x03, 0x53, 0x11, 0x5C, 0x75, 0x2C, 0x2A, 0x70,
0xDF, 0xC0, 0xDF, 0x8B, 0x71, 0xEC, 0x00, 0x00, 0x6B, 0x2C, 0xE2, 0x41, 0x61, 0x08, 0x62, 0x02,
0x03, 0x18, 0x08, 0x40, 0x62, 0x08, 0x5D, 0x07, 0xDF, 0xC0, 0x84, 0x80, 0x0F, 0x48, 0x83, 0x93,
0x80, 0x40, 0x98, 0x54, 0x98, 0x10, 0xE2, 0x8A, 0x76, 0x2C, 0xE2, 0x41, 0x61, 0x08, 0x62, 0x02,
0x03, 0x18, 0x95, 0xEC, 0x62, 0x08, 0x5D, 0x07, 0xDF, 0xC0, 0x84, 0x80, 0x83, 0x93, 0x00, 0x48,
0x9A, 0x00, 0x98, 0x95, 0x98, 0x51, 0xE2, 0x8A, 0x86, 0xAC, 0x98, 0x96, 0x98, 0x52, 0x83, 0x52,
0x03, 0x53, 0x91, 0x9C, 0xA1, 0xAC, 0x2A, 0x70, 0xDF, 0xC0, 0xDF, 0x8B, 0x9D, 0x2C, 0x00, 0x00,
0x97, 0x2C, 0xE2, 0x41, 0x61, 0x08, 0x62, 0x02, 0x03, 0x18, 0x08, 0x40, 0x62, 0x08, 0x5D, 0x07,
0xDF, 0xC0, 0x84, 0x80, 0x10, 0x88, 0x83, 0x93, 0x80, 0x40, 0x18, 0x56, 0x18, 0x12, 0xE2, 0x8A,
0xA2, 0xAC, 0x55, 0xB0, 0x9B, 0x40, 0xD7, 0xC1, 0x02, 0xF0, 0x57, 0x82, 0x03, 0x18, 0xC9, 0x2D,
0x57, 0x88, 0x01, 0xBE, 0x9B, 0x40, 0x57, 0x88, 0x33, 0x7E, 0x84, 0x80, 0x83, 0x93, 0x80, 0x81,
0x57, 0x88, 0x33, 0x7E, 0x84, 0x80, 0x80, 0x88, 0x03, 0x9D, 0xEC, 0x2C, 0x3C, 0xB0, 0xDD, 0x80,
0x5D, 0x88, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08, 0xEF, 0xC0, 0x57, 0xD8, 0xDC, 0x2C,
0x57, 0x88, 0x33, 0x7E, 0x84, 0x80, 0x6F, 0xC8, 0xDF, 0xC0, 0x07, 0x70, 0x03, 0xD0, 0xDF, 0x4C,
0xFF, 0x7E, 0x03, 0x9D, 0xD6, 0x2C, 0xE7, 0x6C, 0x57, 0x88, 0x33, 0x7E, 0x84, 0x80, 0x6F, 0xC8,
0xDF, 0xC0, 0x05, 0x30, 0x03, 0xD0, 0xDF, 0x4C, 0xFF, 0x7E, 0x03, 0x9D, 0xE2, 0xEC, 0x5F, 0xC8,
0x01, 0x79, 0x83, 0x93, 0x80, 0x40, 0xC0, 0x6C, 0x3D, 0xF0, 0xDD, 0x80, 0x5D, 0x88, 0x95, 0x00,
0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08, 0xEF, 0xC0, 0x6F, 0xC8, 0x03, 0x59, 0xF9, 0x6C, 0x6F, 0x4B,
0x18, 0xAD, 0x57, 0x88, 0x2D, 0x7E, 0x84, 0x80, 0x6F, 0xC8, 0x83, 0x93, 0x80, 0x40, 0x57, 0x88,
0x2D, 0x7E, 0x84, 0x80, 0x00, 0x48, 0xDD, 0x80, 0x1A, 0x70, 0xDE, 0x80, 0x5D, 0x88, 0x96, 0x00,
0x5E, 0x88, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x3E, 0xF0, 0xDD, 0x80, 0x5D, 0x88, 0x95, 0x00,
0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08, 0xEF, 0xC0, 0x6F, 0xC8, 0x03, 0x9D, 0x2B, 0x2D, 0x49, 0xED,
0x57, 0x88, 0x2D, 0x7E, 0x84, 0x80, 0x83, 0x93, 0x80, 0x81, 0x57, 0x88, 0x2D, 0x7E, 0x84, 0x80,
0x00, 0x48, 0xDD, 0x80, 0x1A, 0x70, 0xDE, 0x80, 0x5D, 0x88, 0x96, 0x00, 0x5E, 0x88, 0x95, 0x00,
0x17, 0x94, 0x17, 0x50, 0x0C, 0xAD, 0x10, 0xF0, 0x6F, 0xC2, 0x03, 0x18, 0x49, 0xED, 0x57, 0x88,
0x2B, 0x7E, 0x84, 0x80, 0x6F, 0xC8, 0x83, 0x93, 0x80, 0x40, 0x57, 0x88, 0x2B, 0x7E, 0x84, 0x80,
0x00, 0x48, 0xDD, 0x80, 0x1B, 0xB0, 0xDE, 0x80, 0x5D, 0x88, 0x96, 0x00, 0x5E, 0x88, 0x95, 0x00,
0x17, 0x94, 0x17, 0x50, 0x57, 0x88, 0x29, 0x3E, 0x84, 0x80, 0x83, 0x93, 0x80, 0x81, 0x80, 0xCA,
0x76, 0x6D, 0x6F, 0xC8, 0xFF, 0x3A, 0x03, 0x9D, 0x5E, 0x6D, 0x57, 0x88, 0x2B, 0x7E, 0x84, 0x80,
0x07, 0x70, 0x83, 0x93, 0x80, 0x40, 0xFF, 0xB0, 0xDD, 0x80, 0x1B, 0xB0, 0xDE, 0x80, 0x5D, 0x88,
0x96, 0x00, 0x5E, 0x88, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x71, 0x2D, 0x57, 0x88, 0x2B, 0x7E,
0x84, 0x80, 0x07, 0x70, 0x83, 0x93, 0x80, 0x40, 0x57, 0x88, 0x2B, 0x7E, 0x84, 0x80, 0x00, 0x48,
0xDD, 0x80, 0x1B, 0xB0, 0xDE, 0x80, 0x5D, 0x88, 0x96, 0x00, 0x5E, 0x88, 0x95, 0x00, 0x17, 0x94,
0x17, 0x50, 0x57, 0x88, 0x29, 0x3E, 0x84, 0x80, 0x83, 0x93, 0x80, 0x81, 0x3F, 0x30, 0xDD, 0x80,
0x5D, 0x88, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08, 0xEF, 0xC0, 0x0A, 0x30, 0x6F, 0xC2,
0x03, 0x18, 0x9C, 0x2D, 0x57, 0x88, 0x25, 0x3E, 0x84, 0x80, 0x6F, 0xC8, 0x83, 0x93, 0x80, 0x40,
0x57, 0x88, 0x25, 0x3E, 0x84, 0x80, 0x00, 0x48, 0xDD, 0x80, 0x1C, 0x70, 0xDE, 0x80, 0x5D, 0x88,
0x96, 0x00, 0x5E, 0x88, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x57, 0x88, 0xA8, 0x3E, 0x84, 0x80,
0x83, 0x93, 0x80, 0x81, 0x80, 0xCA, 0xC7, 0x6D, 0x6F, 0xC8, 0xFF, 0x3A, 0x03, 0x9D, 0xB0, 0xED,
0x57, 0x88, 0x25, 0x3E, 0x84, 0x80, 0xFF, 0xB0, 0x83, 0x93, 0x80, 0x81, 0xDD, 0x80, 0x1C, 0x70,
0xDE, 0x80, 0x5D, 0x88, 0x96, 0x00, 0x5E, 0x88, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0xC2, 0xED,
0x57, 0x88, 0x25, 0x3E, 0x84, 0x80, 0x83, 0x93, 0x80, 0x81, 0x57, 0x88, 0x25, 0x3E, 0x84, 0x80,
0x00, 0x48, 0xDD, 0x80, 0x1C, 0x70, 0xDE, 0x80, 0x5D, 0x88, 0x96, 0x00, 0x5E, 0x88, 0x95, 0x00,
0x17, 0x94, 0x17, 0x50, 0x57, 0x88, 0xA8, 0x3E, 0x84, 0x80, 0x83, 0x93, 0x80, 0x81, 0xD7, 0x0A,
0xB4, 0xEC, 0x03, 0x30, 0x9B, 0x40, 0x3C, 0xB0, 0xDD, 0x80, 0x5D, 0x88, 0x95, 0x00, 0x97, 0xD4,
0x97, 0x90, 0x0D, 0x08, 0xEF, 0xC0, 0x6F, 0xC8, 0x03, 0xBA, 0x03, 0x9D, 0xCB, 0x6D, 0x3E, 0xF0,
0xDD, 0x80, 0x5D, 0x88, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08, 0xB6, 0x40, 0x3F, 0x30,
0xDD, 0x80, 0x5D, 0x88, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08, 0xB8, 0x00, 0x36, 0x42,
0x03, 0x5C, 0x07, 0xEE, 0x0A, 0x30, 0x36, 0x42, 0x03, 0x18, 0x07, 0xEE, 0x36, 0x48, 0xDD, 0x80,
0x1B, 0xB0, 0xDE, 0x80, 0x5D, 0x88, 0x96, 0x00, 0x5E, 0x88, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50,
0x38, 0x08, 0xDD, 0x80, 0x1C, 0x70, 0xDE, 0x80, 0x5D, 0x88, 0x96, 0x00, 0x5E, 0x88, 0x95, 0x00,
0x17, 0x94, 0x17, 0x50, 0x36, 0x48, 0xB5, 0x40, 0x38, 0x08, 0xB7, 0x80, 0x1D, 0x2E, 0x09, 0x30,
0xDD, 0x80, 0x1B, 0xB0, 0xDE, 0x80, 0x5D, 0x88, 0x96, 0x00, 0x5E, 0x88, 0x95, 0x00, 0x17, 0x94,
0x17, 0x50, 0x1C, 0x70, 0xDD, 0xC1, 0xDE, 0x80, 0x5D, 0x88, 0x96, 0x00, 0x5E, 0x88, 0x95, 0x00,
0x17, 0x94, 0x17, 0x50, 0x09, 0x30, 0xB5, 0x40, 0xB7, 0xC1, 0x04, 0xF0, 0x9B, 0x40, 0x3C, 0xB0,
0xDD, 0x80, 0x5D, 0x88, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08, 0xEF, 0xC0, 0x6F, 0xC8,
0x04, 0x7A, 0x03, 0x9D, 0x1F, 0x6E, 0x3E, 0xF0, 0xDD, 0x80, 0x5D, 0x88, 0x95, 0x00, 0x97, 0xD4,
0x97, 0x90, 0x0D, 0x08, 0xBA, 0x40, 0x3F, 0x30, 0xDD, 0x80, 0x5D, 0x88, 0x95, 0x00, 0x97, 0xD4,
0x97, 0x90, 0x0D, 0x08, 0xBC, 0x40, 0x3A, 0x42, 0x03, 0x5C, 0x5A, 0x2E, 0x02, 0xF0, 0x3C, 0x42,
0x03, 0x5C, 0x5A, 0x2E, 0x3A, 0x48, 0xDD, 0x80, 0x1B, 0xB0, 0xDE, 0x80, 0x5D, 0x88, 0x96, 0x00,
0x5E, 0x88, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x3C, 0x48, 0xDD, 0x80, 0x1C, 0x70, 0xDE, 0x80,
0x5D, 0x88, 0x96, 0x00, 0x5E, 0x88, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x3A, 0x48, 0xB9, 0x40,
0x3C, 0x48, 0x71, 0x2E, 0x23, 0x70, 0xDD, 0x80, 0x1B, 0xB0, 0xDE, 0x80, 0x5D, 0x88, 0x96, 0x00,
0x5E, 0x88, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x10, 0xF0, 0xDD, 0x80, 0x1C, 0x70, 0xDE, 0x80,
0x5D, 0x88, 0x96, 0x00, 0x5E, 0x88, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x23, 0x70, 0xB9, 0x40,
0x10, 0xF0, 0xBB, 0x80, 0x05, 0x30, 0x9B, 0x40, 0x3C, 0xB0, 0xDD, 0x80, 0x5D, 0x88, 0x95, 0x00,
0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08, 0xEF, 0xC0, 0x6F, 0xC8, 0x05, 0xBA, 0x03, 0x9D, 0x74, 0x2E,
0x3E, 0xF0, 0xDD, 0x80, 0x5D, 0x88, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08, 0xC6, 0x00,
0x3F, 0x30, 0xDD, 0x80, 0x5D, 0x88, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08, 0xC8, 0xC0,
0x46, 0x02, 0x03, 0x5C, 0xB2, 0x2E, 0x10, 0xF0, 0x46, 0x02, 0x03, 0x18, 0xB2, 0x2E, 0x48, 0xC8,
0x03, 0x59, 0xB2, 0x2E, 0x46, 0x08, 0xDD, 0x80, 0x1B, 0xB0, 0xDE, 0x80, 0x5D, 0x88, 0x96, 0x00,
0x5E, 0x88, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x48, 0xC8, 0xDD, 0x80, 0x1C, 0x70, 0xDE, 0x80,
0x5D, 0x88, 0x96, 0x00, 0x5E, 0x88, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x46, 0x08, 0xC5, 0x00,
0x48, 0xC8, 0xC9, 0x2E, 0x0B, 0x70, 0xDD, 0x80, 0x1B, 0xB0, 0xDE, 0x80, 0x5D, 0x88, 0x96, 0x00,
0x5E, 0x88, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x07, 0x70, 0xDD, 0x80, 0x1C, 0x70, 0xDE, 0x80,
0x5D, 0x88, 0x96, 0x00, 0x5E, 0x88, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x0B, 0x70, 0xC5, 0x00,
0x07, 0x70, 0xC7, 0x40, 0x06, 0x30, 0x9B, 0x40, 0x3C, 0xB0, 0xDD, 0x80, 0x5D, 0x88, 0x95, 0x00,
0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08, 0xEF, 0xC0, 0x6F, 0xC8, 0x06, 0xBA, 0x03, 0x9D, 0xCC, 0x2E,
0x3D, 0xF0, 0xDD, 0x80, 0x5D, 0x88, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08, 0xCC, 0x00,
0x3E, 0xF0, 0xDD, 0x80, 0x5D, 0x88, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08, 0xCA, 0x00,
0x3F, 0x30, 0xDD, 0x80, 0x5D, 0x88, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08, 0xCE, 0x40,
0x4A, 0x08, 0x4C, 0x02, 0x03, 0x5C, 0x1F, 0xAF, 0x4E, 0x48, 0x4A, 0x02, 0x03, 0x5C, 0x1F, 0xAF,
0x4E, 0x48, 0x03, 0x59, 0x1F, 0xAF, 0x4C, 0x08, 0xDD, 0x80, 0x1A, 0x70, 0xDE, 0x80, 0x5D, 0x88,
0x96, 0x00, 0x5E, 0x88, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x4A, 0x08, 0xDD, 0x80, 0x1B, 0xB0,
0xDE, 0x80, 0x5D, 0x88, 0x96, 0x00, 0x5E, 0x88, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x4E, 0x48,
0xDD, 0x80, 0x1C, 0x70, 0xDE, 0x80, 0x5D, 0x88, 0x96, 0x00, 0x5E, 0x88, 0x95, 0x00, 0x17, 0x94,
0x17, 0x50, 0x4C, 0x08, 0xCB, 0x40, 0x4A, 0x08, 0xC9, 0x00, 0x4E, 0x48, 0x42, 0xEF, 0x73, 0xF0,
0xDD, 0x80, 0x1A, 0x70, 0xDE, 0x80, 0x5D, 0x88, 0x96, 0x00, 0x5E, 0x88, 0x95, 0x00, 0x17, 0x94,
0x17, 0x50, 0x64, 0x70, 0xDD, 0x80, 0x1B, 0xB0, 0xDE, 0x80, 0x5D, 0x88, 0x96, 0x00, 0x5E, 0x88,
0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x2D, 0xB0, 0xDD, 0x80, 0x1C, 0x70, 0xDE, 0x80, 0x5D, 0x88,
0x96, 0x00, 0x5E, 0x88, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x73, 0xF0, 0xCB, 0x40, 0x64, 0x70,
0xC9, 0x00, 0x2D, 0xB0, 0xCD, 0x40, 0x07, 0x70, 0x9B, 0x40, 0x3C, 0xB0, 0xDD, 0x80, 0x5D, 0x88,
0x95, 0x00, 0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08, 0xEF, 0xC0, 0x6F, 0xC8, 0x07, 0xFA, 0x03, 0x9D,
0x45, 0x2F, 0x3E, 0xF0, 0xDD, 0x80, 0x5D, 0x88, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08,
0xBE, 0x80, 0x3F, 0x30, 0xDD, 0x80, 0x5D, 0x88, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08,
0xC0, 0x80, 0x3E, 0x82, 0x03, 0x5C, 0x83, 0x2F, 0x10, 0xF0, 0x3E, 0x82, 0x03, 0x18, 0x83, 0x2F,
0x40, 0x88, 0x03, 0x59, 0x83, 0x2F, 0x3E, 0x88, 0xDD, 0x80, 0x1B, 0xB0, 0xDE, 0x80, 0x5D, 0x88,
0x96, 0x00, 0x5E, 0x88, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x40, 0x88, 0xDD, 0x80, 0x1C, 0x70,
0xDE, 0x80, 0x5D, 0x88, 0x96, 0x00, 0x5E, 0x88, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x3E, 0x88,
0xBD, 0x80, 0x40, 0x88, 0x9A, 0x6F, 0x0E, 0x70, 0xDD, 0x80, 0x1B, 0xB0, 0xDE, 0x80, 0x5D, 0x88,
0x96, 0x00, 0x5E, 0x88, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x04, 0xF0, 0xDD, 0x80, 0x1C, 0x70,
0xDE, 0x80, 0x5D, 0x88, 0x96, 0x00, 0x5E, 0x88, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x0E, 0x70,
0xBD, 0x80, 0x04, 0xF0, 0xBF, 0xC0, 0x08, 0xF0, 0x9B, 0x40, 0x3C, 0xB0, 0xDD, 0x80, 0x5D, 0x88,
0x95, 0x00, 0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08, 0xEF, 0xC0, 0x6F, 0xC8, 0x08, 0x7A, 0x03, 0x9D,
0x9D, 0xAF, 0x3E, 0xF0, 0xDD, 0x80, 0x5D, 0x88, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08,
0xC2, 0xC0, 0x3F, 0x30, 0xDD, 0x80, 0x5D, 0x88, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08,
0xC4, 0xC0, 0x42, 0xC8, 0x44, 0xC2, 0x03, 0x18, 0xD9, 0xAF, 0x02, 0xF0, 0x44, 0xC2, 0x03, 0x5C,
0xD9, 0xAF, 0x42, 0xC8, 0xDD, 0x80, 0x1B, 0xB0, 0xDE, 0x80, 0x5D, 0x88, 0x96, 0x00, 0x5E, 0x88,
0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x44, 0xC8, 0xDD, 0x80, 0x1C, 0x70, 0xDE, 0x80, 0x5D, 0x88,
0x96, 0x00, 0x5E, 0x88, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x42, 0xC8, 0xC1, 0xC0, 0x44, 0xC8,
0xF0, 0x6F, 0x84, 0x30, 0xDD, 0x80, 0x1B, 0xB0, 0xDE, 0x80, 0x5D, 0x88, 0x96, 0x00, 0x5E, 0x88,
0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x34, 0x70, 0xDD, 0x80, 0x1C, 0x70, 0xDE, 0x80, 0x5D, 0x88,
0x96, 0x00, 0x5E, 0x88, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x84, 0x30, 0xC1, 0xC0, 0x34, 0x70,
0xC3, 0x00, 0x09, 0x30, 0x9B, 0x40, 0x3C, 0xB0, 0xDD, 0x80, 0x5D, 0x88, 0x95, 0x00, 0x97, 0xD4,
0x97, 0x90, 0x0D, 0x08, 0xEF, 0xC0, 0x6F, 0xC8, 0x09, 0xBA, 0x03, 0x59, 0x08, 0x40, 0xF3, 0xEF
};

515
libloragw/src/arb_fw.var Normal file
View File

@ -0,0 +1,515 @@
static uint8_t arb_firmware[8192] = {
0x8A, 0x51, 0xE9, 0xAF, 0x00, 0xB0, 0x8A, 0xC0, 0x04, 0x88, 0x84, 0x0A, 0x82, 0x47, 0x00, 0xF4,
0x01, 0x34, 0x02, 0x34, 0x04, 0x34, 0x08, 0x34, 0x10, 0x34, 0x20, 0x34, 0x40, 0x34, 0x80, 0x34,
0x01, 0x34, 0x02, 0x34, 0x04, 0x34, 0x08, 0x34, 0x10, 0x34, 0x20, 0x34, 0x40, 0x34, 0x80, 0x34,
0x00, 0xF4, 0x00, 0xF4, 0x00, 0xF4, 0x00, 0xF4, 0x00, 0xF4, 0x01, 0x34, 0x03, 0x74, 0x07, 0xB4,
0x0F, 0xF4, 0x1F, 0x34, 0x3F, 0x74, 0x7F, 0xB4, 0xFF, 0xF4, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0x64, 0xC0, 0x80, 0x81, 0x84, 0x0A, 0x04, 0xC6,
0x03, 0x59, 0x00, 0xF4, 0x04, 0xC6, 0xF5, 0xAB, 0x95, 0x41, 0x96, 0x41, 0x97, 0x81, 0x98, 0x01,
0x99, 0x41, 0x9A, 0x41, 0x9B, 0x81, 0x9C, 0x41, 0x9E, 0x81, 0xDC, 0x81, 0x10, 0xF0, 0x5C, 0x42,
0x03, 0x18, 0x25, 0xAC, 0x5C, 0x48, 0x22, 0xFE, 0x84, 0x80, 0x80, 0x81, 0x5C, 0x48, 0xB0, 0x3E,
0x84, 0x80, 0x80, 0x81, 0x5C, 0x48, 0xD0, 0x3E, 0x84, 0x80, 0x80, 0x81, 0x5C, 0x48, 0xA0, 0xFE,
0x84, 0x80, 0x80, 0x81, 0x5C, 0x48, 0xC0, 0xFE, 0x84, 0x80, 0x80, 0x81, 0x5C, 0x48, 0xE0, 0x3E,
0x84, 0x80, 0xFF, 0xB0, 0x80, 0x40, 0xDC, 0xCA, 0x06, 0x6C, 0xC6, 0x41, 0xC7, 0x81, 0xC9, 0x41,
0xC4, 0x01, 0xC8, 0x01, 0x6C, 0x50, 0xEC, 0x90, 0xA1, 0x01, 0xDC, 0x81, 0x40, 0xF0, 0x5C, 0x42,
0x03, 0x18, 0x3A, 0xEC, 0x5C, 0x48, 0xA0, 0xFE, 0x84, 0x80, 0xFF, 0xB0, 0x83, 0xD7, 0x80, 0x40,
0xDC, 0xCA, 0x2E, 0xEC, 0xC5, 0x41, 0x08, 0xF0, 0x45, 0x02, 0x03, 0x18, 0x08, 0x40, 0x45, 0x08,
0x3A, 0x7E, 0x84, 0x80, 0x83, 0x93, 0x80, 0x81, 0x45, 0x08, 0x32, 0x3E, 0x84, 0x80, 0x80, 0x81,
0xC5, 0x8A, 0x3B, 0x2C, 0x01, 0xF0, 0xA0, 0x80, 0x8A, 0x51, 0xFC, 0x63, 0x8A, 0x51, 0x03, 0x30,
0x9E, 0x40, 0x02, 0xF0, 0x9E, 0x40, 0x01, 0xF0, 0x9E, 0x40, 0x9E, 0x81, 0x9B, 0x81, 0xE1, 0x41,
0xE1, 0x8A, 0xE2, 0x00, 0x61, 0x08, 0x96, 0x00, 0x62, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50,
0x2A, 0x70, 0x83, 0x52, 0x03, 0x53, 0xE1, 0x00, 0x61, 0x08, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90,
0x0D, 0x08, 0xDF, 0xC0, 0x5F, 0xC8, 0xF7, 0xFA, 0x03, 0x9D, 0x70, 0xAC, 0xEB, 0xC1, 0x72, 0xEC,
0xEB, 0xC1, 0xEB, 0x0A, 0x6B, 0x88, 0x03, 0x59, 0xE2, 0xEC, 0x01, 0xF0, 0x9B, 0x40, 0x29, 0x70,
0x83, 0x52, 0x03, 0x53, 0xE1, 0x00, 0x61, 0x08, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08,
0xDF, 0xC0, 0x5F, 0x4B, 0x77, 0x6C, 0x28, 0x30, 0x83, 0x52, 0x03, 0x53, 0xE1, 0x00, 0x61, 0x08,
0x95, 0x00, 0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08, 0xCA, 0x00, 0x0D, 0x70, 0x4A, 0x02, 0x03, 0x18,
0xD6, 0x2C, 0x05, 0x30, 0x4A, 0x02, 0xD5, 0x81, 0x03, 0x5C, 0xD7, 0x6C, 0xD5, 0xCA, 0x4A, 0x08,
0xE1, 0x00, 0x01, 0xF0, 0xE2, 0x00, 0x61, 0x08, 0x96, 0x00, 0x62, 0x08, 0x95, 0x00, 0x17, 0x94,
0x17, 0x50, 0x2A, 0x70, 0x83, 0x52, 0x03, 0x53, 0xE1, 0x00, 0x61, 0x08, 0x95, 0x00, 0x97, 0xD4,
0x97, 0x90, 0x0D, 0x08, 0xD6, 0x40, 0x10, 0xF0, 0x56, 0x42, 0x03, 0x5C, 0xB1, 0xEC, 0x10, 0xF0,
0xD6, 0x40, 0xF0, 0xB0, 0xD6, 0x0E, 0xD6, 0xC5, 0x56, 0x48, 0xE1, 0x00, 0x03, 0x30, 0xE2, 0x00,
0x61, 0x08, 0x96, 0x00, 0x62, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x2B, 0xB0, 0x83, 0x52,
0x03, 0x53, 0xE1, 0x00, 0x61, 0x08, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08, 0xE0, 0xC0,
0x01, 0xF0, 0xE0, 0x45, 0x60, 0xC8, 0xE1, 0x00, 0x04, 0xF0, 0xE2, 0x00, 0x61, 0x08, 0x96, 0x00,
0x62, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x9B, 0x81, 0xEB, 0x6C, 0xD5, 0x81, 0xFF, 0xB0,
0xE1, 0x00, 0x01, 0xF0, 0xE2, 0x00, 0x61, 0x08, 0x96, 0x00, 0x62, 0x08, 0x95, 0x00, 0x17, 0x94,
0x17, 0x50, 0xA1, 0xAC, 0x07, 0x70, 0xD5, 0x81, 0xD5, 0xCA, 0xCA, 0x00, 0x30, 0x30, 0xD6, 0x40,
0x01, 0xF0, 0xE0, 0x01, 0x9B, 0x40, 0x0E, 0x58, 0xEB, 0x6C, 0x25, 0x70, 0x83, 0x52, 0x03, 0x53,
0xE1, 0x00, 0x61, 0x08, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08, 0xD7, 0x80, 0x07, 0x70,
0xD7, 0x05, 0x57, 0x88, 0xC5, 0x00, 0x08, 0xF0, 0x45, 0x02, 0x03, 0x18, 0xEB, 0x6C, 0x45, 0x08,
0x8A, 0x51, 0x07, 0xA5, 0x8A, 0x51, 0x0E, 0x58, 0xEB, 0x6C, 0xC5, 0x8A, 0xFB, 0xAC, 0xEA, 0x40,
0xE1, 0x00, 0x00, 0xB0, 0xE2, 0x00, 0x61, 0x08, 0x96, 0x00, 0x62, 0x08, 0x95, 0x00, 0x17, 0x94,
0x17, 0x50, 0x8E, 0xDC, 0x08, 0x40, 0x18, 0x14, 0x05, 0x30, 0xE8, 0x00, 0xE8, 0xCB, 0x16, 0xED,
0x21, 0x30, 0x83, 0x52, 0x03, 0x53, 0xE1, 0x00, 0x61, 0x08, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90,
0x0D, 0x08, 0xC6, 0x00, 0x22, 0x30, 0x83, 0x52, 0x03, 0x53, 0xE1, 0x00, 0x61, 0x08, 0x95, 0x00,
0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08, 0xC7, 0x40, 0x24, 0x30, 0x83, 0x52, 0x03, 0x53, 0xE1, 0x00,
0x61, 0x08, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08, 0xC9, 0x00, 0x20, 0xF0, 0x83, 0x52,
0x03, 0x53, 0xE1, 0x00, 0x61, 0x08, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08, 0xC4, 0xC0,
0x23, 0x70, 0x83, 0x52, 0x03, 0x53, 0xE1, 0x00, 0x61, 0x08, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90,
0x0D, 0x08, 0xC8, 0xC0, 0x18, 0xD0, 0x55, 0xCB, 0x56, 0x2D, 0x48, 0xC8, 0xE8, 0x00, 0xE9, 0x81,
0xE8, 0x1B, 0xE9, 0xC3, 0x4A, 0x46, 0x69, 0x44, 0x03, 0x59, 0x60, 0xAD, 0x0A, 0x30, 0x49, 0x02,
0x03, 0x5C, 0x8D, 0x2D, 0x44, 0xC8, 0x80, 0x7A, 0x78, 0x7E, 0x03, 0x5C, 0x81, 0xAD, 0x8D, 0x2D,
0x44, 0xC8, 0x80, 0x7A, 0x78, 0x7E, 0x03, 0x5C, 0xC4, 0xDB, 0x56, 0x2D, 0x44, 0xC8, 0x3A, 0x7E,
0x84, 0x80, 0x83, 0x93, 0x00, 0x48, 0x01, 0xBE, 0xE8, 0x00, 0x44, 0xC8, 0x3A, 0x7E, 0x84, 0x80,
0x68, 0x08, 0x80, 0x40, 0x44, 0xC8, 0x3A, 0x7E, 0x84, 0x80, 0x00, 0x48, 0xE1, 0x00, 0x44, 0xC8,
0x01, 0xBE, 0xE2, 0x00, 0x61, 0x08, 0x96, 0x00, 0x62, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50,
0x56, 0x2D, 0xC4, 0xDB, 0x8D, 0x2D, 0x48, 0xC8, 0x80, 0x7A, 0x73, 0xBE, 0x03, 0x18, 0x8D, 0x2D,
0x48, 0xC8, 0x80, 0x7A, 0x7B, 0xFE, 0x03, 0x18, 0x8F, 0x6D, 0x6C, 0xD5, 0x30, 0xAE, 0x6C, 0x91,
0x48, 0xC8, 0x11, 0xFE, 0x84, 0x80, 0x8A, 0x51, 0x02, 0xA0, 0x8A, 0x51, 0xC7, 0xC5, 0x48, 0xC8,
0xE8, 0x00, 0x03, 0xD0, 0xE8, 0xCD, 0x03, 0xD0, 0xE8, 0xCD, 0x03, 0xD0, 0xE8, 0xCD, 0x44, 0xC8,
0x68, 0x87, 0xD8, 0x7E, 0xDE, 0x80, 0xA0, 0xFE, 0x84, 0x80, 0x83, 0xD7, 0x00, 0x48, 0xDC, 0x40,
0x3E, 0xF0, 0x83, 0x52, 0x03, 0x53, 0xE1, 0x00, 0x61, 0x08, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90,
0x0D, 0x08, 0xC2, 0xC0, 0x3F, 0x30, 0x83, 0x52, 0x03, 0x53, 0xE1, 0x00, 0x61, 0x08, 0x95, 0x00,
0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08, 0xC3, 0x00, 0x6C, 0x91, 0xDC, 0x81, 0x08, 0xF0, 0x5C, 0x42,
0x03, 0x18, 0xF5, 0xAD, 0x5C, 0x48, 0xE0, 0x3E, 0x84, 0x80, 0x83, 0x93, 0x00, 0x48, 0x5E, 0xC6,
0x03, 0x9D, 0xF1, 0x6D, 0x5C, 0x48, 0x01, 0xBE, 0x84, 0x80, 0x8A, 0x51, 0x02, 0xA0, 0x8A, 0x51,
0x42, 0x05, 0x03, 0x59, 0xF1, 0x6D, 0x46, 0x08, 0xE1, 0x00, 0x5C, 0x48, 0xB0, 0x3E, 0x84, 0x80,
0x00, 0x48, 0xE2, 0x00, 0x5C, 0x48, 0x22, 0xFE, 0x84, 0x80, 0x00, 0x48, 0xE3, 0x40, 0x56, 0x48,
0xE4, 0x00, 0x48, 0xC8, 0xE5, 0x40, 0x47, 0x48, 0x52, 0xE7, 0x8A, 0x51, 0xE8, 0x00, 0x68, 0x4C,
0x03, 0x5C, 0xEE, 0xAD, 0x83, 0x52, 0x03, 0x53, 0x6C, 0xD5, 0xF1, 0x6D, 0x83, 0x52, 0x03, 0x53,
0x6C, 0x91, 0x6C, 0xD9, 0xF5, 0xAD, 0xDC, 0xCA, 0xBE, 0xAD, 0x6C, 0xD9, 0x30, 0xAE, 0x08, 0xF0,
0xDC, 0x40, 0x10, 0xF0, 0x5C, 0x42, 0x03, 0x18, 0x30, 0xAE, 0x5C, 0x48, 0xE0, 0x3E, 0x84, 0x80,
0x83, 0x93, 0x00, 0x48, 0x5E, 0xC6, 0x03, 0x9D, 0x2C, 0xEE, 0x5C, 0x48, 0x01, 0xBE, 0x84, 0x80,
0x8A, 0x51, 0x02, 0xA0, 0x8A, 0x51, 0x43, 0x45, 0x03, 0x59, 0x2C, 0xEE, 0x46, 0x08, 0xE1, 0x00,
0x5C, 0x48, 0xB0, 0x3E, 0x84, 0x80, 0x00, 0x48, 0xE2, 0x00, 0x5C, 0x48, 0x22, 0xFE, 0x84, 0x80,
0x00, 0x48, 0xE3, 0x40, 0x56, 0x48, 0xE4, 0x00, 0x48, 0xC8, 0xE5, 0x40, 0x47, 0x48, 0x52, 0xE7,
0x8A, 0x51, 0xE8, 0x00, 0x68, 0x4C, 0x03, 0x5C, 0x29, 0xEE, 0x83, 0x52, 0x03, 0x53, 0x6C, 0xD5,
0x2C, 0xEE, 0x83, 0x52, 0x03, 0x53, 0x6C, 0x91, 0x6C, 0xD9, 0x30, 0xAE, 0xDC, 0xCA, 0xF9, 0xAD,
0x6C, 0xD9, 0x08, 0x40, 0x48, 0xC8, 0x80, 0x7A, 0x77, 0xFE, 0x03, 0x5C, 0x3B, 0x6E, 0x60, 0xC8,
0x01, 0xBE, 0xDD, 0x80, 0x3D, 0x6E, 0xDD, 0xC1, 0xDD, 0x0A, 0xDB, 0xC1, 0x5D, 0x88, 0x5B, 0x82,
0x03, 0x18, 0x08, 0x40, 0x11, 0x30, 0xDC, 0x40, 0x3C, 0xB0, 0x83, 0x52, 0x03, 0x53, 0xE1, 0x00,
0x61, 0x08, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08, 0xD8, 0x00, 0x3D, 0xF0, 0x83, 0x52,
0x03, 0x53, 0xE1, 0x00, 0x61, 0x08, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08, 0xD9, 0x40,
0x48, 0xC8, 0x80, 0x7A, 0x75, 0xBE, 0x03, 0x5C, 0x65, 0x2E, 0x58, 0x08, 0x80, 0x7A, 0x70, 0x3E,
0x03, 0x5C, 0xD8, 0x1B, 0x75, 0x6E, 0x58, 0x08, 0x6C, 0x2E, 0x59, 0x48, 0x80, 0x7A, 0x70, 0x3E,
0x03, 0x5C, 0xD9, 0x5B, 0x6E, 0x6E, 0x59, 0x48, 0xDC, 0x40, 0x75, 0x6E, 0x58, 0x08, 0x80, 0x7A,
0x70, 0x3E, 0x03, 0x18, 0x75, 0x6E, 0xD8, 0x5F, 0x63, 0x2E, 0x10, 0xF0, 0x5C, 0x42, 0x03, 0x18,
0x50, 0xEF, 0x5C, 0x48, 0x22, 0xFE, 0x84, 0x80, 0x46, 0x08, 0x83, 0x93, 0x80, 0x40, 0x5C, 0x48,
0xB0, 0x3E, 0x84, 0x80, 0x47, 0x48, 0x80, 0x40, 0x5C, 0x48, 0xD0, 0x3E, 0x84, 0x80, 0x49, 0x08,
0x80, 0x40, 0x5C, 0x48, 0xA0, 0xFE, 0x84, 0x80, 0x44, 0xC8, 0x80, 0x40, 0x5C, 0x48, 0xC0, 0xFE,
0x84, 0x80, 0x48, 0xC8, 0x80, 0x40, 0x5C, 0x48, 0xE0, 0x3E, 0x84, 0x80, 0x5E, 0x88, 0x80, 0x40,
0x5E, 0x88, 0xA0, 0xFE, 0x84, 0x80, 0x5C, 0x48, 0x83, 0xD7, 0x80, 0x40, 0x48, 0xC8, 0xE1, 0x00,
0x17, 0xB0, 0xE2, 0x00, 0x61, 0x08, 0x96, 0x00, 0x62, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50,
0x44, 0xC8, 0xE1, 0x00, 0x18, 0x30, 0xE2, 0x00, 0x61, 0x08, 0x96, 0x00, 0x62, 0x08, 0x95, 0x00,
0x17, 0x94, 0x17, 0x50, 0x46, 0x08, 0xE1, 0x00, 0x19, 0x70, 0xE2, 0x00, 0x61, 0x08, 0x96, 0x00,
0x62, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x47, 0x48, 0xE1, 0x00, 0x1A, 0x70, 0xE2, 0x00,
0x61, 0x08, 0x96, 0x00, 0x62, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x5C, 0x48, 0xE1, 0x00,
0x16, 0x70, 0xE2, 0x00, 0x61, 0x08, 0x96, 0x00, 0x62, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50,
0x49, 0x08, 0xE1, 0x00, 0x1B, 0xB0, 0xE2, 0x00, 0x61, 0x08, 0x96, 0x00, 0x62, 0x08, 0x95, 0x00,
0x17, 0x94, 0x17, 0x50, 0x5B, 0x88, 0xE1, 0x00, 0x11, 0x30, 0xE2, 0x00, 0x61, 0x08, 0x96, 0x00,
0x62, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x08, 0xF0, 0x5C, 0x42, 0x5C, 0x48, 0x03, 0x18,
0x12, 0xEF, 0x01, 0xBE, 0x84, 0x80, 0x8A, 0x51, 0x02, 0xA0, 0x8A, 0x51, 0xE1, 0x00, 0x12, 0x30,
0xE2, 0x00, 0x61, 0x08, 0x96, 0x00, 0x62, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x05, 0x30,
0xE8, 0x00, 0xE8, 0xCB, 0xF9, 0xAE, 0x83, 0x52, 0x12, 0x30, 0x03, 0x53, 0xE1, 0x41, 0xE2, 0x00,
0x61, 0x08, 0x96, 0x00, 0x62, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x55, 0xCB, 0x50, 0xEF,
0x48, 0xC8, 0xE8, 0x00, 0xE9, 0x81, 0xE8, 0x1B, 0xE9, 0xC3, 0x4A, 0x46, 0x69, 0x44, 0x03, 0x59,
0x30, 0xEF, 0x50, 0xEF, 0x01, 0xBE, 0x84, 0x80, 0x8A, 0x51, 0x02, 0xA0, 0x8A, 0x51, 0xE1, 0x00,
0x13, 0x70, 0xE2, 0x00, 0x61, 0x08, 0x96, 0x00, 0x62, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50,
0x05, 0x30, 0xE8, 0x00, 0xE8, 0xCB, 0x22, 0xEF, 0x83, 0x52, 0x13, 0x70, 0x03, 0x53, 0xE1, 0x41,
0xE2, 0x00, 0x61, 0x08, 0x96, 0x00, 0x62, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x06, 0xEF,
0x44, 0xC8, 0x80, 0x7A, 0x78, 0x7E, 0x03, 0x5C, 0xC4, 0xDB, 0x50, 0xEF, 0x44, 0xC8, 0x32, 0x3E,
0x84, 0x80, 0x83, 0x93, 0x00, 0x48, 0x01, 0xBE, 0xE8, 0x00, 0x44, 0xC8, 0x32, 0x3E, 0x84, 0x80,
0x68, 0x08, 0x80, 0x40, 0x44, 0xC8, 0x32, 0x3E, 0x84, 0x80, 0x00, 0x48, 0xE1, 0x00, 0x44, 0xC8,
0x09, 0xFE, 0xE2, 0x00, 0x61, 0x08, 0x96, 0x00, 0x62, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50,
0xDB, 0x0A, 0x3E, 0x6E, 0xE7, 0x80, 0x62, 0x02, 0x62, 0x08, 0x03, 0x18, 0x9D, 0xAF, 0x67, 0x82,
0xD0, 0xC0, 0x63, 0x48, 0x61, 0x02, 0x03, 0x5C, 0x61, 0x2F, 0x63, 0x48, 0x61, 0x02, 0xCC, 0x00,
0x68, 0x2F, 0x61, 0x08, 0x63, 0x42, 0xCC, 0x00, 0xFF, 0xB0, 0xD0, 0x87, 0xCC, 0x86, 0xCC, 0x8A,
0x65, 0x48, 0x11, 0xFE, 0x84, 0x80, 0x8A, 0x51, 0x02, 0xA0, 0x8A, 0x51, 0xE6, 0x40, 0x67, 0x88,
0x66, 0x42, 0xD2, 0x00, 0x61, 0x49, 0xCE, 0x40, 0x62, 0x08, 0x52, 0x87, 0xD1, 0x00, 0x4E, 0x89,
0xE6, 0x40, 0x63, 0x48, 0x66, 0x42, 0x03, 0x5C, 0x80, 0xAF, 0x63, 0x48, 0x4E, 0xC7, 0x84, 0xEF,
0xD1, 0x8A, 0x4E, 0x89, 0xCD, 0x40, 0x63, 0x42, 0xCD, 0x40, 0x50, 0xC8, 0x51, 0x02, 0x03, 0x5C,
0xD3, 0xAF, 0x51, 0x08, 0x50, 0xC2, 0x03, 0x5C, 0x91, 0x2F, 0x4D, 0x48, 0x4C, 0x02, 0x03, 0x18,
0xD3, 0xAF, 0xDA, 0x81, 0x5A, 0x48, 0x03, 0x59, 0x99, 0x6F, 0x51, 0x08, 0xCF, 0x80, 0x4D, 0x48,
0xE0, 0x2F, 0x50, 0xC8, 0xCF, 0x80, 0x4C, 0x08, 0xE0, 0x2F, 0x67, 0x82, 0x03, 0x18, 0xD6, 0xAF,
0x67, 0x88, 0x62, 0x02, 0xD0, 0xC0, 0x61, 0x08, 0x63, 0x42, 0x03, 0x5C, 0xAB, 0xAF, 0x61, 0x08,
0x63, 0x42, 0xCC, 0x00, 0xB2, 0x6F, 0x63, 0x48, 0x61, 0x02, 0xCC, 0x00, 0xFF, 0xB0, 0xD0, 0x87,
0xCC, 0x86, 0xCC, 0x8A, 0x65, 0x48, 0x11, 0xFE, 0x84, 0x80, 0x8A, 0x51, 0x02, 0xA0, 0x8A, 0x51,
0xE6, 0x40, 0x62, 0x08, 0x66, 0x42, 0xD2, 0x00, 0x63, 0x89, 0xCE, 0x40, 0x67, 0x88, 0x52, 0x87,
0xD1, 0x00, 0x4E, 0x89, 0xE6, 0x40, 0x61, 0x08, 0x66, 0x42, 0x03, 0x5C, 0xCA, 0x6F, 0x61, 0x08,
0x4E, 0xC7, 0xCE, 0xAF, 0xD1, 0x8A, 0x4E, 0x89, 0xCD, 0x40, 0x61, 0x02, 0xCD, 0x40, 0x50, 0xC8,
0x51, 0x02, 0x03, 0x18, 0x89, 0x2F, 0xDA, 0x81, 0xDA, 0xCA, 0x92, 0x2F, 0xCF, 0xC1, 0x63, 0x48,
0x61, 0x02, 0x03, 0x5C, 0xDE, 0xEF, 0x63, 0x48, 0x61, 0x02, 0xE0, 0x2F, 0x61, 0x08, 0x63, 0x42,
0xCB, 0x40, 0xCF, 0xC8, 0x03, 0x9D, 0x00, 0xF4, 0x64, 0x08, 0x4B, 0x42, 0x03, 0x5C, 0x01, 0x34,
0x00, 0xF4, 0xEC, 0x81, 0x83, 0x93, 0x22, 0x30, 0x84, 0x80, 0x61, 0x70, 0x8A, 0x51, 0xF4, 0x23,
0x8A, 0x51, 0xA0, 0x30, 0x84, 0x80, 0xF0, 0xB0, 0x8A, 0x51, 0xF4, 0x23, 0x8A, 0x51, 0x83, 0xD7,
0xA0, 0x30, 0x84, 0x80, 0xE0, 0x70, 0x8A, 0x51, 0xF4, 0x23, 0x83, 0x01, 0x8A, 0x51, 0x4A, 0xAC,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF
};

515
libloragw/src/cal_fw.var Normal file
View File

@ -0,0 +1,515 @@
static uint8_t cal_firmware_sx125x[8192] = {
0x8A, 0x51, 0xF0, 0x6F, 0x00, 0xB0, 0x8A, 0xC0, 0x04, 0x88, 0x84, 0x0A, 0x82, 0x47, 0x00, 0xF4,
0x40, 0x34, 0x2B, 0xF4, 0x1C, 0xB4, 0x13, 0xB4, 0x0D, 0xB4, 0x08, 0x34, 0x06, 0x74, 0x04, 0x34,
0x02, 0x34, 0x10, 0x34, 0x0B, 0xB4, 0x07, 0xB4, 0x05, 0x74, 0x03, 0x74, 0x02, 0x34, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0x64, 0xC0,
0x80, 0x81, 0x84, 0x0A, 0x04, 0xC6, 0x03, 0x59, 0x00, 0xF4, 0x04, 0xC6, 0x30, 0x69, 0xB1, 0x00,
0x04, 0xF0, 0xA2, 0xC0, 0x10, 0xF0, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00,
0x17, 0x94, 0x17, 0x50, 0x2A, 0x08, 0xA2, 0xC0, 0x14, 0x30, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00,
0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x2B, 0x48, 0xA2, 0xC0, 0x15, 0x70, 0xA3, 0x00,
0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x2C, 0x08, 0xA2, 0xC0,
0x11, 0x30, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50,
0x2D, 0x48, 0xA2, 0xC0, 0x12, 0x30, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00,
0x17, 0x94, 0x17, 0x50, 0x29, 0x08, 0xA2, 0xC0, 0x13, 0x70, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00,
0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x31, 0x58, 0xB5, 0x29, 0x2E, 0x48, 0xB0, 0xC0,
0x01, 0xF0, 0xD7, 0xA7, 0x8A, 0x51, 0x83, 0x52, 0x03, 0x53, 0xA1, 0xC0, 0x21, 0xC8, 0x95, 0x00,
0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08, 0xC3, 0x39, 0x30, 0xC4, 0xA2, 0xC0, 0x01, 0xF0, 0xA3, 0x00,
0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x01, 0xF0, 0x83, 0x52,
0x03, 0x53, 0xA1, 0xC0, 0x21, 0xC8, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08, 0xD3, 0x67,
0x8A, 0x51, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50,
0x01, 0xF0, 0x83, 0x52, 0x03, 0x53, 0xA1, 0xC0, 0x21, 0xC8, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90,
0x0D, 0x08, 0xA2, 0xC0, 0x01, 0xF0, 0xA2, 0x10, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08,
0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0xB4, 0x41, 0x1B, 0xEA, 0x2E, 0x48, 0xB0, 0xC0, 0x02, 0xF0,
0xD7, 0xA7, 0x8A, 0x51, 0x83, 0x52, 0x03, 0x53, 0xA1, 0xC0, 0x21, 0xC8, 0x95, 0x00, 0x97, 0xD4,
0x97, 0x90, 0x0D, 0x08, 0xC3, 0x39, 0x30, 0xC4, 0xA2, 0xC0, 0x02, 0xF0, 0xA3, 0x00, 0x22, 0xC8,
0x96, 0x00, 0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x02, 0xF0, 0x83, 0x52, 0x03, 0x53,
0xA1, 0xC0, 0x21, 0xC8, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08, 0xCF, 0xA7, 0x8A, 0x51,
0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x02, 0xF0,
0x83, 0x52, 0x03, 0x53, 0xA1, 0xC0, 0x21, 0xC8, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08,
0xA2, 0xC0, 0x02, 0xF0, 0xA2, 0x10, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00,
0x17, 0x94, 0x17, 0x50, 0xB3, 0x29, 0x2F, 0x88, 0xA4, 0xC0, 0x32, 0x70, 0x2D, 0x27, 0x8A, 0x51,
0x32, 0x08, 0xA2, 0xC0, 0x1B, 0xB0, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00,
0x17, 0x94, 0x17, 0x50, 0x33, 0x48, 0xA2, 0xC0, 0x1C, 0x70, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00,
0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x07, 0x70, 0x9B, 0x40, 0x3C, 0xB0, 0x83, 0x52,
0x03, 0x53, 0xA1, 0xC0, 0x21, 0xC8, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08, 0xB4, 0x00,
0x06, 0xBA, 0x03, 0x59, 0x0E, 0xAA, 0x34, 0x08, 0x07, 0xFA, 0x03, 0x59, 0x2F, 0x2A, 0x06, 0x30,
0x9B, 0x40, 0x3C, 0xB0, 0x83, 0x52, 0x03, 0x53, 0xA1, 0xC0, 0x21, 0xC8, 0x95, 0x00, 0x97, 0xD4,
0x97, 0x90, 0x0D, 0x08, 0xB4, 0x00, 0x06, 0xBA, 0x03, 0x59, 0xF3, 0x69, 0x21, 0x6A, 0x10, 0xF0,
0xA2, 0x01, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50,
0x08, 0x40, 0xB3, 0x40, 0x04, 0xF0, 0xA2, 0xC0, 0x10, 0xF0, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00,
0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x2D, 0x48, 0xA2, 0xC0, 0x14, 0x30, 0xA3, 0x00,
0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x2E, 0x48, 0xA2, 0xC0,
0x15, 0x70, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50,
0x2C, 0x08, 0xA2, 0xC0, 0x13, 0x70, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00,
0x17, 0x94, 0x17, 0x50, 0x30, 0xC8, 0x20, 0x38, 0xD5, 0x40, 0xA2, 0xC0, 0x18, 0x30, 0xA3, 0x00,
0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x01, 0xF0, 0xB9, 0x81,
0xC2, 0x01, 0x84, 0x80, 0x8A, 0x51, 0x02, 0xA0, 0x8A, 0x51, 0xFF, 0x3A, 0x01, 0xBE, 0xBA, 0x40,
0x01, 0xF0, 0x84, 0x80, 0x8A, 0x51, 0x02, 0xA0, 0x8A, 0x51, 0xFF, 0x3A, 0x01, 0xBE, 0xC3, 0x00,
0x01, 0xF0, 0x84, 0x80, 0x8A, 0x51, 0x02, 0xA0, 0x8A, 0x51, 0xFF, 0x3A, 0x01, 0xBE, 0xBB, 0x80,
0x01, 0xF0, 0x84, 0x80, 0x8A, 0x51, 0x02, 0xA0, 0x8A, 0x51, 0xC4, 0xC0, 0x01, 0xF0, 0x84, 0x80,
0x8A, 0x51, 0x02, 0xA0, 0x8A, 0x51, 0xBC, 0x40, 0x01, 0xF0, 0x84, 0x80, 0x8A, 0x51, 0x02, 0xA0,
0x8A, 0x51, 0xFF, 0x3A, 0x01, 0xBE, 0xC5, 0x00, 0x01, 0xF0, 0x84, 0x80, 0x8A, 0x51, 0x02, 0xA0,
0x8A, 0x51, 0xBD, 0x80, 0x01, 0xF0, 0x84, 0x80, 0x8A, 0x51, 0x02, 0xA0, 0x8A, 0x51, 0xC6, 0x00,
0x0B, 0x70, 0xD7, 0x80, 0x57, 0x88, 0xB4, 0x00, 0x33, 0x98, 0x06, 0xAB, 0x57, 0x88, 0xB1, 0x00,
0x01, 0xF0, 0xE1, 0x27, 0x8A, 0x51, 0x83, 0x52, 0x03, 0x53, 0xA1, 0xC0, 0x21, 0xC8, 0x95, 0x00,
0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08, 0xC3, 0x39, 0x31, 0x04, 0xA2, 0xC0, 0x01, 0xF0, 0xA3, 0x00,
0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x01, 0xF0, 0x83, 0x52,
0x03, 0x53, 0xA1, 0xC0, 0x21, 0xC8, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08, 0xD3, 0x67,
0x8A, 0x51, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50,
0x01, 0xF0, 0x83, 0x52, 0x03, 0x53, 0xA1, 0xC0, 0x21, 0xC8, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90,
0x0D, 0x08, 0xA2, 0xC0, 0x01, 0xF0, 0xA2, 0x10, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08,
0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x39, 0x48, 0xA2, 0xC0, 0x11, 0x30, 0xA3, 0x00, 0x22, 0xC8,
0x96, 0x00, 0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x42, 0xC8, 0xA2, 0xC0, 0x12, 0x30,
0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x30, 0xC8,
0xA4, 0xC0, 0x4B, 0xB0, 0x2D, 0x27, 0x8A, 0x51, 0xD6, 0x81, 0x69, 0x2B, 0x57, 0x88, 0xB1, 0x00,
0x02, 0xF0, 0xE1, 0x27, 0x8A, 0x51, 0x83, 0x52, 0x03, 0x53, 0xA1, 0xC0, 0x21, 0xC8, 0x95, 0x00,
0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08, 0xC3, 0x39, 0x31, 0x04, 0xA2, 0xC0, 0x02, 0xF0, 0xA3, 0x00,
0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x02, 0xF0, 0x83, 0x52,
0x03, 0x53, 0xA1, 0xC0, 0x21, 0xC8, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08, 0xCF, 0xA7,
0x8A, 0x51, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50,
0x02, 0xF0, 0x83, 0x52, 0x03, 0x53, 0xA1, 0xC0, 0x21, 0xC8, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90,
0x0D, 0x08, 0xA2, 0xC0, 0x02, 0xF0, 0xA2, 0x10, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08,
0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0xEB, 0x6A, 0x56, 0x48, 0xCA, 0x27, 0x8A, 0x51, 0xA3, 0x00,
0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0xB1, 0x27, 0x8A, 0x51,
0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x30, 0xC8,
0xA4, 0xC0, 0x51, 0x70, 0x2D, 0x27, 0x8A, 0x51, 0x4B, 0xB0, 0xA1, 0xC0, 0x51, 0x70, 0x79, 0x67,
0x8A, 0x51, 0xCD, 0x40, 0x4D, 0x48, 0x03, 0x59, 0x69, 0x2B, 0x51, 0x08, 0xCB, 0x40, 0x52, 0x08,
0xCC, 0x00, 0x05, 0x30, 0xD6, 0xCA, 0x56, 0x42, 0x03, 0x5C, 0x44, 0xAB, 0x2F, 0x88, 0xD1, 0x00,
0x51, 0x70, 0xD2, 0x41, 0xA1, 0xC0, 0x4B, 0xB0, 0x79, 0x67, 0x8A, 0x51, 0xCD, 0x40, 0x4D, 0x48,
0x03, 0x9D, 0x7F, 0xEB, 0x07, 0x70, 0xD7, 0x03, 0x57, 0x82, 0x03, 0x18, 0xAA, 0xEA, 0x4B, 0x48,
0xB7, 0x80, 0x4C, 0x08, 0xB8, 0x00, 0xD3, 0x81, 0xD4, 0x41, 0xD7, 0xC1, 0x53, 0x48, 0xB9, 0x40,
0x54, 0x08, 0xC2, 0xC0, 0x57, 0x88, 0x01, 0xBE, 0x84, 0x80, 0x8A, 0x51, 0x02, 0xA0, 0x8A, 0x51,
0xA1, 0xC0, 0x53, 0x48, 0xDD, 0x66, 0x83, 0x52, 0x03, 0x53, 0xBA, 0x40, 0x57, 0x88, 0x01, 0xBE,
0x84, 0x80, 0x8A, 0x51, 0x02, 0xA0, 0x8A, 0x51, 0xA1, 0xC0, 0x54, 0x08, 0xDD, 0x66, 0x83, 0x52,
0x03, 0x53, 0xC3, 0x00, 0x57, 0x88, 0x01, 0xBE, 0x84, 0x80, 0x8A, 0x51, 0x02, 0xA0, 0x8A, 0x51,
0xA1, 0xC0, 0x53, 0x48, 0xDD, 0x66, 0x83, 0x52, 0x03, 0x53, 0xBB, 0x80, 0x57, 0x88, 0x01, 0xBE,
0x84, 0x80, 0x8A, 0x51, 0x02, 0xA0, 0x8A, 0x51, 0xEB, 0xA7, 0x8A, 0x51, 0xDD, 0x66, 0x83, 0x52,
0x03, 0x53, 0xC4, 0xC0, 0x57, 0x88, 0x01, 0xBE, 0x84, 0x80, 0x8A, 0x51, 0x02, 0xA0, 0x8A, 0x51,
0xDC, 0x67, 0x8A, 0x51, 0xDD, 0x66, 0x83, 0x52, 0x03, 0x53, 0xBC, 0x40, 0x57, 0x88, 0x01, 0xBE,
0x84, 0x80, 0x8A, 0x51, 0x02, 0xA0, 0x8A, 0x51, 0xA1, 0xC0, 0x54, 0x08, 0xDD, 0x66, 0x83, 0x52,
0x03, 0x53, 0xC5, 0x00, 0x57, 0x88, 0x01, 0xBE, 0x84, 0x80, 0x8A, 0x51, 0x02, 0xA0, 0x8A, 0x51,
0xDC, 0x67, 0x8A, 0x51, 0xDD, 0x66, 0x83, 0x52, 0x03, 0x53, 0xBD, 0x80, 0x57, 0x88, 0x01, 0xBE,
0x84, 0x80, 0x8A, 0x51, 0x02, 0xA0, 0x8A, 0x51, 0xEB, 0xA7, 0x8A, 0x51, 0xDD, 0x66, 0x8A, 0x51,
0x83, 0x52, 0x03, 0x53, 0xC6, 0x00, 0x39, 0x48, 0xA2, 0xC0, 0x11, 0x30, 0xA3, 0x00, 0x22, 0xC8,
0x96, 0x00, 0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x42, 0xC8, 0xA2, 0xC0, 0x12, 0x30,
0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x30, 0xC8,
0xA4, 0xC0, 0x4F, 0xF0, 0x2D, 0x27, 0x8A, 0x51, 0x05, 0x30, 0xCE, 0x81, 0xA1, 0xC0, 0x57, 0x88,
0x96, 0x27, 0x8A, 0x51, 0xA0, 0xFE, 0x84, 0x80, 0x4F, 0x88, 0xE6, 0x67, 0x8A, 0x51, 0x96, 0x27,
0x8A, 0x51, 0xA0, 0xFE, 0x84, 0x80, 0x50, 0xC8, 0x83, 0xD7, 0x80, 0x40, 0xD6, 0x81, 0x05, 0x30,
0xD6, 0xCA, 0x56, 0x42, 0x03, 0x18, 0x5A, 0xEC, 0x56, 0x48, 0x39, 0x7E, 0x84, 0x80, 0x83, 0x93,
0x00, 0x48, 0xA2, 0xC0, 0x11, 0x30, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00,
0x17, 0x94, 0x17, 0x50, 0xB1, 0x27, 0x8A, 0x51, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08,
0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x30, 0xC8, 0xA4, 0xC0, 0x51, 0x70, 0x2D, 0x27, 0x8A, 0x51,
0x51, 0x70, 0xA1, 0xC0, 0x4F, 0xF0, 0x79, 0x67, 0x8A, 0x51, 0xCD, 0x40, 0x4D, 0x48, 0x03, 0x59,
0x44, 0x6C, 0x51, 0x08, 0xBE, 0xA7, 0x8A, 0x51, 0x05, 0x30, 0xA1, 0xC0, 0x57, 0x88, 0x96, 0x27,
0x8A, 0x51, 0xA0, 0xFE, 0x56, 0xC7, 0xB1, 0x00, 0x84, 0x80, 0x51, 0x08, 0xE6, 0x67, 0x8A, 0x51,
0x96, 0x27, 0x8A, 0x51, 0xA0, 0xFE, 0x56, 0xC7, 0xB1, 0x00, 0x84, 0x80, 0x52, 0x08, 0x83, 0xD7,
0x80, 0x40, 0x17, 0xEC, 0x57, 0x88, 0xE0, 0x3E, 0x84, 0x80, 0x4E, 0x48, 0x83, 0x93, 0x80, 0x40,
0x4E, 0x48, 0x39, 0x7E, 0x84, 0x80, 0x00, 0x48, 0xD3, 0x40, 0xC4, 0xE7, 0x8A, 0x51, 0x09, 0x30,
0xD7, 0x0A, 0x57, 0x82, 0x03, 0x5C, 0x86, 0xEB, 0xA1, 0x01, 0xA1, 0x43, 0x53, 0x48, 0xDD, 0x66,
0x8A, 0x51, 0x83, 0x52, 0x03, 0x53, 0xB9, 0x40, 0xA1, 0x01, 0xA1, 0x43, 0x54, 0x08, 0xDD, 0x66,
0x8A, 0x51, 0x83, 0x52, 0x03, 0x53, 0xC2, 0xC0, 0xA1, 0x01, 0xA1, 0x43, 0x53, 0x48, 0xDD, 0x66,
0x8A, 0x51, 0x83, 0x52, 0x03, 0x53, 0xBA, 0x40, 0x54, 0x08, 0xC3, 0x00, 0xA1, 0x01, 0xA1, 0x43,
0x53, 0x48, 0xDD, 0x66, 0x8A, 0x51, 0x83, 0x52, 0x03, 0x53, 0xBB, 0x80, 0xA1, 0x01, 0xA1, 0x4A,
0x54, 0x08, 0xDD, 0x66, 0x8A, 0x51, 0x83, 0x52, 0x03, 0x53, 0xC4, 0xC0, 0x53, 0x48, 0xBC, 0x40,
0xA1, 0x01, 0xA1, 0x43, 0x54, 0x08, 0xDD, 0x66, 0x8A, 0x51, 0x83, 0x52, 0x03, 0x53, 0xC5, 0x00,
0x53, 0x48, 0xBD, 0x80, 0x54, 0x08, 0xC6, 0x00, 0x53, 0x48, 0xBE, 0x80, 0xA1, 0x01, 0xA1, 0x4A,
0x54, 0x08, 0xDD, 0x66, 0x8A, 0x51, 0x83, 0x52, 0x03, 0x53, 0xC7, 0x40, 0xA1, 0x01, 0xA1, 0x4A,
0x53, 0x48, 0xDD, 0x66, 0x8A, 0x51, 0x83, 0x52, 0x03, 0x53, 0xBF, 0xC0, 0xA1, 0x01, 0xA1, 0x43,
0x54, 0x08, 0xDD, 0x66, 0x8A, 0x51, 0x83, 0x52, 0x03, 0x53, 0xC8, 0xC0, 0xA1, 0x01, 0xA1, 0x4A,
0x53, 0x48, 0xDD, 0x66, 0x8A, 0x51, 0x83, 0x52, 0x03, 0x53, 0xC0, 0x80, 0x54, 0x08, 0xC9, 0x00,
0xA1, 0x01, 0xA1, 0x4A, 0x53, 0x48, 0xDD, 0x66, 0x8A, 0x51, 0x83, 0x52, 0x03, 0x53, 0xC1, 0xC0,
0xA1, 0x01, 0xA1, 0x4A, 0x54, 0x08, 0xDD, 0x66, 0x8A, 0x51, 0x83, 0x52, 0x03, 0x53, 0xCA, 0x00,
0xCE, 0x81, 0x39, 0x48, 0xA2, 0xC0, 0x11, 0x30, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08,
0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x42, 0xC8, 0xA2, 0xC0, 0x12, 0x30, 0xA3, 0x00, 0x22, 0xC8,
0x96, 0x00, 0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x30, 0xC8, 0xA4, 0xC0, 0x4F, 0xF0,
0x2D, 0x27, 0x8A, 0x51, 0xD6, 0x81, 0x09, 0x30, 0xD6, 0xCA, 0x56, 0x42, 0x03, 0x18, 0x1D, 0x2D,
0x56, 0x48, 0xCA, 0x27, 0x8A, 0x51, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00,
0x17, 0x94, 0x17, 0x50, 0xB1, 0x27, 0x8A, 0x51, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08,
0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x30, 0xC8, 0xA4, 0xC0, 0x51, 0x70, 0x2D, 0x27, 0x8A, 0x51,
0x51, 0x70, 0xA1, 0xC0, 0x4F, 0xF0, 0x79, 0x67, 0x8A, 0x51, 0xCD, 0x40, 0x4D, 0x48, 0x03, 0x59,
0xF3, 0x6C, 0x51, 0x08, 0xBE, 0xA7, 0x8A, 0x51, 0xF3, 0x6C, 0x4E, 0x48, 0x39, 0x7E, 0x84, 0x80,
0x00, 0x48, 0xD3, 0x40, 0xC4, 0xE7, 0x8A, 0x51, 0x53, 0x48, 0xA2, 0xC0, 0x11, 0x30, 0xA3, 0x00,
0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x54, 0x08, 0xA2, 0xC0,
0x12, 0x30, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50,
0x4F, 0x88, 0xB5, 0x40, 0x50, 0xC8, 0xB6, 0x40, 0x2F, 0x88, 0xA2, 0xC0, 0x19, 0x70, 0xA3, 0x00,
0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x34, 0x08, 0xA2, 0xC0,
0x1A, 0x70, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50,
0x37, 0x88, 0xA2, 0xC0, 0x1B, 0xB0, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00,
0x17, 0x94, 0x17, 0x50, 0x38, 0x08, 0xA2, 0xC0, 0x1C, 0x70, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00,
0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x06, 0x30, 0x9B, 0x40, 0x3C, 0xB0, 0x83, 0x52,
0x03, 0x53, 0xA1, 0xC0, 0x21, 0xC8, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08, 0xD5, 0x40,
0x9E, 0x40, 0x55, 0x48, 0x06, 0xBA, 0x03, 0x9D, 0x66, 0x2D, 0x35, 0x48, 0xA2, 0xC0, 0x19, 0x70,
0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x36, 0x48,
0xA2, 0xC0, 0x1A, 0x70, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00, 0x17, 0x94,
0x17, 0x50, 0x53, 0x48, 0xA2, 0xC0, 0x1B, 0xB0, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08,
0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x54, 0x08, 0xA2, 0xC0, 0x1C, 0x70, 0xA3, 0x00, 0x22, 0xC8,
0x96, 0x00, 0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x07, 0x70, 0x9B, 0x40, 0x3C, 0xB0,
0x83, 0x52, 0x03, 0x53, 0xA1, 0xC0, 0x21, 0xC8, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08,
0xD5, 0x40, 0x9E, 0x40, 0x55, 0x48, 0x07, 0xFA, 0x03, 0x9D, 0x9F, 0xAD, 0x83, 0x96, 0x60, 0xC8,
0x83, 0x52, 0xA2, 0xC0, 0x19, 0x70, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00,
0x17, 0x94, 0x17, 0x50, 0x83, 0x96, 0x61, 0x08, 0x83, 0x52, 0xA2, 0xC0, 0x1A, 0x70, 0xA3, 0x00,
0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x83, 0x96, 0x62, 0x08,
0x83, 0x52, 0xA2, 0xC0, 0x1B, 0xB0, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00,
0x17, 0x94, 0x17, 0x50, 0x83, 0x96, 0x63, 0x48, 0x83, 0x52, 0xA2, 0xC0, 0x1C, 0x70, 0xA3, 0x00,
0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x08, 0xF0, 0x9B, 0x40,
0x3C, 0xB0, 0x83, 0x52, 0x03, 0x53, 0xA1, 0xC0, 0x21, 0xC8, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90,
0x0D, 0x08, 0xD5, 0x40, 0x9E, 0x40, 0x55, 0x48, 0x08, 0x7A, 0x03, 0x9D, 0xE0, 0xED, 0x83, 0x96,
0x64, 0x08, 0x83, 0x52, 0xA2, 0xC0, 0x19, 0x70, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08,
0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x83, 0x96, 0x65, 0x48, 0x83, 0x52, 0xA2, 0xC0, 0x1A, 0x70,
0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x83, 0x96,
0x66, 0x48, 0x83, 0x52, 0xA2, 0xC0, 0x1B, 0xB0, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08,
0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x83, 0x96, 0x67, 0x88, 0x83, 0x52, 0xA2, 0xC0, 0x1C, 0x70,
0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x09, 0x30,
0x9B, 0x40, 0x3C, 0xB0, 0x83, 0x52, 0x03, 0x53, 0xA1, 0xC0, 0x21, 0xC8, 0x95, 0x00, 0x97, 0xD4,
0x97, 0x90, 0x0D, 0x08, 0xD5, 0x40, 0x9E, 0x40, 0x55, 0x48, 0x09, 0xBA, 0x03, 0x9D, 0x21, 0xAE,
0x83, 0x96, 0x68, 0x08, 0x83, 0x52, 0xA2, 0xC0, 0x19, 0x70, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00,
0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x83, 0x96, 0x69, 0x48, 0x83, 0x52, 0xA2, 0xC0,
0x1A, 0x70, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50,
0x83, 0x96, 0x6A, 0x48, 0x83, 0x52, 0xA2, 0xC0, 0x1B, 0xB0, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00,
0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x83, 0x96, 0x6B, 0x88, 0x83, 0x52, 0xA2, 0xC0,
0x1C, 0x70, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50,
0x0A, 0x30, 0x9B, 0x40, 0x3C, 0xB0, 0x83, 0x52, 0x03, 0x53, 0xA1, 0xC0, 0x21, 0xC8, 0x95, 0x00,
0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08, 0xD5, 0x40, 0x9E, 0x40, 0x55, 0x48, 0x0A, 0xBA, 0x03, 0x9D,
0x62, 0xEE, 0xD7, 0xC1, 0x03, 0xD0, 0x57, 0x0D, 0xA0, 0xFE, 0x84, 0x80, 0x83, 0x93, 0x00, 0x48,
0xA2, 0xC0, 0x19, 0x70, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00, 0x17, 0x94,
0x17, 0x50, 0x03, 0xD0, 0x57, 0x0D, 0xA0, 0xFE, 0x84, 0x80, 0x83, 0xD7, 0x00, 0x48, 0xA2, 0xC0,
0x1A, 0x70, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50,
0x03, 0xD0, 0x57, 0x0D, 0xA1, 0x3E, 0x84, 0x80, 0x83, 0x93, 0x00, 0x48, 0xA2, 0xC0, 0x1B, 0xB0,
0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x03, 0xD0,
0x57, 0x0D, 0xA1, 0x3E, 0x84, 0x80, 0x83, 0xD7, 0x00, 0x48, 0xA2, 0xC0, 0x1C, 0x70, 0xA3, 0x00,
0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x57, 0x88, 0x0C, 0xFE,
0x9B, 0x40, 0x3C, 0xB0, 0x83, 0x52, 0x03, 0x53, 0xA1, 0xC0, 0x21, 0xC8, 0x95, 0x00, 0x97, 0xD4,
0x97, 0x90, 0x0D, 0x08, 0xA4, 0xE7, 0x8A, 0x51, 0x03, 0x9D, 0xB1, 0x2E, 0x14, 0x30, 0xD7, 0x0A,
0x57, 0x82, 0x03, 0x5C, 0x73, 0x6E, 0x57, 0x88, 0x0C, 0xFE, 0x9B, 0x40, 0x3C, 0xB0, 0x83, 0x52,
0x03, 0x53, 0xA1, 0xC0, 0x21, 0xC8, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08, 0xA4, 0xE7,
0x8A, 0x51, 0x03, 0x9D, 0xC6, 0x2E, 0x10, 0xF0, 0xA2, 0x01, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00,
0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x08, 0x40, 0xAB, 0x40, 0xAB, 0x9F, 0x09, 0xEF,
0xA1, 0x1F, 0x04, 0xAF, 0x2B, 0x48, 0xA2, 0xC0, 0xA3, 0x41, 0xA2, 0xDB, 0xA3, 0x83, 0x80, 0xF0,
0xA4, 0xC0, 0xFF, 0xB0, 0xA5, 0x00, 0x22, 0xC8, 0x24, 0xC2, 0xA6, 0x00, 0x23, 0x08, 0x03, 0x5C,
0x23, 0x4A, 0x25, 0x02, 0xA7, 0x40, 0x21, 0xC8, 0xA8, 0xC0, 0xA9, 0x41, 0xA8, 0xDB, 0xA9, 0x83,
0x29, 0x08, 0x80, 0x7A, 0xAA, 0x00, 0x27, 0x48, 0x80, 0x7A, 0x2A, 0x02, 0x03, 0x9D, 0x02, 0xAF,
0x26, 0x08, 0x28, 0xC2, 0x03, 0x5C, 0x80, 0x34, 0x83, 0x52, 0x03, 0x53, 0x21, 0xC8, 0x2B, 0xC7,
0x08, 0x40, 0x21, 0xC8, 0x80, 0x7A, 0x7F, 0x3E, 0x03, 0x5C, 0x04, 0xAF, 0x21, 0xC8, 0xA2, 0xC0,
0xA3, 0x41, 0xA2, 0xDB, 0xA3, 0x83, 0x2B, 0x48, 0xA4, 0xC0, 0xA5, 0x41, 0xA4, 0xDB, 0xA5, 0x83,
0x7F, 0x70, 0xA6, 0x00, 0x24, 0xC8, 0x26, 0x02, 0xA7, 0x40, 0x25, 0x49, 0x03, 0x18, 0x01, 0xBE,
0xA8, 0xC0, 0x80, 0x7A, 0xA9, 0x00, 0x23, 0x08, 0x80, 0x7A, 0x29, 0x02, 0x03, 0x9D, 0x2A, 0x2F,
0x22, 0xC8, 0x27, 0x42, 0x03, 0x5C, 0x7F, 0xB4, 0x04, 0xAF, 0xA8, 0xC0, 0x24, 0xC8, 0x20, 0x38,
0xA7, 0x40, 0xA2, 0xC0, 0x18, 0x30, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00,
0x17, 0x94, 0x17, 0x50, 0x05, 0x30, 0xA5, 0x00, 0xA5, 0xCB, 0x3C, 0x6F, 0x83, 0x52, 0x03, 0x53,
0x24, 0xC8, 0x30, 0x78, 0xA7, 0x40, 0xA2, 0xC0, 0x18, 0x30, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00,
0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x05, 0x30, 0xA5, 0x00, 0xA5, 0xCB, 0x4E, 0x6F,
0x39, 0xB0, 0x83, 0x52, 0x03, 0x53, 0xA1, 0xC0, 0x21, 0xC8, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90,
0x0D, 0x08, 0xA6, 0x00, 0x55, 0xB0, 0x9E, 0x40, 0x26, 0x9C, 0x50, 0xEF, 0x28, 0xC8, 0x84, 0x80,
0x3A, 0xB0, 0x83, 0x52, 0x03, 0x53, 0xA1, 0xC0, 0x21, 0xC8, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90,
0x0D, 0x08, 0x83, 0x93, 0x80, 0x40, 0x28, 0x0A, 0x84, 0x80, 0x3B, 0xF0, 0x83, 0x52, 0x03, 0x53,
0xA1, 0xC0, 0x21, 0xC8, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08, 0x83, 0x93, 0x80, 0x40,
0x08, 0x40, 0xA4, 0xC0, 0x84, 0x80, 0x83, 0x93, 0x00, 0x48, 0xA2, 0xC0, 0x21, 0xC8, 0xB8, 0x27,
0x8A, 0x51, 0x03, 0x5C, 0x01, 0x34, 0x21, 0xC8, 0x84, 0x80, 0x00, 0x48, 0xA2, 0xC0, 0x24, 0xC8,
0xB8, 0x27, 0x8A, 0x51, 0x03, 0x5C, 0x00, 0xF4, 0x24, 0x0A, 0x84, 0x80, 0x00, 0x48, 0xA2, 0xC0,
0x21, 0x0A, 0xB8, 0x27, 0x8A, 0x51, 0x03, 0x5C, 0x01, 0x34, 0x00, 0xF4, 0xA3, 0x00, 0xA2, 0x01,
0x21, 0xC8, 0x23, 0x58, 0xA2, 0x87, 0x03, 0xD0, 0xA1, 0x8D, 0x03, 0xD0, 0xA3, 0x8C, 0xA3, 0x48,
0x03, 0x9D, 0x98, 0x2F, 0x22, 0xC8, 0x08, 0x40, 0xD5, 0x40, 0x9E, 0x40, 0x57, 0x88, 0x0C, 0xFE,
0xB1, 0x00, 0x00, 0xB0, 0x03, 0x18, 0x01, 0xF0, 0xB2, 0x00, 0x55, 0x48, 0x31, 0x46, 0x32, 0x04,
0x08, 0x40, 0x56, 0x48, 0x42, 0xFE, 0x84, 0x80, 0x83, 0x93, 0x00, 0x48, 0xA2, 0xC0, 0x12, 0x74,
0x84, 0x80, 0x00, 0x48, 0xA3, 0x00, 0x22, 0xC8, 0x23, 0x02, 0x08, 0x40, 0xCF, 0x80, 0x52, 0x08,
0xD0, 0xC0, 0x56, 0x48, 0xCE, 0x40, 0x08, 0x40, 0x4E, 0x48, 0x42, 0xFE, 0x84, 0x80, 0x00, 0x48,
0xD4, 0x00, 0x08, 0x40, 0x39, 0x7E, 0x84, 0x80, 0x00, 0x48, 0xA2, 0xC0, 0x11, 0x74, 0xFD, 0xF9,
0x02, 0x38, 0xA2, 0xC0, 0x02, 0x34, 0xFD, 0xF9, 0x02, 0x38, 0xA2, 0xC0, 0x01, 0x34, 0x03, 0xD0,
0xB0, 0x8D, 0x03, 0xD0, 0xB0, 0x8D, 0x08, 0x40, 0xFF, 0x3A, 0x01, 0xBE, 0xA1, 0xC0, 0x53, 0x48,
0x08, 0x40, 0x03, 0xD0, 0xB1, 0xCD, 0x03, 0xD0, 0xB1, 0xCD, 0x08, 0x40, 0x80, 0x40, 0x05, 0x30,
0xA1, 0xC0, 0x57, 0x88, 0x08, 0x40, 0xFF, 0x3A, 0x01, 0xBE, 0xA1, 0xC0, 0x54, 0x08, 0x08, 0x40,
0xA0, 0x30, 0x83, 0x93, 0x84, 0x80, 0xF0, 0xB0, 0x8A, 0x51, 0x2F, 0xE1, 0x8A, 0x51, 0xA0, 0x30,
0x83, 0xD7, 0x84, 0x80, 0xE0, 0x70, 0x8A, 0x51, 0x2F, 0xE1, 0x83, 0x01, 0x8A, 0x95, 0xD5, 0x2A,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF,
0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0x01, 0xF0, 0xA0, 0x80, 0x95, 0x41,
0x96, 0x41, 0x97, 0x81, 0x98, 0x01, 0x99, 0x41, 0x9A, 0x41, 0x9B, 0x81, 0x9C, 0x41, 0x9E, 0x81,
0x9B, 0x81, 0x1C, 0x70, 0xA2, 0x01, 0xA2, 0x4A, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08,
0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x01, 0xF0, 0x9B, 0x40, 0x3C, 0xB0, 0x83, 0x52, 0x03, 0x53,
0xA1, 0xC0, 0x21, 0xC8, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08, 0xE5, 0x40, 0x65, 0x48,
0x03, 0x59, 0xED, 0x6A, 0x9B, 0x81, 0x10, 0xF0, 0xA2, 0x01, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00,
0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x3C, 0xB0, 0x83, 0x52, 0x03, 0x53, 0xA1, 0xC0,
0x21, 0xC8, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08, 0xE5, 0x40, 0x9E, 0x40, 0x65, 0xCB,
0x04, 0x6B, 0x3D, 0xF0, 0x83, 0x52, 0x03, 0x53, 0xA1, 0xC0, 0x21, 0xC8, 0x95, 0x00, 0x97, 0xD4,
0x97, 0x90, 0x0D, 0x08, 0xE2, 0x00, 0xA2, 0xC0, 0x1A, 0x70, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00,
0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x3E, 0xF0, 0x83, 0x52, 0x03, 0x53, 0xA1, 0xC0,
0x21, 0xC8, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08, 0xE3, 0x40, 0xA2, 0xC0, 0x1B, 0xB0,
0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x3F, 0x30,
0x83, 0x52, 0x03, 0x53, 0xA1, 0xC0, 0x21, 0xC8, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08,
0xE4, 0x00, 0x03, 0x30, 0xE4, 0x85, 0x64, 0x08, 0xA2, 0xC0, 0x1C, 0x70, 0xA3, 0x00, 0x22, 0xC8,
0x96, 0x00, 0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x01, 0xF0, 0x9E, 0x40, 0x9B, 0x40,
0x3C, 0xB0, 0x83, 0x52, 0x03, 0x53, 0xA1, 0xC0, 0x21, 0xC8, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90,
0x0D, 0x08, 0xE5, 0x40, 0x9E, 0x40, 0x65, 0x48, 0x02, 0x7A, 0x03, 0x9D, 0x50, 0xAB, 0x3D, 0xF0,
0x83, 0x52, 0x03, 0x53, 0xA1, 0xC0, 0x21, 0xC8, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08,
0xDF, 0xC0, 0xA2, 0xC0, 0x1A, 0x70, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00,
0x17, 0x94, 0x17, 0x50, 0x3E, 0xF0, 0x83, 0x52, 0x03, 0x53, 0xA1, 0xC0, 0x21, 0xC8, 0x95, 0x00,
0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08, 0xE0, 0xC0, 0xA2, 0xC0, 0x1B, 0xB0, 0xA3, 0x00, 0x22, 0xC8,
0x96, 0x00, 0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x02, 0xF0, 0x9E, 0x40, 0x9B, 0x40,
0x3C, 0xB0, 0x83, 0x52, 0x03, 0x53, 0xA1, 0xC0, 0x21, 0xC8, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90,
0x0D, 0x08, 0xE5, 0x40, 0x9E, 0x40, 0x65, 0x48, 0x03, 0xBA, 0x03, 0x9D, 0x88, 0xAB, 0x3D, 0xF0,
0x83, 0x52, 0x03, 0x53, 0xA1, 0xC0, 0x21, 0xC8, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08,
0xDC, 0x40, 0xA2, 0xC0, 0x1A, 0x70, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00,
0x17, 0x94, 0x17, 0x50, 0x3E, 0xF0, 0x83, 0x52, 0x03, 0x53, 0xA1, 0xC0, 0x21, 0xC8, 0x95, 0x00,
0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08, 0xDD, 0x80, 0xA2, 0xC0, 0x1B, 0xB0, 0xA3, 0x00, 0x22, 0xC8,
0x96, 0x00, 0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x03, 0x30, 0x9E, 0x40, 0x9B, 0x40,
0x3C, 0xB0, 0x83, 0x52, 0x03, 0x53, 0xA1, 0xC0, 0x21, 0xC8, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90,
0x0D, 0x08, 0xE5, 0x40, 0x9E, 0x40, 0x65, 0x48, 0x04, 0x7A, 0x03, 0x9D, 0xC0, 0xAB, 0x9E, 0x81,
0x9B, 0x81, 0x3D, 0xF0, 0x83, 0x52, 0x03, 0x53, 0xA1, 0xC0, 0x21, 0xC8, 0x95, 0x00, 0x97, 0xD4,
0x97, 0x90, 0x0D, 0x08, 0xDE, 0x80, 0xA2, 0xC0, 0x1A, 0x70, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00,
0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x3E, 0xF0, 0x83, 0x52, 0x03, 0x53, 0xA1, 0xC0,
0x21, 0xC8, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08, 0xE1, 0x00, 0xA2, 0xC0, 0x1B, 0xB0,
0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0xE2, 0x48,
0x03, 0x9D, 0x02, 0x2C, 0x63, 0x48, 0x56, 0xA4, 0x00, 0xB0, 0x8A, 0x95, 0x5C, 0xA4, 0x8A, 0x95,
0x11, 0x30, 0x3B, 0x2C, 0x62, 0x8B, 0x0C, 0x6C, 0x63, 0x48, 0x56, 0xA4, 0x01, 0xF0, 0x8A, 0x95,
0x5C, 0xA4, 0x8A, 0x95, 0x22, 0x30, 0x3B, 0x2C, 0x62, 0x08, 0x02, 0x7A, 0x03, 0x9D, 0x18, 0x6C,
0x63, 0x48, 0x4C, 0x64, 0x00, 0xB0, 0x8A, 0x51, 0x39, 0xA2, 0x8A, 0x95, 0x33, 0xB0, 0x3B, 0x2C,
0x62, 0x08, 0x03, 0xBA, 0x03, 0x9D, 0x24, 0x6C, 0x63, 0x48, 0x4C, 0x64, 0x01, 0xF0, 0x8A, 0x51,
0x39, 0xA2, 0x8A, 0x95, 0x44, 0x30, 0x3B, 0x2C, 0x62, 0x08, 0x04, 0x7A, 0x03, 0x9D, 0x30, 0x6C,
0x63, 0x48, 0x3E, 0xE4, 0x00, 0xB0, 0x8A, 0x51, 0x37, 0xE1, 0x8A, 0x95, 0x55, 0xB0, 0x3B, 0x2C,
0x62, 0x08, 0x05, 0xBA, 0x03, 0x9D, 0x04, 0x6B, 0x63, 0x48, 0x3E, 0xE4, 0x01, 0xF0, 0x8A, 0x51,
0x37, 0xE1, 0x8A, 0x95, 0x66, 0xB0, 0x9B, 0x40, 0x9E, 0x40, 0x04, 0x6B, 0xA9, 0x00, 0x5F, 0xC8,
0xAA, 0x00, 0x60, 0xC8, 0xAB, 0x40, 0x5C, 0x48, 0xAC, 0x00, 0x5D, 0x88, 0xAD, 0x40, 0x5E, 0x88,
0xAE, 0x40, 0x64, 0x08, 0xAF, 0x80, 0x08, 0x40, 0xAC, 0x00, 0x5F, 0xC8, 0xAD, 0x40, 0x60, 0xC8,
0xAE, 0x40, 0x61, 0x08, 0xAF, 0x80, 0x64, 0x08, 0xB0, 0xC0, 0x08, 0x40, 0xA9, 0x00, 0x61, 0x08,
0xAA, 0x00, 0x64, 0x08, 0xAB, 0x40, 0x08, 0x40, 0xB3, 0x40, 0x04, 0xF0, 0xA2, 0xC0, 0x10, 0xF0,
0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x29, 0x08,
0xA2, 0xC0, 0x13, 0x70, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00, 0x17, 0x94,
0x17, 0x50, 0x14, 0x30, 0xA2, 0x01, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00,
0x17, 0x94, 0x17, 0x50, 0x15, 0x70, 0xA2, 0x01, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08,
0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x2A, 0x08, 0xD6, 0x40, 0x0B, 0x70, 0xD7, 0xC1, 0xDB, 0x80,
0x5B, 0x88, 0xB4, 0x00, 0x33, 0x98, 0xE0, 0xAC, 0x5B, 0x88, 0xAC, 0x00, 0x01, 0xF0, 0xFB, 0xE7,
0x8A, 0x95, 0x83, 0x52, 0x03, 0x53, 0xA1, 0xC0, 0x21, 0xC8, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90,
0x0D, 0x08, 0xC3, 0x39, 0x2C, 0x04, 0xA2, 0xC0, 0x01, 0xF0, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00,
0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x01, 0xF0, 0x83, 0x52, 0x03, 0x53, 0xA1, 0xC0,
0x21, 0xC8, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08, 0x8A, 0x51, 0xD3, 0x67, 0x8A, 0x95,
0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x01, 0xF0,
0x83, 0x52, 0x03, 0x53, 0xA1, 0xC0, 0x21, 0xC8, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08,
0xA2, 0xC0, 0x01, 0xF0, 0xA2, 0x10, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00,
0x17, 0x94, 0x17, 0x50, 0x2B, 0x48, 0xA4, 0xC0, 0x53, 0xB0, 0x8A, 0x51, 0x2D, 0x27, 0x8A, 0x95,
0x56, 0xB0, 0xA1, 0xC0, 0x53, 0xB0, 0x8A, 0x51, 0x79, 0x67, 0x8A, 0x95, 0xD0, 0xC0, 0x50, 0xC8,
0x03, 0x9D, 0x1F, 0x6D, 0x07, 0x70, 0xDB, 0x03, 0x5B, 0x82, 0x03, 0x5C, 0x1F, 0x6D, 0x88, 0x6C,
0x5B, 0x88, 0xAC, 0x00, 0x02, 0xF0, 0xFB, 0xE7, 0x8A, 0x95, 0x83, 0x52, 0x03, 0x53, 0xA1, 0xC0,
0x21, 0xC8, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08, 0xC3, 0x39, 0x2C, 0x04, 0xA2, 0xC0,
0x02, 0xF0, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50,
0x02, 0xF0, 0x83, 0x52, 0x03, 0x53, 0xA1, 0xC0, 0x21, 0xC8, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90,
0x0D, 0x08, 0x8A, 0x51, 0xCF, 0xA7, 0x8A, 0x95, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08,
0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x02, 0xF0, 0x83, 0x52, 0x03, 0x53, 0xA1, 0xC0, 0x21, 0xC8,
0x95, 0x00, 0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08, 0xA2, 0xC0, 0x02, 0xF0, 0xA2, 0x10, 0xA3, 0x00,
0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0xCA, 0xEC, 0x53, 0x48,
0xB5, 0x40, 0x54, 0x08, 0xB6, 0x40, 0xF3, 0xA7, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08,
0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x2B, 0x48, 0xA4, 0xC0, 0x56, 0xB0, 0x8A, 0x51, 0x2D, 0x27,
0x8A, 0x95, 0x56, 0x48, 0xB9, 0x40, 0x57, 0x88, 0xBA, 0x40, 0x29, 0x08, 0xFE, 0xFC, 0xAD, 0x40,
0x29, 0x49, 0xAE, 0x40, 0x29, 0x08, 0x01, 0x7C, 0xAF, 0x80, 0x29, 0x08, 0x02, 0x7C, 0xB0, 0xC0,
0x14, 0x30, 0xA2, 0x01, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00, 0x17, 0x94,
0x17, 0x50, 0x15, 0x70, 0xA2, 0x01, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00,
0x17, 0x94, 0x17, 0x50, 0x2D, 0x48, 0xA2, 0xC0, 0x13, 0x70, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00,
0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x2B, 0x48, 0xA4, 0xC0, 0x53, 0xB0, 0x8A, 0x51,
0x2D, 0x27, 0x8A, 0x95, 0xDB, 0xC1, 0xDB, 0x0A, 0x5B, 0x88, 0x2D, 0x7E, 0x84, 0x80, 0x00, 0x48,
0xA2, 0xC0, 0x13, 0x70, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00, 0x17, 0x94,
0x17, 0x50, 0x2B, 0x48, 0xA4, 0xC0, 0x56, 0xB0, 0x8A, 0x51, 0x2D, 0x27, 0x8A, 0x95, 0x53, 0xB0,
0xA1, 0xC0, 0x56, 0xB0, 0x8A, 0x51, 0x79, 0x67, 0x8A, 0x95, 0xD0, 0xC0, 0x50, 0xC8, 0x03, 0x59,
0x85, 0xED, 0x56, 0x48, 0xD3, 0x40, 0x57, 0x88, 0xD4, 0x00, 0x04, 0xF0, 0xDB, 0x0A, 0x5B, 0x82,
0x03, 0x5C, 0x64, 0xED, 0x53, 0x48, 0xB7, 0x80, 0x54, 0x08, 0xB8, 0x00, 0xF3, 0xA7, 0xA3, 0x00,
0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0xD8, 0x41, 0xD9, 0x81,
0xDB, 0xC1, 0x58, 0x08, 0xBD, 0x80, 0x59, 0x48, 0xC6, 0x00, 0x5B, 0x88, 0x0A, 0xFE, 0x84, 0x80,
0x8A, 0x51, 0x02, 0xA0, 0x8A, 0x95, 0x58, 0x87, 0xBE, 0x80, 0x5B, 0x88, 0x0A, 0xFE, 0x84, 0x80,
0x8A, 0x51, 0x02, 0xA0, 0x8A, 0x95, 0x59, 0xC7, 0xC7, 0x40, 0x5B, 0x88, 0x0A, 0xFE, 0x84, 0x80,
0x8A, 0x51, 0x02, 0xA0, 0x8A, 0x95, 0x58, 0x87, 0xBF, 0xC0, 0x5B, 0x88, 0x0A, 0xFE, 0x84, 0x80,
0x8A, 0x51, 0x02, 0xA0, 0x8A, 0x95, 0x59, 0x42, 0xC8, 0xC0, 0x5B, 0x88, 0x0A, 0xFE, 0x84, 0x80,
0x8A, 0x51, 0x02, 0xA0, 0x8A, 0x95, 0x58, 0x02, 0xC0, 0x80, 0x5B, 0x88, 0x0A, 0xFE, 0x84, 0x80,
0x8A, 0x51, 0x02, 0xA0, 0x8A, 0x95, 0x59, 0xC7, 0xC9, 0x00, 0x5B, 0x88, 0x0A, 0xFE, 0x84, 0x80,
0x8A, 0x51, 0x02, 0xA0, 0x8A, 0x95, 0x58, 0x02, 0xC1, 0xC0, 0x5B, 0x88, 0x0A, 0xFE, 0x84, 0x80,
0x8A, 0x51, 0x02, 0xA0, 0x8A, 0x95, 0x59, 0x42, 0xCA, 0x00, 0xE1, 0x27, 0x8A, 0x95, 0x97, 0x67,
0x8A, 0x95, 0xDA, 0x67, 0x8A, 0x95, 0x97, 0x67, 0x8A, 0x95, 0xF7, 0xE7, 0xA3, 0x00, 0x22, 0xC8,
0x96, 0x00, 0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x46, 0x08, 0xA2, 0xC0, 0x15, 0x70,
0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x2B, 0x48,
0xA4, 0xC0, 0x51, 0x70, 0x8A, 0x51, 0x2D, 0x27, 0x8A, 0x95, 0xCF, 0xC1, 0xDA, 0x81, 0x05, 0x30,
0xDA, 0xCA, 0x5A, 0x42, 0x03, 0x18, 0x32, 0xEE, 0x1F, 0xF0, 0xCB, 0x67, 0x8A, 0x95, 0x97, 0x67,
0x8A, 0x95, 0xAE, 0x67, 0x8A, 0x95, 0x97, 0x67, 0x8A, 0x95, 0xBE, 0xA7, 0x8A, 0x95, 0xA3, 0x00,
0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0xD3, 0x67, 0xA3, 0x00,
0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x2B, 0x48, 0xA4, 0xC0,
0x56, 0xB0, 0x8A, 0x51, 0x2D, 0x27, 0x8A, 0x95, 0x56, 0xB0, 0xA1, 0xC0, 0x51, 0x70, 0x8A, 0x51,
0x79, 0x67, 0x8A, 0x95, 0xD0, 0xC0, 0x50, 0xC8, 0x03, 0x59, 0xFF, 0x2D, 0x56, 0x48, 0xE7, 0xA7,
0x8A, 0x95, 0xFF, 0x2D, 0x4F, 0x88, 0x3D, 0xBE, 0x84, 0x80, 0x00, 0x48, 0xD8, 0x00, 0xED, 0xA7,
0x8A, 0x95, 0x06, 0x30, 0xDB, 0x0A, 0x5B, 0x82, 0x58, 0x08, 0x03, 0x5C, 0x9A, 0x2D, 0xFF, 0x7E,
0xBD, 0x80, 0x59, 0x48, 0xFF, 0x7E, 0xC6, 0x00, 0x58, 0x08, 0xFF, 0x7E, 0xBE, 0x80, 0x59, 0x48,
0xC7, 0x40, 0x58, 0x08, 0xFF, 0x7E, 0xBF, 0xC0, 0x59, 0x48, 0x01, 0xBE, 0xC8, 0xC0, 0x58, 0x08,
0xC0, 0x80, 0x59, 0x48, 0xFF, 0x7E, 0xC9, 0x00, 0x58, 0x08, 0xC1, 0xC0, 0x59, 0x48, 0xCA, 0x00,
0x58, 0x08, 0xC2, 0xC0, 0x59, 0x48, 0x01, 0xBE, 0xCB, 0x40, 0x58, 0x08, 0x01, 0xBE, 0xC3, 0x00,
0x59, 0x48, 0xFF, 0x7E, 0xCC, 0x00, 0x58, 0x08, 0x01, 0xBE, 0xC4, 0xC0, 0x59, 0x48, 0xCD, 0x40,
0x58, 0x08, 0x01, 0xBE, 0xC5, 0x00, 0x59, 0x48, 0x01, 0xBE, 0xCE, 0x40, 0xE1, 0x27, 0x8A, 0x95,
0x97, 0x67, 0x8A, 0x95, 0xDA, 0x67, 0x8A, 0x95, 0x97, 0x67, 0x8A, 0x95, 0xF7, 0xE7, 0xA3, 0x00,
0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x46, 0x08, 0xA2, 0xC0,
0x15, 0x70, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50,
0x2B, 0x48, 0xA4, 0xC0, 0x51, 0x70, 0x8A, 0x51, 0x2D, 0x27, 0x8A, 0x95, 0xCF, 0xC1, 0xDA, 0x81,
0x09, 0x30, 0xDA, 0xCA, 0x5A, 0x42, 0x03, 0x18, 0xC3, 0x2E, 0x1F, 0xF0, 0xCB, 0x67, 0x8A, 0x95,
0x97, 0x67, 0x8A, 0x95, 0xAE, 0x67, 0x8A, 0x95, 0x97, 0x67, 0x8A, 0x95, 0xBE, 0xA7, 0x8A, 0x95,
0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0xD3, 0x67,
0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x2B, 0x48,
0xA4, 0xC0, 0x56, 0xB0, 0x8A, 0x51, 0x2D, 0x27, 0x8A, 0x95, 0x56, 0xB0, 0xA1, 0xC0, 0x51, 0x70,
0x8A, 0x51, 0x79, 0x67, 0x8A, 0x95, 0xD0, 0xC0, 0x50, 0xC8, 0x03, 0x59, 0x90, 0xAE, 0x56, 0x48,
0xE7, 0xA7, 0x8A, 0x95, 0x90, 0xAE, 0x4F, 0x88, 0x3D, 0xBE, 0x84, 0x80, 0x00, 0x48, 0xD8, 0x00,
0xED, 0xA7, 0x8A, 0x95, 0x51, 0x08, 0xBB, 0x80, 0x52, 0x08, 0xBC, 0x40, 0x58, 0x08, 0xA2, 0xC0,
0x14, 0x30, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50,
0x59, 0x48, 0xA2, 0xC0, 0x15, 0x70, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00,
0x17, 0x94, 0x17, 0x50, 0x2A, 0x08, 0xA2, 0xC0, 0x19, 0x70, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00,
0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x34, 0x08, 0xA2, 0xC0, 0x1A, 0x70, 0xA3, 0x00,
0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x35, 0x48, 0xA2, 0xC0,
0x1B, 0xB0, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50,
0x36, 0x48, 0xA2, 0xC0, 0x1C, 0x70, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00,
0x17, 0x94, 0x17, 0x50, 0x06, 0x30, 0x9B, 0x40, 0x3C, 0xB0, 0x83, 0x52, 0x03, 0x53, 0xA1, 0xC0,
0x21, 0xC8, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08, 0xD5, 0x40, 0x9E, 0x40, 0x55, 0x48,
0x06, 0xBA, 0x03, 0x9D, 0x0C, 0xEF, 0x39, 0x48, 0xA2, 0xC0, 0x19, 0x70, 0xA3, 0x00, 0x22, 0xC8,
0x96, 0x00, 0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x3A, 0x48, 0xA2, 0xC0, 0x1A, 0x70,
0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x58, 0x08,
0xA2, 0xC0, 0x1B, 0xB0, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00, 0x17, 0x94,
0x17, 0x50, 0x59, 0x48, 0xA2, 0xC0, 0x1C, 0x70, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08,
0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x07, 0x70, 0x9B, 0x40, 0x3C, 0xB0, 0x83, 0x52, 0x03, 0x53,
0xA1, 0xC0, 0x21, 0xC8, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08, 0xD5, 0x40, 0x9E, 0x40,
0x55, 0x48, 0x07, 0xFA, 0x03, 0x9D, 0x45, 0x2F, 0x3B, 0x88, 0xA2, 0xC0, 0x19, 0x70, 0xA3, 0x00,
0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x3C, 0x48, 0xA2, 0xC0,
0x1A, 0x70, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50,
0x37, 0x88, 0xA2, 0xC0, 0x1B, 0xB0, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00,
0x17, 0x94, 0x17, 0x50, 0x38, 0x08, 0xA2, 0xC0, 0x1C, 0x70, 0xA3, 0x00, 0x22, 0xC8, 0x96, 0x00,
0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x08, 0xF0, 0x9B, 0x40, 0x3C, 0xB0, 0x83, 0x52,
0x03, 0x53, 0xA1, 0xC0, 0x21, 0xC8, 0x95, 0x00, 0x97, 0xD4, 0x97, 0x90, 0x0D, 0x08, 0xD5, 0x40,
0x9E, 0x40, 0x55, 0x48, 0x08, 0x7A, 0x03, 0x9D, 0x7E, 0xEF, 0x10, 0xF0, 0xA2, 0x01, 0xA3, 0x00,
0x22, 0xC8, 0x96, 0x00, 0x23, 0x08, 0x95, 0x00, 0x17, 0x94, 0x17, 0x50, 0x08, 0x40, 0xA4, 0xC0,
0x21, 0xC8, 0x80, 0x7A, 0xA3, 0x00, 0x24, 0xC8, 0x80, 0x7A, 0xA3, 0x42, 0x03, 0x18, 0xA2, 0x2F,
0x21, 0xC8, 0x08, 0x40, 0x24, 0xC8, 0x80, 0x7A, 0xA3, 0x00, 0x22, 0xC8, 0x80, 0x7A, 0xA3, 0x42,
0x03, 0x18, 0xAC, 0x6F, 0x22, 0xC8, 0x08, 0x40, 0x24, 0xC8, 0x08, 0x40, 0xAC, 0x00, 0x5A, 0x48,
0x3D, 0xBE, 0x84, 0x80, 0x2C, 0x08, 0x83, 0x93, 0x80, 0x40, 0x1F, 0xF0, 0xA1, 0xC0, 0xE0, 0x70,
0xA2, 0xC0, 0x5A, 0x48, 0x46, 0x3E, 0x84, 0x80, 0x00, 0x48, 0x08, 0x40, 0xAC, 0x00, 0x5A, 0x48,
0x46, 0x3E, 0x84, 0x80, 0x2C, 0x08, 0x83, 0x93, 0x80, 0x40, 0x5A, 0x48, 0x3D, 0xBE, 0x84, 0x80,
0x00, 0x48, 0xA2, 0xC0, 0x14, 0x74, 0xA1, 0xC0, 0xE0, 0x70, 0xA2, 0xC0, 0x5A, 0x48, 0x3D, 0xBE,
0x84, 0x80, 0x00, 0x48, 0x08, 0x40, 0x5A, 0x48, 0x46, 0x3E, 0x84, 0x80, 0x83, 0x93, 0x00, 0x48,
0xA2, 0xC0, 0x15, 0xB4, 0xBD, 0x80, 0x1F, 0xF0, 0xA1, 0xC0, 0xE0, 0x70, 0xA2, 0xC0, 0x46, 0x08,
0x08, 0x40, 0x1F, 0xF0, 0xA1, 0xC0, 0xE0, 0x70, 0xA2, 0xC0, 0x3D, 0x88, 0x08, 0x40, 0xD1, 0x00,
0x57, 0x88, 0xD2, 0x00, 0x5A, 0x48, 0xCF, 0x80, 0x08, 0x40, 0x4F, 0x88, 0x46, 0x3E, 0x84, 0x80,
0x00, 0x48, 0xD9, 0x40, 0x08, 0x40, 0x29, 0x43, 0xFF, 0x3A, 0xA2, 0xC0, 0x13, 0xB4, 0xC6, 0x00,
0x3D, 0x88, 0xA2, 0xC0, 0x14, 0x74, 0x03, 0xD0, 0xAC, 0xCD, 0x03, 0xD0, 0xAC, 0xCD, 0x08, 0x40
};

View File

@ -0,0 +1,60 @@
/*
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(C)2019 Semtech
Description:
LoRa concentrator HAL auxiliary functions
License: Revised BSD License, see LICENSE.TXT file include in the project
*/
/* -------------------------------------------------------------------------- */
/* --- DEPENDANCIES --------------------------------------------------------- */
/* fix an issue between POSIX and C99 */
#if __STDC_VERSION__ >= 199901L
#define _XOPEN_SOURCE 600
#else
#define _XOPEN_SOURCE 500
#endif
#include <stdio.h> /* printf fprintf */
#include <time.h> /* clock_nanosleep */
/* -------------------------------------------------------------------------- */
/* --- PRIVATE MACROS ------------------------------------------------------- */
#if DEBUG_AUX == 1
#define DEBUG_MSG(str) fprintf(stderr, str)
#define DEBUG_PRINTF(fmt, args...) fprintf(stderr,"%s:%d: "fmt, __FUNCTION__, __LINE__, args)
#else
#define DEBUG_MSG(str)
#define DEBUG_PRINTF(fmt, args...)
#endif
/* -------------------------------------------------------------------------- */
/* --- PUBLIC FUNCTIONS DEFINITION ------------------------------------------ */
/* This implementation is POSIX-pecific and require a fix to be compatible with C99 */
void wait_ms(unsigned long a) {
struct timespec dly;
struct timespec rem;
dly.tv_sec = a / 1000;
dly.tv_nsec = ((long)a % 1000) * 1000000;
DEBUG_PRINTF("NOTE dly: %ld sec %ld ns\n", dly.tv_sec, dly.tv_nsec);
if((dly.tv_sec > 0) || ((dly.tv_sec == 0) && (dly.tv_nsec > 100000))) {
clock_nanosleep(CLOCK_MONOTONIC, 0, &dly, &rem);
DEBUG_PRINTF("NOTE remain: %ld sec %ld ns\n", rem.tv_sec, rem.tv_nsec);
}
return;
}
/* --- EOF ------------------------------------------------------------------ */

1044
libloragw/src/loragw_cal.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,201 @@
/*
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(C)2019 Semtech
Description:
LoRa concentrator debug functions
License: Revised BSD License, see LICENSE.TXT file include in the project
*/
/* -------------------------------------------------------------------------- */
/* --- DEPENDANCIES --------------------------------------------------------- */
#include <stdint.h> /* C99 types */
#include <stdbool.h> /* bool type */
#include <stdio.h> /* printf fprintf */
#include <string.h> /* memcmp */
#include <time.h>
#include "loragw_aux.h"
#include "loragw_reg.h"
#include "loragw_hal.h"
#include "loragw_debug.h"
#include "tinymt32.h"
/* -------------------------------------------------------------------------- */
/* --- DEBUG CONSTANTS ------------------------------------------------------ */
/* -------------------------------------------------------------------------- */
/* --- PRIVATE MACROS ------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* --- PRIVATE CONSTANTS & TYPES -------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* --- PRIVATE VARIABLES ---------------------------------------------------- */
static tinymt32_t tinymt;
/* -------------------------------------------------------------------------- */
/* --- PRIVATE FUNCTIONS DECLARATION ---------------------------------------- */
/* -------------------------------------------------------------------------- */
/* --- PRIVATE FUNCTIONS DEFINITION ----------------------------------------- */
/* -------------------------------------------------------------------------- */
/* --- PUBLIC FUNCTIONS DEFINITION ------------------------------------------ */
void dbg_init_random(void) {
tinymt.mat1 = 0x8f7011ee;
tinymt.mat2 = 0xfc78ff1f;
tinymt.tmat = 0x3793fdff;
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
void dbg_init_gpio(void) {
/* Select GPIO_6 to be controlled by HOST */
lgw_reg_w(SX1302_REG_GPIO_GPIO_SEL_6_SELECTION, 0);
/* Configure it as an OUTPUT */
lgw_reg_w(SX1302_REG_GPIO_GPIO_DIR_L_DIRECTION, 0xFF);
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
void dbg_toggle_gpio(void) {
/* Set GPIO_6 to high */
lgw_reg_w(SX1302_REG_GPIO_GPIO_OUT_L_OUT_VALUE, 64);
/* Set GPIO_6 to low */
lgw_reg_w(SX1302_REG_GPIO_GPIO_OUT_L_OUT_VALUE, 0);
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
void dbg_log_buffer_to_file(FILE * file, uint8_t * buffer, uint16_t size) {
int i;
char stat_timestamp[24];
time_t t;
t = time(NULL);
strftime(stat_timestamp, sizeof stat_timestamp, "%F %T %Z", gmtime(&t));
fprintf(file, "---------(%s)------------\n", stat_timestamp);
for (i = 0; i < size; i++) {
fprintf(file, "%02X ", buffer[i]);
}
fprintf(file, "\n");
fflush(file);
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
void dbg_log_payload_diff_to_file(FILE * file, uint8_t * buffer1, uint8_t * buffer2, uint16_t size) {
int i, j;
uint16_t nb_bits_diff = 0;
uint8_t debug_payload_diff[255];
fprintf(file, "Diff: ");
/* bit comparison of payloads */
for (j = 0; j < size; j++) {
debug_payload_diff[j] = buffer1[j] ^ buffer2[j];
fprintf(file, "%02X ", debug_payload_diff[j]);
}
fprintf(file, "\n");
/* count number of bits flipped, and display bit by bit */
for (j = 0; j < size; j++) {
for (i = 7; i >= 0; i--) {
fprintf(file, "%u", TAKE_N_BITS_FROM(debug_payload_diff[j], i, 1));
if (TAKE_N_BITS_FROM(debug_payload_diff[j], i, 1) == 1) {
nb_bits_diff += 1;
}
}
fprintf(file, " ");
}
fprintf(file, "\n");
fprintf(file, "%u bits flipped\n", nb_bits_diff);
fflush(file);
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
void dbg_generate_random_payload(uint32_t pkt_cnt, uint8_t * buffer_expected, uint8_t size) {
int k;
/* construct payload we should get for this packet counter */
tinymt32_init(&tinymt, (int)pkt_cnt);
buffer_expected[4] = (uint8_t)(pkt_cnt >> 24);
buffer_expected[5] = (uint8_t)(pkt_cnt >> 16);
buffer_expected[6] = (uint8_t)(pkt_cnt >> 8);
buffer_expected[7] = (uint8_t)(pkt_cnt >> 0);
tinymt32_generate_uint32(&tinymt); /* dummy: for sync with random size generation */
for (k = 8; k < (int)size; k++) {
buffer_expected[k] = (uint8_t)tinymt32_generate_uint32(&tinymt);
}
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
int dbg_check_payload(struct lgw_conf_debug_s * context, FILE * file, uint8_t * payload_received, uint8_t size, uint8_t ref_payload_idx, uint8_t sf) {
int k;
uint32_t debug_payload_cnt;
/* If the 4 first bytes of received payload match with the expected ones, go on with comparison */
if (memcmp((void*)payload_received, (void*)(context->ref_payload[ref_payload_idx].payload), 4) == 0) {
/* get counter to initialize random seed */
debug_payload_cnt = (unsigned int)(payload_received[4] << 24) | (unsigned int)(payload_received[5] << 16) | (unsigned int)(payload_received[6] << 8) | (unsigned int)(payload_received[7] << 0);
/* check if we missed some packets */
if (debug_payload_cnt > (context->ref_payload[ref_payload_idx].prev_cnt + 1)) {
printf("ERROR: 0x%08X missed %u pkt before %u (SF%u, size:%u)\n", context->ref_payload[ref_payload_idx].id, debug_payload_cnt - context->ref_payload[ref_payload_idx].prev_cnt - 1, debug_payload_cnt, sf, size);
if (file != NULL) {
fprintf(file, "ERROR: 0x%08X missed %u pkt before %u (SF%u, size:%u)\n", context->ref_payload[ref_payload_idx].id, debug_payload_cnt - context->ref_payload[ref_payload_idx].prev_cnt - 1, debug_payload_cnt, sf, size);
fflush(file);
}
} else if (debug_payload_cnt < context->ref_payload[ref_payload_idx].prev_cnt) {
if (file != NULL) {
fprintf(file, "INFO: 0x%08X got missing pkt %u (SF%u, size:%u) ?\n", context->ref_payload[ref_payload_idx].id, debug_payload_cnt, sf, size);
fflush(file);
}
} else {
#if 0
if (file != NULL) {
fprintf(file, "0x%08X %u (SF%u, size:%u)\n", context.ref_payload[ref_payload_idx].id, debug_payload_cnt, sf, size);
}
#endif
}
context->ref_payload[ref_payload_idx].prev_cnt = debug_payload_cnt;
/* generate the random payload which is expected for this packet count */
dbg_generate_random_payload(debug_payload_cnt, context->ref_payload[ref_payload_idx].payload, size);
/* compare expected with received */
if (memcmp((void *)payload_received, (void *)(context->ref_payload[ref_payload_idx].payload), size) != 0) {
if (file != NULL) {
fprintf(file, "RECEIVED:");
for (k = 0; k < (int)size; k++) {
fprintf(file, "%02X ", payload_received[k]);
}
fprintf(file, "\n");
fprintf(file, "EXPECTED:");
for (k = 0; k < (int)size; k++) {
fprintf(file, "%02X ", context->ref_payload[ref_payload_idx].payload[k]);
}
fprintf(file, "\n");
}
return -1;
} else {
return 1; /* matches */
}
}
return 0; /* ignored */
}

837
libloragw/src/loragw_gps.c Normal file
View File

@ -0,0 +1,837 @@
/*
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(C)2019 Semtech
Description:
Library of functions to manage a GNSS module (typically GPS) for accurate
timestamping of packets and synchronisation of gateways.
A limited set of module brands/models are supported.
License: Revised BSD License, see LICENSE.TXT file include in the project
*/
/* -------------------------------------------------------------------------- */
/* --- DEPENDANCIES --------------------------------------------------------- */
#define _GNU_SOURCE /* needed for qsort_r to be defined */
#include <stdint.h> /* C99 types */
#include <stdbool.h> /* bool type */
#include <stdio.h> /* printf fprintf */
#include <string.h> /* memcpy */
#include <errno.h>
#include <time.h> /* struct timespec */
#include <fcntl.h> /* open */
#include <termios.h> /* tcflush */
#include <math.h> /* modf */
#include <stdlib.h>
#include "loragw_gps.h"
/* -------------------------------------------------------------------------- */
/* --- PRIVATE MACROS ------------------------------------------------------- */
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
#if DEBUG_GPS == 1
#define DEBUG_MSG(args...) fprintf(stderr, args)
#define DEBUG_PRINTF(fmt, args...) fprintf(stderr,"%s:%d: "fmt, __FUNCTION__, __LINE__, args)
#define DEBUG_ARRAY(a,b,c) for(a=0;a<b;++a) fprintf(stderr,"%x.",c[a]);fprintf(stderr,"end\n")
#define CHECK_NULL(a) if(a==NULL){fprintf(stderr,"%s:%d: ERROR: NULL POINTER AS ARGUMENT\n", __FUNCTION__, __LINE__);return LGW_GPS_ERROR;}
#else
#define DEBUG_MSG(args...)
#define DEBUG_PRINTF(fmt, args...)
#define DEBUG_ARRAY(a,b,c) for(a=0;a!=0;){}
#define CHECK_NULL(a) if(a==NULL){return LGW_GPS_ERROR;}
#endif
#define TRACE() fprintf(stderr, "@ %s %d\n", __FUNCTION__, __LINE__);
/* -------------------------------------------------------------------------- */
/* --- PRIVATE CONSTANTS ---------------------------------------------------- */
#define TS_CPS 1E6 /* count-per-second of the timestamp counter */
#define PLUS_10PPM 1.00001
#define MINUS_10PPM 0.99999
#define DEFAULT_BAUDRATE B9600
#define UBX_MSG_NAVTIMEGPS_LEN 16
/* -------------------------------------------------------------------------- */
/* --- PRIVATE VARIABLES ---------------------------------------------------- */
/* result of the NMEA parsing */
static short gps_yea = 0; /* year (2 or 4 digits) */
static short gps_mon = 0; /* month (1-12) */
static short gps_day = 0; /* day of the month (1-31) */
static short gps_hou = 0; /* hours (0-23) */
static short gps_min = 0; /* minutes (0-59) */
static short gps_sec = 0; /* seconds (0-60)(60 is for leap second) */
static float gps_fra = 0.0; /* fractions of seconds (<1) */
static bool gps_time_ok = false;
static int16_t gps_week = 0; /* GPS week number of the navigation epoch */
static uint32_t gps_iTOW = 0; /* GPS time of week in milliseconds */
static int32_t gps_fTOW = 0; /* Fractional part of iTOW (+/-500000) in nanosec */
static short gps_dla = 0; /* degrees of latitude */
static double gps_mla = 0.0; /* minutes of latitude */
static char gps_ola = 0; /* orientation (N-S) of latitude */
static short gps_dlo = 0; /* degrees of longitude */
static double gps_mlo = 0.0; /* minutes of longitude */
static char gps_olo = 0; /* orientation (E-W) of longitude */
static short gps_alt = 0; /* altitude */
static bool gps_pos_ok = false;
static char gps_mod = 'N'; /* GPS mode (N no fix, A autonomous, D differential) */
static short gps_sat = 0; /* number of satellites used for fix */
static struct termios ttyopt_restore;
/* -------------------------------------------------------------------------- */
/* --- PRIVATE FUNCTIONS DECLARATION ---------------------------------------- */
static int nmea_checksum(const char *nmea_string, int buff_size, char *checksum);
static char nibble_to_hexchar(uint8_t a);
static bool validate_nmea_checksum(const char *serial_buff, int buff_size);
static bool match_label(const char *s, char *label, int size, char wildcard);
static int str_chop(char *s, int buff_size, char separator, int *idx_ary, int max_idx);
/* -------------------------------------------------------------------------- */
/* --- PRIVATE FUNCTIONS DEFINITION ----------------------------------------- */
/*
Calculate the checksum for a NMEA string
Skip the first '$' if necessary and calculate checksum until '*' character is
reached (or buff_size exceeded).
Checksum must point to a 2-byte (or more) char array.
Return position of the checksum in the string
*/
static int nmea_checksum(const char *nmea_string, int buff_size, char *checksum) {
int i = 0;
uint8_t check_num = 0;
/* check input parameters */
if ((nmea_string == NULL) || (checksum == NULL) || (buff_size <= 1)) {
DEBUG_MSG("Invalid parameters for nmea_checksum\n");
return -1;
}
/* skip the first '$' if necessary */
if (nmea_string[i] == '$') {
i += 1;
}
/* xor until '*' or max length is reached */
while (nmea_string[i] != '*') {
check_num ^= nmea_string[i];
i += 1;
if (i >= buff_size) {
DEBUG_MSG("Maximum length reached for nmea_checksum\n");
return -1;
}
}
/* Convert checksum value to 2 hexadecimal characters */
checksum[0] = nibble_to_hexchar(check_num / 16); /* upper nibble */
checksum[1] = nibble_to_hexchar(check_num % 16); /* lower nibble */
return i + 1;
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
static char nibble_to_hexchar(uint8_t a) {
if (a < 10) {
return '0' + a;
} else if (a < 16) {
return 'A' + (a-10);
} else {
return '?';
}
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
/*
Calculate the checksum of a NMEA frame and compare it to the checksum that is
present at the end of it.
Return true if it matches
*/
static bool validate_nmea_checksum(const char *serial_buff, int buff_size) {
int checksum_index;
char checksum[2]; /* 2 characters to calculate NMEA checksum */
checksum_index = nmea_checksum(serial_buff, buff_size, checksum);
/* could we calculate a verification checksum ? */
if (checksum_index < 0) {
DEBUG_MSG("ERROR: IMPOSSIBLE TO PARSE NMEA SENTENCE\n");
return false;
}
/* check if there are enough char in the serial buffer to read checksum */
if (checksum_index >= (buff_size - 2)) {
DEBUG_MSG("ERROR: IMPOSSIBLE TO READ NMEA SENTENCE CHECKSUM\n");
return false;
}
/* check the checksum per se */
if ((serial_buff[checksum_index] == checksum[0]) && (serial_buff[checksum_index+1] == checksum[1])) {
return true;
} else {
DEBUG_MSG("ERROR: NMEA CHECKSUM %c%c DOESN'T MATCH VERIFICATION CHECKSUM %c%c\n", serial_buff[checksum_index], serial_buff[checksum_index+1], checksum[0], checksum[1]);
return false;
}
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
/*
Return true if the "label" string (can contain wildcard characters) matches
the begining of the "s" string
*/
static bool match_label(const char *s, char *label, int size, char wildcard) {
int i;
for (i=0; i < size; i++) {
if (label[i] == wildcard) continue;
if (label[i] != s[i]) return false;
}
return true;
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
/*
Chop a string into smaller strings
Replace every separator in the input character buffer by a null character so
that all s[index] are valid strings.
Populate an array of integer 'idx_ary' representing indexes of token in the
string.
buff_size and max_idx are there to prevent segfaults.
Return the number of token found (number of idx_ary filled).
*/
int str_chop(char *s, int buff_size, char separator, int *idx_ary, int max_idx) {
int i = 0; /* index in the string */
int j = 0; /* index in the result array */
if ((s == NULL) || (buff_size < 0) || (separator == 0) || (idx_ary == NULL) || (max_idx < 0)) {
/* unsafe to do anything */
return -1;
}
if ((buff_size == 0) || (max_idx == 0)) {
/* nothing to do */
return 0;
}
s[buff_size - 1] = 0; /* add string terminator at the end of the buffer, just to be sure */
idx_ary[j] = 0;
j += 1;
/* loop until string terminator is reached */
while (s[i] != 0) {
if (s[i] == separator) {
s[i] = 0; /* replace separator by string terminator */
if (j >= max_idx) { /* no more room in the index array */
return j;
}
idx_ary[j] = i+1; /* next token start after replaced separator */
++j;
}
++i;
}
return j;
}
/* -------------------------------------------------------------------------- */
/* --- PUBLIC FUNCTIONS DEFINITION ------------------------------------------ */
int lgw_gps_enable(char *tty_path, char *gps_family, speed_t target_brate, int *fd_ptr) {
int i;
struct termios ttyopt; /* serial port options */
int gps_tty_dev; /* file descriptor to the serial port of the GNSS module */
uint8_t ubx_cmd_timegps[UBX_MSG_NAVTIMEGPS_LEN] = {
0xB5, 0x62, /* UBX Sync Chars */
0x06, 0x01, /* CFG-MSG Class/ID */
0x08, 0x00, /* Payload length */
0x01, 0x20, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, /* Enable NAV-TIMEGPS output on serial */
0x32, 0x94 }; /* Checksum */
ssize_t num_written;
/* check input parameters */
CHECK_NULL(tty_path);
CHECK_NULL(fd_ptr);
/* open TTY device */
gps_tty_dev = open(tty_path, O_RDWR | O_NOCTTY);
if (gps_tty_dev <= 0) {
DEBUG_MSG("ERROR: TTY PORT FAIL TO OPEN, CHECK PATH AND ACCESS RIGHTS\n");
return LGW_GPS_ERROR;
}
*fd_ptr = gps_tty_dev;
/* manage the different GPS modules families */
if (gps_family == NULL) {
DEBUG_MSG("WARNING: this version of GPS module may not be supported\n");
} else if (strncmp(gps_family, "ubx7", 4) != 0) {
/* The current implementation relies on proprietary messages from U-Blox */
/* GPS modules (UBX, NAV-TIMEGPS...) and has only be tested with a u-blox 7. */
/* Those messages allow to get NATIVE GPS time (no leap seconds) required */
/* for class-B handling and GPS synchronization */
/* see lgw_parse_ubx() function for details */
DEBUG_MSG("WARNING: this version of GPS module may not be supported\n");
}
/* manage the target bitrate */
if (target_brate != 0) {
DEBUG_MSG("WARNING: target_brate parameter ignored for now\n"); // TODO
}
/* get actual serial port configuration */
i = tcgetattr(gps_tty_dev, &ttyopt);
if (i != 0) {
DEBUG_MSG("ERROR: IMPOSSIBLE TO GET TTY PORT CONFIGURATION\n");
return LGW_GPS_ERROR;
}
/* Save current serial port configuration for restoring later */
memcpy(&ttyopt_restore, &ttyopt, sizeof ttyopt);
/* update baudrates */
cfsetispeed(&ttyopt, DEFAULT_BAUDRATE);
cfsetospeed(&ttyopt, DEFAULT_BAUDRATE);
/* update terminal parameters */
/* The following configuration should allow to:
- Get ASCII NMEA messages
- Get UBX binary messages
- Send UBX binary commands
Note: as binary data have to be read/written, we need to disable
various character processing to avoid loosing data */
/* Control Modes */
ttyopt.c_cflag |= CLOCAL; /* local connection, no modem control */
ttyopt.c_cflag |= CREAD; /* enable receiving characters */
ttyopt.c_cflag |= CS8; /* 8 bit frames */
ttyopt.c_cflag &= ~PARENB; /* no parity */
ttyopt.c_cflag &= ~CSTOPB; /* one stop bit */
/* Input Modes */
ttyopt.c_iflag |= IGNPAR; /* ignore bytes with parity errors */
ttyopt.c_iflag &= ~ICRNL; /* do not map CR to NL on input*/
ttyopt.c_iflag &= ~IGNCR; /* do not ignore carriage return on input */
ttyopt.c_iflag &= ~IXON; /* disable Start/Stop output control */
ttyopt.c_iflag &= ~IXOFF; /* do not send Start/Stop characters */
/* Output Modes */
ttyopt.c_oflag = 0; /* disable everything on output as we only write binary */
/* Local Modes */
ttyopt.c_lflag &= ~ICANON; /* disable canonical input - cannot use with binary input */
ttyopt.c_lflag &= ~ISIG; /* disable check for INTR, QUIT, SUSP special characters */
ttyopt.c_lflag &= ~IEXTEN; /* disable any special control character */
ttyopt.c_lflag &= ~ECHO; /* do not echo back every character typed */
ttyopt.c_lflag &= ~ECHOE; /* does not erase the last character in current line */
ttyopt.c_lflag &= ~ECHOK; /* do not echo NL after KILL character */
/* settings for non-canonical mode
read will block for until the lesser of VMIN or requested chars have been received */
ttyopt.c_cc[VMIN] = LGW_GPS_MIN_MSG_SIZE;
ttyopt.c_cc[VTIME] = 0;
/* set new serial ports parameters */
i = tcsetattr(gps_tty_dev, TCSANOW, &ttyopt);
if (i != 0){
DEBUG_MSG("ERROR: IMPOSSIBLE TO UPDATE TTY PORT CONFIGURATION\n");
return LGW_GPS_ERROR;
}
tcflush(gps_tty_dev, TCIOFLUSH);
/* Send UBX CFG NAV-TIMEGPS message to tell GPS module to output native GPS time */
/* This is a binary message, serial port has to be properly configured to handle this */
num_written = write (gps_tty_dev, ubx_cmd_timegps, UBX_MSG_NAVTIMEGPS_LEN);
if (num_written != UBX_MSG_NAVTIMEGPS_LEN) {
DEBUG_MSG("ERROR: Failed to write on serial port (written=%d)\n", (int) num_written);
}
/* get timezone info */
tzset();
/* initialize global variables */
gps_time_ok = false;
gps_pos_ok = false;
gps_mod = 'N';
return LGW_GPS_SUCCESS;
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
int lgw_gps_disable(int fd) {
int i;
/* restore serial ports parameters */
i = tcsetattr(fd, TCSANOW, &ttyopt_restore);
if (i != 0){
DEBUG_MSG("ERROR: IMPOSSIBLE TO RESTORE TTY PORT CONFIGURATION - %s\n", strerror(errno));
return LGW_GPS_ERROR;
}
tcflush(fd, TCIOFLUSH);
i = close(fd);
if (i != 0) {
DEBUG_PRINTF("ERROR: TTY PORT FAIL TO CLOSE - %s\n", strerror(errno));
return LGW_GPS_ERROR;
}
return LGW_GPS_SUCCESS;
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
enum gps_msg lgw_parse_ubx(const char *serial_buff, size_t buff_size, size_t *msg_size) {
bool valid = 0; /* iTOW, fTOW and week validity */
unsigned int payload_length;
uint8_t ck_a, ck_b;
uint8_t ck_a_rcv, ck_b_rcv;
unsigned int i;
*msg_size = 0; /* ensure msg_size alway receives a value */
/* check input parameters */
if (serial_buff == NULL) {
return IGNORED;
}
if (buff_size < 8) {
DEBUG_MSG("ERROR: TOO SHORT TO BE A VALID UBX MESSAGE\n");
return IGNORED;
}
/* display received serial data and checksum */
DEBUG_MSG("Note: parsing UBX frame> ");
for (i=0; i<buff_size; i++) {
DEBUG_MSG("%02x ", serial_buff[i]);
}
DEBUG_MSG("\n");
/* Check for UBX sync chars 0xB5 0x62 */
if ((serial_buff[0] == (char)0xB5) && (serial_buff[1] == (char)0x62)) {
/* Get payload length to compute message size */
payload_length = (uint8_t)serial_buff[4];
payload_length |= (uint8_t)serial_buff[5] << 8;
*msg_size = 6 + payload_length + 2; /* header + payload + checksum */
/* check for complete message in buffer */
if(*msg_size <= buff_size) {
/* Validate checksum of message */
ck_a_rcv = serial_buff[*msg_size-2]; /* received checksum */
ck_b_rcv = serial_buff[*msg_size-1]; /* received checksum */
/* Use 8-bit Fletcher Algorithm to compute checksum of actual payload */
ck_a = 0; ck_b = 0;
for (i=0; i<(4 + payload_length); i++) {
ck_a = ck_a + serial_buff[i+2];
ck_b = ck_b + ck_a;
}
/* Compare checksums and parse if OK */
if ((ck_a == ck_a_rcv) && (ck_b == ck_b_rcv)) {
/* Check for Class 0x01 (NAV) and ID 0x20 (NAV-TIMEGPS) */
if ((serial_buff[2] == 0x01) && (serial_buff[3] == 0x20)) {
/* Check validity of information */
valid = serial_buff[17] & 0x3; /* towValid, weekValid */
if (valid) {
/* Parse buffer to extract GPS time */
/* Warning: payload byte ordering is Little Endian */
gps_iTOW = (uint8_t)serial_buff[6];
gps_iTOW |= (uint8_t)serial_buff[7] << 8;
gps_iTOW |= (uint8_t)serial_buff[8] << 16;
gps_iTOW |= (uint8_t)serial_buff[9] << 24; /* GPS time of week, in ms */
gps_fTOW = (uint8_t)serial_buff[10];
gps_fTOW |= (uint8_t)serial_buff[11] << 8;
gps_fTOW |= (uint8_t)serial_buff[12] << 16;
gps_fTOW |= (uint8_t)serial_buff[13] << 24; /* Fractional part of iTOW, in ns */
gps_week = (uint8_t)serial_buff[14];
gps_week |= (uint8_t)serial_buff[15] << 8; /* GPS week number */
gps_time_ok = true;
#if 0
/* For debug */
{
short ubx_gps_hou = 0; /* hours (0-23) */
short ubx_gps_min = 0; /* minutes (0-59) */
short ubx_gps_sec = 0; /* seconds (0-59) */
/* Format GPS time in hh:mm:ss based on iTOW */
ubx_gps_sec = (gps_iTOW / 1000) % 60;
ubx_gps_min = (gps_iTOW / 1000 / 60) % 60;
ubx_gps_hou = (gps_iTOW / 1000 / 60 / 60) % 24;
printf(" GPS time = %02d:%02d:%02d\n", ubx_gps_hou, ubx_gps_min, ubx_gps_sec);
}
#endif
} else { /* valid */
gps_time_ok = false;
}
return UBX_NAV_TIMEGPS;
} else if ((serial_buff[2] == 0x05) && (serial_buff[3] == 0x00)) {
DEBUG_MSG("NOTE: UBX ACK-NAK received\n");
return IGNORED;
} else if ((serial_buff[2] == 0x05) && (serial_buff[3] == 0x01)) {
DEBUG_MSG("NOTE: UBX ACK-ACK received\n");
return IGNORED;
} else { /* not a supported message */
DEBUG_MSG("ERROR: UBX message is not supported (%02x %02x)\n", serial_buff[2], serial_buff[3]);
return IGNORED;
}
} else { /* checksum failed */
DEBUG_MSG("ERROR: UBX message is corrupted, checksum failed\n");
return INVALID;
}
} else { /* message contains less bytes than indicated by header */
DEBUG_MSG("ERROR: UBX message incomplete\n");
return INCOMPLETE;
}
} else { /* Not a UBX message */
/* Ignore messages which are not UBX ones for now */
return IGNORED;
}
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
enum gps_msg lgw_parse_nmea(const char *serial_buff, int buff_size) {
int i, j, k;
int str_index[30]; /* string index from the string chopping */
int nb_fields; /* number of strings detected by string chopping */
char parser_buf[256]; /* parsing modifies buffer so need a local copy */
/* check input parameters */
if (serial_buff == NULL) {
return UNKNOWN;
}
if(buff_size > (int)(sizeof(parser_buf) - 1)) {
DEBUG_MSG("Note: input string to big for parsing\n");
return INVALID;
}
/* look for some NMEA sentences in particular */
if (buff_size < 8) {
DEBUG_MSG("ERROR: TOO SHORT TO BE A VALID NMEA SENTENCE\n");
return UNKNOWN;
} else if (!validate_nmea_checksum(serial_buff, buff_size)) {
DEBUG_MSG("Warning: invalid NMEA sentence (bad checksum)\n");
return INVALID;
} else if (match_label(serial_buff, "$G?RMC", 6, '?')) {
/*
NMEA sentence format: $xxRMC,time,status,lat,NS,long,EW,spd,cog,date,mv,mvEW,posMode*cs<CR><LF>
Valid fix: $GPRMC,083559.34,A,4717.11437,N,00833.91522,E,0.004,77.52,091202,,,A*00
No fix: $GPRMC,,V,,,,,,,,,,N*00
*/
memcpy(parser_buf, serial_buff, buff_size);
parser_buf[buff_size] = '\0';
nb_fields = str_chop(parser_buf, buff_size, ',', str_index, ARRAY_SIZE(str_index));
if (nb_fields != 13) {
DEBUG_MSG("Warning: invalid RMC sentence (number of fields)\n");
return IGNORED;
}
/* parse GPS status */
gps_mod = *(parser_buf + str_index[12]); /* get first character, no need to bother with sscanf */
if ((gps_mod != 'N') && (gps_mod != 'A') && (gps_mod != 'D')) {
gps_mod = 'N';
}
/* parse complete time */
i = sscanf(parser_buf + str_index[1], "%2hd%2hd%2hd%4f", &gps_hou, &gps_min, &gps_sec, &gps_fra);
j = sscanf(parser_buf + str_index[9], "%2hd%2hd%2hd", &gps_day, &gps_mon, &gps_yea);
if ((i == 4) && (j == 3)) {
if ((gps_mod == 'A') || (gps_mod == 'D')) {
gps_time_ok = true;
DEBUG_MSG("Note: Valid RMC sentence, GPS locked, date: 20%02d-%02d-%02dT%02d:%02d:%06.3fZ\n", gps_yea, gps_mon, gps_day, gps_hou, gps_min, gps_fra + (float)gps_sec);
} else {
gps_time_ok = false;
DEBUG_MSG("Note: Valid RMC sentence, no satellite fix, estimated date: 20%02d-%02d-%02dT%02d:%02d:%06.3fZ\n", gps_yea, gps_mon, gps_day, gps_hou, gps_min, gps_fra + (float)gps_sec);
}
} else {
/* could not get a valid hour AND date */
gps_time_ok = false;
DEBUG_MSG("Note: Valid RMC sentence, mode %c, no date\n", gps_mod);
}
return NMEA_RMC;
} else if (match_label(serial_buff, "$G?GGA", 6, '?')) {
/*
NMEA sentence format: $xxGGA,time,lat,NS,long,EW,quality,numSV,HDOP,alt,M,sep,M,diffAge,diffStation*cs<CR><LF>
Valid fix: $GPGGA,092725.00,4717.11399,N,00833.91590,E,1,08,1.01,499.6,M,48.0,M,,*5B
*/
memcpy(parser_buf, serial_buff, buff_size);
parser_buf[buff_size] = '\0';
nb_fields = str_chop(parser_buf, buff_size, ',', str_index, ARRAY_SIZE(str_index));
if (nb_fields != 15) {
DEBUG_MSG("Warning: invalid GGA sentence (number of fields)\n");
return IGNORED;
}
/* parse number of satellites used for fix */
sscanf(parser_buf + str_index[7], "%hd", &gps_sat);
/* parse 3D coordinates */
i = sscanf(parser_buf + str_index[2], "%2hd%10lf", &gps_dla, &gps_mla);
gps_ola = *(parser_buf + str_index[3]);
j = sscanf(parser_buf + str_index[4], "%3hd%10lf", &gps_dlo, &gps_mlo);
gps_olo = *(parser_buf + str_index[5]);
k = sscanf(parser_buf + str_index[9], "%hd", &gps_alt);
if ((i == 2) && (j == 2) && (k == 1) && ((gps_ola=='N')||(gps_ola=='S')) && ((gps_olo=='E')||(gps_olo=='W'))) {
gps_pos_ok = true;
DEBUG_MSG("Note: Valid GGA sentence, %d sat, lat %02ddeg %06.3fmin %c, lon %03ddeg%06.3fmin %c, alt %d\n", gps_sat, gps_dla, gps_mla, gps_ola, gps_dlo, gps_mlo, gps_olo, gps_alt);
} else {
/* could not get a valid latitude, longitude AND altitude */
gps_pos_ok = false;
DEBUG_MSG("Note: Valid GGA sentence, %d sat, no coordinates\n", gps_sat);
}
return NMEA_GGA;
} else {
DEBUG_MSG("Note: ignored NMEA sentence\n"); /* quite verbose */
return IGNORED;
}
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
int lgw_gps_get(struct timespec *utc, struct timespec *gps_time, struct coord_s *loc, struct coord_s *err) {
struct tm x;
time_t y;
double intpart, fractpart;
if (utc != NULL) {
if (!gps_time_ok) {
DEBUG_MSG("ERROR: NO VALID TIME TO RETURN\n");
return LGW_GPS_ERROR;
}
memset(&x, 0, sizeof(x));
if (gps_yea < 100) { /* 2-digits year, 20xx */
x.tm_year = gps_yea + 100; /* 100 years offset to 1900 */
} else { /* 4-digits year, Gregorian calendar */
x.tm_year = gps_yea - 1900;
}
x.tm_mon = gps_mon - 1; /* tm_mon is [0,11], gps_mon is [1,12] */
x.tm_mday = gps_day;
x.tm_hour = gps_hou;
x.tm_min = gps_min;
x.tm_sec = gps_sec;
y = mktime(&x) - timezone; /* need to substract timezone bc mktime assumes time vector is local time */
if (y == (time_t)(-1)) {
DEBUG_MSG("ERROR: FAILED TO CONVERT BROKEN-DOWN TIME\n");
return LGW_GPS_ERROR;
}
utc->tv_sec = y;
utc->tv_nsec = (int32_t)(gps_fra * 1e9);
}
if (gps_time != NULL) {
if (!gps_time_ok) {
DEBUG_MSG("ERROR: NO VALID TIME TO RETURN\n");
return LGW_GPS_ERROR;
}
fractpart = modf(((double)gps_iTOW / 1E3) + ((double)gps_fTOW / 1E9), &intpart);
/* Number of seconds since beginning on current GPS week */
gps_time->tv_sec = (time_t)intpart;
/* Number of seconds since GPS epoch 06.Jan.1980 */
gps_time->tv_sec += (time_t)gps_week * 604800; /* day*hours*minutes*secondes: 7*24*60*60; */
/* Fractional part in nanoseconds */
gps_time->tv_nsec = (long)(fractpart * 1E9);
}
if (loc != NULL) {
if (!gps_pos_ok) {
DEBUG_MSG("ERROR: NO VALID POSITION TO RETURN\n");
return LGW_GPS_ERROR;
}
loc->lat = ((double)gps_dla + (gps_mla/60.0)) * ((gps_ola == 'N')?1.0:-1.0);
loc->lon = ((double)gps_dlo + (gps_mlo/60.0)) * ((gps_olo == 'E')?1.0:-1.0);
loc->alt = gps_alt;
}
if (err != NULL) {
DEBUG_MSG("Warning: localization error processing not implemented yet\n");
err->lat = 0.0;
err->lon = 0.0;
err->alt = 0;
}
return LGW_GPS_SUCCESS;
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
int lgw_gps_sync(struct tref *ref, uint32_t count_us, struct timespec utc, struct timespec gps_time) {
double cnt_diff; /* internal concentrator time difference (in seconds) */
double utc_diff; /* UTC time difference (in seconds) */
double slope; /* time slope between new reference and old reference (for sanity check) */
bool aber_n0; /* is the update value for synchronization aberrant or not ? */
static bool aber_min1 = false; /* keep track of whether value at sync N-1 was aberrant or not */
static bool aber_min2 = false; /* keep track of whether value at sync N-2 was aberrant or not */
CHECK_NULL(ref);
/* calculate the slope */
cnt_diff = (double)(count_us - ref->count_us) / (double)(TS_CPS); /* uncorrected by xtal_err */
utc_diff = (double)(utc.tv_sec - (ref->utc).tv_sec) + (1E-9 * (double)(utc.tv_nsec - (ref->utc).tv_nsec));
/* detect aberrant points by measuring if slope limits are exceeded */
if (utc_diff != 0) { // prevent divide by zero
slope = cnt_diff/utc_diff;
if ((slope > PLUS_10PPM) || (slope < MINUS_10PPM)) {
DEBUG_MSG("Warning: correction range exceeded\n");
aber_n0 = true;
} else {
aber_n0 = false;
}
} else {
DEBUG_MSG("Warning: aberrant UTC value for synchronization\n");
aber_n0 = true;
}
/* watch if the 3 latest sync point were aberrant or not */
if (aber_n0 == false) {
/* value no aberrant -> sync with smoothed slope */
ref->systime = time(NULL);
ref->count_us = count_us;
ref->utc.tv_sec = utc.tv_sec;
ref->utc.tv_nsec = utc.tv_nsec;
ref->gps.tv_sec = gps_time.tv_sec;
ref->gps.tv_nsec = gps_time.tv_nsec;
ref->xtal_err = slope;
aber_min2 = aber_min1;
aber_min1 = aber_n0;
return LGW_GPS_SUCCESS;
} else if (aber_n0 && aber_min1 && aber_min2) {
/* 3 successive aberrant values -> sync reset (keep xtal_err) */
ref->systime = time(NULL);
ref->count_us = count_us;
ref->utc.tv_sec = utc.tv_sec;
ref->utc.tv_nsec = utc.tv_nsec;
ref->gps.tv_sec = gps_time.tv_sec;
ref->gps.tv_nsec = gps_time.tv_nsec;
/* reset xtal_err only if the present value is out of range */
if ((ref->xtal_err > PLUS_10PPM) || (ref->xtal_err < MINUS_10PPM)) {
ref->xtal_err = 1.0;
}
DEBUG_MSG("Warning: 3 successive aberrant sync attempts, sync reset\n");
aber_min2 = aber_min1;
aber_min1 = aber_n0;
return LGW_GPS_SUCCESS;
} else {
/* only 1 or 2 successive aberrant values -> ignore and return an error */
aber_min2 = aber_min1;
aber_min1 = aber_n0;
return LGW_GPS_ERROR;
}
return LGW_GPS_SUCCESS;
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
int lgw_cnt2utc(struct tref ref, uint32_t count_us, struct timespec *utc) {
double delta_sec;
double intpart, fractpart;
long tmp;
CHECK_NULL(utc);
if ((ref.systime == 0) || (ref.xtal_err > PLUS_10PPM) || (ref.xtal_err < MINUS_10PPM)) {
DEBUG_MSG("ERROR: INVALID REFERENCE FOR CNT -> UTC CONVERSION\n");
return LGW_GPS_ERROR;
}
/* calculate delta in seconds between reference count_us and target count_us */
delta_sec = (double)(count_us - ref.count_us) / (TS_CPS * ref.xtal_err);
/* now add that delta to reference UTC time */
fractpart = modf (delta_sec , &intpart);
tmp = ref.utc.tv_nsec + (long)(fractpart * 1E9);
if (tmp < (long)1E9) { /* the nanosecond part doesn't overflow */
utc->tv_sec = ref.utc.tv_sec + (time_t)intpart;
utc->tv_nsec = tmp;
} else { /* must carry one second */
utc->tv_sec = ref.utc.tv_sec + (time_t)intpart + 1;
utc->tv_nsec = tmp - (long)1E9;
}
return LGW_GPS_SUCCESS;
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
int lgw_utc2cnt(struct tref ref, struct timespec utc, uint32_t *count_us) {
double delta_sec;
CHECK_NULL(count_us);
if ((ref.systime == 0) || (ref.xtal_err > PLUS_10PPM) || (ref.xtal_err < MINUS_10PPM)) {
DEBUG_MSG("ERROR: INVALID REFERENCE FOR UTC -> CNT CONVERSION\n");
return LGW_GPS_ERROR;
}
/* calculate delta in seconds between reference utc and target utc */
delta_sec = (double)(utc.tv_sec - ref.utc.tv_sec);
delta_sec += 1E-9 * (double)(utc.tv_nsec - ref.utc.tv_nsec);
/* now convert that to internal counter tics and add that to reference counter value */
*count_us = ref.count_us + (uint32_t)(delta_sec * TS_CPS * ref.xtal_err);
return LGW_GPS_SUCCESS;
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
int lgw_cnt2gps(struct tref ref, uint32_t count_us, struct timespec *gps_time) {
double delta_sec;
double intpart, fractpart;
long tmp;
CHECK_NULL(gps_time);
if ((ref.systime == 0) || (ref.xtal_err > PLUS_10PPM) || (ref.xtal_err < MINUS_10PPM)) {
DEBUG_MSG("ERROR: INVALID REFERENCE FOR CNT -> GPS CONVERSION\n");
return LGW_GPS_ERROR;
}
/* calculate delta in milliseconds between reference count_us and target count_us */
delta_sec = (double)(count_us - ref.count_us) / (TS_CPS * ref.xtal_err);
/* now add that delta to reference GPS time */
fractpart = modf (delta_sec , &intpart);
tmp = ref.gps.tv_nsec + (long)(fractpart * 1E9);
if (tmp < (long)1E9) { /* the nanosecond part doesn't overflow */
gps_time->tv_sec = ref.gps.tv_sec + (time_t)intpart;
gps_time->tv_nsec = tmp;
} else { /* must carry one second */
gps_time->tv_sec = ref.gps.tv_sec + (time_t)intpart + 1;
gps_time->tv_nsec = tmp - (long)1E9;
}
return LGW_GPS_SUCCESS;
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
int lgw_gps2cnt(struct tref ref, struct timespec gps_time, uint32_t *count_us) {
double delta_sec;
CHECK_NULL(count_us);
if ((ref.systime == 0) || (ref.xtal_err > PLUS_10PPM) || (ref.xtal_err < MINUS_10PPM)) {
DEBUG_MSG("ERROR: INVALID REFERENCE FOR GPS -> CNT CONVERSION\n");
return LGW_GPS_ERROR;
}
/* calculate delta in seconds between reference gps time and target gps time */
delta_sec = (double)(gps_time.tv_sec - ref.gps.tv_sec);
delta_sec += 1E-9 * (double)(gps_time.tv_nsec - ref.gps.tv_nsec);
/* now convert that to internal counter tics and add that to reference counter value */
*count_us = ref.count_us + (uint32_t)(delta_sec * TS_CPS * ref.xtal_err);
return LGW_GPS_SUCCESS;
}
/* --- EOF ------------------------------------------------------------------ */

1048
libloragw/src/loragw_hal.c Normal file

File diff suppressed because it is too large Load Diff

156
libloragw/src/loragw_i2c.c Normal file
View File

@ -0,0 +1,156 @@
/*
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(C)2019 Semtech
Description:
Host specific functions to address the LoRa concentrator I2C peripherals.
License: Revised BSD License, see LICENSE.TXT file include in the project
*/
/* -------------------------------------------------------------------------- */
/* --- DEPENDANCIES --------------------------------------------------------- */
#include <stdint.h> /* C99 types */
#include <stdio.h> /* printf fprintf */
#include <stdlib.h> /* malloc free */
#include <unistd.h> /* lseek, close */
#include <fcntl.h> /* open */
#include <string.h> /* memset */
#include <sys/ioctl.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include "loragw_i2c.h"
#include "loragw_aux.h"
/* -------------------------------------------------------------------------- */
/* --- PRIVATE MACROS ------------------------------------------------------- */
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
#if DEBUG_I2C == 1
#define DEBUG_MSG(str) fprintf(stderr, str)
#define DEBUG_PRINTF(fmt, args...) fprintf(stderr,"%s:%d: "fmt, __FUNCTION__, __LINE__, args)
#define CHECK_NULL(a) if(a==NULL){fprintf(stderr,"%s:%d: ERROR: NULL POINTER AS ARGUMENT\n", __FUNCTION__, __LINE__);return LGW_SPI_ERROR;}
#else
#define DEBUG_MSG(str)
#define DEBUG_PRINTF(fmt, args...)
#define CHECK_NULL(a) if(a==NULL){return LGW_SPI_ERROR;}
#endif
/* -------------------------------------------------------------------------- */
/* --- PRIVATE CONSTANTS ---------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* --- PUBLIC FUNCTIONS DEFINITION ------------------------------------------ */
int i2c_linuxdev_open(const char *path, uint8_t device_addr, int *i2c_fd) {
int dev;
/* Check input variables */
if (path == NULL) {
DEBUG_MSG("ERROR: null pointer path");
return LGW_I2C_ERROR;
}
if (i2c_fd == NULL) {
DEBUG_MSG("ERROR: null pointer i2c_fd");
return LGW_I2C_ERROR;
}
/* Open I2C device */
dev = open(path, O_RDWR);
if (dev < 0) {
DEBUG_PRINTF("ERROR: Failed to open I2C %s - %s", path, strerror(errno));
return LGW_I2C_ERROR;
}
/* Setting I2C device mode to slave */
if (ioctl(dev, I2C_SLAVE, device_addr) < 0) {
DEBUG_PRINTF("ERROR: Failed to acquire bus access and/or talk to slave - %s\n", strerror(errno));
return LGW_I2C_ERROR;
}
DEBUG_MSG("INFO: I2C port opened successfully");
*i2c_fd = dev; /* return file descriptor index */
return LGW_I2C_SUCCESS;
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
int i2c_linuxdev_read(int i2c_fd, uint8_t device_addr, uint8_t reg_addr, uint8_t *data) {
uint8_t *inbuff, outbuff;
struct i2c_rdwr_ioctl_data packets;
struct i2c_msg messages[2];
outbuff = reg_addr;
messages[0].addr = device_addr;
messages[0].flags= 0;
messages[0].len = sizeof(outbuff);
messages[0].buf = &outbuff;
inbuff = data;
messages[1].addr = device_addr;
messages[1].flags = I2C_M_RD;
messages[1].len = sizeof(*inbuff);
messages[1].buf = inbuff;
packets.msgs = messages;
packets.nmsgs = 2;
if (ioctl(i2c_fd, I2C_RDWR, &packets) < 0) {
DEBUG_PRINTF("ERROR: Read from I2C Device failed (%d, 0x%02x, 0x%02x) - %s", i2c_fd, device_addr, reg_addr, strerror(errno));
return LGW_I2C_ERROR;
}
return LGW_I2C_SUCCESS;
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
int i2c_linuxdev_write(int i2c_fd, uint8_t device_addr, uint8_t reg_addr, uint8_t data) {
unsigned char buff[2];
struct i2c_rdwr_ioctl_data packets;
struct i2c_msg messages[1];
buff[0] = reg_addr;
buff[1] = data;
messages[0].addr = device_addr;
messages[0].flags = 0;
messages[0].len = sizeof(buff);
messages[0].buf = buff;
packets.msgs = messages;
packets.nmsgs = 1;
if (ioctl(i2c_fd, I2C_RDWR, &packets) < 0) {
DEBUG_PRINTF("ERROR: Write to I2C Device failed (%d, 0x%02x, 0x%02x) - %s", i2c_fd, device_addr, reg_addr, strerror(errno));
return LGW_I2C_ERROR;
}
return LGW_I2C_SUCCESS;
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
int i2c_linuxdev_close(int i2c_fd) {
int i;
i = close(i2c_fd);
if (i == 0) {
DEBUG_MSG("INFO: I2C port closed successfully");
return LGW_I2C_SUCCESS;
} else {
DEBUG_PRINTF("ERROR: Failed to close I2C - %s", strerror(errno));
return LGW_I2C_ERROR;
}
}
/* --- EOF ------------------------------------------------------------------ */

1501
libloragw/src/loragw_reg.c Normal file

File diff suppressed because it is too large Load Diff

352
libloragw/src/loragw_spi.c Normal file
View File

@ -0,0 +1,352 @@
/*
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(C)2019 Semtech
Description:
Host specific functions to address the LoRa concentrator registers through
a SPI interface.
Single-byte read/write and burst read/write.
Could be used with multiple SPI ports in parallel (explicit file descriptor)
License: Revised BSD License, see LICENSE.TXT file include in the project
*/
/* -------------------------------------------------------------------------- */
/* --- DEPENDANCIES --------------------------------------------------------- */
#include <stdint.h> /* C99 types */
#include <stdio.h> /* printf fprintf */
#include <stdlib.h> /* malloc free */
#include <unistd.h> /* lseek, close */
#include <fcntl.h> /* open */
#include <string.h> /* memset */
#include <sys/ioctl.h>
#include <linux/spi/spidev.h>
#include "loragw_spi.h"
#include "loragw_aux.h"
/* -------------------------------------------------------------------------- */
/* --- PRIVATE MACROS ------------------------------------------------------- */
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
#if DEBUG_SPI == 1
#define DEBUG_MSG(str) fprintf(stderr, str)
#define DEBUG_PRINTF(fmt, args...) fprintf(stderr,"%s:%d: "fmt, __FUNCTION__, __LINE__, args)
#define CHECK_NULL(a) if(a==NULL){fprintf(stderr,"%s:%d: ERROR: NULL POINTER AS ARGUMENT\n", __FUNCTION__, __LINE__);return LGW_SPI_ERROR;}
#else
#define DEBUG_MSG(str)
#define DEBUG_PRINTF(fmt, args...)
#define CHECK_NULL(a) if(a==NULL){return LGW_SPI_ERROR;}
#endif
/* -------------------------------------------------------------------------- */
/* --- PRIVATE CONSTANTS ---------------------------------------------------- */
#define READ_ACCESS 0x00
#define WRITE_ACCESS 0x80
/* -------------------------------------------------------------------------- */
/* --- PUBLIC FUNCTIONS DEFINITION ------------------------------------------ */
/* SPI initialization and configuration */
int lgw_spi_open(const char * spidev_path, void **spi_target_ptr) {
int *spi_device = NULL;
int dev;
int a=0, b=0;
int i;
/* check input variables */
CHECK_NULL(spi_target_ptr); /* cannot be null, must point on a void pointer (*spi_target_ptr can be null) */
/* allocate memory for the device descriptor */
spi_device = malloc(sizeof(int));
if (spi_device == NULL) {
DEBUG_MSG("ERROR: MALLOC FAIL\n");
return LGW_SPI_ERROR;
}
/* open SPI device */
dev = open(spidev_path, O_RDWR);
if (dev < 0) {
DEBUG_PRINTF("ERROR: failed to open SPI device %s\n", spidev_path);
return LGW_SPI_ERROR;
}
/* setting SPI mode to 'mode 0' */
i = SPI_MODE_0;
a = ioctl(dev, SPI_IOC_WR_MODE, &i);
b = ioctl(dev, SPI_IOC_RD_MODE, &i);
if ((a < 0) || (b < 0)) {
DEBUG_MSG("ERROR: SPI PORT FAIL TO SET IN MODE 0\n");
close(dev);
free(spi_device);
return LGW_SPI_ERROR;
}
/* setting SPI max clk (in Hz) */
i = SPI_SPEED;
a = ioctl(dev, SPI_IOC_WR_MAX_SPEED_HZ, &i);
b = ioctl(dev, SPI_IOC_RD_MAX_SPEED_HZ, &i);
if ((a < 0) || (b < 0)) {
DEBUG_MSG("ERROR: SPI PORT FAIL TO SET MAX SPEED\n");
close(dev);
free(spi_device);
return LGW_SPI_ERROR;
}
/* setting SPI to MSB first */
i = 0;
a = ioctl(dev, SPI_IOC_WR_LSB_FIRST, &i);
b = ioctl(dev, SPI_IOC_RD_LSB_FIRST, &i);
if ((a < 0) || (b < 0)) {
DEBUG_MSG("ERROR: SPI PORT FAIL TO SET MSB FIRST\n");
close(dev);
free(spi_device);
return LGW_SPI_ERROR;
}
/* setting SPI to 8 bits per word */
i = 0;
a = ioctl(dev, SPI_IOC_WR_BITS_PER_WORD, &i);
b = ioctl(dev, SPI_IOC_RD_BITS_PER_WORD, &i);
if ((a < 0) || (b < 0)) {
DEBUG_MSG("ERROR: SPI PORT FAIL TO SET 8 BITS-PER-WORD\n");
close(dev);
return LGW_SPI_ERROR;
}
*spi_device = dev;
*spi_target_ptr = (void *)spi_device;
DEBUG_MSG("Note: SPI port opened and configured ok\n");
return LGW_SPI_SUCCESS;
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
/* SPI release */
int lgw_spi_close(void *spi_target) {
int spi_device;
int a;
/* check input variables */
CHECK_NULL(spi_target);
/* close file & deallocate file descriptor */
spi_device = *(int *)spi_target; /* must check that spi_target is not null beforehand */
a = close(spi_device);
free(spi_target);
/* determine return code */
if (a < 0) {
DEBUG_MSG("ERROR: SPI PORT FAILED TO CLOSE\n");
return LGW_SPI_ERROR;
} else {
DEBUG_MSG("Note: SPI port closed\n");
return LGW_SPI_SUCCESS;
}
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
/* Simple write */
int lgw_spi_w(void *spi_target, uint8_t spi_mux_target, uint16_t address, uint8_t data) {
int spi_device;
uint8_t out_buf[4];
uint8_t command_size;
struct spi_ioc_transfer k;
int a;
/* check input variables */
CHECK_NULL(spi_target);
spi_device = *(int *)spi_target; /* must check that spi_target is not null beforehand */
/* prepare frame to be sent */
out_buf[0] = spi_mux_target;
out_buf[1] = WRITE_ACCESS | ((address >> 8) & 0x7F);
out_buf[2] = ((address >> 0) & 0xFF);
out_buf[3] = data;
command_size = 4;
/* I/O transaction */
memset(&k, 0, sizeof(k)); /* clear k */
k.tx_buf = (unsigned long) out_buf;
k.len = command_size;
k.speed_hz = SPI_SPEED;
k.cs_change = 0;
k.bits_per_word = 8;
a = ioctl(spi_device, SPI_IOC_MESSAGE(1), &k);
/* determine return code */
if (a != (int)k.len) {
DEBUG_MSG("ERROR: SPI WRITE FAILURE\n");
return LGW_SPI_ERROR;
} else {
DEBUG_MSG("Note: SPI write success\n");
return LGW_SPI_SUCCESS;
}
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
/* Simple read */
int lgw_spi_r(void *spi_target, uint8_t spi_mux_target, uint16_t address, uint8_t *data) {
int spi_device;
uint8_t out_buf[5];
uint8_t command_size;
uint8_t in_buf[ARRAY_SIZE(out_buf)];
struct spi_ioc_transfer k;
int a;
/* check input variables */
CHECK_NULL(spi_target);
CHECK_NULL(data);
spi_device = *(int *)spi_target; /* must check that spi_target is not null beforehand */
/* prepare frame to be sent */
out_buf[0] = spi_mux_target;
out_buf[1] = READ_ACCESS | ((address >> 8) & 0x7F);
out_buf[2] = ((address >> 0) & 0xFF);
out_buf[3] = 0x00;
out_buf[4] = 0x00;
command_size = 5;
/* I/O transaction */
memset(&k, 0, sizeof(k)); /* clear k */
k.tx_buf = (unsigned long) out_buf;
k.rx_buf = (unsigned long) in_buf;
k.len = command_size;
k.cs_change = 0;
a = ioctl(spi_device, SPI_IOC_MESSAGE(1), &k);
/* determine return code */
if (a != (int)k.len) {
DEBUG_MSG("ERROR: SPI READ FAILURE\n");
return LGW_SPI_ERROR;
} else {
DEBUG_MSG("Note: SPI read success\n");
*data = in_buf[command_size - 1];
return LGW_SPI_SUCCESS;
}
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
/* Burst (multiple-byte) write */
int lgw_spi_wb(void *spi_target, uint8_t spi_mux_target, uint16_t address, const uint8_t *data, uint16_t size) {
int spi_device;
uint8_t command[3];
uint8_t command_size;
struct spi_ioc_transfer k[2];
int size_to_do, chunk_size, offset;
int byte_transfered = 0;
int i;
/* check input parameters */
CHECK_NULL(spi_target);
CHECK_NULL(data);
if (size == 0) {
DEBUG_MSG("ERROR: BURST OF NULL LENGTH\n");
return LGW_SPI_ERROR;
}
spi_device = *(int *)spi_target; /* must check that spi_target is not null beforehand */
/* prepare command byte */
command[0] = spi_mux_target;
command[1] = WRITE_ACCESS | ((address >> 8) & 0x7F);
command[2] = ((address >> 0) & 0xFF);
command_size = 3;
size_to_do = size;
/* I/O transaction */
memset(&k, 0, sizeof(k)); /* clear k */
k[0].tx_buf = (unsigned long) &command[0];
k[0].len = command_size;
k[0].cs_change = 0;
k[1].cs_change = 0;
for (i=0; size_to_do > 0; ++i) {
chunk_size = (size_to_do < LGW_BURST_CHUNK) ? size_to_do : LGW_BURST_CHUNK;
offset = i * LGW_BURST_CHUNK;
k[1].tx_buf = (unsigned long)(data + offset);
k[1].len = chunk_size;
byte_transfered += (ioctl(spi_device, SPI_IOC_MESSAGE(2), &k) - k[0].len );
DEBUG_PRINTF("BURST WRITE: to trans %d # chunk %d # transferred %d \n", size_to_do, chunk_size, byte_transfered);
size_to_do -= chunk_size; /* subtract the quantity of data already transferred */
}
/* determine return code */
if (byte_transfered != size) {
DEBUG_MSG("ERROR: SPI BURST WRITE FAILURE\n");
return LGW_SPI_ERROR;
} else {
DEBUG_MSG("Note: SPI burst write success\n");
return LGW_SPI_SUCCESS;
}
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
/* Burst (multiple-byte) read */
int lgw_spi_rb(void *spi_target, uint8_t spi_mux_target, uint16_t address, uint8_t *data, uint16_t size) {
int spi_device;
uint8_t command[4];
uint8_t command_size;
struct spi_ioc_transfer k[2];
int size_to_do, chunk_size, offset;
int byte_transfered = 0;
int i;
/* check input parameters */
CHECK_NULL(spi_target);
CHECK_NULL(data);
if (size == 0) {
DEBUG_MSG("ERROR: BURST OF NULL LENGTH\n");
return LGW_SPI_ERROR;
}
spi_device = *(int *)spi_target; /* must check that spi_target is not null beforehand */
/* prepare command byte */
command[0] = spi_mux_target;
command[1] = READ_ACCESS | ((address >> 8) & 0x7F);
command[2] = ((address >> 0) & 0xFF);
command[3] = 0x00;
command_size = 4;
size_to_do = size;
/* I/O transaction */
memset(&k, 0, sizeof(k)); /* clear k */
k[0].tx_buf = (unsigned long) &command[0];
k[0].len = command_size;
k[0].cs_change = 0;
k[1].cs_change = 0;
for (i=0; size_to_do > 0; ++i) {
chunk_size = (size_to_do < LGW_BURST_CHUNK) ? size_to_do : LGW_BURST_CHUNK;
offset = i * LGW_BURST_CHUNK;
k[1].rx_buf = (unsigned long)(data + offset);
k[1].len = chunk_size;
byte_transfered += (ioctl(spi_device, SPI_IOC_MESSAGE(2), &k) - k[0].len );
DEBUG_PRINTF("BURST READ: to trans %d # chunk %d # transferred %d \n", size_to_do, chunk_size, byte_transfered);
size_to_do -= chunk_size; /* subtract the quantity of data already transferred */
}
/* determine return code */
if (byte_transfered != size) {
DEBUG_MSG("ERROR: SPI BURST READ FAILURE\n");
return LGW_SPI_ERROR;
} else {
DEBUG_MSG("Note: SPI burst read success\n");
return LGW_SPI_SUCCESS;
}
}
/* --- EOF ------------------------------------------------------------------ */

View File

@ -0,0 +1,213 @@
/*
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(C)2019 Semtech
Description:
Basic driver for ST ts751 temperature sensor
License: Revised BSD License, see LICENSE.TXT file include in the project
*/
/* -------------------------------------------------------------------------- */
/* --- DEPENDANCIES --------------------------------------------------------- */
#include <stdint.h> /* C99 types */
#include <stdbool.h> /* bool type */
#include <stdio.h> /* printf fprintf */
#include "loragw_i2c.h"
#include "loragw_stts751.h"
/* -------------------------------------------------------------------------- */
/* --- PRIVATE MACROS ------------------------------------------------------- */
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
#if DEBUG_I2C == 1
#define DEBUG_MSG(str) fprintf(stderr, str)
#define DEBUG_PRINTF(fmt, args...) fprintf(stderr,"%s:%d: "fmt, __FUNCTION__, __LINE__, args)
#define CHECK_NULL(a) if(a==NULL){fprintf(stderr,"%s:%d: ERROR: NULL POINTER AS ARGUMENT\n", __FUNCTION__, __LINE__);return LGW_REG_ERROR;}
#else
#define DEBUG_MSG(str)
#define DEBUG_PRINTF(fmt, args...)
#define CHECK_NULL(a) if(a==NULL){return LGW_REG_ERROR;}
#endif
/* -------------------------------------------------------------------------- */
/* --- PRIVATE CONSTANTS ---------------------------------------------------- */
#define STTS751_REG_TEMP_H 0x00
#define STTS751_REG_STATUS 0x01
#define STTS751_STATUS_TRIPT BIT(0)
#define STTS751_STATUS_TRIPL BIT(5)
#define STTS751_STATUS_TRIPH BIT(6)
#define STTS751_REG_TEMP_L 0x02
#define STTS751_REG_CONF 0x03
#define STTS751_CONF_RES_MASK 0x0C
#define STTS751_CONF_RES_SHIFT 2
#define STTS751_CONF_EVENT_DIS BIT(7)
#define STTS751_CONF_STOP BIT(6)
#define STTS751_REG_RATE 0x04
#define STTS751_REG_HLIM_H 0x05
#define STTS751_REG_HLIM_L 0x06
#define STTS751_REG_LLIM_H 0x07
#define STTS751_REG_LLIM_L 0x08
#define STTS751_REG_TLIM 0x20
#define STTS751_REG_HYST 0x21
#define STTS751_REG_SMBUS_TO 0x22
#define STTS751_REG_PROD_ID 0xFD
#define STTS751_REG_MAN_ID 0xFE
#define STTS751_REG_REV_ID 0xFF
#define STTS751_0_PROD_ID 0x00
#define STTS751_1_PROD_ID 0x01
#define ST_MAN_ID 0x53
/* -------------------------------------------------------------------------- */
/* --- PRIVATE VARIABLES ---------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* --- INTERNAL SHARED VARIABLES -------------------------------------------- */
extern int lgw_i2c_target;
/* -------------------------------------------------------------------------- */
/* --- PRIVATE FUNCTIONS ---------------------------------------------------- */
int stts751_configure( int i2c_fd )
{
int err;
uint8_t val;
/* Check Input Params */
if( i2c_fd <= 0 )
{
printf( "ERROR: invalid I2C file descriptor\n" );
return LGW_I2C_ERROR;
}
DEBUG_MSG("INFO: configuring STTS751 temperature sensor...\n");
/* Get product ID */
err = i2c_linuxdev_read( i2c_fd, I2C_PORT_TEMP_SENSOR, STTS751_REG_PROD_ID, &val );
if ( err != 0 )
{
printf( "ERROR: failed to read I2C device 0x%02X (err=%i)\n", I2C_PORT_TEMP_SENSOR, err );
return LGW_I2C_ERROR;
}
switch( val )
{
case STTS751_0_PROD_ID:
DEBUG_MSG("INFO: Product ID: STTS751-0\n");
break;
case STTS751_1_PROD_ID:
DEBUG_MSG("INFO: Product ID: STTS751-1\n");
break;
default:
printf("ERROR: Product ID: UNKNOWN\n");
return LGW_I2C_ERROR;
}
/* Get Manufacturer ID */
err = i2c_linuxdev_read( i2c_fd, I2C_PORT_TEMP_SENSOR, STTS751_REG_MAN_ID, &val );
if ( err != 0 )
{
printf( "ERROR: failed to read I2C device 0x%02X (err=%i)\n", I2C_PORT_TEMP_SENSOR, err );
return LGW_I2C_ERROR;
}
if ( val != ST_MAN_ID )
{
printf( "ERROR: Manufacturer ID: UNKNOWN\n" );
return LGW_I2C_ERROR;
}
else
{
DEBUG_PRINTF("INFO: Manufacturer ID: 0x%02X\n", val);
}
/* Get revision number */
err = i2c_linuxdev_read( i2c_fd, I2C_PORT_TEMP_SENSOR, STTS751_REG_REV_ID, &val );
if ( err != 0 )
{
printf( "ERROR: failed to read I2C device 0x%02X (err=%i)\n", I2C_PORT_TEMP_SENSOR, err );
return LGW_I2C_ERROR;
}
DEBUG_PRINTF("INFO: Revision number: 0x%02X\n", val);
/* Set conversion resolution to 12 bits */
err = i2c_linuxdev_write( i2c_fd, I2C_PORT_TEMP_SENSOR, STTS751_REG_CONF, 0x8C ); /* TODO: do not hardcode the whole byte */
if ( err != 0 )
{
printf( "ERROR: failed to write I2C device 0x%02X (err=%i)\n", I2C_PORT_TEMP_SENSOR, err );
return LGW_I2C_ERROR;
}
/* Set conversion rate to 1 / second */
err = i2c_linuxdev_write( i2c_fd, I2C_PORT_TEMP_SENSOR, STTS751_REG_RATE, 0x04 );
if ( err != 0 )
{
printf( "ERROR: failed to write I2C device 0x%02X (err=%i)\n", I2C_PORT_TEMP_SENSOR, err );
return LGW_I2C_ERROR;
}
return LGW_I2C_SUCCESS;
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
int stts751_get_temperature( int i2c_fd, float * temperature)
{
int err;
uint8_t high_byte, low_byte;
int8_t h;
/* Check Input Params */
if( i2c_fd <= 0 )
{
printf( "ERROR: invalid I2C file descriptor\n" );
return LGW_I2C_ERROR;
}
/* Read Temperature LSB */
err = i2c_linuxdev_read( i2c_fd, I2C_PORT_TEMP_SENSOR, STTS751_REG_TEMP_L, &low_byte );
if ( err != 0 )
{
printf( "ERROR: failed to read I2C device 0x%02X (err=%i)\n", I2C_PORT_TEMP_SENSOR, err );
return LGW_I2C_ERROR;
}
/* Read Temperature MSB */
err = i2c_linuxdev_read( i2c_fd, I2C_PORT_TEMP_SENSOR, STTS751_REG_TEMP_H, &high_byte );
if ( err != 0 )
{
printf( "ERROR: failed to read I2C device 0x%02X (err=%i)\n", I2C_PORT_TEMP_SENSOR, err );
return LGW_I2C_ERROR;
}
h = (int8_t)high_byte;
*temperature = ((h << 8) | low_byte) / 256.0;
DEBUG_PRINTF("Temperature: %f C (h:0x%02X l:0x%02X)\n", *temperature, high_byte, low_byte);
return LGW_I2C_SUCCESS;
}
/* -------------------------------------------------------------------------- */
/* --- PUBLIC FUNCTIONS DEFINITION ------------------------------------------ */
int lgw_stts751_configure(void) {
return stts751_configure(lgw_i2c_target);
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
int lgw_stts751_get_temperature(float * temperature) {
return stts751_get_temperature(lgw_i2c_target, temperature);
}
/* --- EOF ------------------------------------------------------------------ */

View File

@ -0,0 +1,285 @@
/*
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(C)2019 Semtech
Description:
Functions used to handle LoRa concentrator SX1250 radios.
License: Revised BSD License, see LICENSE.TXT file include in the project
*/
/* -------------------------------------------------------------------------- */
/* --- DEPENDANCIES --------------------------------------------------------- */
#include <stdint.h> /* C99 types */
#include <stdio.h> /* printf fprintf */
#include <stdlib.h> /* malloc free */
#include <unistd.h> /* lseek, close */
#include <fcntl.h> /* open */
#include <string.h> /* memset */
#include <sys/ioctl.h>
#include <linux/spi/spidev.h>
#include "loragw_spi.h"
#include "loragw_reg.h"
#include "loragw_aux.h"
#include "loragw_sx1250.h"
/* -------------------------------------------------------------------------- */
/* --- PRIVATE MACROS ------------------------------------------------------- */
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
#if DEBUG_RAD == 1
#define DEBUG_MSG(str) fprintf(stderr, str)
#define DEBUG_PRINTF(fmt, args...) fprintf(stderr,"%s:%d: "fmt, __FUNCTION__, __LINE__, args)
#define CHECK_NULL(a) if(a==NULL){fprintf(stderr,"%s:%d: ERROR: NULL POINTER AS ARGUMENT\n", __FUNCTION__, __LINE__);return LGW_SPI_ERROR;}
#else
#define DEBUG_MSG(str)
#define DEBUG_PRINTF(fmt, args...)
#define CHECK_NULL(a) if(a==NULL){return LGW_SPI_ERROR;}
#endif
#define SX1250_FREQ_TO_REG(f) (uint32_t)((uint64_t)f * (1 << 25) / 32000000U)
/* -------------------------------------------------------------------------- */
/* --- PRIVATE CONSTANTS ---------------------------------------------------- */
#define WAIT_BUSY_SX1250_MS 1
/* -------------------------------------------------------------------------- */
/* --- INTERNAL SHARED VARIABLES -------------------------------------------- */
extern void *lgw_spi_target; /*! generic pointer to the SPI device */
/* -------------------------------------------------------------------------- */
/* --- PUBLIC FUNCTIONS DEFINITION ------------------------------------------ */
int sx1250_write_command(uint8_t rf_chain, sx1250_op_code_t op_code, uint8_t *data, uint16_t size) {
int spi_device;
int cmd_size = 2; /* header + op_code */
uint8_t out_buf[cmd_size + size];
uint8_t command_size;
struct spi_ioc_transfer k;
int a, i;
/* wait BUSY */
wait_ms(WAIT_BUSY_SX1250_MS);
/* check input variables */
CHECK_NULL(lgw_spi_target);
spi_device = *(int *)lgw_spi_target; /* must check that spi_target is not null beforehand */
/* prepare frame to be sent */
out_buf[0] = (rf_chain == 0) ? LGW_SPI_MUX_TARGET_RADIOA : LGW_SPI_MUX_TARGET_RADIOB;
out_buf[1] = (uint8_t)op_code;
for(i = 0; i < (int)size; i++) {
out_buf[cmd_size + i] = data[i];
}
command_size = cmd_size + size;
/* I/O transaction */
memset(&k, 0, sizeof(k)); /* clear k */
k.tx_buf = (unsigned long) out_buf;
k.len = command_size;
k.speed_hz = SPI_SPEED;
k.cs_change = 0;
k.bits_per_word = 8;
a = ioctl(spi_device, SPI_IOC_MESSAGE(1), &k);
/* determine return code */
if (a != (int)k.len) {
DEBUG_MSG("ERROR: SPI WRITE FAILURE\n");
return LGW_SPI_ERROR;
} else {
DEBUG_MSG("Note: SPI write success\n");
return LGW_SPI_SUCCESS;
}
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
int sx1250_read_command(uint8_t rf_chain, sx1250_op_code_t op_code, uint8_t *data, uint16_t size) {
int spi_device;
int cmd_size = 2; /* header + op_code + NOP */
uint8_t out_buf[cmd_size + size];
uint8_t command_size;
uint8_t in_buf[ARRAY_SIZE(out_buf)];
struct spi_ioc_transfer k;
int a, i;
/* wait BUSY */
wait_ms(WAIT_BUSY_SX1250_MS);
/* check input variables */
CHECK_NULL(lgw_spi_target);
CHECK_NULL(data);
spi_device = *(int *)lgw_spi_target; /* must check that spi_target is not null beforehand */
/* prepare frame to be sent */
out_buf[0] = (rf_chain == 0) ? LGW_SPI_MUX_TARGET_RADIOA : LGW_SPI_MUX_TARGET_RADIOB;
out_buf[1] = (uint8_t)op_code;
for(i = 0; i < (int)size; i++) {
out_buf[cmd_size + i] = data[i];
}
command_size = cmd_size + size;
/* I/O transaction */
memset(&k, 0, sizeof(k)); /* clear k */
k.tx_buf = (unsigned long) out_buf;
k.rx_buf = (unsigned long) in_buf;
k.len = command_size;
k.cs_change = 0;
a = ioctl(spi_device, SPI_IOC_MESSAGE(1), &k);
/* determine return code */
if (a != (int)k.len) {
DEBUG_MSG("ERROR: SPI READ FAILURE\n");
return LGW_SPI_ERROR;
} else {
DEBUG_MSG("Note: SPI read success\n");
//*data = in_buf[command_size - 1];
memcpy(data, in_buf + cmd_size, size);
return LGW_SPI_SUCCESS;
}
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
int sx1250_calibrate(uint8_t rf_chain, uint32_t freq_hz) {
uint8_t buff[16];
buff[0] = 0x00;
sx1250_read_command(rf_chain, GET_STATUS, buff, 1);
/* Run calibration */
if ((freq_hz > 430E6) && (freq_hz < 440E6)) {
buff[0] = 0x6B;
buff[1] = 0x6F;
} else if ((freq_hz > 470E6) && (freq_hz < 510E6)) {
buff[0] = 0x75;
buff[1] = 0x81;
} else if ((freq_hz > 779E6) && (freq_hz < 787E6)) {
buff[0] = 0xC1;
buff[1] = 0xC5;
} else if ((freq_hz > 863E6) && (freq_hz < 870E6)) {
buff[0] = 0xD7;
buff[1] = 0xDB;
} else if ((freq_hz > 902E6) && (freq_hz < 928E6)) {
buff[0] = 0xE1;
buff[1] = 0xE9;
} else {
printf("ERROR: failed to calibrate sx1250 radio, frequency range not supported (%u)\n", freq_hz);
return -1;
}
sx1250_write_command(rf_chain, CALIBRATE_IMAGE, buff, 2);
/* Wait for calibration to complete */
wait_ms(10);
buff[0] = 0x00;
buff[1] = 0x00;
buff[2] = 0x00;
sx1250_read_command(rf_chain, GET_DEVICE_ERRORS, buff, 3);
if (TAKE_N_BITS_FROM(buff[2], 4, 1) != 0) {
printf("ERROR: sx1250 Image Calibration Error\n");
return -1;
}
return 0;
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
int sx1250_setup(uint8_t rf_chain, uint32_t freq_hz) {
int32_t freq_reg;
uint8_t buff[16];
/* Set Radio in Standby mode */
buff[0] = (uint8_t)STDBY_XOSC;
sx1250_write_command(rf_chain, SET_STANDBY, buff, 1);
wait_ms(10);
buff[0] = 0x00;
sx1250_read_command(rf_chain, GET_STATUS, buff, 1);
/* Set Bitrate to maximum (to lower TX to FS switch time) */
buff[0] = 0x06;
buff[1] = 0xA1;
buff[2] = 0x01;
sx1250_write_command(rf_chain, WRITE_REGISTER, buff, 3);
buff[0] = 0x06;
buff[1] = 0xA2;
buff[2] = 0x00;
sx1250_write_command(rf_chain, WRITE_REGISTER, buff, 3);
buff[0] = 0x06;
buff[1] = 0xA3;
buff[2] = 0x00;
sx1250_write_command(rf_chain, WRITE_REGISTER, buff, 3);
/* Configure DIO for Rx */
buff[0] = 0x05;
buff[1] = 0x82;
buff[2] = 0x00;
sx1250_write_command(rf_chain, WRITE_REGISTER, buff, 3); /* Drive strength to min */
buff[0] = 0x05;
buff[1] = 0x83;
buff[2] = 0x00;
sx1250_write_command(rf_chain, WRITE_REGISTER, buff, 3); /* Input enable, all disabled */
buff[0] = 0x05;
buff[1] = 0x84;
buff[2] = 0x00;
sx1250_write_command(rf_chain, WRITE_REGISTER, buff, 3); /* No pull up */
buff[0] = 0x05;
buff[1] = 0x85;
buff[2] = 0x00;
sx1250_write_command(rf_chain, WRITE_REGISTER, buff, 3); /* No pull down */
buff[0] = 0x05;
buff[1] = 0x80;
buff[2] = 0x00;
sx1250_write_command(rf_chain, WRITE_REGISTER, buff, 3); /* Output enable, all enabled */
/* Set fix gain (??) */
buff[0] = 0x08;
buff[1] = 0xB6;
buff[2] = 0x2A;
sx1250_write_command(rf_chain, WRITE_REGISTER, buff, 3);
/* Set frequency */
freq_reg = SX1250_FREQ_TO_REG(freq_hz);
buff[0] = (uint8_t)(freq_reg >> 24);
buff[1] = (uint8_t)(freq_reg >> 16);
buff[2] = (uint8_t)(freq_reg >> 8);
buff[3] = (uint8_t)(freq_reg >> 0);
sx1250_write_command(rf_chain, SET_RF_FREQUENCY, buff, 4);
/* Set frequency offset to 0 */
buff[0] = 0x08;
buff[1] = 0x8F;
buff[2] = 0x00;
buff[3] = 0x00;
buff[4] = 0x00;
sx1250_write_command(rf_chain, WRITE_REGISTER, buff, 5);
/* Set Radio in Rx mode, necessary to give a clock to SX1302 */
buff[0] = 0xFF;
buff[1] = 0xFF;
buff[2] = 0xFF;
sx1250_write_command(rf_chain, SET_RX, buff, 3); /* Rx Continuous */
buff[0] = 0x05;
buff[1] = 0x87;
buff[2] = 0x0B;
sx1250_write_command(rf_chain, WRITE_REGISTER, buff, 3); /* FPGA_MODE_RX */
return 0;
}
/* --- EOF ------------------------------------------------------------------ */

View File

@ -0,0 +1,384 @@
/*
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(C)2019 Semtech
Description:
Functions used to handle LoRa concentrator SX1255/SX1257 radios.
License: Revised BSD License, see LICENSE.TXT file include in the project
*/
/* -------------------------------------------------------------------------- */
/* --- DEPENDANCIES --------------------------------------------------------- */
#include <stdint.h> /* C99 types */
#include <stdbool.h> /* bool type */
#include <stdio.h> /* printf fprintf */
#include <string.h> /* memset */
#include <sys/ioctl.h>
#include <linux/spi/spidev.h>
#include "loragw_sx125x.h"
#include "loragw_spi.h"
#include "loragw_aux.h"
#include "loragw_reg.h"
#include "loragw_hal.h"
/* -------------------------------------------------------------------------- */
/* --- PRIVATE MACROS ------------------------------------------------------- */
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
#if DEBUG_RAD == 1
#define DEBUG_MSG(str) fprintf(stderr, str)
#define DEBUG_PRINTF(fmt, args...) fprintf(stderr,"%s:%d: "fmt, __FUNCTION__, __LINE__, args)
#define CHECK_NULL(a) if(a==NULL){fprintf(stderr,"%s:%d: ERROR: NULL POINTER AS ARGUMENT\n", __FUNCTION__, __LINE__);return LGW_REG_ERROR;}
#else
#define DEBUG_MSG(str)
#define DEBUG_PRINTF(fmt, args...)
#define CHECK_NULL(a) if(a==NULL){return LGW_REG_ERROR;}
#endif
/* -------------------------------------------------------------------------- */
/* --- PRIVATE TYPES -------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* --- PRIVATE CONSTANTS ---------------------------------------------------- */
#define PLL_LOCK_MAX_ATTEMPTS 5
#define READ_ACCESS 0x00
#define WRITE_ACCESS 0x80
static const struct radio_reg_s sx125x_regs[RADIO_TOTALREGS] = {
{0,0,8}, /* MODE */
{0,3,1}, /* MODE__PA_DRIVER_EN */
{0,2,1}, /* MODE__TX_EN */
{0,1,1}, /* MODE__RX_EN */
{0,0,1}, /* MODE__STANDBY_EN */
{1,0,8}, /* FRF_RX_MSB */
{2,0,8}, /* FRF_RX_MID */
{3,0,8}, /* FRF_RX_LSB */
{4,0,8}, /* FRF_TX_MSB */
{5,0,8}, /* FRF_TX_MID */
{6,0,8}, /* FRF_TX_LSB */
{7,0,8}, /* VERSION */
{8,0,8}, /* TX_GAIN */
{8,4,3}, /* TX_GAIN__DAC_GAIN */
{8,0,4}, /* TX_GAIN__MIX_GAIN */
{10,0,8}, /* TX_BW */
{10,5,2}, /* TX_BW__PLL_BW */
{10,0,5}, /* TX_BW__ANA_BW */
{11,0,8}, /* TX_DAC_BW */
{12,0,8}, /* RX_ANA_GAIN */
{12,5,3}, /* RX_ANA_GAIN__LNA_GAIN */
{12,1,4}, /* RX_ANA_GAIN__BB_GAIN */
{12,0,1}, /* RX_ANA_GAIN__LNA_ZIN */
{13,0,8}, /* RX_BW */
{13,5,3}, /* RX_BW__ADC_BW */
{13,2,3}, /* RX_BW__ADC_TRIM */
{13,0,2}, /* RX_BW__BB_BW */
{14,0,8}, /* RX_PLL_BW */
{14,1,2}, /* RX_PLL_BW__PLL_BW */
{14,0,1}, /* RX_PLL_BW__ADC_TEMP_EN */
{15,0,8}, /* DIO_MAPPING */
{15,6,2}, /* DIO_MAPPING__DIO_0_MAPPING */
{15,4,2}, /* DIO_MAPPING__DIO_1_MAPPING */
{15,2,2}, /* DIO_MAPPING__DIO_2_MAPPING */
{15,0,2}, /* DIO_MAPPING__DIO_3_MAPPING */
{16,0,8}, /* CLK_SELECT */
{16,3,1}, /* CLK_SELECT__DIG_LOOPBACK_EN */
{16,2,1}, /* CLK_SELECT__RF_LOOPBACK_EN */
{16,1,1}, /* CLK_SELECT__CLK_OUT */
{16,0,1}, /* CLK_SELECT__DAC_CLK_SELECT */
{17,0,8}, /* MODE_STATUS */
{17,2,1}, /* MODE_STATUS__LOW_BAT_EN */
{17,1,1}, /* MODE_STATUS__RX_PLL_LOCKED */
{17,0,1}, /* MODE_STATUS__TX_PLL_LOCKED */
{26,0,8}, /* LOW_BAT_THRESH */
{38,0,8}, /* SX1257_XOSC_TEST */
{38,4,3}, /* SX1257_XOSC_TEST__DISABLE */
{38,0,4}, /* SX1257_XOSC_TEST__GM_STARTUP */
{40,0,8}, /* SX1255_XOSC_TEST */
{40,4,3}, /* SX1255_XOSC_TEST__DISABLE */
{40,0,4} /* SX1255_XOSC_TEST__GM_STARTUP */
};
/* -------------------------------------------------------------------------- */
/* --- PRIVATE VARIABLES ---------------------------------------------------- */
extern void *lgw_spi_target; /*! generic pointer to the SPI device */
/* -------------------------------------------------------------------------- */
/* --- PRIVATE FUNCTIONS ---------------------------------------------------- */
/* Simple read */
int sx125x_reg_r(void *spi_target, uint8_t spi_mux_target, uint8_t address, uint8_t *data) {
int spi_device;
uint8_t out_buf[3];
uint8_t command_size;
uint8_t in_buf[ARRAY_SIZE(out_buf)];
struct spi_ioc_transfer k;
int a;
/* check input variables */
CHECK_NULL(spi_target);
CHECK_NULL(data);
spi_device = *(int *)spi_target; /* must check that spi_target is not null beforehand */
/* prepare frame to be sent */
out_buf[0] = spi_mux_target;
out_buf[1] = READ_ACCESS | (address & 0x7F);
out_buf[2] = 0x00;
command_size = 3;
/* I/O transaction */
memset(&k, 0, sizeof(k)); /* clear k */
k.tx_buf = (unsigned long) out_buf;
k.rx_buf = (unsigned long) in_buf;
k.len = command_size;
k.cs_change = 0;
a = ioctl(spi_device, SPI_IOC_MESSAGE(1), &k);
/* determine return code */
if (a != (int)k.len) {
DEBUG_MSG("ERROR: SPI READ FAILURE\n");
return LGW_SPI_ERROR;
} else {
//DEBUG_MSG("Note: SPI read success\n");
*data = in_buf[command_size - 1];
return LGW_SPI_SUCCESS;
}
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
int sx125x_reg_w(void *spi_target, uint8_t spi_mux_target, uint8_t address, uint8_t data) {
int spi_device;
uint8_t out_buf[3];
uint8_t command_size;
struct spi_ioc_transfer k;
int a;
/* check input variables */
CHECK_NULL(spi_target);
spi_device = *(int *)spi_target; /* must check that spi_target is not null beforehand */
/* prepare frame to be sent */
out_buf[0] = spi_mux_target;
out_buf[1] = WRITE_ACCESS | (address & 0x7F);
out_buf[2] = data;
command_size = 3;
/* I/O transaction */
memset(&k, 0, sizeof(k)); /* clear k */
k.tx_buf = (unsigned long) out_buf;
k.len = command_size;
k.speed_hz = SPI_SPEED;
k.cs_change = 0;
k.bits_per_word = 8;
a = ioctl(spi_device, SPI_IOC_MESSAGE(1), &k);
/* determine return code */
if (a != (int)k.len) {
DEBUG_MSG("ERROR: SPI WRITE FAILURE\n");
return LGW_SPI_ERROR;
} else {
//DEBUG_MSG("Note: SPI write success\n");
return LGW_SPI_SUCCESS;
}
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
int lgw_sx125x_reg_w(radio_reg_t idx, uint8_t data, uint8_t rf_chain) {
int spi_stat;
struct radio_reg_s reg;
uint8_t mask;
uint8_t r;
uint8_t w;
uint8_t val_check;
/* checking input parameters */
if (rf_chain >= LGW_RF_CHAIN_NB) {
DEBUG_MSG("ERROR: INVALID RF_CHAIN\n");
return LGW_REG_ERROR;
}
if (idx >= RADIO_TOTALREGS) {
DEBUG_MSG("ERROR: REGISTER NUMBER OUT OF DEFINED RANGE\n");
return LGW_REG_ERROR;
}
reg = sx125x_regs[idx];
if ((reg.leng == 8) && (reg.offs == 0)){
/* direct write */
spi_stat = sx125x_reg_w(lgw_spi_target, ((rf_chain == 0) ? LGW_SPI_MUX_TARGET_RADIOA : LGW_SPI_MUX_TARGET_RADIOB), reg.addr, data);
} else {
/* read-modify-write */
spi_stat = sx125x_reg_r(lgw_spi_target, ((rf_chain == 0) ? LGW_SPI_MUX_TARGET_RADIOA : LGW_SPI_MUX_TARGET_RADIOB), reg.addr, &r);
mask = ((1 << reg.leng) - 1) << reg.offs;
w = (r & ~mask) | ((data << reg.offs) & mask);
spi_stat |= sx125x_reg_w(lgw_spi_target, ((rf_chain == 0) ? LGW_SPI_MUX_TARGET_RADIOA : LGW_SPI_MUX_TARGET_RADIOB), reg.addr, w);
}
/* Check that we can read what we have written */
lgw_sx125x_reg_r(idx, &val_check, rf_chain);
if (val_check != data) {
printf("ERROR: sx125x register %d write failed (w:%u r:%u)!!\n", idx, data, val_check);
spi_stat = LGW_SPI_ERROR;
}
if (spi_stat != LGW_SPI_SUCCESS) {
DEBUG_MSG("ERROR: SPI ERROR DURING RADIO REGISTER WRITE\n");
return LGW_REG_ERROR;
} else {
return LGW_REG_SUCCESS;
}
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
int lgw_sx125x_reg_r(radio_reg_t idx, uint8_t *data, uint8_t rf_chain) {
int spi_stat;
struct radio_reg_s reg;
uint8_t mask;
uint8_t r;
/* checking input parameters */
if (rf_chain >= LGW_RF_CHAIN_NB) {
DEBUG_MSG("ERROR: INVALID RF_CHAIN\n");
return LGW_REG_ERROR;
}
if (idx >= RADIO_TOTALREGS) {
DEBUG_MSG("ERROR: REGISTER NUMBER OUT OF DEFINED RANGE\n");
return LGW_REG_ERROR;
}
reg = sx125x_regs[idx];
spi_stat = sx125x_reg_r(lgw_spi_target, ((rf_chain == 0) ? LGW_SPI_MUX_TARGET_RADIOA : LGW_SPI_MUX_TARGET_RADIOB), reg.addr, &r);
mask = ((1 << reg.leng) - 1) << reg.offs;
*data = (r & mask) >> reg.offs;
if (spi_stat != LGW_SPI_SUCCESS) {
DEBUG_MSG("ERROR: SPI ERROR DURING RADIO REGISTER READ\n");
return LGW_REG_ERROR;
} else {
return LGW_REG_SUCCESS;
}
}
/* -------------------------------------------------------------------------- */
/* --- PRIVATE FUNCTIONS DEFINITION ----------------------------------------- */
/* -------------------------------------------------------------------------- */
/* --- PUBLIC FUNCTIONS DEFINITION ------------------------------------------ */
int sx125x_setup(uint8_t rf_chain, uint8_t rf_clkout, bool rf_enable, uint8_t rf_radio_type, uint32_t freq_hz) {
uint32_t part_int = 0;
uint32_t part_frac = 0;
int cpt_attempts = 0;
uint8_t val;
if (rf_chain >= LGW_RF_CHAIN_NB) {
DEBUG_MSG("ERROR: INVALID RF_CHAIN\n");
return -1;
}
/* Get version to identify SX1255/57 silicon revision */
lgw_sx125x_reg_r(SX125x_REG_VERSION, &val, rf_chain);
DEBUG_PRINTF("Note: SX125x #%d version register returned 0x%02x\n", rf_chain, val);
/* General radio setup */
if (rf_clkout == rf_chain) {
lgw_sx125x_reg_w(SX125x_REG_CLK_SELECT, SX125x_TX_DAC_CLK_SEL + 2, rf_chain);
DEBUG_PRINTF("Note: SX125x #%d clock output enabled\n", rf_chain);
} else {
lgw_sx125x_reg_w(SX125x_REG_CLK_SELECT, SX125x_TX_DAC_CLK_SEL, rf_chain);
DEBUG_PRINTF("Note: SX125x #%d clock output disabled\n", rf_chain);
}
switch (rf_radio_type) {
case LGW_RADIO_TYPE_SX1255:
lgw_sx125x_reg_w(SX125x_REG_SX1255_XOSC_TEST__GM_STARTUP, SX125x_XOSC_GM_STARTUP, rf_chain);
lgw_sx125x_reg_w(SX125x_REG_SX1255_XOSC_TEST__DISABLE, SX125x_XOSC_DISABLE, rf_chain);
break;
case LGW_RADIO_TYPE_SX1257:
lgw_sx125x_reg_w(SX125x_REG_SX1257_XOSC_TEST__GM_STARTUP, SX125x_XOSC_GM_STARTUP, rf_chain);
lgw_sx125x_reg_w(SX125x_REG_SX1257_XOSC_TEST__DISABLE, SX125x_XOSC_DISABLE, rf_chain);
break;
default:
DEBUG_PRINTF("ERROR: UNEXPECTED VALUE %d FOR RADIO TYPE\n", rf_radio_type);
break;
}
if (rf_enable == true) {
/* Tx gain and trim */
lgw_sx125x_reg_w(SX125x_REG_TX_GAIN__MIX_GAIN, SX125x_TX_MIX_GAIN, rf_chain);
lgw_sx125x_reg_w(SX125x_REG_TX_GAIN__DAC_GAIN, SX125x_TX_DAC_GAIN, rf_chain);
lgw_sx125x_reg_w(SX125x_REG_TX_BW__ANA_BW, SX125x_TX_ANA_BW, rf_chain);
lgw_sx125x_reg_w(SX125x_REG_TX_BW__PLL_BW, SX125x_TX_PLL_BW, rf_chain);
lgw_sx125x_reg_w(SX125x_REG_TX_DAC_BW, SX125x_TX_DAC_BW, rf_chain);
/* Rx gain and trim */
lgw_sx125x_reg_w(SX125x_REG_RX_ANA_GAIN__LNA_ZIN, SX125x_LNA_ZIN, rf_chain);
lgw_sx125x_reg_w(SX125x_REG_RX_ANA_GAIN__BB_GAIN, SX125x_RX_BB_GAIN, rf_chain);
lgw_sx125x_reg_w(SX125x_REG_RX_ANA_GAIN__LNA_GAIN, SX125x_RX_LNA_GAIN, rf_chain);
lgw_sx125x_reg_w(SX125x_REG_RX_BW__BB_BW, SX125x_RX_BB_BW, rf_chain);
lgw_sx125x_reg_w(SX125x_REG_RX_BW__ADC_TRIM, SX125x_RX_ADC_TRIM, rf_chain);
lgw_sx125x_reg_w(SX125x_REG_RX_BW__ADC_BW, SX125x_RX_ADC_BW, rf_chain);
lgw_sx125x_reg_w(SX125x_REG_RX_PLL_BW__ADC_TEMP_EN, SX125x_ADC_TEMP, rf_chain);
lgw_sx125x_reg_w(SX125x_REG_RX_PLL_BW__PLL_BW, SX125x_RX_PLL_BW, rf_chain);
/* set RX PLL frequency */
switch (rf_radio_type) {
case LGW_RADIO_TYPE_SX1255:
part_int = freq_hz / (SX125x_32MHz_FRAC << 7); /* integer part, gives the MSB */
part_frac = ((freq_hz % (SX125x_32MHz_FRAC << 7)) << 9) / SX125x_32MHz_FRAC; /* fractional part, gives middle part and LSB */
break;
case LGW_RADIO_TYPE_SX1257:
part_int = freq_hz / (SX125x_32MHz_FRAC << 8); /* integer part, gives the MSB */
part_frac = ((freq_hz % (SX125x_32MHz_FRAC << 8)) << 8) / SX125x_32MHz_FRAC; /* fractional part, gives middle part and LSB */
break;
default:
DEBUG_PRINTF("ERROR: UNEXPECTED VALUE %d FOR RADIO TYPE\n", rf_radio_type);
break;
}
lgw_sx125x_reg_w(SX125x_REG_FRF_RX_MSB, 0xFF & part_int, rf_chain);
lgw_sx125x_reg_w(SX125x_REG_FRF_RX_MID, 0xFF & (part_frac >> 8), rf_chain);
lgw_sx125x_reg_w(SX125x_REG_FRF_RX_LSB, 0xFF & part_frac, rf_chain);
/* start and PLL lock */
do {
if (cpt_attempts >= PLL_LOCK_MAX_ATTEMPTS) {
DEBUG_MSG("ERROR: FAIL TO LOCK PLL\n");
return -1;
}
lgw_sx125x_reg_w(SX125x_REG_MODE, 1, rf_chain);
lgw_sx125x_reg_w(SX125x_REG_MODE, 3, rf_chain);
++cpt_attempts;
DEBUG_PRINTF("Note: SX125x #%d PLL start (attempt %d)\n", rf_chain, cpt_attempts);
wait_ms(1);
lgw_sx125x_reg_r(SX125x_REG_MODE_STATUS, &val, rf_chain);
} while ((val & 0x02) == 0);
} else {
DEBUG_PRINTF("Note: SX125x #%d kept in standby mode\n", rf_chain);
}
return 0;
}
/* --- EOF ------------------------------------------------------------------ */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,370 @@
/*
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(C)2019 Semtech
Description:
SX1302 RX buffer Hardware Abstraction Layer
License: Revised BSD License, see LICENSE.TXT file include in the project
*/
/* -------------------------------------------------------------------------- */
/* --- DEPENDANCIES --------------------------------------------------------- */
#include <stdint.h> /* C99 types */
#include <stdio.h> /* printf fprintf */
#include <string.h> /* memset */
#include <assert.h> /* assert */
#include "loragw_aux.h"
#include "loragw_reg.h"
#include "loragw_sx1302_rx.h"
#include "loragw_sx1302_timestamp.h"
/* -------------------------------------------------------------------------- */
/* --- PRIVATE MACROS ------------------------------------------------------- */
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
#if DEBUG_SX1302 == 1
#define DEBUG_MSG(str) fprintf(stderr, str)
#define DEBUG_PRINTF(fmt, args...) fprintf(stderr, fmt, args)
#define CHECK_NULL(a) if(a==NULL){fprintf(stderr,"%s:%d: ERROR: NULL POINTER AS ARGUMENT\n", __FUNCTION__, __LINE__);return LGW_REG_ERROR;}
#else
#define DEBUG_MSG(str)
#define DEBUG_PRINTF(fmt, args...)
#define CHECK_NULL(a) if(a==NULL){return LGW_REG_ERROR;}
#endif
#define SX1302_PKT_PAYLOAD_LENGTH(buffer, start_index) TAKE_N_BITS_FROM(buffer[start_index + 2], 0, 8)
#define SX1302_PKT_CHANNEL(buffer, start_index) TAKE_N_BITS_FROM(buffer[start_index + 3], 0, 8)
#define SX1302_PKT_CRC_EN(buffer, start_index) TAKE_N_BITS_FROM(buffer[start_index + 4], 0, 1)
#define SX1302_PKT_CODING_RATE(buffer, start_index) TAKE_N_BITS_FROM(buffer[start_index + 4], 1, 3)
#define SX1302_PKT_DATARATE(buffer, start_index) TAKE_N_BITS_FROM(buffer[start_index + 4], 4, 4)
#define SX1302_PKT_MODEM_ID(buffer, start_index) TAKE_N_BITS_FROM(buffer[start_index + 5], 0, 8)
#define SX1302_PKT_FREQ_OFFSET_7_0(buffer, start_index) TAKE_N_BITS_FROM(buffer[start_index + 6], 0, 8)
#define SX1302_PKT_FREQ_OFFSET_15_8(buffer, start_index) TAKE_N_BITS_FROM(buffer[start_index + 7], 0, 8)
#define SX1302_PKT_FREQ_OFFSET_19_16(buffer, start_index) TAKE_N_BITS_FROM(buffer[start_index + 8], 0, 4)
#define SX1302_PKT_CRC_ERROR(buffer, start_index) TAKE_N_BITS_FROM(buffer[start_index + 9], 0, 1)
#define SX1302_PKT_SYNC_ERROR(buffer, start_index) TAKE_N_BITS_FROM(buffer[start_index + 9], 2, 1)
#define SX1302_PKT_HEADER_ERROR(buffer, start_index) TAKE_N_BITS_FROM(buffer[start_index + 9], 3, 1)
#define SX1302_PKT_TIMING_SET(buffer, start_index) TAKE_N_BITS_FROM(buffer[start_index + 9], 4, 1)
#define SX1302_PKT_SNR_AVG(buffer, start_index) TAKE_N_BITS_FROM(buffer[start_index + 10], 0, 8)
#define SX1302_PKT_RSSI_CHAN(buffer, start_index) TAKE_N_BITS_FROM(buffer[start_index + 11], 0, 8)
#define SX1302_PKT_RSSI_SIG(buffer, start_index) TAKE_N_BITS_FROM(buffer[start_index + 12], 0, 8)
#define SX1302_PKT_RSSI_CHAN_MAX_NEG_DELTA(buffer, start_index) TAKE_N_BITS_FROM(buffer[start_index + 13], 0, 4)
#define SX1302_PKT_RSSI_CHAN_MAX_POS_DELTA(buffer, start_index) TAKE_N_BITS_FROM(buffer[start_index + 13], 4, 4)
#define SX1302_PKT_RSSI_SIG_MAX_NEG_DELTA(buffer, start_index) TAKE_N_BITS_FROM(buffer[start_index + 14], 0, 4)
#define SX1302_PKT_RSSI_SIG_MAX_POS_DELTA(buffer, start_index) TAKE_N_BITS_FROM(buffer[start_index + 14], 4, 4)
#define SX1302_PKT_TIMESTAMP_7_0(buffer, start_index) TAKE_N_BITS_FROM(buffer[start_index + 15], 0, 8)
#define SX1302_PKT_TIMESTAMP_15_8(buffer, start_index) TAKE_N_BITS_FROM(buffer[start_index + 16], 0, 8)
#define SX1302_PKT_TIMESTAMP_23_16(buffer, start_index) TAKE_N_BITS_FROM(buffer[start_index + 17], 0, 8)
#define SX1302_PKT_TIMESTAMP_31_24(buffer, start_index) TAKE_N_BITS_FROM(buffer[start_index + 18], 0, 8)
#define SX1302_PKT_CRC_PAYLOAD_7_0(buffer, start_index) TAKE_N_BITS_FROM(buffer[start_index + 19], 0, 8)
#define SX1302_PKT_CRC_PAYLOAD_15_8(buffer, start_index) TAKE_N_BITS_FROM(buffer[start_index + 20], 0, 8)
#define SX1302_PKT_NUM_TS_METRICS(buffer, start_index) TAKE_N_BITS_FROM(buffer[start_index + 21], 0, 8)
/* -------------------------------------------------------------------------- */
/* --- PRIVATE TYPES -------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* --- PRIVATE CONSTANTS ---------------------------------------------------- */
/* RX buffer packet structure */
#define SX1302_PKT_SYNCWORD_BYTE_0 0xA5
#define SX1302_PKT_SYNCWORD_BYTE_1 0xC0
#define SX1302_PKT_HEAD_METADATA 9
#define SX1302_PKT_TAIL_METADATA 14
/* modem IDs */
#if FPGA_BOARD_16_CH
#define SX1302_LORA_MODEM_ID_MAX 15
#define SX1302_LORA_STD_MODEM_ID 16
#define SX1302_FSK_MODEM_ID 17
#else
#define SX1302_LORA_MODEM_ID_MAX 11
#define SX1302_LORA_STD_MODEM_ID 12
#define SX1302_FSK_MODEM_ID 13
#endif
/* -------------------------------------------------------------------------- */
/* --- PRIVATE VARIABLES ---------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* --- PRIVATE FUNCTIONS DECLARATION ---------------------------------------- */
/* -------------------------------------------------------------------------- */
/* --- INTERNAL SHARED VARIABLES -------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* --- PRIVATE FUNCTIONS DEFINITION ----------------------------------------- */
/* -------------------------------------------------------------------------- */
/* --- PUBLIC FUNCTIONS DEFINITION ------------------------------------------ */
int rx_buffer_new(rx_buffer_t * self) {
/* Check input params */
CHECK_NULL(self);
/* Initialize members */
memset(self->buffer, 0, sizeof self->buffer);
self->buffer_size = 0;
self->buffer_index = 0;
return LGW_REG_SUCCESS;
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
int rx_buffer_del(rx_buffer_t * self) {
/* Check input params */
CHECK_NULL(self);
/* Reset index & size */
self->buffer_size = 0;
self->buffer_index = 0;
return LGW_REG_SUCCESS;
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
int rx_buffer_fetch(rx_buffer_t * self) {
int i, res;
uint8_t buff[2];
/* Check input params */
CHECK_NULL(self);
/* Check if there is data in the FIFO */
lgw_reg_rb(SX1302_REG_RX_TOP_RX_BUFFER_NB_BYTES_MSB_RX_BUFFER_NB_BYTES, buff, sizeof buff);
self->buffer_size = (uint16_t)((buff[0] << 8) & 0xFF00);
self->buffer_size |= (uint16_t)((buff[1] << 0) & 0x00FF);
/* Fetch bytes from fifo if any */
if (self->buffer_size > 0) {
DEBUG_MSG ("-----------------\n");
DEBUG_PRINTF("%s: nb_bytes to be fetched: %u (%u %u)\n", __FUNCTION__, self->buffer_size, buff[1], buff[0]);
memset(self->buffer, 0, sizeof self->buffer);
res = lgw_mem_rb(0x4000, self->buffer, self->buffer_size, true);
if (res != LGW_REG_SUCCESS) {
printf("ERROR: Failed to read RX buffer, SPI error\n");
return LGW_REG_ERROR;
}
/* print debug info : TODO to be removed */
DEBUG_MSG("RX_BUFFER: ");
for (i = 0; i < self->buffer_size; i++) {
DEBUG_PRINTF("%02X ", self->buffer[i]);
}
DEBUG_MSG("\n");
}
/* Initialize the current buffer index to iterate on */
self->buffer_index = 0;
return LGW_REG_SUCCESS;
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
int rx_buffer_pop(rx_buffer_t * self, rx_packet_t * pkt) {
int i;
uint8_t checksum_rcv, checksum_calc = 0;
uint16_t checksum_idx;
uint16_t pkt_num_bytes;
/* Check input params */
CHECK_NULL(self);
CHECK_NULL(pkt);
/* Is there any data to be parsed ? */
if (self->buffer_index >= self->buffer_size) {
DEBUG_MSG("INFO: No more data to be parsed\n");
return LGW_REG_ERROR;
}
/* Get pkt sync words */
if ((self->buffer[self->buffer_index] != SX1302_PKT_SYNCWORD_BYTE_0) || (self->buffer[self->buffer_index + 1] != SX1302_PKT_SYNCWORD_BYTE_1)) {
printf("INFO: searching syncword...\n");
self->buffer_index += 1;
return LGW_REG_ERROR;
/* TODO: while loop until syncword found ?? */
}
DEBUG_PRINTF("INFO: pkt syncword found at index %u\n", self->buffer_index);
/* Get payload length */
pkt->rxbytenb_modem = SX1302_PKT_PAYLOAD_LENGTH(self->buffer, self->buffer_index);
/* Get fine timestamp metrics */
pkt->num_ts_metrics_stored = SX1302_PKT_NUM_TS_METRICS(self->buffer, self->buffer_index + pkt->rxbytenb_modem);
/* Calculate the total number of bytes in the packet */
pkt_num_bytes = SX1302_PKT_HEAD_METADATA + pkt->rxbytenb_modem + SX1302_PKT_TAIL_METADATA + (2 * pkt->num_ts_metrics_stored);
/* Check if we have a complete packet in the rx buffer fetched */
if((self->buffer_index + pkt_num_bytes) > self->buffer_size) {
printf("WARNING: aborting truncated message (size=%u)\n", self->buffer_size);
return LGW_REG_ERROR;
}
/* Get the checksum as received in the RX buffer */
checksum_idx = pkt_num_bytes - 1;
checksum_rcv = self->buffer[self->buffer_index + pkt_num_bytes - 1];
/* Calculate the checksum from the actual payload bytes received */
for (i = 0; i < (int)checksum_idx; i++) {
checksum_calc += self->buffer[self->buffer_index + i];
}
/* Check if the checksum is correct */
if (checksum_rcv != checksum_calc) {
printf("WARNING: checksum failed (got:0x%02X calc:0x%02X)\n", checksum_rcv, checksum_calc);
return LGW_REG_ERROR;
} else {
DEBUG_PRINTF("Packet checksum OK (0x%02X)\n", checksum_rcv);
}
/* Parse packet metadata */
pkt->modem_id = SX1302_PKT_MODEM_ID(self->buffer, self->buffer_index);
pkt->rx_channel_in = SX1302_PKT_CHANNEL(self->buffer, self->buffer_index);
pkt->crc_en = SX1302_PKT_CRC_EN(self->buffer, self->buffer_index);
pkt->payload_crc_error = SX1302_PKT_CRC_ERROR(self->buffer, self->buffer_index + pkt->rxbytenb_modem);
pkt->sync_error = SX1302_PKT_SYNC_ERROR(self->buffer, self->buffer_index + pkt->rxbytenb_modem);
pkt->header_error = SX1302_PKT_HEADER_ERROR(self->buffer, self->buffer_index + pkt->rxbytenb_modem);
pkt->timing_set = SX1302_PKT_TIMING_SET(self->buffer, self->buffer_index + pkt->rxbytenb_modem);
pkt->coding_rate = SX1302_PKT_CODING_RATE(self->buffer, self->buffer_index);
pkt->rx_rate_sf = SX1302_PKT_DATARATE(self->buffer, self->buffer_index);
pkt->rssi_chan_avg = SX1302_PKT_RSSI_CHAN(self->buffer, self->buffer_index + pkt->rxbytenb_modem);
pkt->rssi_signal_avg = SX1302_PKT_RSSI_SIG(self->buffer, self->buffer_index + pkt->rxbytenb_modem);
pkt->rx_crc16_value = (uint16_t)((SX1302_PKT_CRC_PAYLOAD_7_0(self->buffer, self->buffer_index + pkt->rxbytenb_modem) << 0) & 0x00FF);
pkt->rx_crc16_value |= (uint16_t)((SX1302_PKT_CRC_PAYLOAD_15_8(self->buffer, self->buffer_index + pkt->rxbytenb_modem) << 8) & 0xFF00);
pkt->snr_average = (int8_t)SX1302_PKT_SNR_AVG(self->buffer, self->buffer_index + pkt->rxbytenb_modem);
pkt->frequency_offset_error = (int32_t)((SX1302_PKT_FREQ_OFFSET_19_16(self->buffer, self->buffer_index) << 16) | (SX1302_PKT_FREQ_OFFSET_15_8(self->buffer, self->buffer_index) << 8) | (SX1302_PKT_FREQ_OFFSET_7_0(self->buffer, self->buffer_index) << 0));
if (pkt->frequency_offset_error >= (1<<19)) { /* Handle signed value on 20bits */
pkt->frequency_offset_error = (pkt->frequency_offset_error - (1<<20));
}
/* Packet timestamp (32MHz ) */
pkt->timestamp_cnt = (uint32_t)((SX1302_PKT_TIMESTAMP_7_0(self->buffer, self->buffer_index + pkt->rxbytenb_modem) << 0) & 0x000000FF);
pkt->timestamp_cnt |= (uint32_t)((SX1302_PKT_TIMESTAMP_15_8(self->buffer, self->buffer_index + pkt->rxbytenb_modem) << 8) & 0x0000FF00);
pkt->timestamp_cnt |= (uint32_t)((SX1302_PKT_TIMESTAMP_23_16(self->buffer, self->buffer_index + pkt->rxbytenb_modem) << 16) & 0x00FF0000);
pkt->timestamp_cnt |= (uint32_t)((SX1302_PKT_TIMESTAMP_31_24(self->buffer, self->buffer_index + pkt->rxbytenb_modem) << 24) & 0xFF000000);
#if 0
/* Scale packet timestamp to 1 MHz (microseconds) */
pkt->timestamp_cnt /= 32;
/* Expand 27-bits counter to 32-bits counter, based on current wrapping status */
pkt->timestamp_cnt = timestamp_counter_expand(&counter_us, false, pkt->timestamp_cnt);
#endif
DEBUG_MSG ("-----------------\n");
DEBUG_PRINTF(" modem: %u\n", pkt->modem_id);
DEBUG_PRINTF(" chan: %u\n", pkt->rx_channel_in);
DEBUG_PRINTF(" size: %u\n", pkt->rxbytenb_modem);
DEBUG_PRINTF(" crc_en: %u\n", pkt->crc_en);
DEBUG_PRINTF(" crc_err: %u\n", pkt->payload_crc_error);
DEBUG_PRINTF(" sync_err: %u\n", pkt->sync_error);
DEBUG_PRINTF(" hdr_err: %u\n", pkt->header_error);
DEBUG_PRINTF(" timing_set: %u\n", pkt->timing_set);
DEBUG_PRINTF(" codr: %u\n", pkt->coding_rate);
DEBUG_PRINTF(" datr: %u\n", pkt->rx_rate_sf);
DEBUG_PRINTF(" num_ts: %u\n", pkt->num_ts_metrics_stored);
DEBUG_MSG ("-----------------\n");
/* Sanity checks: check the range of few metadata */
if (pkt->modem_id > SX1302_FSK_MODEM_ID) {
printf("ERROR: modem_id is out of range - %u\n", pkt->modem_id);
return LGW_REG_ERROR;
} else {
if (pkt->modem_id <= SX1302_LORA_STD_MODEM_ID) { /* LoRa modems */
if (pkt->rx_channel_in > 9) {
printf("ERROR: channel is out of range - %u\n", pkt->rx_channel_in);
return LGW_REG_ERROR;
}
if ((pkt->rx_rate_sf < 5) || (pkt->rx_rate_sf > 12)) {
printf("ERROR: SF is out of range - %u\n", pkt->rx_rate_sf);
return LGW_REG_ERROR;
}
} else { /* FSK modem */
/* TODO: not checked */
}
}
/* Parse & copy payload in packet struct */
memcpy((void *)pkt->payload, (void *)(&(self->buffer[self->buffer_index + SX1302_PKT_HEAD_METADATA])), pkt->rxbytenb_modem);
/* move buffer index toward next message */
self->buffer_index += (SX1302_PKT_HEAD_METADATA + pkt->rxbytenb_modem + SX1302_PKT_TAIL_METADATA + (2 * pkt->num_ts_metrics_stored));
return LGW_REG_SUCCESS;
}
/* -------------------------------------------------------------------------- */
/* --- DEBUG FUNCTIONS DEFINITION ------------------------------------------- */
uint16_t rx_buffer_read_ptr_addr(void) {
int32_t val;
uint16_t addr;
lgw_reg_r(SX1302_REG_RX_TOP_RX_BUFFER_LAST_ADDR_READ_MSB_LAST_ADDR_READ, &val); /* mandatory to read MSB first */
addr = (uint16_t)(val << 8);
lgw_reg_r(SX1302_REG_RX_TOP_RX_BUFFER_LAST_ADDR_READ_LSB_LAST_ADDR_READ, &val);
addr |= (uint16_t)val;
return addr;
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
uint16_t rx_buffer_write_ptr_addr(void) {
int32_t val;
uint16_t addr;
lgw_reg_r(SX1302_REG_RX_TOP_RX_BUFFER_LAST_ADDR_WRITE_MSB_LAST_ADDR_WRITE, &val); /* mandatory to read MSB first */
addr = (uint16_t)(val << 8);
lgw_reg_r(SX1302_REG_RX_TOP_RX_BUFFER_LAST_ADDR_WRITE_LSB_LAST_ADDR_WRITE, &val);
addr |= (uint16_t)val;
return addr;
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
void rx_buffer_dump(FILE * file, uint16_t start_addr, uint16_t end_addr) {
int i;
uint8_t rx_buffer_debug[4096];
printf("Dumping %u bytes, from 0x%X to 0x%X\n", end_addr - start_addr + 1, start_addr, end_addr);
memset(rx_buffer_debug, 0, sizeof rx_buffer_debug);
lgw_reg_w(SX1302_REG_RX_TOP_RX_BUFFER_DIRECT_RAM_IF, 1);
lgw_mem_rb(0x4000 + start_addr, rx_buffer_debug, end_addr - start_addr + 1, false);
lgw_reg_w(SX1302_REG_RX_TOP_RX_BUFFER_DIRECT_RAM_IF, 0);
for (i = 0; i < (end_addr - start_addr + 1); i++) {
if (file == NULL) {
printf("%02X ", rx_buffer_debug[i]);
} else {
fprintf(file, "%02X ", rx_buffer_debug[i]);
}
}
if (file == NULL) {
printf("\n");
} else {
fprintf(file, "\n");
}
/* Switching to direct-access memory could lead to corruption, so to be done only for debugging */
assert(0);
}
/* --- EOF ------------------------------------------------------------------ */

View File

@ -0,0 +1,295 @@
/*
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(C)2019 Semtech
Description:
SX1302 timestamp counter Hardware Abstraction Layer
Handles the conversion of a 32-bits 32MHz counter into a 32-bits 1 MHz counter.
This modules MUST be called regularly by the application to maintain counter
wrapping handling for conversion in 1MHz counter.
Provides function to compute the correction to be applied to the received
timestamp for demodulation processing time.
License: Revised BSD License, see LICENSE.TXT file include in the project
*/
/* -------------------------------------------------------------------------- */
/* --- DEPENDANCIES --------------------------------------------------------- */
#include <stdint.h> /* C99 types */
#include <stdio.h> /* printf fprintf */
#include "loragw_sx1302_timestamp.h"
#include "loragw_reg.h"
#include "loragw_hal.h"
#include "loragw_sx1302.h"
/* -------------------------------------------------------------------------- */
/* --- PRIVATE MACROS ------------------------------------------------------- */
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
#if DEBUG_SX1302 == 1
#define DEBUG_MSG(str) fprintf(stderr, str)
#define DEBUG_PRINTF(fmt, args...) fprintf(stderr, fmt, args)
#define CHECK_NULL(a) if(a==NULL){fprintf(stderr,"%s:%d: ERROR: NULL POINTER AS ARGUMENT\n", __FUNCTION__, __LINE__);return LGW_REG_ERROR;}
#else
#define DEBUG_MSG(str)
#define DEBUG_PRINTF(fmt, args...)
#define CHECK_NULL(a) if(a==NULL){return LGW_REG_ERROR;}
#endif
/* -------------------------------------------------------------------------- */
/* --- PRIVATE TYPES -------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* --- PRIVATE CONSTANTS ---------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* --- PRIVATE VARIABLES ---------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* --- PRIVATE FUNCTIONS DECLARATION ---------------------------------------- */
/* -------------------------------------------------------------------------- */
/* --- INTERNAL SHARED VARIABLES -------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* --- PRIVATE FUNCTIONS DEFINITION ----------------------------------------- */
/* -------------------------------------------------------------------------- */
/* --- PUBLIC FUNCTIONS DEFINITION ------------------------------------------ */
void timestamp_counter_new(timestamp_counter_t * self) {
self->counter_us_raw_27bits_inst_prev = 0;
self->counter_us_raw_27bits_pps_prev = 0;
self->counter_us_raw_27bits_inst_wrap = 0;
self->counter_us_raw_27bits_pps_wrap = 0;
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
void timestamp_counter_delete(timestamp_counter_t * self) {
self->counter_us_raw_27bits_inst_prev = 0;
self->counter_us_raw_27bits_pps_prev = 0;
self->counter_us_raw_27bits_inst_wrap = 0;
self->counter_us_raw_27bits_pps_wrap = 0;
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
void timestamp_counter_update(timestamp_counter_t * self, bool pps, uint32_t cnt) {
uint32_t counter_us_raw_27bits_prev;
uint8_t counter_us_raw_27bits_wrap;
/* Get the previous counter value and wrap status */
if (pps == true) {
counter_us_raw_27bits_prev = self->counter_us_raw_27bits_pps_prev;
counter_us_raw_27bits_wrap = self->counter_us_raw_27bits_pps_wrap;
} else {
counter_us_raw_27bits_prev = self->counter_us_raw_27bits_inst_prev;
counter_us_raw_27bits_wrap = self->counter_us_raw_27bits_inst_wrap;
}
/* Check if counter has wrapped, and update wrap status if necessary */
if (cnt < counter_us_raw_27bits_prev) {
counter_us_raw_27bits_wrap += 1;
counter_us_raw_27bits_wrap = counter_us_raw_27bits_wrap % 32;
}
/* Store counter value and wrap status for next time */
if (pps == true) {
self->counter_us_raw_27bits_pps_prev = cnt;
self->counter_us_raw_27bits_pps_wrap = counter_us_raw_27bits_wrap;
} else {
self->counter_us_raw_27bits_inst_prev = cnt;
self->counter_us_raw_27bits_inst_wrap = counter_us_raw_27bits_wrap;
}
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
uint32_t timestamp_counter_get(timestamp_counter_t * self, bool pps) {
int x;
uint8_t buff[4];
uint32_t counter_us_raw_27bits_now;
/* Get the 32MHz timestamp counter - 4 bytes */
/* step of 31.25 ns */
x = lgw_reg_rb((pps == true) ? SX1302_REG_TIMESTAMP_TIMESTAMP_PPS_MSB2_TIMESTAMP_PPS : SX1302_REG_TIMESTAMP_TIMESTAMP_MSB2_TIMESTAMP, &buff[0], 4);
if (x != LGW_REG_SUCCESS) {
printf("ERROR: Failed to get timestamp counter value\n");
return 0;
}
counter_us_raw_27bits_now = (uint32_t)((buff[0] << 24) & 0xFF000000);
counter_us_raw_27bits_now |= (uint32_t)((buff[1] << 16) & 0x00FF0000);
counter_us_raw_27bits_now |= (uint32_t)((buff[2] << 8) & 0x0000FF00);
counter_us_raw_27bits_now |= (uint32_t)((buff[3] << 0) & 0x000000FF);
counter_us_raw_27bits_now /= 32; /* scale to 1MHz */
/* Update counter wrapping status */
timestamp_counter_update(self, pps, counter_us_raw_27bits_now);
/* Convert 27-bits counter to 32-bits counter */
return timestamp_counter_expand(self, pps, counter_us_raw_27bits_now);
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
uint32_t timestamp_counter_expand(timestamp_counter_t * self, bool pps, uint32_t cnt_us) {
uint32_t counter_us_32bits;
if (pps == true) {
counter_us_32bits = (self->counter_us_raw_27bits_pps_wrap << 27) | cnt_us;
} else {
counter_us_32bits = (self->counter_us_raw_27bits_inst_wrap << 27) | cnt_us;
}
#if 0
/* DEBUG: to be enabled when running test_loragw_counter test application
This generates a CSV log, and can be plotted with gnuplot:
> set datafile separator comma
> plot for [col=1:2:1] 'log_count.txt' using col with lines
*/
printf("%u,%u,%u\n", cnt_us, counter_us_32bits, (pps == true) ? self->counter_us_raw_27bits_pps_wrap : self->counter_us_raw_27bits_inst_wrap);
#endif
return counter_us_32bits;
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
int timestamp_counter_mode(bool enable_precision_ts, uint8_t max_ts_metrics, uint8_t nb_symbols) {
if (enable_precision_ts == false) {
DEBUG_MSG("INFO: using legacy timestamp\n");
/* Latch end-of-packet timestamp (sx1301 compatibility) */
lgw_reg_w(SX1302_REG_RX_TOP_RX_BUFFER_LEGACY_TIMESTAMP, 0x01);
} else {
DEBUG_PRINTF("INFO: using precision timestamp (max_ts_metrics:%u nb_symbols:%u)\n", max_ts_metrics, nb_symbols);
/* Latch end-of-preamble timestamp */
lgw_reg_w(SX1302_REG_RX_TOP_RX_BUFFER_LEGACY_TIMESTAMP, 0x00);
lgw_reg_w(SX1302_REG_RX_TOP_RX_BUFFER_TIMESTAMP_CFG_MAX_TS_METRICS, max_ts_metrics);
/* LoRa multi-SF modems */
lgw_reg_w(SX1302_REG_RX_TOP_TIMESTAMP_ENABLE, 0x01);
lgw_reg_w(SX1302_REG_RX_TOP_TIMESTAMP_NB_SYMB, nb_symbols);
/* LoRa service modem */
lgw_reg_w(SX1302_REG_RX_TOP_LORA_SERVICE_FSK_TIMESTAMP_ENABLE, 0x01);
lgw_reg_w(SX1302_REG_RX_TOP_LORA_SERVICE_FSK_TIMESTAMP_NB_SYMB, nb_symbols);
}
return LGW_REG_SUCCESS;
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
uint32_t timestamp_counter_correction(int ifmod, uint8_t bandwidth, uint8_t datarate, uint8_t coderate, uint32_t crc_en, uint16_t payload_length) {
int32_t val;
uint32_t sf = (uint32_t)datarate, cr = (uint32_t)coderate, bw_pow, ppm;
uint32_t clk_period;
uint32_t nb_nibble, nb_nibble_in_hdr, nb_nibble_in_last_block;
uint32_t dft_peak_en, nb_iter;
uint32_t demap_delay, decode_delay, fft_delay_state3, fft_delay, delay_x;
uint32_t timestamp_correction;
/* determine if 'PPM mode' is on */
if (SET_PPM_ON(bandwidth, datarate)) {
ppm = 1;
} else {
ppm = 0;
}
/* timestamp correction code, base delay */
switch (bandwidth)
{
case BW_125KHZ:
bw_pow = 1;
delay_x = 16000000 / bw_pow + 2031250;
break;
case BW_250KHZ:
bw_pow = 2;
delay_x = 16000000 / bw_pow + 2031250;
break;
case BW_500KHZ:
bw_pow = 4;
delay_x = 16000000 / bw_pow + 2031250;
break;
default:
DEBUG_PRINTF("ERROR: UNEXPECTED VALUE %d IN SWITCH STATEMENT\n", bandwidth);
delay_x = 0;
bw_pow = 0;
break;
}
clk_period = 250000;
nb_nibble = (payload_length + 2 * crc_en) * 2 + 5;
if ((sf == 5) || (sf == 6)) {
nb_nibble_in_hdr = sf;
} else {
nb_nibble_in_hdr = sf - 2;
}
nb_nibble_in_last_block = nb_nibble - nb_nibble_in_hdr - (sf - 2 * ppm) * ((nb_nibble - nb_nibble_in_hdr) / (sf - 2 * ppm));
if (nb_nibble_in_last_block == 0) {
nb_nibble_in_last_block = sf - 2 * ppm;
}
nb_iter = ((sf + 1) >> 1);
/* timestamp correction code, variable delay */
if (ifmod == IF_LORA_STD) {
lgw_reg_r(SX1302_REG_RX_TOP_LORA_SERVICE_FSK_RX_CFG0_DFT_PEAK_EN, &val);
} else {
lgw_reg_r(SX1302_REG_RX_TOP_RX_CFG0_DFT_PEAK_EN, &val);
}
if (val != 0) {
/* TODO: should we differentiate the mode (FULL/TRACK) ? */
dft_peak_en = 1;
} else {
dft_peak_en = 0;
}
if ((sf >= 5) && (sf <= 12) && (bw_pow > 0)) {
if ((2 * (payload_length + 2 * crc_en) - (sf - 7)) <= 0) { /* payload fits entirely in first 8 symbols (header) */
if (sf > 6) {
nb_nibble_in_last_block = sf - 2;
} else {
nb_nibble_in_last_block = sf; // can't be acheived
}
dft_peak_en = 0;
cr = 4; /* header coding rate is 4 */
demap_delay = clk_period + (1 << sf) * clk_period * 3 / 4 + 3 * clk_period + (sf - 2) * clk_period;
} else {
demap_delay = clk_period + (1 << sf) * clk_period * (1 - ppm / 4) + 3 * clk_period + (sf - 2 * ppm) * clk_period;
}
fft_delay_state3 = clk_period * (((1 << sf) - 6) + 2 * ((1 << sf) * (nb_iter - 1) + 6)) + 4 * clk_period;
if (dft_peak_en) {
fft_delay = (5 - 2 * ppm) * ((1 << sf) * clk_period + 7 * clk_period) + 2 * clk_period;
} else {
fft_delay = (1 << sf) * 2 * clk_period + 3 * clk_period;
}
decode_delay = 5 * clk_period + (9 * clk_period + clk_period * cr) * nb_nibble_in_last_block + 3 * clk_period;
timestamp_correction = (uint32_t)(delay_x + fft_delay_state3 + fft_delay + demap_delay + decode_delay + 0.5e6) / 1e6;
//printf("INFO: timestamp_correction = %u us (delay_x %u, fft_delay_state3=%u, fft_delay=%u, demap_delay=%u, decode_delay = %u)\n", timestamp_correction, delay_x, fft_delay_state3, fft_delay, demap_delay, decode_delay);
}
else
{
timestamp_correction = 0;
DEBUG_MSG("WARNING: invalid packet, no timestamp correction\n");
}
return timestamp_correction;
}
/* --- EOF ------------------------------------------------------------------ */

View File

@ -0,0 +1,698 @@
/*
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(C)2019 Semtech
Description:
Minimum test program for HAL calibration
License: Revised BSD License, see LICENSE.TXT file include in the project
*/
/* -------------------------------------------------------------------------- */
/* --- DEPENDANCIES --------------------------------------------------------- */
/* fix an issue between POSIX and C99 */
#if __STDC_VERSION__ >= 199901L
#define _XOPEN_SOURCE 600
#else
#define _XOPEN_SOURCE 500
#endif
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <math.h>
#include <signal.h> /* sigaction */
#include <getopt.h> /* getopt_long */
#include <sys/time.h>
#include "loragw_hal.h"
#include "loragw_reg.h"
#include "loragw_sx1302.h"
#include "loragw_sx125x.h"
#include "loragw_aux.h"
#include "loragw_cal.h"
/* -------------------------------------------------------------------------- */
/* --- PRIVATE MACROS ------------------------------------------------------- */
#define RAND_RANGE(min, max) (rand() % (max + 1 - min) + min)
#define DEBUG_MSG(str) fprintf(stderr, str)
#define DEBUG_PRINTF(fmt, args...) fprintf(stderr,"%s:%d: "fmt, __FUNCTION__, __LINE__, args)
/* -------------------------------------------------------------------------- */
/* --- PRIVATE CONSTANTS ---------------------------------------------------- */
#define LINUXDEV_PATH_DEFAULT "/dev/spidev0.0"
#define DEFAULT_CLK_SRC 0
#define DEFAULT_FREQ_HZ 868500000U
#define DEFAULT_DAC_GAIN 3
#define DEFAULT_MIX_GAIN 15
#define CAL_TX_TONE_FREQ_HZ 250000
#define CAL_DEC_GAIN 8
#define CAL_SIG_ANA_DURATION 0 /* correlation duration: 0:1, 1:2, 2:4, 3:8 ms) */
#define TEST_FREQ_SCAN 0
#define TEST_OFFSET_IQ 1
#define TEST_AMP_PHI 2
/* -------------------------------------------------------------------------- */
/* --- PRIVATE TYPES -------------------------------------------------------- */
struct cal_tx_log {
int32_t mean;
int32_t i_offset;
int32_t q_offset;
};
/* -------------------------------------------------------------------------- */
/* --- PRIVATE VARIABLES ---------------------------------------------------- */
FILE * fp;
static uint32_t rf_rx_freq[LGW_RF_CHAIN_NB] = {865500000, 865500000};
static lgw_radio_type_t rf_radio_type[LGW_RF_CHAIN_NB] = {LGW_RADIO_TYPE_SX1257, LGW_RADIO_TYPE_SX1257};
static struct lgw_tx_gain_lut_s txlut; /* TX gain table */
/* Signal handling variables */
static int exit_sig = 0; /* 1 -> application terminates cleanly (shut down hardware, close open files, etc) */
static int quit_sig = 0; /* 1 -> application terminates without shutting down the hardware */
#include "../src/cal_fw.var" /* text_cal_sx1257_16_Nov_1 */
/* -------------------------------------------------------------------------- */
/* --- PRIVATE FUNCTIONS ---------------------------------------------------- */
/* describe command line options */
void usage(void) {
//printf("Library version information: %s\n", lgw_version_info());
printf("Available options:\n");
printf(" -h print this help\n");
printf(" -d <path> use Linux SPI device driver\n");
printf(" => default path: " LINUXDEV_PATH_DEFAULT "\n");
printf(" -k <uint> Concentrator clock source (Radio A or Radio B) [0..1]\n");
printf(" -c <uint> RF chain to be used for TX (Radio A or Radio B) [0..1]\n");
printf(" -r <uint> Radio type (1255, 1257, 1250)\n");
printf(" -f <float> Radio TX frequency in MHz\n");
printf( "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" );
printf(" --pa <uint> PA gain [0..3]\n");
printf(" --dig <uint> sx1302 digital gain [0..3]\n");
printf(" --dac <uint> sx1257 DAC gain [0..3]\n");
printf(" --mix <uint> sx1257 MIX gain [0..15]\n");
}
/* handle signals */
static void sig_handler(int sigio)
{
if (sigio == SIGQUIT) {
quit_sig = 1;
}
else if((sigio == SIGINT) || (sigio == SIGTERM)) {
exit_sig = 1;
}
}
int setup_tx_dc_offset(uint8_t rf_chain, uint32_t freq_hz, uint8_t dac_gain, uint8_t mix_gain, uint8_t radio_type) {
uint32_t rx_freq_hz, tx_freq_hz;
uint32_t rx_freq_int, rx_freq_frac;
uint32_t tx_freq_int, tx_freq_frac;
uint8_t rx_pll_locked, tx_pll_locked;
/* Set PLL frequencies */
rx_freq_hz = freq_hz - CAL_TX_TONE_FREQ_HZ;
tx_freq_hz = freq_hz;
switch (radio_type) {
case LGW_RADIO_TYPE_SX1255:
rx_freq_int = rx_freq_hz / (SX125x_32MHz_FRAC << 7); /* integer part, gives the MSB */
rx_freq_frac = ((rx_freq_hz % (SX125x_32MHz_FRAC << 7)) << 9) / SX125x_32MHz_FRAC; /* fractional part, gives middle part and LSB */
tx_freq_int = tx_freq_hz / (SX125x_32MHz_FRAC << 7); /* integer part, gives the MSB */
tx_freq_frac = ((tx_freq_hz % (SX125x_32MHz_FRAC << 7)) << 9) / SX125x_32MHz_FRAC; /* fractional part, gives middle part and LSB */
break;
case LGW_RADIO_TYPE_SX1257:
rx_freq_int = rx_freq_hz / (SX125x_32MHz_FRAC << 8); /* integer part, gives the MSB */
rx_freq_frac = ((rx_freq_hz % (SX125x_32MHz_FRAC << 8)) << 8) / SX125x_32MHz_FRAC; /* fractional part, gives middle part and LSB */
tx_freq_int = tx_freq_hz / (SX125x_32MHz_FRAC << 8); /* integer part, gives the MSB */
tx_freq_frac = ((tx_freq_hz % (SX125x_32MHz_FRAC << 8)) << 8) / SX125x_32MHz_FRAC; /* fractional part, gives middle part and LSB */
break;
default:
DEBUG_PRINTF("ERROR: UNEXPECTED VALUE %d FOR RADIO TYPE\n", radio_type);
return LGW_HAL_ERROR;
}
lgw_sx125x_reg_w(SX125x_REG_FRF_RX_MSB, 0xFF & rx_freq_int, rf_chain);
lgw_sx125x_reg_w(SX125x_REG_FRF_RX_MID, 0xFF & (rx_freq_frac >> 8), rf_chain);
lgw_sx125x_reg_w(SX125x_REG_FRF_RX_LSB, 0xFF & rx_freq_frac, rf_chain);
lgw_sx125x_reg_w(SX125x_REG_FRF_TX_MSB, 0xFF & tx_freq_int, rf_chain);
lgw_sx125x_reg_w(SX125x_REG_FRF_TX_MID, 0xFF & (tx_freq_frac >> 8), rf_chain);
lgw_sx125x_reg_w(SX125x_REG_FRF_TX_LSB, 0xFF & tx_freq_frac, rf_chain);
/* Radio settings for calibration */
//lgw_sx125x_reg_w(SX125x_RX_ANA_GAIN__LNA_ZIN, 1, rf_chain); /* Default: 1 */
//lgw_sx125x_reg_w(SX125x_RX_ANA_GAIN__BB_GAIN, 15, rf_chain); /* Default: 15 */
//lgw_sx125x_reg_w(SX125x_RX_ANA_GAIN__LNA_GAIN, 1, rf_chain); /* Default: 1 */
lgw_sx125x_reg_w(SX125x_REG_RX_BW__BB_BW, 0, rf_chain);
lgw_sx125x_reg_w(SX125x_REG_RX_BW__ADC_TRIM, 6, rf_chain);
//lgw_sx125x_reg_w(SX125x_RX_BW__ADC_BW, 7, rf_chain); /* Default: 7 */
lgw_sx125x_reg_w(SX125x_REG_RX_PLL_BW__PLL_BW, 0, rf_chain);
lgw_sx125x_reg_w(SX125x_REG_TX_BW__PLL_BW, 0, rf_chain);
//lgw_sx125x_reg_w(SX125x_TX_BW__ANA_BW, 0, rf_chain); /* Default: 0 */
lgw_sx125x_reg_w(SX125x_REG_TX_DAC_BW, 5, rf_chain);
lgw_sx125x_reg_w(SX125x_REG_CLK_SELECT__DAC_CLK_SELECT, 1, rf_chain); /* Use external clock from SX1302 */
lgw_sx125x_reg_w(SX125x_REG_TX_GAIN__DAC_GAIN, dac_gain, rf_chain);
lgw_sx125x_reg_w(SX125x_REG_TX_GAIN__MIX_GAIN, mix_gain, rf_chain);
lgw_sx125x_reg_w(SX125x_REG_CLK_SELECT__RF_LOOPBACK_EN, 1, rf_chain);
lgw_sx125x_reg_w(SX125x_REG_MODE, 15, rf_chain);
wait_ms(1);
lgw_sx125x_reg_r(SX125x_REG_MODE_STATUS__RX_PLL_LOCKED, &rx_pll_locked, rf_chain);
lgw_sx125x_reg_r(SX125x_REG_MODE_STATUS__TX_PLL_LOCKED, &tx_pll_locked, rf_chain);
if ((rx_pll_locked == 0) || (tx_pll_locked == 0)) {
DEBUG_MSG("ERROR: PLL failed to lock\n");
return LGW_HAL_ERROR;
}
return 0;
}
int cal_tx_dc_offset(uint8_t test_id, uint8_t rf_chain, uint32_t freq_hz, uint8_t dac_gain, uint8_t mix_gain, uint8_t radio_type, int32_t f_offset, int32_t i_offset, int32_t q_offset, bool full_log, bool use_agc, uint8_t amp, uint8_t phi) {
int i;
uint16_t reg;
int32_t val_min, val_max;
int32_t acc;
int32_t val_mean;
float val_std;
float acc2 = 0 ;
int loop_len = 3;
float res_sig[loop_len];
struct timeval start, stop;
//DEBUG_MSG("\n");
//DEBUG_PRINTF("rf_chain:%u, freq_hz:%u, dac_gain:%u, mix_gain:%u, radio_type:%d\n", rf_chain, freq_hz, dac_gain, mix_gain, radio_type);
if (setup_tx_dc_offset(rf_chain, freq_hz, dac_gain, mix_gain, radio_type) != LGW_HAL_SUCCESS) {
return LGW_HAL_ERROR;
}
/* Trig calibration */
/* Select radio to be connected to the Signal Analyzer (warning: RadioA:1, RadioB:0) */
lgw_reg_w(SX1302_REG_RADIO_FE_SIG_ANA_CFG_RADIO_SEL, (rf_chain == 0) ? 1 : 0);
reg = REG_SELECT(rf_chain, SX1302_REG_TX_TOP_A_TX_RFFE_IF_CTRL_TX_MODE,
SX1302_REG_TX_TOP_B_TX_RFFE_IF_CTRL_TX_MODE);
lgw_reg_w(reg, 0);
reg = REG_SELECT(rf_chain, SX1302_REG_TX_TOP_A_TX_TRIG_TX_TRIG_IMMEDIATE,
SX1302_REG_TX_TOP_B_TX_TRIG_TX_TRIG_IMMEDIATE);
lgw_reg_w(reg, 1);
lgw_reg_w(reg, 0);
reg = REG_SELECT(rf_chain, SX1302_REG_RADIO_FE_CTRL0_RADIO_A_DC_NOTCH_EN,
SX1302_REG_RADIO_FE_CTRL0_RADIO_B_DC_NOTCH_EN);
lgw_reg_w(reg, 1);
/* Measuring */
if (use_agc == true) {
uint8_t val_sig, val_sig2;
/* Set calibration parameters */
sx1302_agc_mailbox_write(2, rf_chain + 4); /* Sig ana test radio A/B */
sx1302_agc_mailbox_write(1, f_offset/*(CAL_TX_TONE_FREQ_HZ + f_offset) * 64e-6*/); /* Set frequency */
sx1302_agc_mailbox_write(0, CAL_SIG_ANA_DURATION);
/* */
sx1302_agc_mailbox_write(3, 0x00);
sx1302_agc_mailbox_write(3, 0x01);
sx1302_agc_wait_status(0x01);
sx1302_agc_mailbox_write(2, amp); /* amp */
sx1302_agc_mailbox_write(1, phi); /* phi */
sx1302_agc_mailbox_write(3, 0x02);
sx1302_agc_wait_status(0x02);
sx1302_agc_mailbox_write(2, i_offset); /* i offset init */
sx1302_agc_mailbox_write(1, q_offset); /* q offset init */
sx1302_agc_mailbox_write(3, 0x03);
sx1302_agc_wait_status(0x03);
sx1302_agc_mailbox_write(2, CAL_DEC_GAIN); /* dec_gain */
sx1302_agc_mailbox_write(2, 0); /* threshold (not used) */
sx1302_agc_mailbox_write(3, 0x04);
reg = REG_SELECT(rf_chain, SX1302_REG_TX_TOP_A_TX_TRIG_TX_TRIG_IMMEDIATE,
SX1302_REG_TX_TOP_B_TX_TRIG_TX_TRIG_IMMEDIATE);
lgw_reg_w(reg, 0);
gettimeofday (&start, NULL);
for (i = 0; i < loop_len; i++) {
sx1302_agc_wait_status(0x06);
sx1302_agc_mailbox_write(3, 0x06);
sx1302_agc_wait_status(0x07);
sx1302_agc_mailbox_read(0, &val_sig);
sx1302_agc_mailbox_read(1, &val_sig2);
res_sig[i] = val_sig2 * 256 + val_sig;
if (i == (loop_len - 1)) {
sx1302_agc_mailbox_write(3, 0x07); /* unlock */
} else {
sx1302_agc_mailbox_write(3, 0x00); /* unlock */
}
}
gettimeofday (&stop, NULL);
//printf("processing time: %ld us\n", ((stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec) - start.tv_usec);
} else {
int32_t val;
int32_t abs_lsb, abs_msb;
float abs_iq;
reg = REG_SELECT(rf_chain, SX1302_REG_TX_TOP_A_TX_RFFE_IF_Q_OFFSET_Q_OFFSET,
SX1302_REG_TX_TOP_B_TX_RFFE_IF_Q_OFFSET_Q_OFFSET);
lgw_reg_w(reg, (int8_t)q_offset);
reg = REG_SELECT(rf_chain, SX1302_REG_TX_TOP_A_TX_RFFE_IF_I_OFFSET_I_OFFSET,
SX1302_REG_TX_TOP_B_TX_RFFE_IF_I_OFFSET_I_OFFSET);
lgw_reg_w(reg, (int8_t)i_offset);
reg = REG_SELECT(rf_chain, SX1302_REG_RADIO_FE_CTRL0_RADIO_A_DC_NOTCH_EN,
SX1302_REG_RADIO_FE_CTRL0_RADIO_B_DC_NOTCH_EN);
lgw_reg_w(reg, 1);
reg = REG_SELECT(rf_chain, SX1302_REG_RADIO_FE_CTRL0_RADIO_A_FORCE_HOST_FILTER_GAIN,
SX1302_REG_RADIO_FE_CTRL0_RADIO_B_FORCE_HOST_FILTER_GAIN);
lgw_reg_w(reg, 0x01);
reg = REG_SELECT(rf_chain, SX1302_REG_RADIO_FE_CTRL0_RADIO_A_HOST_FILTER_GAIN,
SX1302_REG_RADIO_FE_CTRL0_RADIO_B_HOST_FILTER_GAIN);
lgw_reg_w(reg, CAL_DEC_GAIN);
lgw_reg_w(SX1302_REG_RADIO_FE_SIG_ANA_CFG_FORCE_HAL_CTRL, 1);
lgw_reg_w(SX1302_REG_RADIO_FE_SIG_ANA_FREQ_FREQ, f_offset);
lgw_reg_w(SX1302_REG_RADIO_FE_SIG_ANA_CFG_DURATION, CAL_SIG_ANA_DURATION);
lgw_reg_w(SX1302_REG_RADIO_FE_SIG_ANA_CFG_EN, 1);
gettimeofday (&start, NULL);
for (i = 0; i < loop_len; i++) {
lgw_reg_w(SX1302_REG_RADIO_FE_SIG_ANA_CFG_START, 0);
lgw_reg_w(SX1302_REG_RADIO_FE_SIG_ANA_CFG_START, 1);
do {
lgw_reg_r(SX1302_REG_RADIO_FE_SIG_ANA_CFG_VALID, &val);
wait_ms(1);
} while (val == 0);
lgw_reg_r(SX1302_REG_RADIO_FE_SIG_ANA_ABS_LSB_CORR_ABS_OUT, &abs_lsb);
lgw_reg_r(SX1302_REG_RADIO_FE_SIG_ANA_ABS_MSB_CORR_ABS_OUT, &abs_msb);
abs_iq = (abs_msb << 8) | abs_lsb;
res_sig[i] = abs_iq;
}
gettimeofday (&stop, NULL);
//printf("processing time: %ld us\n", ((stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec) - start.tv_usec);
lgw_reg_w(SX1302_REG_RADIO_FE_SIG_ANA_CFG_FORCE_HAL_CTRL, 0);
}
if (full_log == true) {
printf("i_offset:%d q_offset:%d f_offset:%d dac_gain:%d mix_gain:%d dec_gain:%d amp:%u phi:%u => ", i_offset, q_offset, f_offset, dac_gain, mix_gain, CAL_DEC_GAIN, amp, phi);
} else {
switch (test_id) {
case TEST_FREQ_SCAN:
fprintf(fp, "%u ", f_offset);
break;
case TEST_OFFSET_IQ:
fprintf(fp, "%d %d ", i_offset, q_offset);
break;
case TEST_AMP_PHI:
fprintf(fp, "%d %d ", amp, phi);
break;
default:
printf("ERROR: wrong test ID (%u)\n", test_id);
break;
}
}
/* Analyze result */
val_min = res_sig[0];
val_max = res_sig[0];
acc = 0;
for (i = 0; i < loop_len; i++) {
if (res_sig[i] > val_max) {
val_max = res_sig[i];
}
if (res_sig[i] < val_min) {
val_min = res_sig[i];
}
acc += res_sig[i];
}
val_mean = acc / loop_len;
for (i = 0; i < loop_len; i++) {
acc2 += pow((res_sig[i]-val_mean),2);
}
val_std = sqrt(acc2/loop_len);
if (full_log == true) {
printf(" min:%u max:%u mean:%u std:%f\n", val_min, val_max, val_mean, val_std);
} else {
switch (test_id) {
case TEST_OFFSET_IQ:
case TEST_AMP_PHI:
fprintf(fp, "%u %u %u %f\n", val_min, val_max, val_mean, val_std);
break;
case TEST_FREQ_SCAN:
fprintf(fp, "%u\n", val_mean);
break;
default:
break;
}
}
return LGW_HAL_SUCCESS;
}
/* -------------------------------------------------------------------------- */
/* --- MAIN FUNCTION -------------------------------------------------------- */
int test_freq_scan(uint8_t rf_chain, bool full_log, bool use_agc) {
int f;
printf("-------------------------------------\n");
for (f = 0; f < 256; f++)
{
cal_tx_dc_offset(TEST_FREQ_SCAN, rf_chain, rf_rx_freq[rf_chain], txlut.lut[0].dac_gain, txlut.lut[0].mix_gain, rf_radio_type[rf_chain], f, 0, 0, full_log, use_agc, 0, 0);
if ((quit_sig == 1) || (exit_sig == 1)) {
break;
}
}
return 0;
}
int test_iq_offset(uint8_t rf_chain, uint8_t f_offset, bool full_log, bool use_agc) {
int i, q;
printf("-------------------------------------\n");
for (i = -128; i < 127; i+=8)
{
for (q = -128; q < 127; q+=8)
{
cal_tx_dc_offset(TEST_OFFSET_IQ, rf_chain, rf_rx_freq[rf_chain], txlut.lut[0].dac_gain, txlut.lut[0].mix_gain, rf_radio_type[rf_chain], f_offset, i, q, full_log, use_agc, 0, 0);
if ((quit_sig == 1) || (exit_sig == 1)) {
return 0;
}
}
}
return 0;
}
int test_amp_phi(uint8_t rf_chain, uint8_t f_offset, bool full_log, bool use_agc) {
int amp, phi;
printf("-------------------------------------\n");
for (amp = 0; amp < 64; amp++)
{
for (phi = 0; phi < 64; phi++)
{
cal_tx_dc_offset(TEST_AMP_PHI, rf_chain, rf_rx_freq[rf_chain], txlut.lut[0].dac_gain, txlut.lut[0].mix_gain, rf_radio_type[rf_chain], f_offset, 0, 0, full_log, use_agc, amp, phi);
if ((quit_sig == 1) || (exit_sig == 1)) {
return 0;
}
}
}
return 0;
}
int test_capture_ram(uint8_t rf_chain) {
uint16_t reg;
setup_tx_dc_offset(rf_chain, rf_rx_freq[rf_chain], txlut.lut[0].dac_gain, txlut.lut[0].mix_gain, rf_radio_type[rf_chain]);
reg = REG_SELECT(rf_chain, SX1302_REG_RADIO_FE_CTRL0_RADIO_A_DC_NOTCH_EN,
SX1302_REG_RADIO_FE_CTRL0_RADIO_B_DC_NOTCH_EN);
lgw_reg_w(reg, 1);
printf("Waiting...\n");
while ((quit_sig != 1) && (exit_sig != 1)) {
wait_ms(1000);
}
return 0;
}
int main(int argc, char **argv)
{
int i, x;
uint32_t ft = DEFAULT_FREQ_HZ;
double arg_d = 0.0;
unsigned int arg_u;
uint8_t clocksource = 0;
uint8_t rf_chain = 0;
lgw_radio_type_t radio_type = LGW_RADIO_TYPE_NONE;
struct lgw_conf_board_s boardconf;
struct lgw_conf_rxrf_s rfconf;
static struct sigaction sigact; /* SIGQUIT&SIGINT&SIGTERM signal handling */
/* SPI interfaces */
const char spidev_path_default[] = LINUXDEV_PATH_DEFAULT;
const char * spidev_path = spidev_path_default;
/* Initialize TX gain LUT */
txlut.size = 1;
memset(txlut.lut, 0, sizeof txlut.lut);
txlut.lut[0].dac_gain = DEFAULT_DAC_GAIN;
txlut.lut[0].mix_gain = DEFAULT_MIX_GAIN;
/* Parameter parsing */
int option_index = 0;
static struct option long_options[] = {
{"dac", 1, 0, 0},
{"mix", 1, 0, 0},
{0, 0, 0, 0}
};
/* parse command line options */
while ((i = getopt_long (argc, argv, "hf:k:r:c:d:", long_options, &option_index)) != -1) {
switch (i) {
case 'h':
usage();
return -1;
break;
case 'd':
if (optarg != NULL) {
spidev_path = optarg;
}
break;
case 'r': /* <uint> Radio type */
i = sscanf(optarg, "%u", &arg_u);
if ((i != 1) || ((arg_u != 1255) && (arg_u != 1257) && (arg_u != 1250))) {
printf("ERROR: argument parsing of -r argument. Use -h to print help\n");
return EXIT_FAILURE;
} else {
switch (arg_u) {
case 1255:
radio_type = LGW_RADIO_TYPE_SX1255;
break;
case 1257:
radio_type = LGW_RADIO_TYPE_SX1257;
break;
default: /* 1250 */
radio_type = LGW_RADIO_TYPE_SX1250;
break;
}
}
break;
case 'k': /* <uint> Clock Source */
i = sscanf(optarg, "%u", &arg_u);
if ((i != 1) || (arg_u > 1)) {
printf("ERROR: argument parsing of -k argument. Use -h to print help\n");
return EXIT_FAILURE;
} else {
clocksource = (uint8_t)arg_u;
}
break;
case 'c': /* <uint> RF chain */
i = sscanf(optarg, "%u", &arg_u);
if ((i != 1) || (arg_u > 1)) {
printf("ERROR: argument parsing of -c argument. Use -h to print help\n");
return EXIT_FAILURE;
} else {
rf_chain = (uint8_t)arg_u;
}
break;
case 'f': /* <float> Radio TX frequency in MHz */
i = sscanf(optarg, "%lf", &arg_d);
if (i != 1) {
printf("ERROR: argument parsing of -f argument. Use -h to print help\n");
return EXIT_FAILURE;
} else {
ft = (uint32_t)((arg_d*1e6) + 0.5); /* .5 Hz offset to get rounding instead of truncating */
}
break;
case 0:
if (strcmp(long_options[option_index].name, "dac") == 0) {
i = sscanf(optarg, "%u", &arg_u);
if ((i != 1) || (arg_u > 3)) {
printf("ERROR: argument parsing of --dac argument. Use -h to print help\n");
return EXIT_FAILURE;
} else {
txlut.size = 1;
txlut.lut[0].dac_gain = (uint8_t)arg_u;
}
} else if (strcmp(long_options[option_index].name, "mix") == 0) {
i = sscanf(optarg, "%u", &arg_u);
if ((i != 1) || (arg_u > 15)) {
printf("ERROR: argument parsing of --mix argument. Use -h to print help\n");
return EXIT_FAILURE;
} else {
txlut.size = 1;
txlut.lut[0].mix_gain = (uint8_t)arg_u;
}
} else {
printf("ERROR: argument parsing options. Use -h to print help\n");
return EXIT_FAILURE;
}
break;
default:
printf("ERROR: argument parsing\n");
usage();
return -1;
}
}
/* Configure signal handling */
sigemptyset( &sigact.sa_mask );
sigact.sa_flags = 0;
sigact.sa_handler = sig_handler;
sigaction( SIGQUIT, &sigact, NULL );
sigaction( SIGINT, &sigact, NULL );
sigaction( SIGTERM, &sigact, NULL );
/* Board reset */
if (system("./reset_lgw.sh start") != 0) {
printf("ERROR: failed to reset SX1302, check your reset_lgw.sh script\n");
exit(EXIT_FAILURE);
}
/* Configure the gateway */
memset(&boardconf, 0, sizeof boardconf);
boardconf.lorawan_public = true;
boardconf.clksrc = clocksource;
boardconf.full_duplex = false;
strncpy(boardconf.spidev_path, spidev_path, sizeof boardconf.spidev_path);
if (lgw_board_setconf(&boardconf) != LGW_HAL_SUCCESS) {
printf("ERROR: failed to configure board\n");
return EXIT_FAILURE;
}
memset(&rfconf, 0, sizeof rfconf);
rfconf.enable = ((rf_chain == 0) ? true : false);
rfconf.freq_hz = ft;
rfconf.type = radio_type;
rfconf.tx_enable = true;
if (lgw_rxrf_setconf(0, &rfconf) != LGW_HAL_SUCCESS) {
printf("ERROR: failed to configure rxrf 0\n");
return EXIT_FAILURE;
}
memset(&rfconf, 0, sizeof rfconf);
rfconf.enable = ((rf_chain == 1) ? true : false);
rfconf.freq_hz = ft;
rfconf.type = radio_type;
rfconf.tx_enable = true;
if (lgw_rxrf_setconf(1, &rfconf) != LGW_HAL_SUCCESS) {
printf("ERROR: failed to configure rxrf 1\n");
return EXIT_FAILURE;
}
if (txlut.size > 0) {
if (lgw_txgain_setconf(rf_chain, &txlut) != LGW_HAL_SUCCESS) {
printf("ERROR: failed to configure txgain lut\n");
return EXIT_FAILURE;
}
}
/* open log file for writing */
fp = fopen("log.txt", "w+");
/* connect the gateway */
x = lgw_connect(spidev_path);
if (x != 0) {
printf("ERROR: failed to connect the gateway\n");
return EXIT_FAILURE;
}
sx1302_radio_reset(rf_chain, LGW_RADIO_TYPE_SX1257);
sx1302_radio_clock_select(clocksource);
sx1302_radio_set_mode(rf_chain, LGW_RADIO_TYPE_SX1257);
printf("Loading CAL fw for sx125x\n");
if (sx1302_agc_load_firmware(cal_firmware_sx125x) != LGW_HAL_SUCCESS) {
return LGW_HAL_ERROR;
}
printf("waiting for capture ram\n");
wait_ms(1000);
/* testing */
printf("testing: rf_chain:%u, dac_gain: %u, mix_gain:%u, dec_gain:%u, sig_ana_duration:%u\n", rf_chain, txlut.lut[0].dac_gain, txlut.lut[0].mix_gain, CAL_DEC_GAIN, CAL_SIG_ANA_DURATION);
test_freq_scan(rf_chain, false, false); /* rf_chain, full_log, use_agc */
/* gnuplot> plot 'log.txt' with lines */
//test_iq_offset(rf_chain, 16, false, false); /* rf_chain, f_offset, full_log, use_agc */
//test_amp_phi(rf_chain, 240, true, true); /* rf_chain, f_offset, full_log, use_agc */
//test_capture_ram(rf_chain);
sx1302_radio_reset(0, LGW_RADIO_TYPE_SX1257);
sx1302_radio_reset(1, LGW_RADIO_TYPE_SX1257);
/* disconnect the gateway */
x = lgw_disconnect();
if (x != 0) {
printf("ERROR: failed to disconnect the gateway\n");
return EXIT_FAILURE;
}
/* Close log file */
fclose(fp);
/* Board reset */
if (system("./reset_lgw.sh stop") != 0) {
printf("ERROR: failed to reset SX1302, check your reset_lgw.sh script\n");
exit(EXIT_FAILURE);
}
printf("=========== Test End ===========\n");
return 0;
}
/* --- EOF ------------------------------------------------------------------ */

View File

@ -0,0 +1,369 @@
/*
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(C)2019 Semtech
Description:
Minimum test program to test the capture RAM block
License: Revised BSD License, see LICENSE.TXT file include in the project
*/
/* -------------------------------------------------------------------------- */
/* --- DEPENDANCIES --------------------------------------------------------- */
/* fix an issue between POSIX and C99 */
#if __STDC_VERSION__ >= 199901L
#define _XOPEN_SOURCE 600
#else
#define _XOPEN_SOURCE 500
#endif
// #include <stdint.h>
#include <stdio.h> /* printf */
#include <stdlib.h>
#include <signal.h> /* sigaction */
#include <getopt.h> /* getopt_long */
#include "loragw_hal.h"
#include "loragw_reg.h"
#include "loragw_aux.h"
#include "loragw_sx1250.h"
#include "loragw_sx125x.h"
#include "loragw_sx1302.h"
/* -------------------------------------------------------------------------- */
/* --- PRIVATE MACROS ------------------------------------------------------- */
#define DEBUG_MSG(str) fprintf(stderr, str)
#define LINUXDEV_PATH_DEFAULT "/dev/spidev0.0"
#define FULL_INIT 0
#define CAPTURE_RAM_SIZE 0x4000
/* -------------------------------------------------------------------------- */
/* --- PRIVATE VARIABLES ---------------------------------------------------- */
/* Signal handling variables */
static int exit_sig = 0; /* 1 -> application terminates cleanly (shut down hardware, close open files, etc) */
static int quit_sig = 0; /* 1 -> application terminates without shutting down the hardware */
uint32_t sampling_frequency[] = {4e6, 4e6, 4e6, 4e6, 4e6, 4e6, 4e6, 0, 0, 1e6, 125e3, 125e3, 125e3, 125e3, 125e3, 125e3, 125e3, 125e3, 8e6, 125e3, 125e3, 125e3, 0, 32e6, 32e6, 0, 32e6, 32e6, 0, 32e6, 32e6, 32e6};
#if FULL_INIT
#include "src/text_agc_sx1250_27_Nov_1.var"
#include "src/text_agc_sx1257_19_Nov_1.var"
#include "src/text_arb_sx1302_13_Nov_3.var"
#define FW_VERSION_CAL 0 /* Expected version of calibration firmware */ /* TODO */
#define FW_VERSION_AGC 1 /* Expected version of AGC firmware */
#define FW_VERSION_ARB 1 /* Expected version of arbiter firmware */
static bool rf_enable[LGW_RF_CHAIN_NB];
static uint32_t rf_rx_freq[LGW_RF_CHAIN_NB]; /* absolute, in Hz */
static lgw_radio_type_t rf_radio_type[LGW_RF_CHAIN_NB];
static uint8_t rf_clkout = 0;
#endif
/* -------------------------------------------------------------------------- */
/* --- PRIVATE FUNCTIONS ---------------------------------------------------- */
/* describe command line options */
void usage(void)
{
printf("Available options:\n");
printf(" -h print this help\n");
printf(" -d <path> use Linux SPI device driver\n");
printf(" => default path: " LINUXDEV_PATH_DEFAULT "\n");
printf(" -s <uint> Capture source [0..31]\n");
}
/* handle signals */
static void sig_handler(int sigio)
{
if (sigio == SIGQUIT) {
quit_sig = 1;
}
else if((sigio == SIGINT) || (sigio == SIGTERM)) {
exit_sig = 1;
}
}
/* Main program */
int main(int argc, char **argv)
{
int i;
int32_t val = 0;
int reg_stat;
unsigned int arg_u;
uint8_t capture_source = 0;
uint16_t period_value = 0;
int16_t real = 0, imag = 0;
#if FULL_INIT
uint32_t val1, val2;
#endif
uint8_t capture_ram_buffer[CAPTURE_RAM_SIZE];
static struct sigaction sigact; /* SIGQUIT&SIGINT&SIGTERM signal handling */
/* SPI interfaces */
const char spidev_path_default[] = LINUXDEV_PATH_DEFAULT;
const char * spidev_path = spidev_path_default;
/* Parameter parsing */
int option_index = 0;
static struct option long_options[] = {
{0, 0, 0, 0}
};
/* parse command line options */
while ((i = getopt_long (argc, argv, "h:s:d:", long_options, &option_index)) != -1) {
switch (i) {
case 'h':
usage();
return -1;
break;
case 'd':
if (optarg != NULL) {
spidev_path = optarg;
}
break;
case 's': /* <uint> Capture Source */
i = sscanf(optarg, "%u", &arg_u);
if ((i != 1) || (arg_u > 31)) {
printf("ERROR: argument parsing of -s argument. Use -h to print help\n");
return EXIT_FAILURE;
} else {
capture_source = arg_u;
}
break;
default:
printf("ERROR: argument parsing\n");
usage();
return -1;
}
}
/* Configure signal handling */
sigemptyset( &sigact.sa_mask );
sigact.sa_flags = 0;
sigact.sa_handler = sig_handler;
sigaction( SIGQUIT, &sigact, NULL );
sigaction( SIGINT, &sigact, NULL );
sigaction( SIGTERM, &sigact, NULL );
#if FULL_INIT
/* Board reset */
if (system("./reset_lgw.sh start") != 0) {
printf("ERROR: failed to reset SX1302, check your reset_lgw.sh script\n");
exit(EXIT_FAILURE);
}
#endif
/* Initialize memory for capture */
for (i = 0; i < CAPTURE_RAM_SIZE; i++) {
capture_ram_buffer[i] = i%256;
}
reg_stat = lgw_connect(spidev_path);
if (reg_stat == LGW_REG_ERROR) {
DEBUG_MSG("ERROR: FAIL TO CONNECT BOARD\n");
return LGW_HAL_ERROR;
}
/* Manual init */
#if FULL_INIT
rf_radio_type[0] = LGW_RADIO_TYPE_SX1250;
rf_radio_type[1] = LGW_RADIO_TYPE_SX1257;
rf_enable[0] = false;
rf_enable[1] = true;
rf_clkout = 1;
rf_rx_freq[1] = 863700000;
/* setup radios */
for (i=0; i < 2; i++)
{
if (rf_enable[i] == true) {
sx1302_radio_reset(i, rf_radio_type[i]);
switch (radio_type) {
case LGW_RADIO_TYPE_SX1250:
sx1250_setup(i, rf_rx_freq[i]);
break;
case LGW_RADIO_TYPE_SX1255:
case LGW_RADIO_TYPE_SX1257:
sx125x_setup(i, rf_clkout, true, rf_radio_type[i], rf_rx_freq[i]);
break;
default:
DEBUG_MSG("ERROR: RADIO TYPE NOT SUPPORTED\n");
return LGW_HAL_ERROR;
}
sx1302_radio_set_mode(i, radio_type);
}
}
/* Select the radio which provides the clock to the sx1302 */
sx1302_radio_clock_select(rf_clkout);
/* Check that the SX1302 timestamp counter is running */
lgw_get_instcnt(&val1);
lgw_get_instcnt(&val2);
if (val1 == val2) {
printf("ERROR: SX1302 timestamp counter is not running (val:%u)\n", (uint32_t)val1);
return -1;
}
/* Configure Radio FE */
sx1302_radio_fe_configure();
/* give radio control to AGC MCU */
lgw_reg_w(SX1302_REG_COMMON_CTRL0_HOST_RADIO_CTRL, 0x00);
/* Load firmware */
switch (rf_radio_type[rf_clkout]) {
case LGW_RADIO_TYPE_SX1250:
printf("Loading AGC fw for sx1250\n");
if (sx1302_agc_load_firmware(agc_firmware_sx1250) != LGW_HAL_SUCCESS) {
return LGW_HAL_ERROR;
}
if (sx1302_agc_start(FW_VERSION_AGC, SX1302_RADIO_TYPE_SX1250, SX1302_AGC_RADIO_GAIN_AUTO, SX1302_AGC_RADIO_GAIN_AUTO, 0) != LGW_HAL_SUCCESS) {
return LGW_HAL_ERROR;
}
break;
case LGW_RADIO_TYPE_SX1257:
printf("Loading AGC fw for sx125x\n");
if (sx1302_agc_load_firmware(agc_firmware_sx125x) != LGW_HAL_SUCCESS) {
return LGW_HAL_ERROR;
}
if (sx1302_agc_start(FW_VERSION_AGC, SX1302_RADIO_TYPE_SX125X, SX1302_AGC_RADIO_GAIN_AUTO, SX1302_AGC_RADIO_GAIN_AUTO, 0) != LGW_HAL_SUCCESS) {
// if (sx1302_agc_start(FW_VERSION_AGC, SX1302_RADIO_TYPE_SX125X, 1, 7, 0) != LGW_HAL_SUCCESS) {
return LGW_HAL_ERROR;
}
break;
default:
break;
}
printf("Loading ARB fw\n");
if (sx1302_arb_load_firmware(arb_firmware) != LGW_HAL_SUCCESS) {
return LGW_HAL_ERROR;
}
if (sx1302_arb_start(FW_VERSION_ARB) != LGW_HAL_SUCCESS) {
return LGW_HAL_ERROR;
}
#endif
// lgw_reg_w(SX1302_REG_CAPTURE_RAM_CLOCK_GATE_OVERRIDE_CLK_OVERRIDE, 3);
/* Configure the Capture Ram block */
lgw_reg_w(SX1302_REG_CAPTURE_RAM_CAPTURE_CFG_ENABLE, 1); /* Enable Capture RAM */
lgw_reg_w(SX1302_REG_CAPTURE_RAM_CAPTURE_CFG_CAPTUREWRAP, 0); /* Capture once, and stop when memory is full */
lgw_reg_w(SX1302_REG_CAPTURE_RAM_CAPTURE_CFG_RAMCONFIG, 0); /* RAM configuration, 0: 4kx32, 1: 2kx64 */
fprintf(stdout, "Capture source: %d\n", capture_source);
lgw_reg_w(SX1302_REG_CAPTURE_RAM_CAPTURE_SOURCE_A_SOURCEMUX, capture_source);
printf("Sampling frequency: %d\n", sampling_frequency[capture_source]);
if (sampling_frequency[capture_source] != 0)
{
period_value = (32e6/sampling_frequency[capture_source]) - 1;
}
else
{
fprintf(stderr ,"ERROR: Sampling frequency is null\n");
return -1;
}
// fprintf(stdout, "period_value=%04X\n", period_value);
lgw_reg_w(SX1302_REG_CAPTURE_RAM_CAPTURE_PERIOD_0_CAPTUREPERIOD, period_value & 0xFF); // LSB
lgw_reg_w(SX1302_REG_CAPTURE_RAM_CAPTURE_PERIOD_1_CAPTUREPERIOD, (period_value>>8) & 0xFF); // MSB
/* Read back registers */
// lgw_reg_r(SX1302_REG_CAPTURE_RAM_CAPTURE_PERIOD_0_CAPTUREPERIOD, &val);
// fprintf(stdout, "SX1302_REG_CAPTURE_RAM_CAPTURE_PERIOD_0_CAPTUREPERIOD value: %d\n", val);
// lgw_reg_r(SX1302_REG_CAPTURE_RAM_CAPTURE_PERIOD_1_CAPTUREPERIOD, &val);
// fprintf(stdout, "SX1302_REG_CAPTURE_RAM_CAPTURE_PERIOD_1_CAPTUREPERIOD value: %d\n", val);
/* Launch capture */
lgw_reg_w(SX1302_REG_CAPTURE_RAM_CAPTURE_CFG_CAPTURESTART, 1);
// lgw_reg_w(SX1302_REG_CAPTURE_RAM_CAPTURE_CFG_CAPTUREFORCETRIGGER, 1);
/* Poll Status.CapComplete */
do{
lgw_reg_r(SX1302_REG_CAPTURE_RAM_STATUS_CAPCOMPLETE, &val);
wait_ms(10);
if ((quit_sig == 1) || (exit_sig == 1)) {
break;
}
} while (val != 1);
lgw_reg_w(SX1302_REG_CAPTURE_RAM_CAPTURE_CFG_CAPTURESTART, 0);
// lgw_reg_r(SX1302_REG_CAPTURE_RAM_LAST_RAM_ADDR_0_LASTRAMADDR, &val);
// fprintf(stdout, "SX1302_REG_CAPTURE_RAM_LAST_RAM_ADDR_0_LASTRAMADDR value: %02x\n", val);
// lgw_reg_r(SX1302_REG_CAPTURE_RAM_LAST_RAM_ADDR_1_LASTRAMADDR, &val);
// fprintf(stdout, "SX1302_REG_CAPTURE_RAM_LAST_RAM_ADDR_1_LASTRAMADDR value: %02x\n", val);
lgw_reg_w(SX1302_REG_COMMON_PAGE_PAGE, 1);
lgw_mem_rb(0, capture_ram_buffer, CAPTURE_RAM_SIZE, false);
lgw_reg_w(SX1302_REG_COMMON_PAGE_PAGE, 0);
printf("Data:\n");
for (i = 0; i < CAPTURE_RAM_SIZE; i += 4)
{
if (((capture_source >= 2) && (capture_source <= 3)) || (capture_source == 9))
{
real = (int16_t)((((uint16_t)(capture_ram_buffer[i+3]) << 8) & 0xFF00) + ((uint16_t)capture_ram_buffer[i+2] & 0x00FF));
imag = (int16_t)((((uint16_t)(capture_ram_buffer[i+1]) << 8) & 0xFF00) + ((uint16_t)capture_ram_buffer[i+0] & 0x00FF));
real >>= 4; // 12 bits I
imag >>= 4; // 12 bits Q
}
else if ((capture_source >= 4) && (capture_source <= 6))
{
real = (int16_t)((((uint16_t)(capture_ram_buffer[i+3]) << 8) & 0xFF00) + ((uint16_t)capture_ram_buffer[i+2] & 0x00FF)); // 16 bits I
imag = (int16_t)((((uint16_t)(capture_ram_buffer[i+1]) << 8) & 0xFF00) + ((uint16_t)capture_ram_buffer[i+0] & 0x00FF)); // 16 bits Q
}
else if ((capture_source >= 10) && (capture_source <= 17))
{
real = (int8_t)(capture_ram_buffer[i+3]); // 8 bits I
imag = (int8_t)(capture_ram_buffer[i+1]); // 8 bits Q
}
else
{
real = 0;
imag = 0;
}
if (((capture_source >= 2) && (capture_source <= 6)) || ((capture_source >= 9) && (capture_source <= 17)))
{
fprintf(stdout, "%d", real);
if (imag >= 0)
{
fprintf(stdout, "+");
}
fprintf(stdout, "%di\n", imag);
}
else
{
printf("%02X ", capture_ram_buffer[i]);
}
}
printf("End of Data\n");
#if FULL_INIT
/* Board reset */
if (system("./reset_lgw.sh stop") != 0) {
printf("ERROR: failed to reset SX1302, check your reset_lgw.sh script\n");
exit(EXIT_FAILURE);
}
#endif
return 0;
}

View File

@ -0,0 +1,257 @@
/*
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(C)2019 Semtech
Description:
Minimum test program for HAL timestamp counter handling
License: Revised BSD License, see LICENSE.TXT file include in the project
*/
/* -------------------------------------------------------------------------- */
/* --- DEPENDANCIES --------------------------------------------------------- */
/* fix an issue between POSIX and C99 */
#if __STDC_VERSION__ >= 199901L
#define _XOPEN_SOURCE 600
#else
#define _XOPEN_SOURCE 500
#endif
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <math.h>
#include "loragw_hal.h"
#include "loragw_reg.h"
#include "loragw_aux.h"
/* -------------------------------------------------------------------------- */
/* --- PRIVATE MACROS ------------------------------------------------------- */
#define LINUXDEV_PATH_DEFAULT "/dev/spidev0.0"
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
#define RAND_RANGE(min, max) (rand() % (max + 1 - min) + min)
/* -------------------------------------------------------------------------- */
/* --- PRIVATE CONSTANTS ---------------------------------------------------- */
#define DEFAULT_FREQ_HZ 868500000U
/* -------------------------------------------------------------------------- */
/* --- PRIVATE VARIABLES ---------------------------------------------------- */
static int exit_sig = 0; /* 1 -> application terminates cleanly (shut down hardware, close open files, etc) */
static int quit_sig = 0; /* 1 -> application terminates without shutting down the hardware */
/* -------------------------------------------------------------------------- */
/* --- PRIVATE FUNCTIONS ---------------------------------------------------- */
static void sig_handler(int sigio) {
if (sigio == SIGQUIT) {
quit_sig = 1;;
} else if ((sigio == SIGINT) || (sigio == SIGTERM)) {
exit_sig = 1;
}
}
void usage(void) {
//printf("Library version information: %s\n", lgw_version_info());
printf( "Available options:\n");
printf( " -h print this help\n");
printf( " -k <uint> Concentrator clock source (Radio A or Radio B) [0..1]\n");
printf( " -r <uint> Radio type (1255, 1257, 1250)\n");
printf( " -p Test PPS trig counter when set\n" );
}
/* -------------------------------------------------------------------------- */
/* --- MAIN FUNCTION -------------------------------------------------------- */
int main(int argc, char **argv)
{
/* SPI interfaces */
const char spidev_path_default[] = LINUXDEV_PATH_DEFAULT;
const char * spidev_path = spidev_path_default;
struct sigaction sigact; /* SIGQUIT&SIGINT&SIGTERM signal handling */
int i, x;
uint32_t fa = DEFAULT_FREQ_HZ;
uint32_t fb = DEFAULT_FREQ_HZ;
unsigned int arg_u;
uint8_t clocksource = 0;
lgw_radio_type_t radio_type = LGW_RADIO_TYPE_NONE;
struct lgw_conf_board_s boardconf;
struct lgw_conf_rxrf_s rfconf;
struct lgw_conf_rxif_s ifconf;
uint32_t counter;
bool trig_cnt = false;
const int32_t channel_if[9] = {
-400000,
-200000,
0,
-400000,
-200000,
0,
200000,
400000,
-200000 /* lora service */
};
const uint8_t channel_rfchain[9] = { 1, 1, 1, 0, 0, 0, 0, 0, 1 };
/* parse command line options */
while ((i = getopt (argc, argv, "hk:r:p")) != -1) {
switch (i) {
case 'h':
usage();
return -1;
break;
case 'r': /* <uint> Radio type */
i = sscanf(optarg, "%u", &arg_u);
if ((i != 1) || ((arg_u != 1255) && (arg_u != 1257) && (arg_u != 1250))) {
printf("ERROR: argument parsing of -r argument. Use -h to print help\n");
return EXIT_FAILURE;
} else {
switch (arg_u) {
case 1255:
radio_type = LGW_RADIO_TYPE_SX1255;
break;
case 1257:
radio_type = LGW_RADIO_TYPE_SX1257;
break;
default: /* 1250 */
radio_type = LGW_RADIO_TYPE_SX1250;
break;
}
}
break;
case 'k': /* <uint> Clock Source */
i = sscanf(optarg, "%u", &arg_u);
if ((i != 1) || (arg_u > 1)) {
printf("ERROR: argument parsing of -k argument. Use -h to print help\n");
return EXIT_FAILURE;
} else {
clocksource = (uint8_t)arg_u;
}
break;
case 'p':
trig_cnt = true;
break;
default:
printf("ERROR: argument parsing\n");
usage();
return -1;
}
}
/* configure signal handling */
sigemptyset(&sigact.sa_mask);
sigact.sa_flags = 0;
sigact.sa_handler = sig_handler;
sigaction(SIGQUIT, &sigact, NULL);
sigaction(SIGINT, &sigact, NULL);
sigaction(SIGTERM, &sigact, NULL);
printf("===== sx1302 counter test =====\n");
/* Board reset */
if (system("./reset_lgw.sh start") != 0) {
printf("ERROR: failed to reset SX1302, check your reset_lgw.sh script\n");
exit(EXIT_FAILURE);
}
/* Configure the gateway */
memset( &boardconf, 0, sizeof boardconf);
boardconf.lorawan_public = true;
boardconf.clksrc = clocksource;
boardconf.full_duplex = false;
strncpy(boardconf.spidev_path, spidev_path, sizeof boardconf.spidev_path);
if (lgw_board_setconf(&boardconf) != LGW_HAL_SUCCESS) {
printf("ERROR: failed to configure board\n");
return EXIT_FAILURE;
}
/* set configuration for RF chains */
memset( &rfconf, 0, sizeof rfconf);
rfconf.enable = true;
rfconf.freq_hz = fa;
rfconf.type = radio_type;
rfconf.tx_enable = false;
if (lgw_rxrf_setconf(0, &rfconf) != LGW_HAL_SUCCESS) {
printf("ERROR: failed to configure rxrf 0\n");
return EXIT_FAILURE;
}
memset( &rfconf, 0, sizeof rfconf);
rfconf.enable = true;
rfconf.freq_hz = fb;
rfconf.type = radio_type;
rfconf.tx_enable = false;
if (lgw_rxrf_setconf(1, &rfconf) != LGW_HAL_SUCCESS) {
printf("ERROR: failed to configure rxrf 1\n");
return EXIT_FAILURE;
}
/* set configuration for LoRa multi-SF channels (bandwidth cannot be set) */
memset(&ifconf, 0, sizeof(ifconf));
for (i = 0; i < 9; i++) {
ifconf.enable = true;
ifconf.rf_chain = channel_rfchain[i];
ifconf.freq_hz = channel_if[i];
ifconf.datarate = DR_LORA_SF7;
if (lgw_rxif_setconf(i, &ifconf) != LGW_HAL_SUCCESS) {
printf("ERROR: failed to configure rxif %d\n", i);
return EXIT_FAILURE;
}
}
/* connect, configure and start the LoRa concentrator */
x = lgw_start();
if (x != 0) {
printf("ERROR: failed to start the gateway\n");
return EXIT_FAILURE;
}
/* Loop until user quits */
while( (quit_sig != 1) && (exit_sig != 1) ) {
if (trig_cnt == false) {
lgw_get_instcnt(&counter);
} else {
lgw_get_trigcnt(&counter);
}
wait_ms(10);
}
/* Stop the gateway */
x = lgw_stop();
if (x != 0) {
printf("ERROR: failed to stop the gateway\n");
return EXIT_FAILURE;
}
/* Board reset */
if (system("./reset_lgw.sh stop") != 0) {
printf("ERROR: failed to reset SX1302, check your reset_lgw.sh script\n");
exit(EXIT_FAILURE);
}
printf("=========== Test End ===========\n");
return 0;
}
/* --- EOF ------------------------------------------------------------------ */

View File

@ -0,0 +1,415 @@
/*
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(C)2019 Semtech
Description:
Minimum test program for the loragw_gps module
License: Revised BSD License, see LICENSE.TXT file include in the project
*/
/* -------------------------------------------------------------------------- */
/* --- DEPENDANCIES --------------------------------------------------------- */
/* fix an issue between POSIX and C99 */
#if __STDC_VERSION__ >= 199901L
#define _XOPEN_SOURCE 600
#else
#define _XOPEN_SOURCE 500
#endif
#include <stdint.h> /* C99 types */
#include <stdbool.h> /* bool type */
#include <stdio.h> /* printf */
#include <string.h> /* memset */
#include <signal.h> /* sigaction */
#include <stdlib.h> /* exit */
#include <unistd.h> /* read */
#include "loragw_hal.h"
#include "loragw_gps.h"
#include "loragw_aux.h"
/* -------------------------------------------------------------------------- */
/* --- PRIVATE MACROS ------------------------------------------------------- */
#define MATCH(a,b) ( ((int32_t)(a-b)<=1) && ((int32_t)(a-b)>=-1) ) /* tolerate 1µs */
/* -------------------------------------------------------------------------- */
/* --- PRIVATE CONSTANTS ---------------------------------------------------- */
#define LINUXDEV_PATH_DEFAULT "/dev/spidev0.0"
/* -------------------------------------------------------------------------- */
/* --- PRIVATE VARIABLES ---------------------------------------------------- */
static int exit_sig = 0; /* 1 -> application terminates cleanly (shut down hardware, close open files, etc) */
static int quit_sig = 0; /* 1 -> application terminates without shutting down the hardware */
struct tref ppm_ref;
/* -------------------------------------------------------------------------- */
/* --- PRIVATE FUNCTIONS DECLARATION ---------------------------------------- */
static void sig_handler(int sigio);
static void gps_process_sync(void);
static void gps_process_coords(void);
/* -------------------------------------------------------------------------- */
/* --- PRIVATE FUNCTIONS DEFINITION ----------------------------------------- */
void usage(void) {
//printf("Library version information: %s\n", lgw_version_info());
printf( "Available options:\n");
printf( " -h print this help\n");
printf( " -k <uint> Concentrator clock source (Radio A or Radio B) [0..1]\n");
printf( " -r <uint> Radio type (1255, 1257, 1250)\n");
}
static void sig_handler(int sigio) {
if (sigio == SIGQUIT) {
quit_sig = 1;;
} else if ((sigio == SIGINT) || (sigio == SIGTERM)) {
exit_sig = 1;
}
}
static void gps_process_sync(void) {
/* variables for PPM pulse GPS synchronization */
uint32_t ppm_tstamp;
struct timespec ppm_gps;
struct timespec ppm_utc;
/* variables for timestamp <-> GPS time conversions */
uint32_t x, z;
struct timespec y;
/* get GPS time for synchronization */
int i = lgw_gps_get(&ppm_utc, &ppm_gps, NULL, NULL);
if (i != LGW_GPS_SUCCESS) {
printf(" No valid reference GPS time available, synchronization impossible.\n");
return;
}
/* get timestamp for synchronization */
i = lgw_get_trigcnt(&ppm_tstamp);
if (i != LGW_HAL_SUCCESS) {
printf(" Failed to read timestamp, synchronization impossible.\n");
return;
}
/* try to update synchronize time reference with the new GPS & timestamp */
i = lgw_gps_sync(&ppm_ref, ppm_tstamp, ppm_utc, ppm_gps);
if (i != LGW_GPS_SUCCESS) {
printf(" Synchronization error.\n");
return;
}
/* display result */
printf(" * Synchronization successful *\n");
printf(" UTC reference time: %lld.%09ld\n", (long long)ppm_ref.utc.tv_sec, ppm_ref.utc.tv_nsec);
printf(" GPS reference time: %lld.%09ld\n", (long long)ppm_ref.gps.tv_sec, ppm_ref.gps.tv_nsec);
printf(" Internal counter reference value: %u\n", ppm_ref.count_us);
printf(" Clock error: %.9f\n", ppm_ref.xtal_err);
x = ppm_tstamp + 500000;
/* CNT -> GPS -> CNT */
printf("\n");
printf(" * Test of timestamp counter <-> GPS value conversion *\n");
printf(" Test value: %u\n", x);
lgw_cnt2gps(ppm_ref, x, &y);
printf(" Conversion to GPS: %lld.%09ld\n", (long long)y.tv_sec, y.tv_nsec);
lgw_gps2cnt(ppm_ref, y, &z);
printf(" Converted back: %u ==> %dµs\n", z, (int32_t)(z-x));
/* Display test result */
if (MATCH(x,z)) {
printf(" ** PASS **: (SX1302 -> GPS -> SX1302) conversion MATCH\n");
} else {
printf(" ** FAILED **: (SX1302 -> GPS -> SX1302) conversion MISMATCH\n");
}
/* CNT -> UTC -> CNT */
printf("\n");
printf(" * Test of timestamp counter <-> UTC value conversion *\n");
printf(" Test value: %u\n", x);
lgw_cnt2utc(ppm_ref, x, &y);
printf(" Conversion to UTC: %lld.%09ld\n", (long long)y.tv_sec, y.tv_nsec);
lgw_utc2cnt(ppm_ref, y, &z);
printf(" Converted back: %u ==> %dµs\n", z, (int32_t)(z-x));
/* Display test result */
if (MATCH(x,z)) {
printf(" ** PASS **: (SX1302 -> UTC -> SX1302) conversion MATCH\n");
} else {
printf(" ** FAILED **: (SX1302 -> UTC -> SX1302) conversion MISMATCH\n");
}
}
static void gps_process_coords(void) {
/* position variable */
struct coord_s coord;
struct coord_s gpserr;
int i = lgw_gps_get(NULL, NULL, &coord, &gpserr);
/* update gateway coordinates */
if (i == LGW_GPS_SUCCESS) {
printf("\n");
printf("# GPS coordinates: latitude %.5f, longitude %.5f, altitude %i m\n", coord.lat, coord.lon, coord.alt);
printf("# GPS err: latitude %.5f, longitude %.5f, altitude %i m\n", gpserr.lat, gpserr.lon, gpserr.alt);
}
}
/* -------------------------------------------------------------------------- */
/* --- MAIN FUNCTION -------------------------------------------------------- */
int main(int argc, char **argv)
{
/* SPI interfaces */
const char spidev_path_default[] = LINUXDEV_PATH_DEFAULT;
const char * spidev_path = spidev_path_default;
struct sigaction sigact; /* SIGQUIT&SIGINT&SIGTERM signal handling */
int i;
unsigned int arg_u;
/* concentrator variables */
uint8_t clocksource = 0;
lgw_radio_type_t radio_type = LGW_RADIO_TYPE_NONE;
struct lgw_conf_board_s boardconf;
struct lgw_conf_rxrf_s rfconf;
/* serial variables */
char serial_buff[128]; /* buffer to receive GPS data */
size_t wr_idx = 0; /* pointer to end of chars in buffer */
int gps_tty_dev; /* file descriptor to the serial port of the GNSS module */
/* NMEA/UBX variables */
enum gps_msg latest_msg; /* keep track of latest NMEA/UBX message parsed */
/* parse command line options */
while ((i = getopt (argc, argv, "hk:r:")) != -1) {
switch (i) {
case 'h':
usage();
return -1;
break;
case 'r': /* <uint> Radio type */
i = sscanf(optarg, "%u", &arg_u);
if ((i != 1) || ((arg_u != 1255) && (arg_u != 1257) && (arg_u != 1250))) {
printf("ERROR: argument parsing of -r argument. Use -h to print help\n");
return EXIT_FAILURE;
} else {
switch (arg_u) {
case 1255:
radio_type = LGW_RADIO_TYPE_SX1255;
break;
case 1257:
radio_type = LGW_RADIO_TYPE_SX1257;
break;
default: /* 1250 */
radio_type = LGW_RADIO_TYPE_SX1250;
break;
}
}
break;
case 'k': /* <uint> Clock Source */
i = sscanf(optarg, "%u", &arg_u);
if ((i != 1) || (arg_u > 1)) {
printf("ERROR: argument parsing of -k argument. Use -h to print help\n");
return EXIT_FAILURE;
} else {
clocksource = (uint8_t)arg_u;
}
break;
default:
printf("ERROR: argument parsing\n");
usage();
exit(EXIT_FAILURE);
}
}
/* Check arguments */
if (radio_type == LGW_RADIO_TYPE_NONE) {
printf("ERROR: radio type must be specified\n");
usage();
exit(EXIT_FAILURE);
}
/* configure signal handling */
sigemptyset(&sigact.sa_mask);
sigact.sa_flags = 0;
sigact.sa_handler = sig_handler;
sigaction(SIGQUIT, &sigact, NULL);
sigaction(SIGINT, &sigact, NULL);
sigaction(SIGTERM, &sigact, NULL);
/* Intro message and library information */
printf("Beginning of test for loragw_gps.c\n");
printf("*** Library version information ***\n%s\n***\n", lgw_version_info());
/* Board reset */
if (system("./reset_lgw.sh start") != 0) {
printf("ERROR: failed to reset SX1302, check your reset_lgw.sh script\n");
exit(EXIT_FAILURE);
}
/* Open and configure GPS */
i = lgw_gps_enable("/dev/ttyS0", "ubx7", 0, &gps_tty_dev);
if (i != LGW_GPS_SUCCESS) {
printf("ERROR: Failed to enable GPS\n");
exit(EXIT_FAILURE);
}
/* start concentrator (default conf for IoT SK) */
/* board config */
memset(&boardconf, 0, sizeof(boardconf));
boardconf.lorawan_public = true;
boardconf.clksrc = clocksource;
boardconf.full_duplex = false;
strncpy(boardconf.spidev_path, spidev_path, sizeof boardconf.spidev_path);
if (lgw_board_setconf(&boardconf) != LGW_HAL_SUCCESS) {
printf("ERROR: failed to configure board\n");
return EXIT_FAILURE;
}
/* set configuration for RF chains */
memset( &rfconf, 0, sizeof rfconf);
rfconf.enable = true;
rfconf.freq_hz = 868000000;
rfconf.rssi_offset = 0.0;
rfconf.type = radio_type;
rfconf.tx_enable = false;
if (lgw_rxrf_setconf(0, &rfconf) != LGW_HAL_SUCCESS) {
printf("ERROR: failed to configure rxrf 0\n");
return EXIT_FAILURE;
}
memset( &rfconf, 0, sizeof rfconf);
rfconf.enable = true;
rfconf.freq_hz = 868000000;
rfconf.rssi_offset = 0.0;
rfconf.type = radio_type;
rfconf.tx_enable = false;
if (lgw_rxrf_setconf(1, &rfconf) != LGW_HAL_SUCCESS) {
printf("ERROR: failed to configure rxrf 1\n");
return EXIT_FAILURE;
}
/* start */
if (lgw_start() != LGW_HAL_SUCCESS) {
printf("ERROR: IMPOSSIBLE TO START THE GATEWAY\n");
exit(EXIT_FAILURE);
}
/* initialize some variables before loop */
memset(serial_buff, 0, sizeof serial_buff);
memset(&ppm_ref, 0, sizeof ppm_ref);
/* loop until user action */
while ((quit_sig != 1) && (exit_sig != 1)) {
size_t rd_idx = 0;
size_t frame_end_idx = 0;
/* blocking non-canonical read on serial port */
ssize_t nb_char = read(gps_tty_dev, serial_buff + wr_idx, LGW_GPS_MIN_MSG_SIZE);
if (nb_char <= 0) {
printf("WARNING: [gps] read() returned value %d\n", nb_char);
continue;
}
wr_idx += (size_t)nb_char;
/*******************************************
* Scan buffer for UBX/NMEA sync chars and *
* attempt to decode frame if one is found *
*******************************************/
while (rd_idx < wr_idx) {
size_t frame_size = 0;
/* Scan buffer for UBX sync char */
if (serial_buff[rd_idx] == LGW_GPS_UBX_SYNC_CHAR) {
/***********************
* Found UBX sync char *
***********************/
latest_msg = lgw_parse_ubx(&serial_buff[rd_idx], (wr_idx - rd_idx), &frame_size);
if (frame_size > 0) {
if (latest_msg == INCOMPLETE) {
/* UBX header found but frame appears to be missing bytes */
frame_size = 0;
} else if (latest_msg == INVALID) {
/* message header received but message appears to be corrupted */
printf("WARNING: [gps] could not get a valid message from GPS (no time)\n");
frame_size = 0;
} else if (latest_msg == UBX_NAV_TIMEGPS) {
printf("\n~~ UBX NAV-TIMEGPS sentence, triggering synchronization attempt ~~\n");
gps_process_sync();
}
}
} else if(serial_buff[rd_idx] == LGW_GPS_NMEA_SYNC_CHAR) {
/************************
* Found NMEA sync char *
************************/
/* scan for NMEA end marker (LF = 0x0a) */
char* nmea_end_ptr = memchr(&serial_buff[rd_idx],(int)0x0a, (wr_idx - rd_idx));
if (nmea_end_ptr) {
/* found end marker */
frame_size = nmea_end_ptr - &serial_buff[rd_idx] + 1;
latest_msg = lgw_parse_nmea(&serial_buff[rd_idx], frame_size);
if(latest_msg == INVALID || latest_msg == UNKNOWN) {
/* checksum failed */
frame_size = 0;
} else if (latest_msg == NMEA_RMC) { /* Get location from RMC frames */
gps_process_coords();
}
}
}
if (frame_size > 0) {
/* At this point message is a checksum verified frame
we're processed or ignored. Remove frame from buffer */
rd_idx += frame_size;
frame_end_idx = rd_idx;
} else {
rd_idx++;
}
} /* ...for(rd_idx = 0... */
if (frame_end_idx) {
/* Frames have been processed. Remove bytes to end of last processed frame */
memcpy(serial_buff,&serial_buff[frame_end_idx],wr_idx - frame_end_idx);
wr_idx -= frame_end_idx;
} /* ...for(rd_idx = 0... */
/* Prevent buffer overflow */
if ((sizeof(serial_buff) - wr_idx) < LGW_GPS_MIN_MSG_SIZE) {
memcpy(serial_buff,&serial_buff[LGW_GPS_MIN_MSG_SIZE],wr_idx - LGW_GPS_MIN_MSG_SIZE);
wr_idx -= LGW_GPS_MIN_MSG_SIZE;
}
}
/* clean up before leaving */
if (exit_sig == 1) {
lgw_gps_disable(gps_tty_dev);
lgw_stop();
}
/* Board reset */
if (system("./reset_lgw.sh stop") != 0) {
printf("ERROR: failed to reset SX1302, check your reset_lgw.sh script\n");
exit(EXIT_FAILURE);
}
printf("\nEnd of test for loragw_gps.c\n");
exit(EXIT_SUCCESS);
}
/* --- EOF ------------------------------------------------------------------ */

View File

@ -0,0 +1,320 @@
/*
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(C)2019 Semtech
Description:
Minimum test program for HAL RX capability
License: Revised BSD License, see LICENSE.TXT file include in the project
*/
/* -------------------------------------------------------------------------- */
/* --- DEPENDANCIES --------------------------------------------------------- */
/* fix an issue between POSIX and C99 */
#if __STDC_VERSION__ >= 199901L
#define _XOPEN_SOURCE 600
#else
#define _XOPEN_SOURCE 500
#endif
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <math.h>
#include "loragw_hal.h"
#include "loragw_reg.h"
#include "loragw_aux.h"
/* -------------------------------------------------------------------------- */
/* --- PRIVATE MACROS ------------------------------------------------------- */
#define LINUXDEV_PATH_DEFAULT "/dev/spidev0.0"
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
#define RAND_RANGE(min, max) (rand() % (max + 1 - min) + min)
/* -------------------------------------------------------------------------- */
/* --- PRIVATE CONSTANTS ---------------------------------------------------- */
#define DEFAULT_FREQ_HZ 868500000U
/* -------------------------------------------------------------------------- */
/* --- PRIVATE VARIABLES ---------------------------------------------------- */
static int exit_sig = 0; /* 1 -> application terminates cleanly (shut down hardware, close open files, etc) */
static int quit_sig = 0; /* 1 -> application terminates without shutting down the hardware */
/* -------------------------------------------------------------------------- */
/* --- PRIVATE FUNCTIONS ---------------------------------------------------- */
static void sig_handler(int sigio) {
if (sigio == SIGQUIT) {
quit_sig = 1;
} else if ((sigio == SIGINT) || (sigio == SIGTERM)) {
exit_sig = 1;
}
}
void usage(void) {
//printf("Library version information: %s\n", lgw_version_info());
printf( "Available options:\n");
printf( " -h print this help\n");
printf( " -k <uint> Concentrator clock source (Radio A or Radio B) [0..1]\n");
printf( " -r <uint> Radio type (1255, 1257, 1250)\n");
printf( " -a <float> Radio A RX frequency in MHz\n");
printf( " -b <float> Radio B RX frequency in MHz\n");
printf( " -n <uint> number of packet received with CRC OK for each HAL start/stop loop\n");
}
/* -------------------------------------------------------------------------- */
/* --- MAIN FUNCTION -------------------------------------------------------- */
int main(int argc, char **argv)
{
/* SPI interfaces */
const char spidev_path_default[] = LINUXDEV_PATH_DEFAULT;
const char * spidev_path = spidev_path_default;
struct sigaction sigact; /* SIGQUIT&SIGINT&SIGTERM signal handling */
int i, j, x;
uint32_t fa = DEFAULT_FREQ_HZ;
uint32_t fb = DEFAULT_FREQ_HZ;
double arg_d = 0.0;
unsigned int arg_u;
uint8_t clocksource = 0;
lgw_radio_type_t radio_type = LGW_RADIO_TYPE_NONE;
struct lgw_conf_board_s boardconf;
struct lgw_conf_rxrf_s rfconf;
struct lgw_conf_rxif_s ifconf;
struct lgw_pkt_rx_s rxpkt[16];
unsigned long nb_pkt_crc_ok = 0, nb_loop = 1, cnt_loop;
int nb_pkt;
const int32_t channel_if[9] = {
-400000,
-200000,
0,
-400000,
-200000,
0,
200000,
400000,
-200000 /* lora service */
};
const uint8_t channel_rfchain[9] = { 1, 1, 1, 0, 0, 0, 0, 0, 1 };
/* parse command line options */
while ((i = getopt (argc, argv, "ha:b:k:r:n:")) != -1) {
switch (i) {
case 'h':
usage();
return -1;
break;
case 'r': /* <uint> Radio type */
i = sscanf(optarg, "%u", &arg_u);
if ((i != 1) || ((arg_u != 1255) && (arg_u != 1257) && (arg_u != 1250))) {
printf("ERROR: argument parsing of -r argument. Use -h to print help\n");
return EXIT_FAILURE;
} else {
switch (arg_u) {
case 1255:
radio_type = LGW_RADIO_TYPE_SX1255;
break;
case 1257:
radio_type = LGW_RADIO_TYPE_SX1257;
break;
default: /* 1250 */
radio_type = LGW_RADIO_TYPE_SX1250;
break;
}
}
break;
case 'k': /* <uint> Clock Source */
i = sscanf(optarg, "%u", &arg_u);
if ((i != 1) || (arg_u > 1)) {
printf("ERROR: argument parsing of -k argument. Use -h to print help\n");
return EXIT_FAILURE;
} else {
clocksource = (uint8_t)arg_u;
}
break;
case 'a': /* <float> Radio A RX frequency in MHz */
i = sscanf(optarg, "%lf", &arg_d);
if (i != 1) {
printf("ERROR: argument parsing of -f argument. Use -h to print help\n");
return EXIT_FAILURE;
} else {
fa = (uint32_t)((arg_d*1e6) + 0.5); /* .5 Hz offset to get rounding instead of truncating */
}
break;
case 'b': /* <float> Radio B RX frequency in MHz */
i = sscanf(optarg, "%lf", &arg_d);
if (i != 1) {
printf("ERROR: argument parsing of -f argument. Use -h to print help\n");
return EXIT_FAILURE;
} else {
fb = (uint32_t)((arg_d*1e6) + 0.5); /* .5 Hz offset to get rounding instead of truncating */
}
break;
case 'n': /* <uint> NUmber of packets to be received before exiting */
i = sscanf(optarg, "%u", &arg_u);
if (i != 1) {
printf("ERROR: argument parsing of -n argument. Use -h to print help\n");
return EXIT_FAILURE;
} else {
nb_loop = arg_u;
}
break;
default:
printf("ERROR: argument parsing\n");
usage();
return -1;
}
}
/* configure signal handling */
sigemptyset(&sigact.sa_mask);
sigact.sa_flags = 0;
sigact.sa_handler = sig_handler;
sigaction(SIGQUIT, &sigact, NULL);
sigaction(SIGINT, &sigact, NULL);
sigaction(SIGTERM, &sigact, NULL);
printf("===== sx1302 HAL RX test =====\n");
/* Configure the gateway */
memset( &boardconf, 0, sizeof boardconf);
boardconf.lorawan_public = true;
boardconf.clksrc = clocksource;
boardconf.full_duplex = false;
strncpy(boardconf.spidev_path, spidev_path, sizeof boardconf.spidev_path);
if (lgw_board_setconf(&boardconf) != LGW_HAL_SUCCESS) {
printf("ERROR: failed to configure board\n");
return EXIT_FAILURE;
}
/* set configuration for RF chains */
memset( &rfconf, 0, sizeof rfconf);
rfconf.enable = true;
rfconf.freq_hz = fa;
rfconf.type = radio_type;
rfconf.tx_enable = false;
if (lgw_rxrf_setconf(0, &rfconf) != LGW_HAL_SUCCESS) {
printf("ERROR: failed to configure rxrf 0\n");
return EXIT_FAILURE;
}
memset( &rfconf, 0, sizeof rfconf);
rfconf.enable = true;
rfconf.freq_hz = fb;
rfconf.type = radio_type;
rfconf.tx_enable = false;
if (lgw_rxrf_setconf(1, &rfconf) != LGW_HAL_SUCCESS) {
printf("ERROR: failed to configure rxrf 1\n");
return EXIT_FAILURE;
}
/* set configuration for LoRa multi-SF channels (bandwidth cannot be set) */
memset(&ifconf, 0, sizeof(ifconf));
for (i = 0; i < 9; i++) {
ifconf.enable = true;
ifconf.rf_chain = channel_rfchain[i];
ifconf.freq_hz = channel_if[i];
ifconf.datarate = DR_LORA_SF7;
if (lgw_rxif_setconf(i, &ifconf) != LGW_HAL_SUCCESS) {
printf("ERROR: failed to configure rxif %d\n", i);
return EXIT_FAILURE;
}
}
/* Loop until user quits */
cnt_loop = 0;
while( (quit_sig != 1) && (exit_sig != 1) )
{
cnt_loop += 1;
/* Board reset */
if (system("./reset_lgw.sh start") != 0) {
printf("ERROR: failed to reset SX1302, check your reset_lgw.sh script\n");
exit(EXIT_FAILURE);
}
/* connect, configure and start the LoRa concentrator */
x = lgw_start();
if (x != 0) {
printf("ERROR: failed to start the gateway\n");
return EXIT_FAILURE;
}
/* Loop until we have enough packets with CRC OK */
printf("Waiting for packets...\n");
nb_pkt_crc_ok = 0;
while ((nb_pkt_crc_ok < nb_loop) && (quit_sig != 1) && (exit_sig != 1)) {
/* fetch N packets */
nb_pkt = lgw_receive(ARRAY_SIZE(rxpkt), rxpkt);
if (nb_pkt == 0) {
wait_ms(10);
} else {
printf("Received %d packets\n", nb_pkt);
for (i = 0; i < nb_pkt; i++) {
if (rxpkt[i].status == STAT_CRC_OK) {
nb_pkt_crc_ok += 1;
}
printf("\n----- %s packet -----\n", (rxpkt[i].modulation == MOD_LORA) ? "LoRa" : "FSK");
printf(" count_us: %u\n", rxpkt[i].count_us);
printf(" size: %u\n", rxpkt[i].size);
printf(" chan: %u\n", rxpkt[i].if_chain);
printf(" status: 0x%02X\n", rxpkt[i].status);
printf(" datr: %u\n", rxpkt[i].datarate);
printf(" codr: %u\n", rxpkt[i].coderate);
printf(" rf_chain %u\n", rxpkt[i].rf_chain);
printf(" freq_hz %u\n", rxpkt[i].freq_hz);
printf(" snr_avg: %.1f\n", rxpkt[i].snr);
printf(" rssi_chan:%.1f\n", rxpkt[i].rssic);
printf(" rssi_sig :%.1f\n", rxpkt[i].rssis);
printf(" crc: 0x%04X\n", rxpkt[i].crc);
for (j = 0; j < rxpkt[i].size; j++) {
printf("%02X ", rxpkt[i].payload[j]);
}
printf("\n");
}
}
}
printf( "\nNb valid packets received: %lu CRC OK (%lu)\n", nb_pkt_crc_ok, cnt_loop );
/* Stop the gateway */
x = lgw_stop();
if (x != 0) {
printf("ERROR: failed to stop the gateway\n");
return EXIT_FAILURE;
}
/* Board reset */
if (system("./reset_lgw.sh stop") != 0) {
printf("ERROR: failed to reset SX1302, check your reset_lgw.sh script\n");
exit(EXIT_FAILURE);
}
}
printf("=========== Test End ===========\n");
return 0;
}
/* --- EOF ------------------------------------------------------------------ */

View File

@ -0,0 +1,583 @@
/*
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(C)2019 Semtech
Description:
Minimum test program for HAL TX capability
License: Revised BSD License, see LICENSE.TXT file include in the project
*/
/* -------------------------------------------------------------------------- */
/* --- DEPENDANCIES --------------------------------------------------------- */
/* fix an issue between POSIX and C99 */
#if __STDC_VERSION__ >= 199901L
#define _XOPEN_SOURCE 600
#else
#define _XOPEN_SOURCE 500
#endif
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <math.h>
#include <signal.h> /* sigaction */
#include <getopt.h> /* getopt_long */
#include "loragw_hal.h"
#include "loragw_reg.h"
#include "loragw_aux.h"
/* -------------------------------------------------------------------------- */
/* --- PRIVATE MACROS ------------------------------------------------------- */
#define RAND_RANGE(min, max) (rand() % (max + 1 - min) + min)
/* -------------------------------------------------------------------------- */
/* --- PRIVATE CONSTANTS ---------------------------------------------------- */
#define LINUXDEV_PATH_DEFAULT "/dev/spidev0.0"
#define DEFAULT_CLK_SRC 0
#define DEFAULT_FREQ_HZ 868500000U
/* -------------------------------------------------------------------------- */
/* --- PRIVATE VARIABLES ---------------------------------------------------- */
/* Signal handling variables */
static int exit_sig = 0; /* 1 -> application terminates cleanly (shut down hardware, close open files, etc) */
static int quit_sig = 0; /* 1 -> application terminates without shutting down the hardware */
/* -------------------------------------------------------------------------- */
/* --- PRIVATE FUNCTIONS ---------------------------------------------------- */
/* describe command line options */
void usage(void) {
//printf("Library version information: %s\n", lgw_version_info());
printf("Available options:\n");
printf(" -h print this help\n");
printf(" -k <uint> Concentrator clock source (Radio A or Radio B) [0..1]\n");
printf(" -c <uint> RF chain to be used for TX (Radio A or Radio B) [0..1]\n");
printf(" -r <uint> Radio type (1255, 1257, 1250)\n");
printf(" -f <float> Radio TX frequency in MHz\n");
printf(" -m <str> modulation type ['CW', 'LORA', 'FSK']\n");
printf(" -o <int> CW frequency offset from Radio TX frequency in kHz [-65..65]\n");
printf(" -s <uint> LoRa datarate 0:random, [5..12]\n");
printf(" -b <uint> LoRa bandwidth in khz 0:random, [125, 250, 500]\n");
printf(" -l <uint> FSK/LoRa preamble length, [6..65535]\n");
printf(" -d <uint> FSK frequency deviation in kHz [1:250]\n");
printf(" -q <float> FSK bitrate in kbps [0.5:250]\n");
printf(" -n <uint> Number of packets to be sent\n");
printf(" -z <uint> size of packets to be sent 0:random, [9..255]\n");
printf(" -t <uint> TX mode timestamped with delay in ms. If delay is 0, TX mode GPS trigger\n");
printf(" -p <int> RF power in dBm\n");
printf(" -i Send LoRa packet using inverted modulation polarity\n");
printf( "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" );
printf(" --pa <uint> PA gain SX125x:[0..3], SX1250:[0,1]\n");
printf(" --dig <uint> sx1302 digital gain for sx125x [0..3]\n");
printf(" --dac <uint> sx125x DAC gain [0..3]\n");
printf(" --mix <uint> sx125x MIX gain [5..15]\n");
printf(" --pwid <uint> sx1250 power index [0..22]\n");
printf( "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" );
printf(" --nhdr Send LoRa packet with implicit header\n");
printf( "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" );
printf(" --loop Number of loops for HAL start/stop (HAL unitary test)\n");
}
/* handle signals */
static void sig_handler(int sigio)
{
if (sigio == SIGQUIT) {
quit_sig = 1;
}
else if((sigio == SIGINT) || (sigio == SIGTERM)) {
exit_sig = 1;
}
}
/* -------------------------------------------------------------------------- */
/* --- MAIN FUNCTION -------------------------------------------------------- */
int main(int argc, char **argv)
{
int i, x;
uint32_t ft = DEFAULT_FREQ_HZ;
int8_t rf_power = 0;
uint8_t sf = 0;
uint16_t bw_khz = 0;
uint32_t nb_pkt = 1;
unsigned int nb_loop = 1, cnt_loop;
uint8_t size = 0;
char mod[64] = "LORA";
float br_kbps = 50;
uint8_t fdev_khz = 25;
int8_t freq_offset = 0;
double arg_d = 0.0;
unsigned int arg_u;
int arg_i;
char arg_s[64];
float xf = 0.0;
uint8_t clocksource = 0;
uint8_t rf_chain = 0;
lgw_radio_type_t radio_type = LGW_RADIO_TYPE_NONE;
uint16_t preamble = 8;
bool invert_pol = false;
bool no_header = false;
struct lgw_conf_board_s boardconf;
struct lgw_conf_rxrf_s rfconf;
struct lgw_pkt_tx_s pkt;
struct lgw_tx_gain_lut_s txlut; /* TX gain table */
uint8_t tx_status;
uint32_t count_us;
uint32_t trig_delay_us = 1000000;
bool trig_delay = false;
/* SPI interfaces */
const char spidev_path_default[] = LINUXDEV_PATH_DEFAULT;
const char * spidev_path = spidev_path_default;
static struct sigaction sigact; /* SIGQUIT&SIGINT&SIGTERM signal handling */
/* Initialize TX gain LUT */
txlut.size = 0;
memset(txlut.lut, 0, sizeof txlut.lut);
/* Parameter parsing */
int option_index = 0;
static struct option long_options[] = {
{"pa", required_argument, 0, 0},
{"dac", required_argument, 0, 0},
{"dig", required_argument, 0, 0},
{"mix", required_argument, 0, 0},
{"pwid", required_argument, 0, 0},
{"loop", required_argument, 0, 0},
{"nhdr", no_argument, 0, 0},
{0, 0, 0, 0}
};
/* parse command line options */
while ((i = getopt_long (argc, argv, "hif:s:b:n:z:p:k:r:c:l:t:m:o:q:d:", long_options, &option_index)) != -1) {
switch (i) {
case 'h':
usage();
return -1;
break;
case 'i': /* Send packet using inverted modulation polarity */
invert_pol = true;
break;
case 'r': /* <uint> Radio type */
i = sscanf(optarg, "%u", &arg_u);
if ((i != 1) || ((arg_u != 1255) && (arg_u != 1257) && (arg_u != 1250))) {
printf("ERROR: argument parsing of -r argument. Use -h to print help\n");
return EXIT_FAILURE;
} else {
switch (arg_u) {
case 1255:
radio_type = LGW_RADIO_TYPE_SX1255;
break;
case 1257:
radio_type = LGW_RADIO_TYPE_SX1257;
break;
default: /* 1250 */
radio_type = LGW_RADIO_TYPE_SX1250;
break;
}
}
break;
case 'l': /* <uint> LoRa/FSK preamble length */
i = sscanf(optarg, "%u", &arg_u);
if ((i != 1) || (arg_u > 65535)) {
printf("ERROR: argument parsing of -l argument. Use -h to print help\n");
return EXIT_FAILURE;
} else {
preamble = (uint16_t)arg_u;
}
break;
case 'm': /* <str> Modulation type */
i = sscanf(optarg, "%s", arg_s);
if ((i != 1) || ((strcmp(arg_s, "CW") != 0) && (strcmp(arg_s, "LORA") != 0) && (strcmp(arg_s, "FSK")))) {
printf("ERROR: invalid modulation type\n");
return EXIT_FAILURE;
} else {
sprintf(mod, "%s", arg_s);
}
break;
case 'o': /* <int> CW frequency offset from Radio TX frequency */
i = sscanf(optarg, "%d", &arg_i);
if ((arg_i < -65) || (arg_i > 65)) {
printf("ERROR: invalid frequency offset\n");
return EXIT_FAILURE;
} else {
freq_offset = (int32_t)arg_i;
}
break;
case 'd': /* <uint> FSK frequency deviation */
i = sscanf(optarg, "%u", &arg_u);
if ((i != 1) || (arg_u < 1) || (arg_u > 250)) {
printf("ERROR: invalid FSK frequency deviation\n");
return EXIT_FAILURE;
} else {
fdev_khz = (uint8_t)arg_u;
}
break;
case 'q': /* <float> FSK bitrate */
i = sscanf(optarg, "%f", &xf);
if ((i != 1) || (xf < 0.5) || (xf > 250)) {
printf("ERROR: invalid FSK bitrate\n");
return EXIT_FAILURE;
} else {
br_kbps = xf;
}
break;
case 't': /* <uint> Trigger delay in ms */
i = sscanf(optarg, "%u", &arg_u);
if (i != 1) {
printf("ERROR: argument parsing of -t argument. Use -h to print help\n");
return EXIT_FAILURE;
} else {
trig_delay = true;
trig_delay_us = (uint32_t)(arg_u * 1E3);
}
break;
case 'k': /* <uint> Clock Source */
i = sscanf(optarg, "%u", &arg_u);
if ((i != 1) || (arg_u > 1)) {
printf("ERROR: argument parsing of -k argument. Use -h to print help\n");
return EXIT_FAILURE;
} else {
clocksource = (uint8_t)arg_u;
}
break;
case 'c': /* <uint> RF chain */
i = sscanf(optarg, "%u", &arg_u);
if ((i != 1) || (arg_u > 1)) {
printf("ERROR: argument parsing of -c argument. Use -h to print help\n");
return EXIT_FAILURE;
} else {
rf_chain = (uint8_t)arg_u;
}
break;
case 'f': /* <float> Radio TX frequency in MHz */
i = sscanf(optarg, "%lf", &arg_d);
if (i != 1) {
printf("ERROR: argument parsing of -f argument. Use -h to print help\n");
return EXIT_FAILURE;
} else {
ft = (uint32_t)((arg_d*1e6) + 0.5); /* .5 Hz offset to get rounding instead of truncating */
}
break;
case 's': /* <uint> LoRa datarate */
i = sscanf(optarg, "%u", &arg_u);
if ((i != 1) || (arg_u < 5) || (arg_u > 12)) {
printf("ERROR: argument parsing of -s argument. Use -h to print help\n");
return EXIT_FAILURE;
} else {
sf = (uint8_t)arg_u;
}
break;
case 'b': /* <uint> LoRa bandwidth in khz */
i = sscanf(optarg, "%u", &arg_u);
if ((i != 1) || ((arg_u != 125) && (arg_u != 250) && (arg_u != 500))) {
printf("ERROR: argument parsing of -b argument. Use -h to print help\n");
return EXIT_FAILURE;
} else {
bw_khz = (uint16_t)arg_u;
}
break;
case 'n': /* <uint> Number of packets to be sent */
i = sscanf(optarg, "%u", &arg_u);
if (i != 1) {
printf("ERROR: argument parsing of -n argument. Use -h to print help\n");
return EXIT_FAILURE;
} else {
nb_pkt = (uint32_t)arg_u;
}
break;
case 'p': /* <int> RF power */
i = sscanf(optarg, "%d", &arg_i);
if (i != 1) {
printf("ERROR: argument parsing of -p argument. Use -h to print help\n");
return EXIT_FAILURE;
} else {
rf_power = (int8_t)arg_i;
txlut.size = 1;
txlut.lut[0].rf_power = rf_power;
}
break;
case 'z': /* <uint> packet size */
i = sscanf(optarg, "%u", &arg_u);
if ((i != 1) || (arg_u < 9) || (arg_u > 255)) {
printf("ERROR: argument parsing of -z argument. Use -h to print help\n");
return EXIT_FAILURE;
} else {
size = (uint8_t)arg_u;
}
break;
case 0:
if (strcmp(long_options[option_index].name, "pa") == 0) {
i = sscanf(optarg, "%u", &arg_u);
if ((i != 1) || (arg_u > 3)) {
printf("ERROR: argument parsing of --pa argument. Use -h to print help\n");
return EXIT_FAILURE;
} else {
txlut.size = 1;
txlut.lut[0].pa_gain = (uint8_t)arg_u;
}
} else if (strcmp(long_options[option_index].name, "dac") == 0) {
i = sscanf(optarg, "%u", &arg_u);
if ((i != 1) || (arg_u > 3)) {
printf("ERROR: argument parsing of --dac argument. Use -h to print help\n");
return EXIT_FAILURE;
} else {
txlut.size = 1;
txlut.lut[0].dac_gain = (uint8_t)arg_u;
}
} else if (strcmp(long_options[option_index].name, "mix") == 0) {
i = sscanf(optarg, "%u", &arg_u);
if ((i != 1) || (arg_u > 15)) {
printf("ERROR: argument parsing of --mix argument. Use -h to print help\n");
return EXIT_FAILURE;
} else {
txlut.size = 1;
txlut.lut[0].mix_gain = (uint8_t)arg_u;
}
} else if (strcmp(long_options[option_index].name, "dig") == 0) {
i = sscanf(optarg, "%u", &arg_u);
if ((i != 1) || (arg_u > 3)) {
printf("ERROR: argument parsing of --dig argument. Use -h to print help\n");
return EXIT_FAILURE;
} else {
txlut.size = 1;
txlut.lut[0].dig_gain = (uint8_t)arg_u;
}
} else if (strcmp(long_options[option_index].name, "pwid") == 0) {
i = sscanf(optarg, "%u", &arg_u);
if ((i != 1) || (arg_u > 22)) {
printf("ERROR: argument parsing of --pwid argument. Use -h to print help\n");
return EXIT_FAILURE;
} else {
txlut.size = 1;
txlut.lut[0].mix_gain = 5; /* TODO: rework this, should not be needed for sx1250 */
txlut.lut[0].pwr_idx = (uint8_t)arg_u;
}
} else if (strcmp(long_options[option_index].name, "loop") == 0) {
printf("%p\n", optarg);
i = sscanf(optarg, "%u", &arg_u);
if (i != 1) {
printf("ERROR: argument parsing of --loop argument. Use -h to print help\n");
return EXIT_FAILURE;
} else {
nb_loop = arg_u;
}
} else if (strcmp(long_options[option_index].name, "nhdr") == 0) {
no_header = true;
} else {
printf("ERROR: argument parsing options. Use -h to print help\n");
return EXIT_FAILURE;
}
break;
default:
printf("ERROR: argument parsing\n");
usage();
return -1;
}
}
/* Summary of packet parameters */
if (strcmp(mod, "CW") == 0) {
printf("Sending %i CW on %u Hz (Freq. offset %d kHz) at %i dBm\n", nb_pkt, ft, freq_offset, rf_power);
}
else if (strcmp(mod, "FSK") == 0) {
printf("Sending %i FSK packets on %u Hz (FDev %u kHz, Bitrate %.2f, %i bytes payload, %i symbols preamble) at %i dBm\n", nb_pkt, ft, fdev_khz, br_kbps, size, preamble, rf_power);
} else {
printf("Sending %i LoRa packets on %u Hz (BW %i kHz, SF %i, CR %i, %i bytes payload, %i symbols preamble, %s header, %s polarity) at %i dBm\n", nb_pkt, ft, bw_khz, sf, 1, size, preamble, (no_header == false) ? "explicit" : "implicit", (invert_pol == false) ? "non-inverted" : "inverted", rf_power);
}
/* Configure signal handling */
sigemptyset( &sigact.sa_mask );
sigact.sa_flags = 0;
sigact.sa_handler = sig_handler;
sigaction( SIGQUIT, &sigact, NULL );
sigaction( SIGINT, &sigact, NULL );
sigaction( SIGTERM, &sigact, NULL );
/* Configure the gateway */
memset( &boardconf, 0, sizeof boardconf);
boardconf.lorawan_public = true;
boardconf.clksrc = clocksource;
boardconf.full_duplex = false;
strncpy(boardconf.spidev_path, spidev_path, sizeof boardconf.spidev_path);
if (lgw_board_setconf(&boardconf) != LGW_HAL_SUCCESS) {
printf("ERROR: failed to configure board\n");
return EXIT_FAILURE;
}
memset( &rfconf, 0, sizeof rfconf);
rfconf.enable = true; /* rf chain 0 needs to be enabled for calibration to work on sx1257 */
rfconf.freq_hz = 868500000; /* dummy */
rfconf.type = radio_type;
rfconf.tx_enable = true;
if (lgw_rxrf_setconf(0, &rfconf) != LGW_HAL_SUCCESS) {
printf("ERROR: failed to configure rxrf 0\n");
return EXIT_FAILURE;
}
memset( &rfconf, 0, sizeof rfconf);
rfconf.enable = (((rf_chain == 1) || (clocksource == 1)) ? true : false);
rfconf.freq_hz = 868500000; /* dummy */
rfconf.type = radio_type;
rfconf.tx_enable = false;
if (lgw_rxrf_setconf(1, &rfconf) != LGW_HAL_SUCCESS) {
printf("ERROR: failed to configure rxrf 1\n");
return EXIT_FAILURE;
}
if (txlut.size > 0) {
if (lgw_txgain_setconf(rf_chain, &txlut) != LGW_HAL_SUCCESS) {
printf("ERROR: failed to configure txgain lut\n");
return EXIT_FAILURE;
}
}
for (cnt_loop = 0; cnt_loop < nb_loop; cnt_loop++) {
/* Board reset */
if (system("./reset_lgw.sh start") != 0) {
printf("ERROR: failed to reset SX1302, check your reset_lgw.sh script\n");
exit(EXIT_FAILURE);
}
/* connect, configure and start the LoRa concentrator */
x = lgw_start();
if (x != 0) {
printf("ERROR: failed to start the gateway\n");
return EXIT_FAILURE;
}
/* Send packets */
memset(&pkt, 0, sizeof pkt);
pkt.rf_chain = rf_chain;
pkt.freq_hz = ft;
pkt.rf_power = rf_power;
if (trig_delay == false) {
pkt.tx_mode = IMMEDIATE;
} else {
if (trig_delay_us == 0) {
pkt.tx_mode = ON_GPS;
} else {
pkt.tx_mode = TIMESTAMPED;
}
}
if ( strcmp( mod, "CW" ) == 0 ) {
pkt.modulation = MOD_CW;
pkt.freq_offset = freq_offset;
pkt.f_dev = fdev_khz;
}
else if( strcmp( mod, "FSK" ) == 0 ) {
pkt.modulation = MOD_FSK;
pkt.no_crc = false;
pkt.datarate = br_kbps * 1e3;
pkt.f_dev = fdev_khz;
} else {
pkt.modulation = MOD_LORA;
pkt.coderate = CR_LORA_4_5;
pkt.no_crc = true;
}
pkt.invert_pol = invert_pol;
pkt.preamble = preamble;
pkt.no_header = no_header;
pkt.payload[0] = 0x40; /* Confirmed Data Up */
pkt.payload[1] = 0xAB;
pkt.payload[2] = 0xAB;
pkt.payload[3] = 0xAB;
pkt.payload[4] = 0xAB;
pkt.payload[5] = 0x00; /* FCTrl */
pkt.payload[6] = 0; /* FCnt */
pkt.payload[7] = 0; /* FCnt */
pkt.payload[8] = 0x02; /* FPort */
for (i = 9; i < 255; i++) {
pkt.payload[i] = i;
}
for (i = 0; i < (int)nb_pkt; i++) {
if (trig_delay == true) {
if (trig_delay_us > 0) {
lgw_get_instcnt(&count_us);
printf("count_us:%u\n", count_us);
pkt.count_us = count_us + trig_delay_us;
printf("programming TX for %u\n", pkt.count_us);
} else {
printf("programming TX for next PPS (GPS)\n");
}
}
if( strcmp( mod, "LORA" ) == 0 ) {
pkt.datarate = (sf == 0) ? (uint8_t)RAND_RANGE(5, 12) : sf;
}
switch (bw_khz) {
case 125:
pkt.bandwidth = BW_125KHZ;
break;
case 250:
pkt.bandwidth = BW_250KHZ;
break;
case 500:
pkt.bandwidth = BW_500KHZ;
break;
default:
pkt.bandwidth = (uint8_t)RAND_RANGE(BW_125KHZ, BW_500KHZ);
break;
}
pkt.size = (size == 0) ? (uint8_t)RAND_RANGE(9, 255) : size;
pkt.payload[6] = (uint8_t)(i >> 0); /* FCnt */
pkt.payload[7] = (uint8_t)(i >> 8); /* FCnt */
x = lgw_send(&pkt);
if (x != 0) {
printf("ERROR: failed to send packet\n");
return EXIT_FAILURE;
}
/* wait for packet to finish sending */
do {
wait_ms(5);
lgw_status(pkt.rf_chain, TX_STATUS, &tx_status); /* get TX status */
} while ((tx_status != TX_FREE) && (quit_sig != 1) && (exit_sig != 1));
if ((quit_sig == 1) || (exit_sig == 1)) {
break;
}
printf("TX done\n");
}
printf( "\nNb packets sent: %u (%u)\n", i, cnt_loop + 1 );
/* Stop the gateway */
x = lgw_stop();
if (x != 0) {
printf("ERROR: failed to stop the gateway\n");
return EXIT_FAILURE;
}
/* Board reset */
if (system("./reset_lgw.sh stop") != 0) {
printf("ERROR: failed to reset SX1302, check your reset_lgw.sh script\n");
exit(EXIT_FAILURE);
}
}
printf("=========== Test End ===========\n");
return 0;
}
/* --- EOF ------------------------------------------------------------------ */

View File

@ -0,0 +1,245 @@
/*
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(C)2019 Semtech
Description:
Minimum test program for the loragw_i2c module
License: Revised BSD License, see LICENSE.TXT file include in the project
*/
/* -------------------------------------------------------------------------- */
/* --- DEPENDANCIES --------------------------------------------------------- */
/* Fix an issue between POSIX and C99 */
#if __STDC_VERSION__ >= 199901L
#define _XOPEN_SOURCE 600
#else
#define _XOPEN_SOURCE 500
#endif
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h> /* sigaction */
#include <unistd.h> /* getopt, access */
#include <time.h>
#include "loragw_i2c.h"
#include "loragw_aux.h"
#include "loragw_hal.h"
/* -------------------------------------------------------------------------- */
/* --- PRIVATE MACROS ------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* --- PRIVATE CONSTANTS ---------------------------------------------------- */
#define I2C_PORT_STTS751 0x39
#define STTS751_REG_TEMP_H 0x00
#define STTS751_REG_TEMP_L 0x02
#define STTS751_REG_CONF 0x03
#define STTS751_REG_RATE 0x04
#define STTS751_REG_PROD_ID 0xFD
#define STTS751_REG_MAN_ID 0xFE
#define STTS751_REG_REV_ID 0xFF
#define STTS751_0_PROD_ID 0x00
#define STTS751_1_PROD_ID 0x01
#define ST_MAN_ID 0x53
/* -------------------------------------------------------------------------- */
/* --- GLOBAL VARIABLES ----------------------------------------------------- */
/* Signal handling variables */
static int exit_sig = 0; /* 1 -> application terminates cleanly (shut down hardware, close open files, etc) */
static int quit_sig = 0; /* 1 -> application terminates without shutting down the hardware */
static int i2c_dev = -1;
/* -------------------------------------------------------------------------- */
/* --- SUBFUNCTIONS DECLARATION --------------------------------------------- */
static void sig_handler(int sigio);
static void usage(void);
/* -------------------------------------------------------------------------- */
/* --- MAIN FUNCTION -------------------------------------------------------- */
int main(int argc, char ** argv)
{
int i, err;
static struct sigaction sigact; /* SIGQUIT&SIGINT&SIGTERM signal handling */
uint8_t val;
uint8_t high_byte, low_byte;
int8_t h;
float temperature;
/* Parse command line options */
while ((i = getopt(argc, argv, "hd:")) != -1) {
switch (i) {
case 'h':
usage();
return EXIT_SUCCESS;
break;
case 'd':
if (optarg != NULL) {
/* TODO */
}
break;
default:
printf("ERROR: argument parsing options, use -h option for help\n");
usage();
return EXIT_FAILURE;
}
}
/* Configure signal handling */
sigemptyset( &sigact.sa_mask );
sigact.sa_flags = 0;
sigact.sa_handler = sig_handler;
sigaction( SIGQUIT, &sigact, NULL );
sigaction( SIGINT, &sigact, NULL );
sigaction( SIGTERM, &sigact, NULL );
printf( "+++ Start of I2C test program +++\n" );
/* Open I2C port expander */
err = i2c_linuxdev_open( I2C_DEVICE, I2C_PORT_STTS751, &i2c_dev );
if ( (err != 0) || (i2c_dev <= 0) )
{
printf( "ERROR: failed to open I2C device %s (err=%i)\n", I2C_DEVICE, err );
return EXIT_FAILURE;
}
/* Get temperature sensor product ID */
err = i2c_linuxdev_read( i2c_dev, I2C_PORT_STTS751, STTS751_REG_PROD_ID, &val );
if ( err != 0 )
{
printf( "ERROR: failed to read I2C device %s (err=%i)\n", I2C_DEVICE, err );
return EXIT_FAILURE;;
}
switch( val )
{
case STTS751_0_PROD_ID:
printf("INFO: Product ID: STTS751-0\n");
break;
case STTS751_1_PROD_ID:
printf("INFO: Product ID: STTS751-1\n");
break;
default:
printf("ERROR: Product ID: UNKNOWN\n");
return EXIT_FAILURE;;
}
/* Get temperature sensor Manufacturer ID */
err = i2c_linuxdev_read( i2c_dev, I2C_PORT_STTS751, STTS751_REG_MAN_ID, &val );
if ( err != 0 )
{
printf( "ERROR: failed to read I2C device %s (err=%i)\n", I2C_DEVICE, err );
return EXIT_FAILURE;;
}
if ( val != ST_MAN_ID )
{
printf( "ERROR: Manufacturer ID: UNKNOWN\n" );
return EXIT_FAILURE;;
}
else
{
printf("INFO: Manufacturer ID: 0x%02X\n", val);
}
/* Get temperature sensor revision number */
err = i2c_linuxdev_read( i2c_dev, I2C_PORT_STTS751, STTS751_REG_REV_ID, &val );
if ( err != 0 )
{
printf( "ERROR: failed to read I2C device %s (err=%i)\n", I2C_DEVICE, err );
return EXIT_FAILURE;;
}
printf("INFO: Revision number: 0x%02X\n", val);
/* Set conversion resolution to 12 bits */
err = i2c_linuxdev_write( i2c_dev, I2C_PORT_STTS751, STTS751_REG_CONF, 0x8C ); /* TODO: do not hardcode the whole byte */
if ( err != 0 )
{
printf( "ERROR: failed to write I2C device 0x%02X (err=%i)\n", I2C_PORT_STTS751, err );
return EXIT_FAILURE;
}
/* Set conversion rate to 1 / second */
err = i2c_linuxdev_write( i2c_dev, I2C_PORT_STTS751, STTS751_REG_RATE, 0x04 );
if ( err != 0 )
{
printf( "ERROR: failed to write I2C device 0x%02X (err=%i)\n", I2C_PORT_STTS751, err );
return EXIT_FAILURE;
}
while ((quit_sig != 1) && (exit_sig != 1)) {
/* Read Temperature LSB */
err = i2c_linuxdev_read( i2c_dev, I2C_PORT_STTS751, STTS751_REG_TEMP_L, &low_byte );
if ( err != 0 )
{
printf( "ERROR: failed to read I2C device 0x%02X (err=%i)\n", I2C_PORT_STTS751, err );
return EXIT_FAILURE;
}
/* Read Temperature MSB */
err = i2c_linuxdev_read( i2c_dev, I2C_PORT_STTS751, STTS751_REG_TEMP_H, &high_byte );
if ( err != 0 )
{
printf( "ERROR: failed to read I2C device 0x%02X (err=%i)\n", I2C_PORT_STTS751, err );
return EXIT_FAILURE;
}
h = (int8_t)high_byte;
temperature = ((h << 8) | low_byte) / 256.0;
printf( "Temperature: %f C (h:0x%02X l:0x%02X)\n", temperature, high_byte, low_byte );
wait_ms( 100 );
}
/* Terminate */
printf( "+++ End of I2C test program +++\n" );
err = i2c_linuxdev_close( i2c_dev );
if ( err != 0 )
{
printf( "ERROR: failed to close I2C device (err=%i)\n", err );
return EXIT_FAILURE;
}
return 0;
}
/* -------------------------------------------------------------------------- */
/* --- SUBFUNCTIONS DEFINITION ---------------------------------------------- */
static void sig_handler(int sigio) {
if (sigio == SIGQUIT) {
quit_sig = 1;
} else if((sigio == SIGINT) || (sigio == SIGTERM)) {
exit_sig = 1;
}
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
static void usage(void) {
printf("~~~ Library version string~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
printf(" %s\n", lgw_version_info());
printf("~~~ Available options ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
printf(" -h print this help\n");
printf(" -d <path> use Linux I2C device driver\n");
printf(" => default path: " I2C_DEVICE "\n");
}
/* --- EOF ------------------------------------------------------------------ */

View File

@ -0,0 +1,204 @@
/*
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(C)2019 Semtech
Description:
Minimum test program for the loragw_reg module
License: Revised BSD License, see LICENSE.TXT file include in the project
*/
/* -------------------------------------------------------------------------- */
/* --- DEPENDANCIES --------------------------------------------------------- */
/* Fix an issue between POSIX and C99 */
#if __STDC_VERSION__ >= 199901L
#define _XOPEN_SOURCE 600
#else
#define _XOPEN_SOURCE 500
#endif
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h> /* getopt, access */
#include <math.h>
#include "loragw_reg.h"
#include "loragw_aux.h"
#include "loragw_hal.h"
/* -------------------------------------------------------------------------- */
/* --- PRIVATE MACROS ------------------------------------------------------- */
#define LINUXDEV_PATH_DEFAULT "/dev/spidev0.0"
/* -------------------------------------------------------------------------- */
/* --- PRIVATE CONSTANTS ---------------------------------------------------- */
extern const struct lgw_reg_s loregs[LGW_TOTALREGS+1];
/* -------------------------------------------------------------------------- */
/* --- SUBFUNCTIONS DECLARATION --------------------------------------------- */
static void usage(void);
/* -------------------------------------------------------------------------- */
/* --- MAIN FUNCTION -------------------------------------------------------- */
int main(int argc, char ** argv)
{
int x, i;
int32_t val;
bool error_found = false;
uint8_t rand_values[LGW_TOTALREGS];
bool reg_ignored[LGW_TOTALREGS]; /* store register to be ignored */
uint8_t reg_val;
uint8_t reg_max;
/* SPI interfaces */
const char spidev_path_default[] = LINUXDEV_PATH_DEFAULT;
const char * spidev_path = spidev_path_default;
/* Parse command line options */
while ((i = getopt(argc, argv, "hd:")) != -1) {
switch (i) {
case 'h':
usage();
return EXIT_SUCCESS;
break;
case 'd':
if (optarg != NULL) {
spidev_path = optarg;
}
break;
default:
printf("ERROR: argument parsing options, use -h option for help\n");
usage();
return EXIT_FAILURE;
}
}
/* Board reset */
if (system("./reset_lgw.sh start") != 0) {
printf("ERROR: failed to reset SX1302, check your reset_lgw.sh script\n");
exit(EXIT_FAILURE);
}
x = lgw_connect(spidev_path);
if (x != LGW_REG_SUCCESS) {
printf("ERROR: failed to connect\n");
return -1;
}
/* The following registers cannot be tested this way */
memset(reg_ignored, 0, sizeof reg_ignored);
reg_ignored[SX1302_REG_COMMON_CTRL0_CLK32_RIF_CTRL] = true; /* all test fails if we set this one to 1 */
/* Test 1: read all registers and check default value for non-read-only registers */
printf("## TEST#1: read all registers and check default value for non-read-only registers\n");
error_found = false;
for (i = 0; i < LGW_TOTALREGS; i++) {
if (loregs[i].rdon == 0) {
x = lgw_reg_r(i, &val);
if (x != LGW_REG_SUCCESS) {
printf("ERROR: failed to read register at index %d\n", i);
return -1;
}
if (val != loregs[i].dflt) {
printf("ERROR: default value for register at index %d is %d, should be %d\n", i, val, loregs[i].dflt);
error_found = true;
}
}
}
printf("------------------\n");
printf(" TEST#1 %s\n", (error_found == false) ? "PASSED" : "FAILED");
printf("------------------\n\n");
/* Test 2: read/write test on all non-read-only, non-pulse, non-w0clr, non-w1clr registers */
printf("## TEST#2: read/write test on all non-read-only, non-pulse, non-w0clr, non-w1clr registers\n");
/* Write all registers with a random value */
error_found = false;
for (i = 0; i < LGW_TOTALREGS; i++) {
if ((loregs[i].rdon == 0) && (reg_ignored[i] == false)) {
/* Peek a random value different form the default reg value */
reg_max = pow(2, loregs[i].leng) - 1;
if (loregs[i].leng == 1) {
reg_val = !loregs[i].dflt;
} else {
/* ensure random value is not the default one */
do {
if (loregs[i].sign == 1) {
reg_val = rand() % (reg_max / 2);
} else {
reg_val = rand() % reg_max;
}
} while (reg_val == loregs[i].dflt);
}
/* Write selected value */
x = lgw_reg_w(i, reg_val);
if (x != LGW_REG_SUCCESS) {
printf("ERROR: failed to read register at index %d\n", i);
return -1;
}
/* store value for later check */
rand_values[i] = reg_val;
}
}
/* Read all registers and check if we got proper random value back */
for (i = 0; i < LGW_TOTALREGS; i++) {
if ((loregs[i].rdon == 0) && (loregs[i].chck == 1) && (reg_ignored[i] == false)) {
x = lgw_reg_r(i, &val);
if (x != LGW_REG_SUCCESS) {
printf("ERROR: failed to read register at index %d\n", i);
return -1;
}
/* check value */
if (val != rand_values[i]) {
printf("ERROR: value read from register at index %d differs from the written value (w:%u r:%d)\n", i, rand_values[i], val);
error_found = true;
} else {
//printf("INFO: MATCH reg %d (%u, %u)\n", i, rand_values[i], (uint8_t)val);
}
}
}
printf("------------------\n");
printf(" TEST#2 %s\n", (error_found == false) ? "PASSED" : "FAILED");
printf("------------------\n\n");
x = lgw_disconnect();
if (x != LGW_REG_SUCCESS) {
printf("ERROR: failed to disconnect\n");
return -1;
}
/* Board reset */
if (system("./reset_lgw.sh stop") != 0) {
printf("ERROR: failed to reset SX1302, check your reset_lgw.sh script\n");
exit(EXIT_FAILURE);
}
return 0;
}
/* -------------------------------------------------------------------------- */
/* --- SUBFUNCTIONS DEFINITION ---------------------------------------------- */
static void usage(void) {
printf("~~~ Library version string~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
printf(" %s\n", lgw_version_info());
printf("~~~ Available options ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
printf(" -h print this help\n");
printf(" -d <path> use Linux SPI device driver\n");
printf(" => default path: " LINUXDEV_PATH_DEFAULT "\n");
}
/* --- EOF ------------------------------------------------------------------ */

View File

@ -0,0 +1,208 @@
/*
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(C)2019 Semtech
Description:
Minimum test program for the loragw_spi module
License: Revised BSD License, see LICENSE.TXT file include in the project
*/
/* -------------------------------------------------------------------------- */
/* --- DEPENDANCIES --------------------------------------------------------- */
/* Fix an issue between POSIX and C99 */
#if __STDC_VERSION__ >= 199901L
#define _XOPEN_SOURCE 600
#else
#define _XOPEN_SOURCE 500
#endif
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h> /* sigaction */
#include <unistd.h> /* getopt, access */
#include <time.h>
#include "loragw_spi.h"
#include "loragw_aux.h"
#include "loragw_hal.h"
/* -------------------------------------------------------------------------- */
/* --- PRIVATE MACROS ------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* --- PRIVATE CONSTANTS ---------------------------------------------------- */
#define BUFF_SIZE 1024
#define SX1302_AGC_MCU_MEM 0x0000
#define SX1302_REG_COMMON 0x5600
#define SX1302_REG_AGC_MCU 0x5780
#define LINUXDEV_PATH_DEFAULT "/dev/spidev0.0"
/* -------------------------------------------------------------------------- */
/* --- GLOBAL VARIABLES ----------------------------------------------------- */
/* Signal handling variables */
static int exit_sig = 0; /* 1 -> application terminates cleanly (shut down hardware, close open files, etc) */
static int quit_sig = 0; /* 1 -> application terminates without shutting down the hardware */
/* -------------------------------------------------------------------------- */
/* --- SUBFUNCTIONS DECLARATION --------------------------------------------- */
static void sig_handler(int sigio);
static void usage(void);
/* -------------------------------------------------------------------------- */
/* --- MAIN FUNCTION -------------------------------------------------------- */
int main(int argc, char ** argv)
{
static struct sigaction sigact; /* SIGQUIT&SIGINT&SIGTERM signal handling */
uint8_t data = 0;
uint8_t test_buff[BUFF_SIZE];
uint8_t read_buff[BUFF_SIZE];
int cycle_number = 0;
int i;
uint16_t size;
/* SPI interfaces */
const char spidev_path_default[] = LINUXDEV_PATH_DEFAULT;
const char * spidev_path = spidev_path_default;
void *spi_target = NULL;
/* Parse command line options */
while ((i = getopt(argc, argv, "hd:")) != -1) {
switch (i) {
case 'h':
usage();
return EXIT_SUCCESS;
break;
case 'd':
if (optarg != NULL) {
spidev_path = optarg;
}
break;
default:
printf("ERROR: argument parsing options, use -h option for help\n");
usage();
return EXIT_FAILURE;
}
}
/* Configure signal handling */
sigemptyset( &sigact.sa_mask );
sigact.sa_flags = 0;
sigact.sa_handler = sig_handler;
sigaction( SIGQUIT, &sigact, NULL );
sigaction( SIGINT, &sigact, NULL );
sigaction( SIGTERM, &sigact, NULL );
/* Board reset */
if (system("./reset_lgw.sh start") != 0) {
printf("ERROR: failed to reset SX1302, check your reset_lgw.sh script\n");
exit(EXIT_FAILURE);
}
printf("Beginning of test for loragw_spi.c\n");
i = lgw_spi_open(spidev_path, &spi_target);
if (i != 0) {
printf("ERROR: failed to open SPI device %s\n", spidev_path);
return -1;
}
/* normal R/W test */
/* TODO */
/* burst R/W test, small bursts << LGW_BURST_CHUNK */
/* TODO */
/* burst R/W test, large bursts >> LGW_BURST_CHUNK */
/* TODO */
lgw_spi_r(spi_target, LGW_SPI_MUX_TARGET_SX1302, SX1302_REG_COMMON + 6, &data);
printf("SX1302 version: 0x%02X\n", data);
lgw_spi_r(spi_target, LGW_SPI_MUX_TARGET_SX1302, SX1302_REG_AGC_MCU + 0, &data);
lgw_spi_w(spi_target, LGW_SPI_MUX_TARGET_SX1302, SX1302_REG_AGC_MCU + 0, 0x06); /* mcu_clear, host_prog */
srand(time(NULL));
/* databuffer R/W stress test */
while ((quit_sig != 1) && (exit_sig != 1)) {
size = rand() % BUFF_SIZE;
for (i = 0; i < size; ++i) {
test_buff[i] = rand() & 0xFF;
}
printf("Cycle %i > ", cycle_number);
lgw_spi_wb(spi_target, LGW_SPI_MUX_TARGET_SX1302, SX1302_AGC_MCU_MEM, test_buff, size);
lgw_spi_rb(spi_target, LGW_SPI_MUX_TARGET_SX1302, SX1302_AGC_MCU_MEM, read_buff, size);
for (i=0; ((i<size) && (test_buff[i] == read_buff[i])); ++i);
if (i != size) {
printf("error during the buffer comparison\n");
printf("Written values:\n");
for (i=0; i<size; ++i) {
printf(" %02X ", test_buff[i]);
if (i%16 == 15) printf("\n");
}
printf("\n");
printf("Read values:\n");
for (i=0; i<size; ++i) {
printf(" %02X ", read_buff[i]);
if (i%16 == 15) printf("\n");
}
printf("\n");
return EXIT_FAILURE;
} else {
printf("did a %i-byte R/W on a data buffer with no error\n", size);
++cycle_number;
}
}
lgw_spi_close(spi_target);
printf("End of test for loragw_spi.c\n");
/* Board reset */
if (system("./reset_lgw.sh stop") != 0) {
printf("ERROR: failed to reset SX1302, check your reset_lgw.sh script\n");
exit(EXIT_FAILURE);
}
return 0;
}
/* -------------------------------------------------------------------------- */
/* --- SUBFUNCTIONS DEFINITION ---------------------------------------------- */
static void sig_handler(int sigio) {
if (sigio == SIGQUIT) {
quit_sig = 1;
} else if((sigio == SIGINT) || (sigio == SIGTERM)) {
exit_sig = 1;
}
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
static void usage(void) {
printf("~~~ Library version string~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
printf(" %s\n", lgw_version_info());
printf("~~~ Available options ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
printf(" -h print this help\n");
printf(" -d <path> use Linux SPI device driver\n");
printf(" => default path: " LINUXDEV_PATH_DEFAULT "\n");
}
/* --- EOF ------------------------------------------------------------------ */

View File

@ -0,0 +1,216 @@
/*
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(C)2019 Semtech
Description:
Minimum test program for the sx1250 module
License: Revised BSD License, see LICENSE.TXT file include in the project
*/
/* -------------------------------------------------------------------------- */
/* --- DEPENDANCIES --------------------------------------------------------- */
/* Fix an issue between POSIX and C99 */
#if __STDC_VERSION__ >= 199901L
#define _XOPEN_SOURCE 600
#else
#define _XOPEN_SOURCE 500
#endif
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h> /* sigaction */
#include <unistd.h> /* getopt, access */
#include "loragw_spi.h"
#include "loragw_aux.h"
#include "loragw_reg.h"
#include "loragw_hal.h"
#include "loragw_sx1250.h"
#include "loragw_sx1302.h"
/* -------------------------------------------------------------------------- */
/* --- PRIVATE MACROS ------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* --- PRIVATE CONSTANTS ---------------------------------------------------- */
#define BUFF_SIZE 16
#define LINUXDEV_PATH_DEFAULT "/dev/spidev0.0"
/* -------------------------------------------------------------------------- */
/* --- GLOBAL VARIABLES ----------------------------------------------------- */
/* Signal handling variables */
static int exit_sig = 0; /* 1 -> application terminates cleanly (shut down hardware, close open files, etc) */
static int quit_sig = 0; /* 1 -> application terminates without shutting down the hardware */
/* -------------------------------------------------------------------------- */
/* --- SUBFUNCTIONS DECLARATION --------------------------------------------- */
static void sig_handler(int sigio);
static void usage(void);
/* -------------------------------------------------------------------------- */
/* --- MAIN FUNCTION -------------------------------------------------------- */
int main(int argc, char ** argv)
{
static struct sigaction sigact; /* SIGQUIT&SIGINT&SIGTERM signal handling */
uint8_t test_buff[BUFF_SIZE];
uint8_t read_buff[BUFF_SIZE];
uint32_t test_val, read_val;
int cycle_number = 0;
int i, x;
/* SPI interfaces */
const char spidev_path_default[] = LINUXDEV_PATH_DEFAULT;
const char * spidev_path = spidev_path_default;
/* Parse command line options */
while ((i = getopt(argc, argv, "hd:")) != -1) {
switch (i) {
case 'h':
usage();
return EXIT_SUCCESS;
break;
case 'd':
if (optarg != NULL) {
spidev_path = optarg;
}
break;
default:
printf("ERROR: argument parsing options, use -h option for help\n");
usage();
return EXIT_FAILURE;
}
}
/* Configure signal handling */
sigemptyset( &sigact.sa_mask );
sigact.sa_flags = 0;
sigact.sa_handler = sig_handler;
sigaction( SIGQUIT, &sigact, NULL );
sigaction( SIGINT, &sigact, NULL );
sigaction( SIGTERM, &sigact, NULL );
/* Board reset */
if (system("./reset_lgw.sh start") != 0) {
printf("ERROR: failed to reset SX1302, check your reset_lgw.sh script\n");
exit(EXIT_FAILURE);
}
x = lgw_connect(spidev_path);
if (x != LGW_REG_SUCCESS) {
printf("ERROR: Failed to connect to the concentrator using SPI %s\n", spidev_path);
return EXIT_FAILURE;
}
/* Reset radios */
for (i = 0; i < LGW_RF_CHAIN_NB; i++) {
sx1302_radio_reset(i, LGW_RADIO_TYPE_SX1250);
sx1302_radio_set_mode(i, LGW_RADIO_TYPE_SX1250);
}
/* Select the radio which provides the clock to the sx1302 */
sx1302_radio_clock_select(0);
/* Ensure we can control the radio */
lgw_reg_w(SX1302_REG_COMMON_CTRL0_HOST_RADIO_CTRL, 0x01);
/* Ensure PA/LNA are disabled */
lgw_reg_w(SX1302_REG_AGC_MCU_CTRL_FORCE_HOST_FE_CTRL, 1);
lgw_reg_w(SX1302_REG_AGC_MCU_RF_EN_A_PA_EN, 0);
lgw_reg_w(SX1302_REG_AGC_MCU_RF_EN_A_LNA_EN, 0);
/* Set Radio in Standby mode */
test_buff[0] = (uint8_t)STDBY_XOSC;
sx1250_write_command(0, SET_STANDBY, test_buff, 1);
sx1250_write_command(1, SET_STANDBY, test_buff, 1);
wait_ms(10);
test_buff[0] = 0x00;
sx1250_read_command(0, GET_STATUS, test_buff, 1);
printf("Radio0: get_status: 0x%02X\n", test_buff[0]);
sx1250_read_command(1, GET_STATUS, test_buff, 1);
printf("Radio1: get_status: 0x%02X\n", test_buff[0]);
/* databuffer R/W stress test */
while ((quit_sig != 1) && (exit_sig != 1)) {
test_buff[0] = rand() & 0x7F;
test_buff[1] = rand() & 0xFF;
test_buff[2] = rand() & 0xFF;
test_buff[3] = rand() & 0xFF;
test_val = (test_buff[0] << 24) | (test_buff[1] << 16) | (test_buff[2] << 8) | (test_buff[3] << 0);
sx1250_write_command(0, SET_RF_FREQUENCY, test_buff, 4);
read_buff[0] = 0x08;
read_buff[1] = 0x8B;
read_buff[2] = 0x00;
read_buff[3] = 0x00;
read_buff[4] = 0x00;
read_buff[5] = 0x00;
read_buff[6] = 0x00;
sx1250_read_command(0, READ_REGISTER, read_buff, 7);
read_val = (read_buff[3] << 24) | (read_buff[4] << 16) | (read_buff[5] << 8) | (read_buff[6] << 0);
printf("Cycle %i > ", cycle_number);
if (read_val != test_val) {
printf("error during the buffer comparison\n");
printf("Written value: %08X\n", test_val);
printf("Read value: %08X\n", read_val);
return EXIT_FAILURE;
} else {
printf("did a %i-byte R/W on a register with no error\n", 4);
++cycle_number;
}
}
lgw_disconnect();
printf("End of test for loragw_spi_sx1250.c\n");
/* Board reset */
if (system("./reset_lgw.sh stop") != 0) {
printf("ERROR: failed to reset SX1302, check your reset_lgw.sh script\n");
exit(EXIT_FAILURE);
}
return 0;
}
/* -------------------------------------------------------------------------- */
/* --- SUBFUNCTIONS DEFINITION ---------------------------------------------- */
static void sig_handler(int sigio) {
if (sigio == SIGQUIT) {
quit_sig = 1;
} else if((sigio == SIGINT) || (sigio == SIGTERM)) {
exit_sig = 1;
}
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
static void usage(void) {
printf("~~~ Library version string~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
printf(" %s\n", lgw_version_info());
printf("~~~ Available options ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
printf(" -h print this help\n");
printf(" -d <path> use Linux SPI device driver\n");
printf(" => default path: " LINUXDEV_PATH_DEFAULT "\n");
}
/* --- EOF ------------------------------------------------------------------ */

48
libtools/Makefile Normal file
View File

@ -0,0 +1,48 @@
### get external defined data
### constant symbols
ARCH ?=
CROSS_COMPILE ?=
CC := $(CROSS_COMPILE)gcc
AR := $(CROSS_COMPILE)ar
CFLAGS := -O2 -Wall -Wextra -std=c99 -Iinc -I.
OBJDIR = obj
INCLUDES = $(wildcard inc/*.h)
### linking options
### general build targets
all: libtinymt32.a libparson.a libbase64.a
clean:
rm -f libtinymt32.a
rm -f libparson.a
rm -f libbase64.a
rm -f $(OBJDIR)/*.o
### library module target
$(OBJDIR):
mkdir -p $(OBJDIR)
$(OBJDIR)/%.o: src/%.c $(INCLUDES) | $(OBJDIR)
$(CC) -c $(CFLAGS) $< -o $@
### static library
libtinymt32.a: $(OBJDIR)/tinymt32.o
$(AR) rcs $@ $^
libparson.a: $(OBJDIR)/parson.o
$(AR) rcs $@ $^
libbase64.a: $(OBJDIR)/base64.o
$(AR) rcs $@ $^
### test programs
### EOF

61
libtools/inc/base64.h Normal file
View File

@ -0,0 +1,61 @@
/*
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(C)2019 Semtech
Description:
Base64 encoding & decoding library
License: Revised BSD License, see LICENSE.TXT file include in the project
*/
#ifndef _BASE64_H
#define _BASE64_H
/* -------------------------------------------------------------------------- */
/* --- DEPENDANCIES --------------------------------------------------------- */
#include <stdint.h> /* C99 types */
/* -------------------------------------------------------------------------- */
/* --- PUBLIC FUNCTIONS PROTOTYPES ------------------------------------------ */
/**
@brief Encode binary data in Base64 string (no padding)
@param in pointer to a table of binary data
@param size number of bytes to be encoded to base64
@param out pointer to a string where the function will output encoded data
@param max_len max length of the out string (including null char)
@return >=0 length of the resulting string (w/o null char), -1 for error
*/
int bin_to_b64_nopad(const uint8_t * in, int size, char * out, int max_len);
/**
@brief Decode Base64 string to binary data (no padding)
@param in string containing only base64 valid characters
@param size number of characters to be decoded from base64 (w/o null char)
@param out pointer to a data buffer where the function will output decoded data
@param out_max_len usable size of the output data buffer
@return >=0 number of bytes written to the data buffer, -1 for error
*/
int b64_to_bin_nopad(const char * in, int size, uint8_t * out, int max_len);
/* === derivative functions === */
/**
@brief Encode binary data in Base64 string (with added padding)
*/
int bin_to_b64(const uint8_t * in, int size, char * out, int max_len);
/**
@brief Decode Base64 string to binary data (remove padding if necessary)
*/
int b64_to_bin(const char * in, int size, uint8_t * out, int max_len);
#endif
/* --- EOF ------------------------------------------------------------------ */

222
libtools/inc/parson.h Normal file
View File

@ -0,0 +1,222 @@
/*
Parson ( http://kgabis.github.com/parson/ )
Copyright (c) 2012 - 2016 Krzysztof Gabis
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef parson_parson_h
#define parson_parson_h
#ifdef __cplusplus
extern "C"
{
#endif
#include <stddef.h> /* size_t */
/* Types and enums */
typedef struct json_object_t JSON_Object;
typedef struct json_array_t JSON_Array;
typedef struct json_value_t JSON_Value;
enum json_value_type {
JSONError = -1,
JSONNull = 1,
JSONString = 2,
JSONNumber = 3,
JSONObject = 4,
JSONArray = 5,
JSONBoolean = 6
};
typedef int JSON_Value_Type;
enum json_result_t {
JSONSuccess = 0,
JSONFailure = -1
};
typedef int JSON_Status;
typedef void * (*JSON_Malloc_Function)(size_t);
typedef void (*JSON_Free_Function)(void *);
/* Call only once, before calling any other function from parson API. If not called, malloc and free
from stdlib will be used for all allocations */
void json_set_allocation_functions(JSON_Malloc_Function malloc_fun, JSON_Free_Function free_fun);
/* Parses first JSON value in a file, returns NULL in case of error */
JSON_Value * json_parse_file(const char *filename);
/* Parses first JSON value in a file and ignores comments (/ * * / and //),
returns NULL in case of error */
JSON_Value * json_parse_file_with_comments(const char *filename);
/* Parses first JSON value in a string, returns NULL in case of error */
JSON_Value * json_parse_string(const char *string);
/* Parses first JSON value in a string and ignores comments (/ * * / and //),
returns NULL in case of error */
JSON_Value * json_parse_string_with_comments(const char *string);
/* Serialization */
size_t json_serialization_size(const JSON_Value *value); /* returns 0 on fail */
JSON_Status json_serialize_to_buffer(const JSON_Value *value, char *buf, size_t buf_size_in_bytes);
JSON_Status json_serialize_to_file(const JSON_Value *value, const char *filename);
char * json_serialize_to_string(const JSON_Value *value);
/* Pretty serialization */
size_t json_serialization_size_pretty(const JSON_Value *value); /* returns 0 on fail */
JSON_Status json_serialize_to_buffer_pretty(const JSON_Value *value, char *buf, size_t buf_size_in_bytes);
JSON_Status json_serialize_to_file_pretty(const JSON_Value *value, const char *filename);
char * json_serialize_to_string_pretty(const JSON_Value *value);
void json_free_serialized_string(char *string); /* frees string from json_serialize_to_string and json_serialize_to_string_pretty */
/* Comparing */
int json_value_equals(const JSON_Value *a, const JSON_Value *b);
/* Validation
This is *NOT* JSON Schema. It validates json by checking if object have identically
named fields with matching types.
For example schema {"name":"", "age":0} will validate
{"name":"Joe", "age":25} and {"name":"Joe", "age":25, "gender":"m"},
but not {"name":"Joe"} or {"name":"Joe", "age":"Cucumber"}.
In case of arrays, only first value in schema is checked against all values in tested array.
Empty objects ({}) validate all objects, empty arrays ([]) validate all arrays,
null validates values of every type.
*/
JSON_Status json_validate(const JSON_Value *schema, const JSON_Value *value);
/*
* JSON Object
*/
JSON_Value * json_object_get_value (const JSON_Object *object, const char *name);
const char * json_object_get_string (const JSON_Object *object, const char *name);
JSON_Object * json_object_get_object (const JSON_Object *object, const char *name);
JSON_Array * json_object_get_array (const JSON_Object *object, const char *name);
double json_object_get_number (const JSON_Object *object, const char *name); /* returns 0 on fail */
int json_object_get_boolean(const JSON_Object *object, const char *name); /* returns -1 on fail */
/* dotget functions enable addressing values with dot notation in nested objects,
just like in structs or c++/java/c# objects (e.g. objectA.objectB.value).
Because valid names in JSON can contain dots, some values may be inaccessible
this way. */
JSON_Value * json_object_dotget_value (const JSON_Object *object, const char *name);
const char * json_object_dotget_string (const JSON_Object *object, const char *name);
JSON_Object * json_object_dotget_object (const JSON_Object *object, const char *name);
JSON_Array * json_object_dotget_array (const JSON_Object *object, const char *name);
double json_object_dotget_number (const JSON_Object *object, const char *name); /* returns 0 on fail */
int json_object_dotget_boolean(const JSON_Object *object, const char *name); /* returns -1 on fail */
/* Functions to get available names */
size_t json_object_get_count(const JSON_Object *object);
const char * json_object_get_name (const JSON_Object *object, size_t index);
/* Creates new name-value pair or frees and replaces old value with a new one.
* json_object_set_value does not copy passed value so it shouldn't be freed afterwards. */
JSON_Status json_object_set_value(JSON_Object *object, const char *name, JSON_Value *value);
JSON_Status json_object_set_string(JSON_Object *object, const char *name, const char *string);
JSON_Status json_object_set_number(JSON_Object *object, const char *name, double number);
JSON_Status json_object_set_boolean(JSON_Object *object, const char *name, int boolean);
JSON_Status json_object_set_null(JSON_Object *object, const char *name);
/* Works like dotget functions, but creates whole hierarchy if necessary.
* json_object_dotset_value does not copy passed value so it shouldn't be freed afterwards. */
JSON_Status json_object_dotset_value(JSON_Object *object, const char *name, JSON_Value *value);
JSON_Status json_object_dotset_string(JSON_Object *object, const char *name, const char *string);
JSON_Status json_object_dotset_number(JSON_Object *object, const char *name, double number);
JSON_Status json_object_dotset_boolean(JSON_Object *object, const char *name, int boolean);
JSON_Status json_object_dotset_null(JSON_Object *object, const char *name);
/* Frees and removes name-value pair */
JSON_Status json_object_remove(JSON_Object *object, const char *name);
/* Works like dotget function, but removes name-value pair only on exact match. */
JSON_Status json_object_dotremove(JSON_Object *object, const char *key);
/* Removes all name-value pairs in object */
JSON_Status json_object_clear(JSON_Object *object);
/*
*JSON Array
*/
JSON_Value * json_array_get_value (const JSON_Array *array, size_t index);
const char * json_array_get_string (const JSON_Array *array, size_t index);
JSON_Object * json_array_get_object (const JSON_Array *array, size_t index);
JSON_Array * json_array_get_array (const JSON_Array *array, size_t index);
double json_array_get_number (const JSON_Array *array, size_t index); /* returns 0 on fail */
int json_array_get_boolean(const JSON_Array *array, size_t index); /* returns -1 on fail */
size_t json_array_get_count (const JSON_Array *array);
/* Frees and removes value at given index, does nothing and returns JSONFailure if index doesn't exist.
* Order of values in array may change during execution. */
JSON_Status json_array_remove(JSON_Array *array, size_t i);
/* Frees and removes from array value at given index and replaces it with given one.
* Does nothing and returns JSONFailure if index doesn't exist.
* json_array_replace_value does not copy passed value so it shouldn't be freed afterwards. */
JSON_Status json_array_replace_value(JSON_Array *array, size_t i, JSON_Value *value);
JSON_Status json_array_replace_string(JSON_Array *array, size_t i, const char* string);
JSON_Status json_array_replace_number(JSON_Array *array, size_t i, double number);
JSON_Status json_array_replace_boolean(JSON_Array *array, size_t i, int boolean);
JSON_Status json_array_replace_null(JSON_Array *array, size_t i);
/* Frees and removes all values from array */
JSON_Status json_array_clear(JSON_Array *array);
/* Appends new value at the end of array.
* json_array_append_value does not copy passed value so it shouldn't be freed afterwards. */
JSON_Status json_array_append_value(JSON_Array *array, JSON_Value *value);
JSON_Status json_array_append_string(JSON_Array *array, const char *string);
JSON_Status json_array_append_number(JSON_Array *array, double number);
JSON_Status json_array_append_boolean(JSON_Array *array, int boolean);
JSON_Status json_array_append_null(JSON_Array *array);
/*
*JSON Value
*/
JSON_Value * json_value_init_object (void);
JSON_Value * json_value_init_array (void);
JSON_Value * json_value_init_string (const char *string); /* copies passed string */
JSON_Value * json_value_init_number (double number);
JSON_Value * json_value_init_boolean(int boolean);
JSON_Value * json_value_init_null (void);
JSON_Value * json_value_deep_copy (const JSON_Value *value);
void json_value_free (JSON_Value *value);
JSON_Value_Type json_value_get_type (const JSON_Value *value);
JSON_Object * json_value_get_object (const JSON_Value *value);
JSON_Array * json_value_get_array (const JSON_Value *value);
const char * json_value_get_string (const JSON_Value *value);
double json_value_get_number (const JSON_Value *value);
int json_value_get_boolean(const JSON_Value *value);
/* Same as above, but shorter */
JSON_Value_Type json_type (const JSON_Value *value);
JSON_Object * json_object (const JSON_Value *value);
JSON_Array * json_array (const JSON_Value *value);
const char * json_string (const JSON_Value *value);
double json_number (const JSON_Value *value);
int json_boolean(const JSON_Value *value);
#ifdef __cplusplus
}
#endif
#endif

247
libtools/inc/tinymt32.h Normal file
View File

@ -0,0 +1,247 @@
#ifndef TINYMT32_H
#define TINYMT32_H
/**
* @file tinymt32.h
*
* @brief Tiny Mersenne Twister only 127 bit internal state
*
* @author Mutsuo Saito (Hiroshima University)
* @author Makoto Matsumoto (University of Tokyo)
*
* Copyright (C) 2011 Mutsuo Saito, Makoto Matsumoto,
* Hiroshima University and The University of Tokyo.
* All rights reserved.
*
* The 3-clause BSD License is applied to this software, see
* LICENSE.txt
*/
#include <stdint.h>
#include <inttypes.h>
#define TINYMT32_MEXP 127
#define TINYMT32_SH0 1
#define TINYMT32_SH1 10
#define TINYMT32_SH8 8
#define TINYMT32_MASK UINT32_C(0x7fffffff)
#define TINYMT32_MUL (1.0f / 16777216.0f)
#if defined(__cplusplus)
extern "C" {
#endif
/**
* tinymt32 internal state vector and parameters
*/
struct TINYMT32_T {
uint32_t status[4];
uint32_t mat1;
uint32_t mat2;
uint32_t tmat;
};
typedef struct TINYMT32_T tinymt32_t;
void tinymt32_init(tinymt32_t * random, uint32_t seed);
void tinymt32_init_by_array(tinymt32_t * random, uint32_t init_key[],
int key_length);
#if defined(__GNUC__)
/**
* This function always returns 127
* @param random not used
* @return always 127
*/
inline static int tinymt32_get_mexp(
tinymt32_t * random __attribute__((unused))) {
return TINYMT32_MEXP;
}
#else
inline static int tinymt32_get_mexp(tinymt32_t * random) {
return TINYMT32_MEXP;
}
#endif
/**
* This function changes internal state of tinymt32.
* Users should not call this function directly.
* @param random tinymt internal status
*/
inline static void tinymt32_next_state(tinymt32_t * random) {
uint32_t x;
uint32_t y;
y = random->status[3];
x = (random->status[0] & TINYMT32_MASK)
^ random->status[1]
^ random->status[2];
x ^= (x << TINYMT32_SH0);
y ^= (y >> TINYMT32_SH0) ^ x;
random->status[0] = random->status[1];
random->status[1] = random->status[2];
random->status[2] = x ^ (y << TINYMT32_SH1);
random->status[3] = y;
random->status[1] ^= -((int32_t)(y & 1)) & random->mat1;
random->status[2] ^= -((int32_t)(y & 1)) & random->mat2;
}
/**
* This function outputs 32-bit unsigned integer from internal state.
* Users should not call this function directly.
* @param random tinymt internal status
* @return 32-bit unsigned pseudorandom number
*/
inline static uint32_t tinymt32_temper(tinymt32_t * random) {
uint32_t t0, t1;
t0 = random->status[3];
#if defined(LINEARITY_CHECK)
t1 = random->status[0]
^ (random->status[2] >> TINYMT32_SH8);
#else
t1 = random->status[0]
+ (random->status[2] >> TINYMT32_SH8);
#endif
t0 ^= t1;
t0 ^= -((int32_t)(t1 & 1)) & random->tmat;
return t0;
}
/**
* This function outputs floating point number from internal state.
* Users should not call this function directly.
* @param random tinymt internal status
* @return floating point number r (1.0 <= r < 2.0)
*/
inline static float tinymt32_temper_conv(tinymt32_t * random) {
uint32_t t0, t1;
union {
uint32_t u;
float f;
} conv;
t0 = random->status[3];
#if defined(LINEARITY_CHECK)
t1 = random->status[0]
^ (random->status[2] >> TINYMT32_SH8);
#else
t1 = random->status[0]
+ (random->status[2] >> TINYMT32_SH8);
#endif
t0 ^= t1;
conv.u = ((t0 ^ (-((int32_t)(t1 & 1)) & random->tmat)) >> 9)
| UINT32_C(0x3f800000);
return conv.f;
}
/**
* This function outputs floating point number from internal state.
* Users should not call this function directly.
* @param random tinymt internal status
* @return floating point number r (1.0 < r < 2.0)
*/
inline static float tinymt32_temper_conv_open(tinymt32_t * random) {
uint32_t t0, t1;
union {
uint32_t u;
float f;
} conv;
t0 = random->status[3];
#if defined(LINEARITY_CHECK)
t1 = random->status[0]
^ (random->status[2] >> TINYMT32_SH8);
#else
t1 = random->status[0]
+ (random->status[2] >> TINYMT32_SH8);
#endif
t0 ^= t1;
conv.u = ((t0 ^ (-((int32_t)(t1 & 1)) & random->tmat)) >> 9)
| UINT32_C(0x3f800001);
return conv.f;
}
/**
* This function outputs 32-bit unsigned integer from internal state.
* @param random tinymt internal status
* @return 32-bit unsigned integer r (0 <= r < 2^32)
*/
inline static uint32_t tinymt32_generate_uint32(tinymt32_t * random) {
tinymt32_next_state(random);
return tinymt32_temper(random);
}
/**
* This function outputs floating point number from internal state.
* This function is implemented using multiplying by (1 / 2^24).
* floating point multiplication is faster than using union trick in
* my Intel CPU.
* @param random tinymt internal status
* @return floating point number r (0.0 <= r < 1.0)
*/
inline static float tinymt32_generate_float(tinymt32_t * random) {
tinymt32_next_state(random);
return (tinymt32_temper(random) >> 8) * TINYMT32_MUL;
}
/**
* This function outputs floating point number from internal state.
* This function is implemented using union trick.
* @param random tinymt internal status
* @return floating point number r (1.0 <= r < 2.0)
*/
inline static float tinymt32_generate_float12(tinymt32_t * random) {
tinymt32_next_state(random);
return tinymt32_temper_conv(random);
}
/**
* This function outputs floating point number from internal state.
* This function is implemented using union trick.
* @param random tinymt internal status
* @return floating point number r (0.0 <= r < 1.0)
*/
inline static float tinymt32_generate_float01(tinymt32_t * random) {
tinymt32_next_state(random);
return tinymt32_temper_conv(random) - 1.0f;
}
/**
* This function outputs floating point number from internal state.
* This function may return 1.0 and never returns 0.0.
* @param random tinymt internal status
* @return floating point number r (0.0 < r <= 1.0)
*/
inline static float tinymt32_generate_floatOC(tinymt32_t * random) {
tinymt32_next_state(random);
return 1.0f - tinymt32_generate_float(random);
}
/**
* This function outputs floating point number from internal state.
* This function returns neither 0.0 nor 1.0.
* @param random tinymt internal status
* @return floating point number r (0.0 < r < 1.0)
*/
inline static float tinymt32_generate_floatOO(tinymt32_t * random) {
tinymt32_next_state(random);
return tinymt32_temper_conv_open(random) - 1.0f;
}
/**
* This function outputs double precision floating point number from
* internal state. The returned value has 32-bit precision.
* In other words, this function makes one double precision floating point
* number from one 32-bit unsigned integer.
* @param random tinymt internal status
* @return floating point number r (0.0 <= r < 1.0)
*/
inline static double tinymt32_generate_32double(tinymt32_t * random) {
tinymt32_next_state(random);
return tinymt32_temper(random) * (1.0 / 4294967296.0);
}
#if defined(__cplusplus)
}
#endif
#endif

307
libtools/src/base64.c Normal file
View File

@ -0,0 +1,307 @@
/*
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(C)2019 Semtech
Description:
Base64 encoding & decoding library
License: Revised BSD License, see LICENSE.TXT file include in the project
*/
/* -------------------------------------------------------------------------- */
/* --- DEPENDANCIES --------------------------------------------------------- */
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include "base64.h"
/* -------------------------------------------------------------------------- */
/* --- PRIVATE MACROS ------------------------------------------------------- */
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
#define CRIT(a) fprintf(stderr, "\nCRITICAL file:%s line:%u msg:%s\n", __FILE__, __LINE__,a);exit(EXIT_FAILURE)
//#define DEBUG(args...) fprintf(stderr,"debug: " args) /* diagnostic message that is destined to the user */
#define DEBUG(args...)
/* -------------------------------------------------------------------------- */
/* --- PRIVATE CONSTANTS ---------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* --- PRIVATE MODULE-WIDE VARIABLES ---------------------------------------- */
static char code_62 = '+'; /* RFC 1421 standard character for code 62 */
static char code_63 = '/'; /* RFC 1421 standard character for code 63 */
static char code_pad = '='; /* RFC 1421 padding character if padding */
/* -------------------------------------------------------------------------- */
/* --- PRIVATE FUNCTIONS DECLARATION ---------------------------------------- */
/**
@brief Convert a code in the range 0-63 to an ASCII character
*/
char code_to_char(uint8_t x);
/**
@brief Convert an ASCII character to a code in the range 0-63
*/
uint8_t char_to_code(char x);
/* -------------------------------------------------------------------------- */
/* --- PRIVATE FUNCTIONS DEFINITION ----------------------------------------- */
char code_to_char(uint8_t x) {
if (x <= 25) {
return 'A' + x;
} else if ((x >= 26) && (x <= 51)) {
return 'a' + (x-26);
} else if ((x >= 52) && (x <= 61)) {
return '0' + (x-52);
} else if (x == 62) {
return code_62;
} else if (x == 63) {
return code_63;
} else {
DEBUG("ERROR: %i IS OUT OF RANGE 0-63 FOR BASE64 ENCODING\n", x);
exit(EXIT_FAILURE);
} //TODO: improve error management
}
uint8_t char_to_code(char x) {
if ((x >= 'A') && (x <= 'Z')) {
return (uint8_t)x - (uint8_t)'A';
} else if ((x >= 'a') && (x <= 'z')) {
return (uint8_t)x - (uint8_t)'a' + 26;
} else if ((x >= '0') && (x <= '9')) {
return (uint8_t)x - (uint8_t)'0' + 52;
} else if (x == code_62) {
return 62;
} else if (x == code_63) {
return 63;
} else {
DEBUG("ERROR: %c (0x%x) IS INVALID CHARACTER FOR BASE64 DECODING\n", x, x);
exit(EXIT_FAILURE);
} //TODO: improve error management
}
/* -------------------------------------------------------------------------- */
/* --- PUBLIC FUNCTIONS DEFINITION ------------------------------------------ */
int bin_to_b64_nopad(const uint8_t * in, int size, char * out, int max_len) {
int i;
int result_len; /* size of the result */
int full_blocks; /* number of 3 unsigned chars / 4 characters blocks */
int last_bytes; /* number of unsigned chars <3 in the last block */
int last_chars; /* number of characters <4 in the last block */
uint32_t b;
/* check input values */
if ((out == NULL) || (in == NULL)) {
DEBUG("ERROR: NULL POINTER AS OUTPUT IN BIN_TO_B64\n");
return -1;
}
if (size == 0) {
*out = 0; /* null string */
return 0;
}
/* calculate the number of base64 'blocks' */
full_blocks = size / 3;
last_bytes = size % 3;
switch (last_bytes) {
case 0: /* no byte left to encode */
last_chars = 0;
break;
case 1: /* 1 byte left to encode -> +2 chars */
last_chars = 2;
break;
case 2: /* 2 bytes left to encode -> +3 chars */
last_chars = 3;
break;
default:
CRIT("switch default that should not be possible");
}
/* check if output buffer is big enough */
result_len = (4*full_blocks) + last_chars;
if (max_len < (result_len + 1)) { /* 1 char added for string terminator */
DEBUG("ERROR: OUTPUT BUFFER TOO SMALL IN BIN_TO_B64\n");
return -1;
}
/* process all the full blocks */
for (i=0; i < full_blocks; ++i) {
b = (0xFF & in[3*i] ) << 16;
b |= (0xFF & in[3*i + 1]) << 8;
b |= 0xFF & in[3*i + 2];
out[4*i + 0] = code_to_char((b >> 18) & 0x3F);
out[4*i + 1] = code_to_char((b >> 12) & 0x3F);
out[4*i + 2] = code_to_char((b >> 6 ) & 0x3F);
out[4*i + 3] = code_to_char( b & 0x3F);
}
/* process the last 'partial' block and terminate string */
i = full_blocks;
if (last_chars == 0) {
out[4*i] = 0; /* null character to terminate string */
} else if (last_chars == 2) {
b = (0xFF & in[3*i] ) << 16;
out[4*i + 0] = code_to_char((b >> 18) & 0x3F);
out[4*i + 1] = code_to_char((b >> 12) & 0x3F);
out[4*i + 2] = 0; /* null character to terminate string */
} else if (last_chars == 3) {
b = (0xFF & in[3*i] ) << 16;
b |= (0xFF & in[3*i + 1]) << 8;
out[4*i + 0] = code_to_char((b >> 18) & 0x3F);
out[4*i + 1] = code_to_char((b >> 12) & 0x3F);
out[4*i + 2] = code_to_char((b >> 6 ) & 0x3F);
out[4*i + 3] = 0; /* null character to terminate string */
}
return result_len;
}
int b64_to_bin_nopad(const char * in, int size, uint8_t * out, int max_len) {
int i;
int result_len; /* size of the result */
int full_blocks; /* number of 3 unsigned chars / 4 characters blocks */
int last_chars; /* number of characters <4 in the last block */
int last_bytes; /* number of unsigned chars <3 in the last block */
uint32_t b;
;
/* check input values */
if ((out == NULL) || (in == NULL)) {
DEBUG("ERROR: NULL POINTER AS OUTPUT OR INPUT IN B64_TO_BIN\n");
return -1;
}
if (size == 0) {
return 0;
}
/* calculate the number of base64 'blocks' */
full_blocks = size / 4;
last_chars = size % 4;
switch (last_chars) {
case 0: /* no char left to decode */
last_bytes = 0;
break;
case 1: /* only 1 char left is an error */
DEBUG("ERROR: ONLY ONE CHAR LEFT IN B64_TO_BIN\n");
return -1;
case 2: /* 2 chars left to decode -> +1 byte */
last_bytes = 1;
break;
case 3: /* 3 chars left to decode -> +2 bytes */
last_bytes = 2;
break;
default:
CRIT("switch default that should not be possible");
}
/* check if output buffer is big enough */
result_len = (3*full_blocks) + last_bytes;
if (max_len < result_len) {
DEBUG("ERROR: OUTPUT BUFFER TOO SMALL IN B64_TO_BIN\n");
return -1;
}
/* process all the full blocks */
for (i=0; i < full_blocks; ++i) {
b = (0x3F & char_to_code(in[4*i] )) << 18;
b |= (0x3F & char_to_code(in[4*i + 1])) << 12;
b |= (0x3F & char_to_code(in[4*i + 2])) << 6;
b |= 0x3F & char_to_code(in[4*i + 3]);
out[3*i + 0] = (b >> 16) & 0xFF;
out[3*i + 1] = (b >> 8 ) & 0xFF;
out[3*i + 2] = b & 0xFF;
}
/* process the last 'partial' block */
i = full_blocks;
if (last_bytes == 1) {
b = (0x3F & char_to_code(in[4*i] )) << 18;
b |= (0x3F & char_to_code(in[4*i + 1])) << 12;
out[3*i + 0] = (b >> 16) & 0xFF;
if (((b >> 12) & 0x0F) != 0) {
DEBUG("WARNING: last character contains unusable bits\n");
}
} else if (last_bytes == 2) {
b = (0x3F & char_to_code(in[4*i] )) << 18;
b |= (0x3F & char_to_code(in[4*i + 1])) << 12;
b |= (0x3F & char_to_code(in[4*i + 2])) << 6;
out[3*i + 0] = (b >> 16) & 0xFF;
out[3*i + 1] = (b >> 8 ) & 0xFF;
if (((b >> 6) & 0x03) != 0) {
DEBUG("WARNING: last character contains unusable bits\n");
}
}
return result_len;
}
int bin_to_b64(const uint8_t * in, int size, char * out, int max_len) {
int ret;
ret = bin_to_b64_nopad(in, size, out, max_len);
if (ret == -1) {
return -1;
}
switch (ret%4) {
case 0: /* nothing to do */
return ret;
case 1:
DEBUG("ERROR: INVALID UNPADDED BASE64 STRING\n");
return -1;
case 2: /* 2 chars in last block, must add 2 padding char */
if (max_len >= (ret + 2 + 1)) {
out[ret] = code_pad;
out[ret+1] = code_pad;
out[ret+2] = 0;
return ret+2;
} else {
DEBUG("ERROR: not enough room to add padding in bin_to_b64\n");
return -1;
}
case 3: /* 3 chars in last block, must add 1 padding char */
if (max_len >= (ret + 1 + 1)) {
out[ret] = code_pad;
out[ret+1] = 0;
return ret+1;
} else {
DEBUG("ERROR: not enough room to add padding in bin_to_b64\n");
return -1;
}
default:
CRIT("switch default that should not be possible");
}
}
int b64_to_bin(const char * in, int size, uint8_t * out, int max_len) {
if (in == NULL) {
DEBUG("ERROR: NULL POINTER AS OUTPUT OR INPUT IN B64_TO_BIN\n");
return -1;
}
if ((size%4 == 0) && (size >= 4)) { /* potentially padded Base64 */
if (in[size-2] == code_pad) { /* 2 padding char to ignore */
return b64_to_bin_nopad(in, size-2, out, max_len);
} else if (in[size-1] == code_pad) { /* 1 padding char to ignore */
return b64_to_bin_nopad(in, size-1, out, max_len);
} else { /* no padding to ignore */
return b64_to_bin_nopad(in, size, out, max_len);
}
} else { /* treat as unpadded Base64 */
return b64_to_bin_nopad(in, size, out, max_len);
}
}
/* --- EOF ------------------------------------------------------------------ */

1765
libtools/src/parson.c Normal file

File diff suppressed because it is too large Load Diff

145
libtools/src/tinymt32.c Normal file
View File

@ -0,0 +1,145 @@
/**
* @file tinymt32.c
*
* @brief Tiny Mersenne Twister only 127 bit internal state
*
* @author Mutsuo Saito (Hiroshima University)
* @author Makoto Matsumoto (The University of Tokyo)
*
* Copyright (C) 2011 Mutsuo Saito, Makoto Matsumoto,
* Hiroshima University and The University of Tokyo.
* All rights reserved.
*
* The 3-clause BSD License is applied to this software, see
* LICENSE.txt
*/
#include "tinymt32.h"
#define MIN_LOOP 8
#define PRE_LOOP 8
/**
* This function represents a function used in the initialization
* by init_by_array
* @param x 32-bit integer
* @return 32-bit integer
*/
static uint32_t ini_func1(uint32_t x) {
return (x ^ (x >> 27)) * UINT32_C(1664525);
}
/**
* This function represents a function used in the initialization
* by init_by_array
* @param x 32-bit integer
* @return 32-bit integer
*/
static uint32_t ini_func2(uint32_t x) {
return (x ^ (x >> 27)) * UINT32_C(1566083941);
}
/**
* This function certificate the period of 2^127-1.
* @param random tinymt state vector.
*/
static void period_certification(tinymt32_t * random) {
if ((random->status[0] & TINYMT32_MASK) == 0 &&
random->status[1] == 0 &&
random->status[2] == 0 &&
random->status[3] == 0) {
random->status[0] = 'T';
random->status[1] = 'I';
random->status[2] = 'N';
random->status[3] = 'Y';
}
}
/**
* This function initializes the internal state array with a 32-bit
* unsigned integer seed.
* @param random tinymt state vector.
* @param seed a 32-bit unsigned integer used as a seed.
*/
void tinymt32_init(tinymt32_t * random, uint32_t seed) {
random->status[0] = seed;
random->status[1] = random->mat1;
random->status[2] = random->mat2;
random->status[3] = random->tmat;
for (int i = 1; i < MIN_LOOP; i++) {
random->status[i & 3] ^= i + UINT32_C(1812433253)
* (random->status[(i - 1) & 3]
^ (random->status[(i - 1) & 3] >> 30));
}
period_certification(random);
for (int i = 0; i < PRE_LOOP; i++) {
tinymt32_next_state(random);
}
}
/**
* This function initializes the internal state array,
* with an array of 32-bit unsigned integers used as seeds
* @param random tinymt state vector.
* @param init_key the array of 32-bit integers, used as a seed.
* @param key_length the length of init_key.
*/
void tinymt32_init_by_array(tinymt32_t * random, uint32_t init_key[],
int key_length) {
const int lag = 1;
const int mid = 1;
const int size = 4;
int i, j;
int count;
uint32_t r;
uint32_t * st = &random->status[0];
st[0] = 0;
st[1] = random->mat1;
st[2] = random->mat2;
st[3] = random->tmat;
if (key_length + 1 > MIN_LOOP) {
count = key_length + 1;
} else {
count = MIN_LOOP;
}
r = ini_func1(st[0] ^ st[mid % size]
^ st[(size - 1) % size]);
st[mid % size] += r;
r += key_length;
st[(mid + lag) % size] += r;
st[0] = r;
count--;
for (i = 1, j = 0; (j < count) && (j < key_length); j++) {
r = ini_func1(st[i % size]
^ st[(i + mid) % size]
^ st[(i + size - 1) % size]);
st[(i + mid) % size] += r;
r += init_key[j] + i;
st[(i + mid + lag) % size] += r;
st[i % size] = r;
i = (i + 1) % size;
}
for (; j < count; j++) {
r = ini_func1(st[i % size]
^ st[(i + mid) % size]
^ st[(i + size - 1) % size]);
st[(i + mid) % size] += r;
r += i;
st[(i + mid + lag) % size] += r;
st[i % size] = r;
i = (i + 1) % size;
}
for (j = 0; j < size; j++) {
r = ini_func2(st[i % size]
+ st[(i + mid) % size]
+ st[(i + size - 1) % size]);
st[(i + mid) % size] ^= r;
r -= i;
st[(i + mid + lag) % size] ^= r;
st[i % size] = r;
i = (i + 1) % size;
}
period_certification(random);
for (i = 0; i < PRE_LOOP; i++) {
tinymt32_next_state(random);
}
}

93
packet_forwarder/Makefile Normal file
View File

@ -0,0 +1,93 @@
### get external defined data
include ../target.cfg
### Application-specific constants
APP_NAME := lora_pkt_fwd
### Environment constants
LGW_PATH ?= ../libloragw
LIB_PATH ?= ../libtools
ARCH ?=
CROSS_COMPILE ?=
OBJDIR = obj
INCLUDES = $(wildcard inc/*.h)
### External constant definitions
# must get library build option to know if mpsse must be linked or not
include $(LGW_PATH)/library.cfg
RELEASE_VERSION := `cat ../VERSION`
### Constant symbols
CC := $(CROSS_COMPILE)gcc
AR := $(CROSS_COMPILE)ar
CFLAGS := -O2 -Wall -Wextra -std=c99 -Iinc -I. -I../libtools/inc
VFLAG := -D VERSION_STRING="\"$(RELEASE_VERSION)\""
### Constants for Lora concentrator HAL library
# List the library sub-modules that are used by the application
LGW_INC =
ifneq ($(wildcard $(LGW_PATH)/inc/config.h),)
# only for HAL version 1.3 and beyond
LGW_INC += $(LGW_PATH)/inc/config.h
endif
LGW_INC += $(LGW_PATH)/inc/loragw_hal.h
### Linking options
LIBS := -lloragw -ltinymt32 -lparson -lbase64 -lrt -lpthread -lm
### General build targets
all: $(APP_NAME)
clean:
rm -f $(OBJDIR)/*.o
rm -f $(APP_NAME)
ifneq ($(strip $(TARGET_IP)),)
ifneq ($(strip $(TARGET_DIR)),)
ifneq ($(strip $(TARGET_USR)),)
install:
@echo "---- Copying packet_forwarder files to $(TARGET_IP):$(TARGET_DIR)"
@ssh $(TARGET_USR)@$(TARGET_IP) "mkdir -p $(TARGET_DIR)"
@scp lora_pkt_fwd $(TARGET_USR)@$(TARGET_IP):$(TARGET_DIR)
install_conf:
@echo "---- Copying packet_forwarder conf files to $(TARGET_IP):$(TARGET_DIR)"
@ssh $(TARGET_USR)@$(TARGET_IP) "mkdir -p $(TARGET_DIR)"
@scp global_conf.json.sx1250 $(TARGET_USR)@$(TARGET_IP):$(TARGET_DIR)
@scp global_conf.json.sx1257 $(TARGET_USR)@$(TARGET_IP):$(TARGET_DIR)
else
@echo "ERROR: TARGET_USR is not configured in target.cfg"
endif
else
@echo "ERROR: TARGET_DIR is not configured in target.cfg"
endif
else
@echo "ERROR: TARGET_IP is not configured in target.cfg"
endif
### Sub-modules compilation
$(OBJDIR):
mkdir -p $(OBJDIR)
$(OBJDIR)/%.o: src/%.c $(INCLUDES) | $(OBJDIR)
$(CC) -c $(CFLAGS) -I$(LGW_PATH)/inc $< -o $@
### Main program compilation and assembly
$(OBJDIR)/$(APP_NAME).o: src/$(APP_NAME).c $(LGW_INC) $(INCLUDES) | $(OBJDIR)
$(CC) -c $(CFLAGS) $(VFLAG) -I$(LGW_PATH)/inc $< -o $@
$(APP_NAME): $(OBJDIR)/$(APP_NAME).o $(LGW_PATH)/libloragw.a $(OBJDIR)/jitqueue.o
$(CC) -L$(LGW_PATH) -L$(LIB_PATH) $< $(OBJDIR)/jitqueue.o -o $@ $(LIBS)
### EOF

View File

@ -0,0 +1,477 @@
______ _
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(C)2019 Semtech
Basic communication protocol between LoRa gateway and Network Server
====================================================================
## 1. Introduction
The protocol between the gateway and the server is purposefully very basic and
for demonstration purpose only, or for use on private and reliable networks.
There is no authentication of the gateway or the server, and the acknowledges
are only used for network quality assessment, not to correct UDP datagrams
losses (no retries).
## 2. System schematic and definitions
((( Y )))
|
|
+ - -|- - - - - - - - - - - - - + xxxxxxxxxxxx +--------+
| +--+-----------+ +------+ | xx x x xxx | |
| | | | | | xx Internet xx | |
| | Concentrator |<--->| Host |<-------xx or xx-------->| |
| | | SPI | | | xx Intranet xx | Server |
| +--------------+ +------+ | xxxx x xxxx | |
| ^ ^ | xxxxxxxx | |
| | PPS +-------+ NMEA | | | |
| +-----| GPS |-------+ | +--------+
| | (opt) | |
| +-------+ |
| |
| Gateway |
+- - - - - - - - - - - - - - - -+
__Concentrator__: radio RX/TX board, based on Semtech multichannel modems
(SX130x), transceivers (SX135x) and/or low-power stand-alone modems (SX127x).
__Host__: embedded computer on which the packet forwarder is run. Drives the
concentrator through a SPI link.
__GPS__: GNSS (GPS, Galileo, GLONASS, etc) receiver with a "1 Pulse Per Second"
output and a serial link to the host to send NMEA frames containing time and
geographical coordinates data. Optional.
__Gateway__: a device composed of at least one radio concentrator, a host, some
network connection to the internet or a private network (Ethernet, 3G, Wifi,
microwave link), and optionally a GPS receiver for synchronization.
__Server__: an abstract computer that will process the RF packets received and
forwarded by the gateway, and issue RF packets in response that the gateway
will have to emit.
It is assumed that the gateway can be behind a NAT or a firewall stopping any
incoming connection.
It is assumed that the server has an static IP address (or an address solvable
through a DNS service) and is able to receive incoming connections on a
specific port.
## 3. Upstream protocol
### 3.1. Sequence diagram ###
+---------+ +---------+
| Gateway | | Server |
+---------+ +---------+
| -----------------------------------\ |
|-| When 1-N RF packets are received | |
| ------------------------------------ |
| |
| PUSH_DATA (token X, GW MAC, JSON payload) |
|------------------------------------------------------------->|
| |
| PUSH_ACK (token X) |
|<-------------------------------------------------------------|
| ------------------------------\ |
| | process packets *after* ack |-|
| ------------------------------- |
| |
### 3.2. PUSH_DATA packet ###
That packet type is used by the gateway mainly to forward the RF packets
received, and associated metadata, to the server.
Bytes | Function
:------:|---------------------------------------------------------------------
0 | protocol version = 2
1-2 | random token
3 | PUSH_DATA identifier 0x00
4-11 | Gateway unique identifier (MAC address)
12-end | JSON object, starting with {, ending with }, see section 4
### 3.3. PUSH_ACK packet ###
That packet type is used by the server to acknowledge immediately all the
PUSH_DATA packets received.
Bytes | Function
:------:|---------------------------------------------------------------------
0 | protocol version = 2
1-2 | same token as the PUSH_DATA packet to acknowledge
3 | PUSH_ACK identifier 0x01
## 4. Upstream JSON data structure
The root object can contain an array named "rxpk":
``` json
{
"rxpk":[ {...}, ...]
}
```
That array contains at least one JSON object, each object contain a RF packet
and associated metadata with the following fields:
Name | Type | Function
:----:|:------:|--------------------------------------------------------------
time | string | UTC time of pkt RX, us precision, ISO 8601 'compact' format
tmms | number | GPS time of pkt RX, number of milliseconds since 06.Jan.1980
tmst | number | Internal timestamp of "RX finished" event (32b unsigned)
freq | number | RX central frequency in MHz (unsigned float, Hz precision)
chan | number | Concentrator "IF" channel used for RX (unsigned integer)
rfch | number | Concentrator "RF chain" used for RX (unsigned integer)
mid | number | Concentrator modem ID on which pkt has been received
stat | number | CRC status: 1 = OK, -1 = fail, 0 = no CRC
modu | string | Modulation identifier "LORA" or "FSK"
datr | string | LoRa datarate identifier (eg. SF12BW500)
datr | number | FSK datarate (unsigned, in bits per second)
codr | string | LoRa ECC coding rate identifier
rssi | number | RSSI of the channel in dBm (signed integer, 1 dB precision)
rssis| number | RSSI of the signal in dBm (signed integer, 1 dB precision)
lsnr | number | Lora SNR ratio in dB (signed float, 0.1 dB precision)
foff | number | LoRa frequency offset in Hz (signed interger)
size | number | RF packet payload size in bytes (unsigned integer)
data | string | Base64 encoded RF packet payload, padded
Example (white-spaces, indentation and newlines added for readability):
``` json
{"rxpk":[
{
"time":"2013-03-31T16:21:17.528002Z",
"tmst":3512348611,
"chan":2,
"rfch":0,
"freq":866.349812,
"stat":1,
"modu":"LORA",
"datr":"SF7BW125",
"codr":"4/6",
"rssi":-35,
"lsnr":5.1,
"size":32,
"data":"-DS4CGaDCdG+48eJNM3Vai-zDpsR71Pn9CPA9uCON84"
},{
"time":"2013-03-31T16:21:17.530974Z",
"tmst":3512348514,
"chan":9,
"rfch":1,
"freq":869.1,
"stat":1,
"modu":"FSK",
"datr":50000,
"rssi":-75,
"size":16,
"data":"VEVTVF9QQUNLRVRfMTIzNA=="
},{
"time":"2013-03-31T16:21:17.532038Z",
"tmst":3316387610,
"chan":0,
"rfch":0,
"freq":863.00981,
"stat":1,
"modu":"LORA",
"datr":"SF10BW125",
"codr":"4/7",
"rssi":-38,
"lsnr":5.5,
"size":32,
"data":"ysgRl452xNLep9S1NTIg2lomKDxUgn3DJ7DE+b00Ass"
}
]}
```
The root object can also contain an object named "stat" :
``` json
{
"rxpk":[ {...}, ...],
"stat":{...}
}
```
It is possible for a packet to contain no "rxpk" array but a "stat" object.
``` json
{
"stat":{...}
}
```
That object contains the status of the gateway, with the following fields:
Name | Type | Function
:----:|:------:|--------------------------------------------------------------
time | string | UTC 'system' time of the gateway, ISO 8601 'expanded' format
lati | number | GPS latitude of the gateway in degree (float, N is +)
long | number | GPS latitude of the gateway in degree (float, E is +)
alti | number | GPS altitude of the gateway in meter RX (integer)
rxnb | number | Number of radio packets received (unsigned integer)
rxok | number | Number of radio packets received with a valid PHY CRC
rxfw | number | Number of radio packets forwarded (unsigned integer)
ackr | number | Percentage of upstream datagrams that were acknowledged
dwnb | number | Number of downlink datagrams received (unsigned integer)
txnb | number | Number of packets emitted (unsigned integer)
Example (white-spaces, indentation and newlines added for readability):
``` json
{"stat":{
"time":"2014-01-12 08:59:28 GMT",
"lati":46.24000,
"long":3.25230,
"alti":145,
"rxnb":2,
"rxok":2,
"rxfw":2,
"ackr":100.0,
"dwnb":2,
"txnb":2
}}
```
## 5. Downstream protocol
### 5.1. Sequence diagram ###
+---------+ +---------+
| Gateway | | Server |
+---------+ +---------+
| -----------------------------------\ |
|-| Every N seconds (keepalive time) | |
| ------------------------------------ |
| |
| PULL_DATA (token Y, MAC@) |
|------------------------------------------------------------->|
| |
| PULL_ACK (token Y) |
|<-------------------------------------------------------------|
| |
+---------+ +---------+
| Gateway | | Server |
+---------+ +---------+
| ------------------------------------------------------\ |
| | Anytime after first PULL_DATA for each packet to TX |-|
| ------------------------------------------------------- |
| |
| PULL_RESP (token Z, JSON payload) |
|<-------------------------------------------------------------|
| |
| TX_ACK (token Z, JSON payload) |
|------------------------------------------------------------->|
### 5.2. PULL_DATA packet ###
That packet type is used by the gateway to poll data from the server.
This data exchange is initialized by the gateway because it might be
impossible for the server to send packets to the gateway if the gateway is
behind a NAT.
When the gateway initialize the exchange, the network route towards the
server will open and will allow for packets to flow both directions.
The gateway must periodically send PULL_DATA packets to be sure the network
route stays open for the server to be used at any time.
Bytes | Function
:------:|---------------------------------------------------------------------
0 | protocol version = 2
1-2 | random token
3 | PULL_DATA identifier 0x02
4-11 | Gateway unique identifier (MAC address)
### 5.3. PULL_ACK packet ###
That packet type is used by the server to confirm that the network route is
open and that the server can send PULL_RESP packets at any time.
Bytes | Function
:------:|---------------------------------------------------------------------
0 | protocol version = 2
1-2 | same token as the PULL_DATA packet to acknowledge
3 | PULL_ACK identifier 0x04
### 5.4. PULL_RESP packet ###
That packet type is used by the server to send RF packets and associated
metadata that will have to be emitted by the gateway.
Bytes | Function
:------:|---------------------------------------------------------------------
0 | protocol version = 2
1-2 | random token
3 | PULL_RESP identifier 0x03
4-end | JSON object, starting with {, ending with }, see section 6
### 5.5. TX_ACK packet ###
That packet type is used by the gateway to send a feedback to the server
to inform if a downlink request has been accepted or rejected by the gateway.
The datagram may optionnaly contain a JSON string to give more details on
acknoledge. If no JSON is present (empty string), this means than no error
occured.
Bytes | Function
:------:|---------------------------------------------------------------------
0 | protocol version = 2
1-2 | same token as the PULL_RESP packet to acknowledge
3 | TX_ACK identifier 0x05
4-11 | Gateway unique identifier (MAC address)
12-end | [optional] JSON object, starting with {, ending with }, see section 6
## 6. Downstream JSON data structure
The root object of PULL_RESP packet must contain an object named "txpk":
``` json
{
"txpk": {...}
}
```
That object contain a RF packet to be emitted and associated metadata with the
following fields:
Name | Type | Function
:----:|:------:|--------------------------------------------------------------
imme | bool | Send packet immediately (will ignore tmst & tmms)
tmst | number | Send packet on a certain timestamp value (will ignore tmms)
tmms | number | Send packet at a certain GPS time (GPS synchronization required)
freq | number | TX central frequency in MHz (unsigned float, Hz precision)
rfch | number | Concentrator "RF chain" used for TX (unsigned integer)
powe | number | TX output power in dBm (unsigned integer, dBm precision)
modu | string | Modulation identifier "LORA" or "FSK"
datr | string | LoRa datarate identifier (eg. SF12BW500)
datr | number | FSK datarate (unsigned, in bits per second)
codr | string | LoRa ECC coding rate identifier
fdev | number | FSK frequency deviation (unsigned integer, in Hz)
ipol | bool | Lora modulation polarization inversion
prea | number | RF preamble size (unsigned integer)
size | number | RF packet payload size in bytes (unsigned integer)
data | string | Base64 encoded RF packet payload, padding optional
ncrc | bool | If true, disable the CRC of the physical layer (optional)
Most fields are optional.
If a field is omitted, default parameters will be used.
Examples (white-spaces, indentation and newlines added for readability):
``` json
{"txpk":{
"imme":true,
"freq":864.123456,
"rfch":0,
"powe":14,
"modu":"LORA",
"datr":"SF11BW125",
"codr":"4/6",
"ipol":false,
"size":32,
"data":"H3P3N2i9qc4yt7rK7ldqoeCVJGBybzPY5h1Dd7P7p8v"
}}
```
``` json
{"txpk":{
"imme":true,
"freq":861.3,
"rfch":0,
"powe":12,
"modu":"FSK",
"datr":50000,
"fdev":3000,
"size":32,
"data":"H3P3N2i9qc4yt7rK7ldqoeCVJGBybzPY5h1Dd7P7p8v"
}}
```
In case of error or warning, the root object of TX_ACK packet must contain an
object named "txpk_ack":
``` json
{
"txpk_ack": {...}
}
```
That object contain status information concerning the associated PULL_RESP packet.
Name | Type | Function
:----:|:------:|-----------------------------------------------------------------------------------------
error | string | Indicates the type of failure that occurred for downlink request (optional)
warn | string | Indicates that downlink request has been accepted with limitation (optional)
value | string | When a warning is raised, it gives indications about the limitation (optional)
value | number | When a warning is raised, it gives indications about the limitation (optional)
The possible values of the "error" field are:
Value | Definition
:-----------------:|---------------------------------------------------------------------
TOO_LATE | Rejected because it was already too late to program this packet for downlink
TOO_EARLY | Rejected because downlink packet timestamp is too much in advance
COLLISION_PACKET | Rejected because there was already a packet programmed in requested timeframe
COLLISION_BEACON | Rejected because there was already a beacon planned in requested timeframe
TX_FREQ | Rejected because requested frequency is not supported by TX RF chain
GPS_UNLOCKED | Rejected because GPS is unlocked, so GPS timestamp cannot be used
The possible values of the "warn" field are:
Value | Definition
:-----------------:|---------------------------------------------------------------------
TX_POWER | The requested power is not supported by the gateway, the power actually used is given in the value field
Examples (white-spaces, indentation and newlines added for readability):
``` json
{"txpk_ack":{
"error":"COLLISION_PACKET"
}}
```
``` json
{"txpk_ack":{
"warn":"TX_POWER",
"value":20
}}
```
## 7. Revisions
### v1.5 ###
* Moved TX_POWER from "error" category to "warn" category in "txpk_ack" object
### v1.4 ###
* Added "tmms" field for GPS time as a monotonic number of milliseconds
ellapsed since January 6th, 1980 (GPS Epoch). No leap second.
### v1.3 ###
* Added downlink feedback from gateway to server (PULL_RESP -> TX_ACK)
### v1.2 ###
* Added value of FSK bitrate for upstream.
* Added parameters for FSK bitrate and frequency deviation for downstream.
### v1.1 ###
* Added syntax for status report JSON object on upstream.
### v1.0 ###
* Initial version.

View File

@ -0,0 +1,98 @@
{
"SX130x_conf": {
"spidev_path": "/dev/spidev0.0",
"lorawan_public": true,
"clksrc": 0,
"antenna_gain": 0, /* antenna gain, in dBi */
"full_duplex": false,
"precision_timestamp": {
"enable": false,
"max_ts_metrics": 255,
"nb_symbols": 1
},
"radio_0": {
"enable": true,
"type": "SX1250",
"freq": 867500000,
"rssi_offset": -215.4,
"rssi_tcomp": {"coeff_a": 0, "coeff_b": 0, "coeff_c": 20.41, "coeff_d": 2162.56, "coeff_e": 0},
"tx_enable": true,
"tx_freq_min": 863000000,
"tx_freq_max": 870000000,
"tx_gain_lut":[
{"rf_power": 12, "pa_gain": 0, "pwr_idx": 16},
{"rf_power": 13, "pa_gain": 0, "pwr_idx": 17},
{"rf_power": 14, "pa_gain": 0, "pwr_idx": 18},
{"rf_power": 15, "pa_gain": 0, "pwr_idx": 20},
{"rf_power": 16, "pa_gain": 1, "pwr_idx": 0},
{"rf_power": 17, "pa_gain": 1, "pwr_idx": 1},
{"rf_power": 18, "pa_gain": 1, "pwr_idx": 2},
{"rf_power": 19, "pa_gain": 1, "pwr_idx": 3},
{"rf_power": 20, "pa_gain": 1, "pwr_idx": 4},
{"rf_power": 21, "pa_gain": 1, "pwr_idx": 5},
{"rf_power": 22, "pa_gain": 1, "pwr_idx": 6},
{"rf_power": 23, "pa_gain": 1, "pwr_idx": 7},
{"rf_power": 24, "pa_gain": 1, "pwr_idx": 8},
{"rf_power": 25, "pa_gain": 1, "pwr_idx": 9},
{"rf_power": 26, "pa_gain": 1, "pwr_idx": 11},
{"rf_power": 27, "pa_gain": 1, "pwr_idx": 13}
]
},
"radio_1": {
"enable": true,
"type": "SX1250",
"freq": 868500000,
"rssi_offset": -215.4,
"rssi_tcomp": {"coeff_a": 0, "coeff_b": 0, "coeff_c": 20.41, "coeff_d": 2162.56, "coeff_e": 0},
"tx_enable": false
},
"chan_multiSF_0": {"enable": true, "radio": 1, "if": -400000},
"chan_multiSF_1": {"enable": true, "radio": 1, "if": -200000},
"chan_multiSF_2": {"enable": true, "radio": 1, "if": 0},
"chan_multiSF_3": {"enable": true, "radio": 0, "if": -400000},
"chan_multiSF_4": {"enable": true, "radio": 0, "if": -200000},
"chan_multiSF_5": {"enable": true, "radio": 0, "if": 0},
"chan_multiSF_6": {"enable": true, "radio": 0, "if": 200000},
"chan_multiSF_7": {"enable": true, "radio": 0, "if": 400000},
"chan_Lora_std": {"enable": true, "radio": 1, "if": -200000, "bandwidth": 250000, "spread_factor": 7,
"implicit_hdr": false, "implicit_payload_length": 17, "implicit_crc_en": false, "implicit_coderate": 1},
"chan_FSK": {"enable": true, "radio": 1, "if": 300000, "bandwidth": 125000, "datarate": 50000}
},
"gateway_conf": {
"gateway_ID": "AA555A0000000000",
/* change with default server address/ports */
"server_address": "localhost",
"serv_port_up": 1730,
"serv_port_down": 1730,
/* adjust the following parameters for your network */
"keepalive_interval": 10,
"stat_interval": 30,
"push_timeout_ms": 100,
/* forward only valid packets */
"forward_crc_valid": true,
"forward_crc_error": false,
"forward_crc_disabled": false,
/* GPS configuration */
"gps_tty_path": "/dev/ttyS0",
/* GPS reference coordinates */
"ref_latitude": 0.0,
"ref_longitude": 0.0,
"ref_altitude": 0,
/* Beaconing parameters */
"beacon_period": 128,
"beacon_freq_hz": 869525000,
"beacon_datarate": 9,
"beacon_bw_hz": 125000,
"beacon_power": 14,
"beacon_infodesc": 0
},
"debug_conf": {
"ref_payload":[
{"id": "0xCAFE1234"},
{"id": "0xCAFE2345"}
],
"log_file": "loragw_hal.log"
}
}

View File

@ -0,0 +1,98 @@
{
"SX130x_conf": {
"spidev_path": "/dev/spidev0.0",
"lorawan_public": true,
"clksrc": 0,
"antenna_gain": 0, /* antenna gain, in dBi */
"full_duplex": false,
"precision_timestamp": {
"enable": false,
"max_ts_metrics": 255,
"nb_symbols": 1
},
"radio_0": {
"enable": true,
"type": "SX1257",
"freq": 867500000,
"rssi_offset": -196.0,
"rssi_tcomp": {"coeff_a": -0.006, "coeff_b": 0.789, "coeff_c": -14.992, "coeff_d": 1988.572, "coeff_e": 105236.996},
"tx_enable": true,
"tx_freq_min": 863000000,
"tx_freq_max": 870000000,
"tx_gain_lut":[
{"rf_power": -6, "pa_gain": 0, "dac_gain": 3, "mix_gain": 8, "dig_gain": 0},
{"rf_power": -3, "pa_gain": 0, "dac_gain": 3, "mix_gain": 10, "dig_gain": 0},
{"rf_power": 0, "pa_gain": 0, "dac_gain": 3, "mix_gain": 12, "dig_gain": 0},
{"rf_power": 3, "pa_gain": 1, "dac_gain": 3, "mix_gain": 8, "dig_gain": 0},
{"rf_power": 6, "pa_gain": 1, "dac_gain": 3, "mix_gain": 10, "dig_gain": 0},
{"rf_power": 10, "pa_gain": 1, "dac_gain": 3, "mix_gain": 12, "dig_gain": 0},
{"rf_power": 11, "pa_gain": 1, "dac_gain": 3, "mix_gain": 13, "dig_gain": 0},
{"rf_power": 12, "pa_gain": 2, "dac_gain": 3, "mix_gain": 9, "dig_gain": 0},
{"rf_power": 13, "pa_gain": 1, "dac_gain": 3, "mix_gain": 15, "dig_gain": 0},
{"rf_power": 14, "pa_gain": 2, "dac_gain": 3, "mix_gain": 10, "dig_gain": 0},
{"rf_power": 16, "pa_gain": 2, "dac_gain": 3, "mix_gain": 11, "dig_gain": 0},
{"rf_power": 20, "pa_gain": 3, "dac_gain": 3, "mix_gain": 9, "dig_gain": 0},
{"rf_power": 23, "pa_gain": 3, "dac_gain": 3, "mix_gain": 10, "dig_gain": 0},
{"rf_power": 25, "pa_gain": 3, "dac_gain": 3, "mix_gain": 11, "dig_gain": 0},
{"rf_power": 26, "pa_gain": 3, "dac_gain": 3, "mix_gain": 12, "dig_gain": 0},
{"rf_power": 27, "pa_gain": 3, "dac_gain": 3, "mix_gain": 14, "dig_gain": 0}
]
},
"radio_1": {
"enable": true,
"type": "SX1257",
"freq": 868500000,
"rssi_offset": -196.0,
"rssi_tcomp": {"coeff_a": -0.006, "coeff_b": 0.789, "coeff_c": -14.992, "coeff_d": 1988.572, "coeff_e": 105236.996},
"tx_enable": false
},
"chan_multiSF_0": {"enable": true, "radio": 1, "if": -400000},
"chan_multiSF_1": {"enable": true, "radio": 1, "if": -200000},
"chan_multiSF_2": {"enable": true, "radio": 1, "if": 0},
"chan_multiSF_3": {"enable": true, "radio": 0, "if": -400000},
"chan_multiSF_4": {"enable": true, "radio": 0, "if": -200000},
"chan_multiSF_5": {"enable": true, "radio": 0, "if": 0},
"chan_multiSF_6": {"enable": true, "radio": 0, "if": 200000},
"chan_multiSF_7": {"enable": true, "radio": 0, "if": 400000},
"chan_Lora_std": {"enable": true, "radio": 1, "if": -200000, "bandwidth": 250000, "spread_factor": 7,
"implicit_hdr": false, "implicit_payload_length": 17, "implicit_crc_en": false, "implicit_coderate": 1},
"chan_FSK": {"enable": true, "radio": 1, "if": 300000, "bandwidth": 125000, "datarate": 50000}
},
"gateway_conf": {
"gateway_ID": "AA555A0000000000",
/* change with default server address/ports */
"server_address": "localhost",
"serv_port_up": 1730,
"serv_port_down": 1730,
/* adjust the following parameters for your network */
"keepalive_interval": 10,
"stat_interval": 30,
"push_timeout_ms": 100,
/* forward only valid packets */
"forward_crc_valid": true,
"forward_crc_error": false,
"forward_crc_disabled": false,
/* GPS configuration */
"gps_tty_path": "/dev/ttyS0",
/* GPS reference coordinates */
"ref_latitude": 0.0,
"ref_longitude": 0.0,
"ref_altitude": 0,
/* Beaconing parameters */
"beacon_period": 128,
"beacon_freq_hz": 869525000,
"beacon_datarate": 9,
"beacon_bw_hz": 125000,
"beacon_power": 14,
"beacon_infodesc": 0
},
"debug_conf": {
"ref_payload":[
{"id": "0xCAFE1234"},
{"id": "0xCAFE2345"}
],
"log_file": "loragw_hal.log"
}
}

View File

@ -0,0 +1,93 @@
{
"SX130x_conf": {
"spidev_path": "/dev/spidev0.0",
"lorawan_public": true,
"clksrc": 1,
"antenna_gain": 0, /* antenna gain, in dBi */
"full_duplex": true,
"precision_timestamp": {
"enable": false,
"max_ts_metrics": 255,
"nb_symbols": 1
},
"radio_0": {
"enable": true,
"type": "SX1257",
"freq": 913800000,
"rssi_offset": -196.0,
"tx_enable": true,
"tx_freq_min": 100000000,
"tx_freq_max": 950000000,
"tx_gain_lut":[
{"rf_power": -6, "pa_gain": 0, "dac_gain": 3, "mix_gain": 8, "dig_gain": 0},
{"rf_power": -3, "pa_gain": 0, "dac_gain": 3, "mix_gain": 10, "dig_gain": 0},
{"rf_power": 0, "pa_gain": 0, "dac_gain": 3, "mix_gain": 12, "dig_gain": 0},
{"rf_power": 3, "pa_gain": 1, "dac_gain": 3, "mix_gain": 8, "dig_gain": 0},
{"rf_power": 6, "pa_gain": 1, "dac_gain": 3, "mix_gain": 10, "dig_gain": 0},
{"rf_power": 10, "pa_gain": 1, "dac_gain": 3, "mix_gain": 12, "dig_gain": 0},
{"rf_power": 11, "pa_gain": 1, "dac_gain": 3, "mix_gain": 13, "dig_gain": 0},
{"rf_power": 12, "pa_gain": 2, "dac_gain": 3, "mix_gain": 9, "dig_gain": 0},
{"rf_power": 13, "pa_gain": 1, "dac_gain": 3, "mix_gain": 15, "dig_gain": 0},
{"rf_power": 14, "pa_gain": 2, "dac_gain": 3, "mix_gain": 10, "dig_gain": 0},
{"rf_power": 16, "pa_gain": 2, "dac_gain": 3, "mix_gain": 11, "dig_gain": 0},
{"rf_power": 20, "pa_gain": 3, "dac_gain": 3, "mix_gain": 9, "dig_gain": 0},
{"rf_power": 23, "pa_gain": 3, "dac_gain": 3, "mix_gain": 10, "dig_gain": 0},
{"rf_power": 25, "pa_gain": 3, "dac_gain": 3, "mix_gain": 11, "dig_gain": 0},
{"rf_power": 26, "pa_gain": 3, "dac_gain": 3, "mix_gain": 12, "dig_gain": 0},
{"rf_power": 27, "pa_gain": 3, "dac_gain": 3, "mix_gain": 14, "dig_gain": 0}
]
},
"radio_1": {
"enable": true,
"type": "SX1257",
"freq": 914600000,
"rssi_offset": -196.0,
"tx_enable": true,
"tx_freq_min": 100000000,
"tx_freq_max": 950000000,
"tx_gain_lut":[
{"rf_power": -6, "pa_gain": 0, "dac_gain": 3, "mix_gain": 8, "dig_gain": 0},
{"rf_power": -3, "pa_gain": 0, "dac_gain": 3, "mix_gain": 10, "dig_gain": 0},
{"rf_power": 0, "pa_gain": 0, "dac_gain": 3, "mix_gain": 12, "dig_gain": 0},
{"rf_power": 3, "pa_gain": 1, "dac_gain": 3, "mix_gain": 8, "dig_gain": 0},
{"rf_power": 6, "pa_gain": 1, "dac_gain": 3, "mix_gain": 10, "dig_gain": 0},
{"rf_power": 10, "pa_gain": 1, "dac_gain": 3, "mix_gain": 12, "dig_gain": 0},
{"rf_power": 11, "pa_gain": 1, "dac_gain": 3, "mix_gain": 13, "dig_gain": 0},
{"rf_power": 12, "pa_gain": 2, "dac_gain": 3, "mix_gain": 9, "dig_gain": 0},
{"rf_power": 13, "pa_gain": 1, "dac_gain": 3, "mix_gain": 15, "dig_gain": 0},
{"rf_power": 14, "pa_gain": 2, "dac_gain": 3, "mix_gain": 10, "dig_gain": 0},
{"rf_power": 16, "pa_gain": 2, "dac_gain": 3, "mix_gain": 11, "dig_gain": 0},
{"rf_power": 20, "pa_gain": 3, "dac_gain": 3, "mix_gain": 9, "dig_gain": 0},
{"rf_power": 23, "pa_gain": 3, "dac_gain": 3, "mix_gain": 10, "dig_gain": 0},
{"rf_power": 25, "pa_gain": 3, "dac_gain": 3, "mix_gain": 11, "dig_gain": 0},
{"rf_power": 26, "pa_gain": 3, "dac_gain": 3, "mix_gain": 12, "dig_gain": 0},
{"rf_power": 27, "pa_gain": 3, "dac_gain": 3, "mix_gain": 14, "dig_gain": 0}
]
},
"chan_multiSF_0": {"enable": true, "radio": 0, "if": -300000},
"chan_multiSF_1": {"enable": true, "radio": 0, "if": -100000},
"chan_multiSF_2": {"enable": true, "radio": 0, "if": 100000},
"chan_multiSF_3": {"enable": true, "radio": 0, "if": 300000},
"chan_multiSF_4": {"enable": true, "radio": 1, "if": -300000},
"chan_multiSF_5": {"enable": true, "radio": 1, "if": -100000},
"chan_multiSF_6": {"enable": true, "radio": 1, "if": 100000},
"chan_multiSF_7": {"enable": true, "radio": 1, "if": 300000}
},
"gateway_conf": {
"gateway_ID": "AA555A0000000000",
/* change with default server address/ports */
"server_address": "localhost",
"serv_port_up": 1750,
"serv_port_down": 1750,
/* adjust the following parameters for your network */
"keepalive_interval": 10,
"stat_interval": 30,
"push_timeout_ms": 100,
/* forward only valid packets */
"forward_crc_valid": true,
"forward_crc_error": false,
"forward_crc_disabled": false
}
}

View File

@ -0,0 +1,154 @@
/*
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(C)2019 Semtech
Description:
LoRa concentrator : Just In Time TX scheduling queue
License: Revised BSD License, see LICENSE.TXT file include in the project
*/
#ifndef _LORA_PKTFWD_JIT_H
#define _LORA_PKTFWD_JIT_H
/* -------------------------------------------------------------------------- */
/* --- DEPENDANCIES --------------------------------------------------------- */
#include <stdint.h> /* C99 types */
#include <stdbool.h> /* bool type */
#include <sys/time.h> /* timeval */
#include "loragw_hal.h"
/* -------------------------------------------------------------------------- */
/* --- PUBLIC CONSTANTS ----------------------------------------------------- */
#define JIT_QUEUE_MAX 32 /* Maximum number of packets to be stored in JiT queue */
#define JIT_NUM_BEACON_IN_QUEUE 3 /* Number of beacons to be loaded in JiT queue at any time */
/* -------------------------------------------------------------------------- */
/* --- PUBLIC TYPES --------------------------------------------------------- */
enum jit_pkt_type_e {
JIT_PKT_TYPE_DOWNLINK_CLASS_A,
JIT_PKT_TYPE_DOWNLINK_CLASS_B,
JIT_PKT_TYPE_DOWNLINK_CLASS_C,
JIT_PKT_TYPE_BEACON
};
enum jit_error_e {
JIT_ERROR_OK, /* Packet ok to be sent */
JIT_ERROR_TOO_LATE, /* Too late to send this packet */
JIT_ERROR_TOO_EARLY, /* Too early to queue this packet */
JIT_ERROR_FULL, /* Downlink queue is full */
JIT_ERROR_EMPTY, /* Downlink queue is empty */
JIT_ERROR_COLLISION_PACKET, /* A packet is already enqueued for this timeframe */
JIT_ERROR_COLLISION_BEACON, /* A beacon is planned for this timeframe */
JIT_ERROR_TX_FREQ, /* The required frequency for downlink is not supported */
JIT_ERROR_TX_POWER, /* The required power for downlink is not supported */
JIT_ERROR_GPS_UNLOCKED, /* GPS timestamp could not be used as GPS is unlocked */
JIT_ERROR_INVALID /* Packet is invalid */
};
struct jit_node_s {
/* API fields */
struct lgw_pkt_tx_s pkt; /* TX packet */
enum jit_pkt_type_e pkt_type; /* Packet type: Downlink, Beacon... */
/* Internal fields */
uint32_t pre_delay; /* Amount of time before packet timestamp to be reserved */
uint32_t post_delay; /* Amount of time after packet timestamp to be reserved (time on air) */
};
struct jit_queue_s {
uint8_t num_pkt; /* Total number of packets in the queue (downlinks, beacons...) */
uint8_t num_beacon; /* Number of beacons in the queue */
struct jit_node_s nodes[JIT_QUEUE_MAX]; /* Nodes/packets array in the queue */
};
/* -------------------------------------------------------------------------- */
/* --- PUBLIC FUNCTIONS PROTOTYPES ------------------------------------------ */
/**
@brief Check if a JiT queue is full.
@param queue[in] Just in Time queue to be checked.
@return true if queue is full, false otherwise.
*/
bool jit_queue_is_full(struct jit_queue_s *queue);
/**
@brief Check if a JiT queue is empty.
@param queue[in] Just in Time queue to be checked.
@return true if queue is empty, false otherwise.
*/
bool jit_queue_is_empty(struct jit_queue_s *queue);
/**
@brief Initialize a Just in Time queue.
@param queue[in] Just in Time queue to be initialized. Memory should have been allocated already.
This function is used to reset every elements in the allocated queue.
*/
void jit_queue_init(struct jit_queue_s *queue);
/**
@brief Add a packet in a Just-in-Time queue
@param queue[in/out] Just in Time queue in which the packet should be inserted
@param time_us[in] Current concentrator time
@param packet[in] Packet to be queued in JiT queue
@param pkt_type[in] Type of packet to be queued: Downlink, Beacon
@return success if the function was able to queue the packet
This function is typically used when a packet is received from server for downlink.
It will check if packet can be queued, with several criterias. Once the packet is queued, it has to be
sent over the air. So all checks should happen before the packet being actually in the queue.
*/
enum jit_error_e jit_enqueue(struct jit_queue_s *queue, uint32_t time_us, struct lgw_pkt_tx_s *packet, enum jit_pkt_type_e pkt_type);
/**
@brief Dequeue a packet from a Just-in-Time queue
@param queue[in/out] Just in Time queue from which the packet should be removed
@param index[in] in the queue where to get the packet to be removed
@param packet[out] that was at index
@param pkt_type[out] Type of packet dequeued: Downlink, Beacon
@return success if the function was able to dequeue the packet
This function is typically used when a packet is about to be placed on concentrator buffer for TX.
The index is generally got using the jit_peek function.
*/
enum jit_error_e jit_dequeue(struct jit_queue_s *queue, int index, struct lgw_pkt_tx_s *packet, enum jit_pkt_type_e *pkt_type);
/**
@brief Check if there is a packet soon to be sent from the JiT queue.
@param queue[in] Just in Time queue to parse for peeking a packet
@param time_us[in] Current concentrator time
@param pkt_idx[out] Packet index which is soon to be dequeued.
@return success if the function was able to parse the queue. pkt_idx is set to -1 if no packet found.
This function is typically used to check in JiT queue if there is a packet soon to be sent.
It search the packet with the highest priority in queue, and check if its timestamp is near
enough the current concentrator time.
*/
enum jit_error_e jit_peek(struct jit_queue_s *queue, uint32_t time_us, int *pkt_idx);
/**
@brief Debug function to print the queue's content on console
@param queue[in] Just in Time queue to be displayed
@param show_all[in] Indicates if empty nodes have to be displayed or not
*/
void jit_print_queue(struct jit_queue_s *queue, bool show_all, int debug_level);
#endif
/* --- EOF ------------------------------------------------------------------ */

View File

@ -0,0 +1,39 @@
/*
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(C)2019 Semtech
Description:
LoRa concentrator : Packet Forwarder trace helpers
License: Revised BSD License, see LICENSE.TXT file include in the project
*/
#ifndef _LORA_PKTFWD_TRACE_H
#define _LORA_PKTFWD_TRACE_H
#define DEBUG_PKT_FWD 0
#define DEBUG_JIT 0
#define DEBUG_JIT_ERROR 1
#define DEBUG_TIMERSYNC 0
#define DEBUG_BEACON 0
#define DEBUG_LOG 1
#define MSG(args...) printf(args) /* message that is destined to the user */
#define MSG_DEBUG(FLAG, fmt, ...) \
do { \
if (FLAG) \
fprintf(stdout, "%s:%d:%s(): " fmt, __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__); \
} while (0)
#define MSG_PRINTF(FLAG, fmt, ...) \
do { \
if (FLAG) \
fprintf(stdout, fmt, ##__VA_ARGS__); \
} while (0)
#endif
/* --- EOF ------------------------------------------------------------------ */

253
packet_forwarder/readme.md Normal file
View File

@ -0,0 +1,253 @@
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(C)2019 Semtech
SX1302 packet forwarder
=======================
## 1. Introduction
The packet forwarder is a program running on the host of a Lora gateway that
forwards RF packets receive by the concentrator to a server through a IP/UDP
link, and emits RF packets that are sent by the server. It can also emit a
network-wide GPS-synchronous beacon signal used for coordinating all nodes of
the network.
To learn more about the network protocol between the gateway and the server,
please read the PROTOCOL.TXT document.
## 2. System schematic and definitions
((( Y )))
|
|
+- -|- - - - - - - - - - - - -+ xxxxxxxxxxxx +--------+
|+--+-----------+ +------+| xx x x xxx | |
|| | | || xx Internet xx | |
|| Concentrator |<----+ Host |<------xx or xx-------->| |
|| | SPI | || xx Intranet xx | Server |
|+--------------+ +------+| xxxx x xxxx | |
| ^ ^ | xxxxxxxx | |
| | PPS +-----+ NMEA | | | |
| +------| GPS |-------+ | +--------+
| +-----+ |
| |
| Gateway |
+- - - - - - - - - - - - - - -+
Concentrator: radio RX/TX board, based on Semtech multichannel modems (SX130x),
transceivers (SX135x) and/or low-power stand-alone modems (SX127x).
Host: embedded computer on which the packet forwarder is run. Drives the
concentrator through a SPI link.
Gateway: a device composed of at least one radio concentrator, a host, some
network connection to the internet or a private network (Ethernet, 3G, Wifi,
microwave link), and optionally a GPS receiver for synchronization.
Server: an abstract computer that will process the RF packets received and
forwarded by the gateway, and issue RF packets in response that the gateway
will have to emit.
## 3. Dependencies
This program uses the Parson library (http://kgabis.github.com/parson/) by
Krzysztof Gabis for JSON parsing.
Many thanks to him for that very practical and well written library.
This program is statically linked with the libloragw Lora concentrator library.
It was tested with v1.3.0 of the library but should work with any later
version provided the API is v1 or a later backward-compatible API.
Data structures of the received packets are accessed by name (ie. not at a
binary level) so new functionalities can be added to the API without affecting
that program at all.
This program follows the v1.3 version of the gateway-to-server protocol.
The last dependency is the hardware concentrator (based on FPGA or SX130x
chips) that must be matched with the proper version of the HAL.
## 4. Usage
* Pick the global_conf.json file that fit with your platform, region and feature
need.
* Update the JSON configuration (global and local) files, as explained below.
* Run:
./lora_pkt_fwd -c global_conf.json
To stop the application, press Ctrl+C.
Unless it is manually stopped or encounter a critical error, the program will
run forever.
The way the program takes configuration files into account is the following:
* if a specific configuration file is given as program argument, use the
one specified.
* if there is a global_conf.json parse it.
The global configuration file should be exactly the same throughout your
network, contain all global parameters (parameters for "sensor" radio
channels) and preferably default "safe" values for parameters that are
specific for each gateway (eg. specify a default MAC address).
In each configuration file, the program looks for a JSON object named
"SX130x_conf" that should contain the parameters for the Lora concentrator
board (RF channels definition, modem parameters, etc) and another JSON object
called "gateway_conf" that should contain the gateway parameters (gateway MAC
address, IP address of the server, keep-alive time, etc).
To learn more about the JSON configuration format, read the provided JSON
files and the libloragw API documentation.
Every X seconds (parameter settable in the configuration files) the program
display statistics on the RF packets received and sent, and the network
datagrams received and sent.
The program also send some statistics to the server in JSON format.
## 5. "Just-In-Time" downlink scheduling
The LoRa concentrator can have only one TX packet programmed for departure at a
time. The actual departure of a downlink packet will be done based on its
timestamp, when the concentrator internal counter reaches timestamps value.
The departure of a beacon will be done based on a GPS PPS event.
It may happen that, due to network variable latency, the gateway receives one
or many downlink packets from the server while a TX is already programmed in the
concentrator. The packet forwarder has to store and order incoming downlink
packets (in a queue), so that they can all be programmed in the concentrator at
the proper time and sent over the air.
Possible failures that may occur and that have to be reported to the server are:
- It is too early or too late to send a given packet
- A packet collides with another packet already queued
- A packet collides with a beacon
- TX RF parameters (frequency, power) are not supported by gateway
- Gateways GPS is unlocked, so cannot process Class B downlink
It is called "Just-in-Time" (JiT) scheduling, because the packet forwarder will
program a downlink or a beacon packet in the concentrator just before it has to
be sent over the air.
Another benefit of JiT is to optimize the gateway downlink capacity by avoiding
to keep the concentrator TX buffer busy for too long.
In order to achieve "Just-in-Time" scheduling, the following software elements
have been added:
- A JiT queue, with associated enqueue/peek/dequeue functions and packet
acceptance criterias. It is where downlink packets are stored, waiting to be
sent.
- A JiT thread, which regularly checks if there is a packet in the JiT queue
ready to be programmed in the concentrator, based on current concentrator
internal time.
### 5.1. Concentrator vs GPS time synchronization
There are 2 cases for which we need to convert a GPS time to concentrator
counter:
- Class B downlink: when the “time” field of JSON “txpk” is filled instead
of the “tmst” field, we need to be able to determine if the packet can be
queued in JiT queue or not, based on its corresponding concentrator
counter value.
Note: even if a Class-B downlink is given with a GPS timestamp, the
concentrator TX mode is configured as “TIMESTAMP”, and not “ON_GPS”. So
at the end, it is the counter value which will be used for transmission.
- Beacons: beacons transmission time is based on GPS clock, and the
concentrator TX mode is configured as “ON_GPS” for accurate beacon
transmission on GPS PPS event. In this case, the concentrator does not
need the packet counter to be set. But, as the JiT thread decides if a
packet has to be peeked or not, based on its concentrator counter, we need
to have the beacon packet counter set (see next chapter for more details
on JiT scheduling).
We also need to convert a SX1302 counter value to GPS UTC time when we receive
an uplink, in order to fill the “time” field of JSON “rxpk” structure.
### 5.2. TX scheduling
The JiT queue implemented is a static array of nodes, where each node contains:
- the downlink packet, with its type (beacon, downlink class A, B or C)
- a “pre delay” which depends on packet type (BEACON_GUARD, TX_START_DELAY…)
- a “post delay” which depends on packet type (“time on air” of this packet
computed based on its size, datarate and coderate, or BEACON_RESERVED)
Several functions are implemented to manipulate this queue or get info from it:
- init: initialize array with default values
- is full / is empty: gives queue status
- enqueue: checks if the given packet can be queued or not, based on several
criterias
- peek: checks if the queue contains a packet that must be passed
immediately to the concentrator for transmission and returns corresponding
index if any.
- dequeue: actually removes from the queue the packet at index given by peek
function
The queue is always kept sorted on ascending timestamp order.
The JiT thread will regularly check in the JiT queue if there is a packet to be
sent soon. If a packet is matching, it is dequeued and programmed in the
concentrator TX buffer.
### 5.3. Fine tuning parameters
There are few parameters of the JiT queue which could be tweaked to adapt to
different system constraints.
- inc/jitqueue.h:
JIT_QUEUE_MAX: The maximum number of nodes in the queue.
- src/jitqueue.c:
TX_JIT_DELAY: The number of milliseconds a packet is programmed in the
concentrator TX buffer before its actual departure time.
TX_MARGIN_DELAY: Packet collision check margin
6. License
-----------
Copyright (C) 2019, SEMTECH S.A.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Semtech corporation nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL SEMTECH S.A. BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
6. License for Parson library
------------------------------
Parson ( http://kgabis.github.com/parson/ )
Copyright (C) 2012 Krzysztof Gabis
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*EOF*

View File

@ -0,0 +1,458 @@
/*
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(C)2019 Semtech
Description:
LoRa concentrator : Just In Time TX scheduling queue
License: Revised BSD License, see LICENSE.TXT file include in the project
*/
/* -------------------------------------------------------------------------- */
/* --- DEPENDANCIES --------------------------------------------------------- */
#define _GNU_SOURCE /* needed for qsort_r to be defined */
#include <stdlib.h> /* qsort_r */
#include <stdio.h> /* printf, fprintf, snprintf, fopen, fputs */
#include <string.h> /* memset, memcpy */
#include <pthread.h>
#include <assert.h>
#include <math.h>
#include "trace.h"
#include "jitqueue.h"
/* -------------------------------------------------------------------------- */
/* --- PRIVATE MACROS ------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* --- PRIVATE CONSTANTS & TYPES -------------------------------------------- */
#define TX_START_DELAY 1500 /* microseconds */
/* TODO: get this value from HAL? */
#define TX_MARGIN_DELAY 1000 /* Packet overlap margin in microseconds */
/* TODO: How much margin should we take? */
#define TX_JIT_DELAY 30000 /* Pre-delay to program packet for TX in microseconds */
#define TX_MAX_ADVANCE_DELAY ((JIT_NUM_BEACON_IN_QUEUE + 1) * 128 * 1E6) /* Maximum advance delay accepted for a TX packet, compared to current time */
#define BEACON_GUARD 3000000 /* Interval where no ping slot can be placed,
to ensure beacon can be sent */
#define BEACON_RESERVED 2120000 /* Time on air of the beacon, with some margin */
/* -------------------------------------------------------------------------- */
/* --- PRIVATE VARIABLES (GLOBAL) ------------------------------------------- */
static pthread_mutex_t mx_jit_queue = PTHREAD_MUTEX_INITIALIZER; /* control access to JIT queue */
/* -------------------------------------------------------------------------- */
/* --- PRIVATE FUNCTIONS DEFINITION ----------------------------------------- */
/* -------------------------------------------------------------------------- */
/* --- PUBLIC FUNCTIONS DEFINITION ----------------------------------------- */
bool jit_queue_is_full(struct jit_queue_s *queue) {
bool result;
pthread_mutex_lock(&mx_jit_queue);
result = (queue->num_pkt == JIT_QUEUE_MAX)?true:false;
pthread_mutex_unlock(&mx_jit_queue);
return result;
}
bool jit_queue_is_empty(struct jit_queue_s *queue) {
bool result;
pthread_mutex_lock(&mx_jit_queue);
result = (queue->num_pkt == 0)?true:false;
pthread_mutex_unlock(&mx_jit_queue);
return result;
}
void jit_queue_init(struct jit_queue_s *queue) {
int i;
pthread_mutex_lock(&mx_jit_queue);
memset(queue, 0, sizeof(*queue));
for (i=0; i<JIT_QUEUE_MAX; i++) {
queue->nodes[i].pre_delay = 0;
queue->nodes[i].post_delay = 0;
}
pthread_mutex_unlock(&mx_jit_queue);
}
int compare(const void *a, const void *b, void *arg)
{
struct jit_node_s *p = (struct jit_node_s *)a;
struct jit_node_s *q = (struct jit_node_s *)b;
int *counter = (int *)arg;
int p_count, q_count;
p_count = p->pkt.count_us;
q_count = q->pkt.count_us;
if (p_count > q_count)
*counter = *counter + 1;
return p_count - q_count;
}
void jit_sort_queue(struct jit_queue_s *queue) {
int counter = 0;
if (queue->num_pkt == 0) {
return;
}
MSG_DEBUG(DEBUG_JIT, "sorting queue in ascending order packet timestamp - queue size:%u\n", queue->num_pkt);
qsort_r(queue->nodes, queue->num_pkt, sizeof(queue->nodes[0]), compare, &counter);
MSG_DEBUG(DEBUG_JIT, "sorting queue done - swapped:%d\n", counter);
}
bool jit_collision_test(uint32_t p1_count_us, uint32_t p1_pre_delay, uint32_t p1_post_delay, uint32_t p2_count_us, uint32_t p2_pre_delay, uint32_t p2_post_delay) {
if (((p1_count_us - p2_count_us) <= (p1_pre_delay + p2_post_delay + TX_MARGIN_DELAY)) ||
((p2_count_us - p1_count_us) <= (p2_pre_delay + p1_post_delay + TX_MARGIN_DELAY))) {
return true;
} else {
return false;
}
}
enum jit_error_e jit_enqueue(struct jit_queue_s *queue, uint32_t time_us, struct lgw_pkt_tx_s *packet, enum jit_pkt_type_e pkt_type) {
int i = 0;
uint32_t packet_post_delay = 0;
uint32_t packet_pre_delay = 0;
uint32_t target_pre_delay = 0;
enum jit_error_e err_collision;
uint32_t asap_count_us;
MSG_DEBUG(DEBUG_JIT, "Current concentrator time is %u, pkt_type=%d\n", time_us, pkt_type);
if (packet == NULL) {
MSG_DEBUG(DEBUG_JIT_ERROR, "ERROR: invalid parameter\n");
return JIT_ERROR_INVALID;
}
if (jit_queue_is_full(queue)) {
MSG_DEBUG(DEBUG_JIT_ERROR, "ERROR: cannot enqueue packet, JIT queue is full\n");
return JIT_ERROR_FULL;
}
/* Compute packet pre/post delays depending on packet's type */
switch (pkt_type) {
case JIT_PKT_TYPE_DOWNLINK_CLASS_A:
case JIT_PKT_TYPE_DOWNLINK_CLASS_B:
case JIT_PKT_TYPE_DOWNLINK_CLASS_C:
packet_pre_delay = TX_START_DELAY + TX_JIT_DELAY;
packet_post_delay = lgw_time_on_air(packet) * 1000UL; /* in us */
break;
case JIT_PKT_TYPE_BEACON:
/* As defined in LoRaWAN spec */
packet_pre_delay = TX_START_DELAY + BEACON_GUARD + TX_JIT_DELAY;
packet_post_delay = BEACON_RESERVED;
break;
default:
break;
}
pthread_mutex_lock(&mx_jit_queue);
/* An immediate downlink becomes a timestamped downlink "ASAP" */
/* Set the packet count_us to the first available slot */
if (pkt_type == JIT_PKT_TYPE_DOWNLINK_CLASS_C) {
/* change tx_mode to timestamped */
packet->tx_mode = TIMESTAMPED;
/* Search for the ASAP timestamp to be given to the packet */
asap_count_us = time_us + 1E6; /* TODO: Take 1 second margin, to be refined */
if (queue->num_pkt == 0) {
/* If the jit queue is empty, we can insert this packet */
MSG_DEBUG(DEBUG_JIT, "DEBUG: insert IMMEDIATE downlink, first in JiT queue (count_us=%u)\n", asap_count_us);
} else {
/* Else we can try to insert it:
- ASAP meaning NOW + MARGIN
- at the last index of the queue
- between 2 downlinks in the queue
*/
/* First, try if the ASAP time collides with an already enqueued downlink */
for (i=0; i<queue->num_pkt; i++) {
if (jit_collision_test(asap_count_us, packet_pre_delay, packet_post_delay, queue->nodes[i].pkt.count_us, queue->nodes[i].pre_delay, queue->nodes[i].post_delay) == true) {
MSG_DEBUG(DEBUG_JIT, "DEBUG: cannot insert IMMEDIATE downlink at count_us=%u, collides with %u (index=%d)\n", asap_count_us, queue->nodes[i].pkt.count_us, i);
break;
}
}
if (i == queue->num_pkt) {
/* No collision with ASAP time, we can insert it */
MSG_DEBUG(DEBUG_JIT, "DEBUG: insert IMMEDIATE downlink ASAP at %u (no collision)\n", asap_count_us);
} else {
/* Search for the best slot then */
for (i=0; i<queue->num_pkt; i++) {
asap_count_us = queue->nodes[i].pkt.count_us + queue->nodes[i].post_delay + packet_pre_delay + TX_JIT_DELAY + TX_MARGIN_DELAY;
if (i == (queue->num_pkt - 1)) {
/* Last packet index, we can insert after this one */
MSG_DEBUG(DEBUG_JIT, "DEBUG: insert IMMEDIATE downlink, last in JiT queue (count_us=%u)\n", asap_count_us);
} else {
/* Check if packet can be inserted between this index and the next one */
MSG_DEBUG(DEBUG_JIT, "DEBUG: try to insert IMMEDIATE downlink (count_us=%u) between index %d and index %d?\n", asap_count_us, i, i+1);
if (jit_collision_test(asap_count_us, packet_pre_delay, packet_post_delay, queue->nodes[i+1].pkt.count_us, queue->nodes[i+1].pre_delay, queue->nodes[i+1].post_delay) == true) {
MSG_DEBUG(DEBUG_JIT, "DEBUG: failed to insert IMMEDIATE downlink (count_us=%u), continue...\n", asap_count_us);
continue;
} else {
MSG_DEBUG(DEBUG_JIT, "DEBUG: insert IMMEDIATE downlink (count_us=%u)\n", asap_count_us);
break;
}
}
}
}
}
/* Set packet with ASAP timestamp */
packet->count_us = asap_count_us;
}
/* Check criteria_1: is it already too late to send this packet ?
* The packet should arrive at least at (tmst - TX_START_DELAY) to be programmed into concentrator
* Note: - Also add some margin, to be checked how much is needed, if needed
* - Valid for both Downlinks and Beacon packets
*
* Warning: unsigned arithmetic (handle roll-over)
* t_packet < t_current + TX_START_DELAY + MARGIN
*/
if ((packet->count_us - time_us) <= (TX_START_DELAY + TX_MARGIN_DELAY + TX_JIT_DELAY)) {
MSG_DEBUG(DEBUG_JIT_ERROR, "ERROR: Packet REJECTED, already too late to send it (current=%u, packet=%u, type=%d)\n", time_us, packet->count_us, pkt_type);
pthread_mutex_unlock(&mx_jit_queue);
return JIT_ERROR_TOO_LATE;
}
/* Check criteria_2: Does packet timestamp seem plausible compared to current time
* We do not expect the server to program a downlink too early compared to current time
* Class A: downlink has to be sent in a 1s or 2s time window after RX
* Class B: downlink has to occur in a 128s time window
* Class C: no check needed, departure time has been calculated previously
* So let's define a safe delay above which we can say that the packet is out of bound: TX_MAX_ADVANCE_DELAY
* Note: - Valid for Downlinks only, not for Beacon packets
*
* Warning: unsigned arithmetic (handle roll-over)
t_packet > t_current + TX_MAX_ADVANCE_DELAY
*/
if ((pkt_type == JIT_PKT_TYPE_DOWNLINK_CLASS_A) || (pkt_type == JIT_PKT_TYPE_DOWNLINK_CLASS_B)) {
if ((packet->count_us - time_us) > TX_MAX_ADVANCE_DELAY) {
MSG_DEBUG(DEBUG_JIT_ERROR, "ERROR: Packet REJECTED, timestamp seems wrong, too much in advance (current=%u, packet=%u, type=%d)\n", time_us, packet->count_us, pkt_type);
pthread_mutex_unlock(&mx_jit_queue);
return JIT_ERROR_TOO_EARLY;
}
}
/* Check criteria_3: does this new packet overlap with a packet already enqueued ?
* Note: - need to take into account packet's pre_delay and post_delay of each packet
* - Valid for both Downlinks and beacon packets
* - Beacon guard can be ignored if we try to queue a Class A downlink
*/
for (i=0; i<queue->num_pkt; i++) {
/* We ignore Beacon Guard for Class A/C downlinks */
if (((pkt_type == JIT_PKT_TYPE_DOWNLINK_CLASS_A) || (pkt_type == JIT_PKT_TYPE_DOWNLINK_CLASS_C)) && (queue->nodes[i].pkt_type == JIT_PKT_TYPE_BEACON)) {
target_pre_delay = TX_START_DELAY;
} else {
target_pre_delay = queue->nodes[i].pre_delay;
}
/* Check if there is a collision
* Warning: unsigned arithmetic (handle roll-over)
* t_packet_new - pre_delay_packet_new < t_packet_prev + post_delay_packet_prev (OVERLAP on post delay)
* t_packet_new + post_delay_packet_new > t_packet_prev - pre_delay_packet_prev (OVERLAP on pre delay)
*/
if (jit_collision_test(packet->count_us, packet_pre_delay, packet_post_delay, queue->nodes[i].pkt.count_us, target_pre_delay, queue->nodes[i].post_delay) == true) {
switch (queue->nodes[i].pkt_type) {
case JIT_PKT_TYPE_DOWNLINK_CLASS_A:
case JIT_PKT_TYPE_DOWNLINK_CLASS_B:
case JIT_PKT_TYPE_DOWNLINK_CLASS_C:
MSG_DEBUG(DEBUG_JIT_ERROR, "ERROR: Packet (type=%d) REJECTED, collision with packet already programmed at %u (%u)\n", pkt_type, queue->nodes[i].pkt.count_us, packet->count_us);
err_collision = JIT_ERROR_COLLISION_PACKET;
break;
case JIT_PKT_TYPE_BEACON:
if (pkt_type != JIT_PKT_TYPE_BEACON) {
/* do not overload logs for beacon/beacon collision, as it is expected to happen with beacon pre-scheduling algorith used */
MSG_DEBUG(DEBUG_JIT_ERROR, "ERROR: Packet (type=%d) REJECTED, collision with beacon already programmed at %u (%u)\n", pkt_type, queue->nodes[i].pkt.count_us, packet->count_us);
}
err_collision = JIT_ERROR_COLLISION_BEACON;
break;
default:
MSG("ERROR: Unknown packet type, should not occur, BUG?\n");
assert(0);
break;
}
pthread_mutex_unlock(&mx_jit_queue);
return err_collision;
}
}
/* Finally enqueue it */
/* Insert packet at the end of the queue */
memcpy(&(queue->nodes[queue->num_pkt].pkt), packet, sizeof(struct lgw_pkt_tx_s));
queue->nodes[queue->num_pkt].pre_delay = packet_pre_delay;
queue->nodes[queue->num_pkt].post_delay = packet_post_delay;
queue->nodes[queue->num_pkt].pkt_type = pkt_type;
if (pkt_type == JIT_PKT_TYPE_BEACON) {
queue->num_beacon++;
}
queue->num_pkt++;
/* Sort the queue in ascending order of packet timestamp */
jit_sort_queue(queue);
/* Done */
pthread_mutex_unlock(&mx_jit_queue);
jit_print_queue(queue, false, DEBUG_JIT);
MSG_DEBUG(DEBUG_JIT, "enqueued packet with count_us=%u (size=%u bytes, toa=%u us, type=%u)\n", packet->count_us, packet->size, packet_post_delay, pkt_type);
return JIT_ERROR_OK;
}
enum jit_error_e jit_dequeue(struct jit_queue_s *queue, int index, struct lgw_pkt_tx_s *packet, enum jit_pkt_type_e *pkt_type) {
if (packet == NULL) {
MSG("ERROR: invalid parameter\n");
return JIT_ERROR_INVALID;
}
if ((index < 0) || (index >= JIT_QUEUE_MAX)) {
MSG("ERROR: invalid parameter\n");
return JIT_ERROR_INVALID;
}
if (jit_queue_is_empty(queue)) {
MSG("ERROR: cannot dequeue packet, JIT queue is empty\n");
return JIT_ERROR_EMPTY;
}
pthread_mutex_lock(&mx_jit_queue);
/* Dequeue requested packet */
memcpy(packet, &(queue->nodes[index].pkt), sizeof(struct lgw_pkt_tx_s));
queue->num_pkt--;
*pkt_type = queue->nodes[index].pkt_type;
if (*pkt_type == JIT_PKT_TYPE_BEACON) {
queue->num_beacon--;
MSG_DEBUG(DEBUG_BEACON, "--- Beacon dequeued ---\n");
}
/* Replace dequeued packet with last packet of the queue */
memcpy(&(queue->nodes[index]), &(queue->nodes[queue->num_pkt]), sizeof(struct jit_node_s));
memset(&(queue->nodes[queue->num_pkt]), 0, sizeof(struct jit_node_s));
/* Sort queue in ascending order of packet timestamp */
jit_sort_queue(queue);
/* Done */
pthread_mutex_unlock(&mx_jit_queue);
jit_print_queue(queue, false, DEBUG_JIT);
MSG_DEBUG(DEBUG_JIT, "dequeued packet with count_us=%u from index %d\n", packet->count_us, index);
return JIT_ERROR_OK;
}
enum jit_error_e jit_peek(struct jit_queue_s *queue, uint32_t time_us, int *pkt_idx) {
/* Return index of node containing a packet inline with given time */
int i = 0;
int idx_highest_priority = -1;
if (pkt_idx == NULL) {
MSG("ERROR: invalid parameter\n");
return JIT_ERROR_INVALID;
}
if (jit_queue_is_empty(queue)) {
return JIT_ERROR_EMPTY;
}
pthread_mutex_lock(&mx_jit_queue);
/* Search for highest priority packet to be sent */
for (i=0; i<queue->num_pkt; i++) {
/* First check if that packet is outdated:
* If a packet seems too much in advance, and was not rejected at enqueue time,
* it means that we missed it for peeking, we need to drop it
*
* Warning: unsigned arithmetic
* t_packet > t_current + TX_MAX_ADVANCE_DELAY
*/
if ((queue->nodes[i].pkt.count_us - time_us) >= TX_MAX_ADVANCE_DELAY) {
/* We drop the packet to avoid lock-up */
queue->num_pkt--;
if (queue->nodes[i].pkt_type == JIT_PKT_TYPE_BEACON) {
queue->num_beacon--;
MSG("WARNING: --- Beacon dropped (current_time=%u, packet_time=%u) ---\n", time_us, queue->nodes[i].pkt.count_us);
} else {
MSG("WARNING: --- Packet dropped (current_time=%u, packet_time=%u) ---\n", time_us, queue->nodes[i].pkt.count_us);
}
/* Replace dropped packet with last packet of the queue */
memcpy(&(queue->nodes[i]), &(queue->nodes[queue->num_pkt]), sizeof(struct jit_node_s));
memset(&(queue->nodes[queue->num_pkt]), 0, sizeof(struct jit_node_s));
/* Sort queue in ascending order of packet timestamp */
jit_sort_queue(queue);
/* restart loop after purge to find packet to be sent */
i = 0;
continue;
}
/* Then look for highest priority packet to be sent:
* Warning: unsigned arithmetic (handle roll-over)
* t_packet < t_highest
*/
if ((idx_highest_priority == -1) || (((queue->nodes[i].pkt.count_us - time_us) < (queue->nodes[idx_highest_priority].pkt.count_us - time_us)))) {
idx_highest_priority = i;
}
}
/* Peek criteria 1: look for a packet to be sent in next TX_JIT_DELAY ms timeframe
* Warning: unsigned arithmetic (handle roll-over)
* t_packet < t_current + TX_JIT_DELAY
*/
if ((queue->nodes[idx_highest_priority].pkt.count_us - time_us) < TX_JIT_DELAY) {
*pkt_idx = idx_highest_priority;
MSG_DEBUG(DEBUG_JIT, "peek packet with count_us=%u at index %d\n",
queue->nodes[idx_highest_priority].pkt.count_us, idx_highest_priority);
} else {
*pkt_idx = -1;
}
pthread_mutex_unlock(&mx_jit_queue);
return JIT_ERROR_OK;
}
void jit_print_queue(struct jit_queue_s *queue, bool show_all, int debug_level) {
int i = 0;
int loop_end;
if (jit_queue_is_empty(queue)) {
MSG_DEBUG(debug_level, "INFO: [jit] queue is empty\n");
} else {
pthread_mutex_lock(&mx_jit_queue);
MSG_DEBUG(debug_level, "INFO: [jit] queue contains %d packets:\n", queue->num_pkt);
MSG_DEBUG(debug_level, "INFO: [jit] queue contains %d beacons:\n", queue->num_beacon);
loop_end = (show_all == true) ? JIT_QUEUE_MAX : queue->num_pkt;
for (i=0; i<loop_end; i++) {
MSG_DEBUG(debug_level, " - node[%d]: count_us=%u - type=%d\n",
i,
queue->nodes[i].pkt.count_us,
queue->nodes[i].pkt_type);
}
pthread_mutex_unlock(&mx_jit_queue);
}
}

File diff suppressed because it is too large Load Diff

194
readme.md
View File

@ -0,0 +1,194 @@
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(C)2019 Semtech
SX1302 LoRa Gateway project
===========================
## 1. Core library: libloragw
This directory contains the sources of the library to build a gateway based on
a Semtech LoRa SX1302 concentrator chip (a.k.a. concentrator).
Once compiled all the code is contained in the libloragw.a file that will be
statically linked (ie. integrated in the final executable).
The library also comes with few basic tests programs that are used to test the
different sub-modules of the library.
Please refer to the readme.md file located in the libloragw directory for
more details.
## 2. Helper programs
Those programs are included in the project to provide examples on how to use
the HAL library, and to help the system builder test different parts of it.
### 2.1. packet_frowarder ###
The packet forwarder is a program running on the host of a Lora gateway that
forwards RF packets receive by the concentrator to a server through a IP/UDP
link, and emits RF packets that are sent by the server.
((( Y )))
|
|
+- -|- - - - - - - - - - - - -+ xxxxxxxxxxxx +--------+
|+--+-----------+ +------+| xx x x xxx | |
|| | | || xx Internet xx | |
|| Concentrator |<----+ Host |<------xx or xx-------->| |
|| | SPI | || xx Intranet xx | Server |
|+--------------+ +------+| xxxx x xxxx | |
| ^ ^ | xxxxxxxx | |
| | PPS +-----+ NMEA | | | |
| +------| GPS |-------+ | +--------+
| +-----+ |
| |
| Gateway |
+- - - - - - - - - - - - - - -+
Uplink: radio packets received by the gateway, with metadata added by the
gateway, forwarded to the server. Might also include gateway status.
Downlink: packets generated by the server, with additional metadata, to be
transmitted by the gateway on the radio channel. Might also include
configuration data for the gateway.
Please refer to the readme.md file located in the packet_forwarder directory
for more details.
### 2.2. util_net_downlink ###
The downlink packet sender is a simple helper program listening on a single
UDP port, responding to PUSH_DATA and PULL_DATA datagrams with proper ACK, and
sending downlink JSON packets to the socket, with given frame parameters, at
regular time interval.
It is a network packet sender.
It can also be used as a UDP packet logger to store received uplinks in a
local CSV file.
Please refer to the readme.md file located in the util_net_downlink directory
for more details.
## 3. Helper scripts
### 3.1. tools/reset_lgw.sh
This script is used to perform the basic initialization of the SX1302 through
the GPIOs defined by the CoreCell reference design.
It gets the SX1302 out of reset and set the Power Enable pin.
This script is called by every program provided here which accesses the SX1302.
It MUST be located in the same directory as the executable of the program.
## 4. Compile, install and run instructions
All the libraries and test programs can be compiled and installed from the
root directory of this project.
### 4.1. Clean and compile everything
`make clean all`
### 4.2. Install executables and associated files in one directory
First edit the target.cfg file located in the root directory of the project
in order to configure where the executables have to be installed.
`TARGET_IP` : sets the IP address of the host of the gateway. In case the
project is compiled on the gateway host itself (Raspberry Pi...), this can
be left set to `localhost`.
`TARGET_DIR` : sets the directory on the gateway host file system in which
the executables must be copied. Note that the directory MUST exist when
invoking the install command.
`TARGET_USR` : sets the linux user to be used to perform the SSH/SCP command
for copying the executables.
In order to avoid entering the user password when installing the files, the
following steps have to be followed.
Lets say you want to copy between two hosts host_src and host_dest (they can
be the same). host_src is the host where you would run the scp command,
irrespective of the direction of the file copy!
* On host_src, run this command as the user that runs scp<br/>
`ssh-keygen -t rsa`
This will prompt for a passphrase. Just press the enter key. It'll then
generate an identification (private key) and a public key. Do not ever share
the private key with anyone! ssh-keygen shows where it saved the public key.
This is by default ~/.ssh/id_rsa.pub
* Transfer the id_rsa.pub file to host_dest<br/>
`ssh-copy-id -i ~/.ssh/id_rsa.pub user@host_dest`
You should be able to log on host_dest without being asked for a password.
Now that everything is set, the following command can be invoked:<br/>
`make install`
In order to also install the packet forwarder JSON configuration files:<br/>
`make install_conf`
### 4.3. Cross-compile from a PC
* Add the path to the binaries of the compiler corresponding to the target
platform to the `PATH` environment variable.
* set the `ARCH` environment variable to `arm`.
* set the `CROSS_COMPILE` environment variable to the prefix corresponding to
the compiler for the target platform.
An example for a Raspberry Pi target:
* `export PATH=[path]/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin`
* `export ARCH=arm`
* `export CROSS_COMPILE=arm-linux-gnueabihf-`
Then, from the same console where the previous environment variables have been
set, do:
`make clean all`
## 5. Third party libraries
This project relies on several third-party open source libraries, that can be
found in the `libtools` directory.
* parson: a JSON parser (http://kgabis.github.com/parson/)
* tinymt32: a pseudo-random generator (only used for debug/test)
## 6. Changelog
### v1.0.0 ###
* HAL: Initial official release for SX1302 CoreCell Reference Design.
### v0.0.1 ###
* HAL: Initial private release for TAP program
## 7. Legal notice
The information presented in this project documentation does not form part of
any quotation or contract, is believed to be accurate and reliable and may be
changed without notice. No liability will be accepted by the publisher for any
consequence of its use. Publication thereof does not convey nor imply any
license under patent or other industrial or intellectual property rights.
Semtech assumes no responsibility or liability whatsoever for any failure or
unexpected operation resulting from misuse, neglect improper installation,
repair or improper handling or unusual physical or electrical stress
including, but not limited to, exposure to parameters beyond the specified
maximum ratings or operation outside the specified range.
SEMTECH PRODUCTS ARE NOT DESIGNED, INTENDED, AUTHORIZED OR WARRANTED TO BE
SUITABLE FOR USE IN LIFE-SUPPORT APPLICATIONS, DEVICES OR SYSTEMS OR OTHER
CRITICAL APPLICATIONS. INCLUSION OF SEMTECH PRODUCTS IN SUCH APPLICATIONS IS
UNDERSTOOD TO BE UNDERTAKEN SOLELY AT THE CUSTOMER'S OWN RISK. Should a
customer purchase or use Semtech products for any such unauthorized
application, the customer shall indemnify and hold Semtech and its officers,
employees, subsidiaries, affiliates, and distributors harmless against all
claims, costs damages and attorney fees which could arise.
*EOF*

10
target.cfg Normal file
View File

@ -0,0 +1,10 @@
# That file will be included in the Makefiles to configure where to install files on the target
# The IP address of the gateway host on which the files need to be installed
TARGET_IP=localhost
# The directory on the gateway file system to which the files need to be installed
TARGET_DIR=/home/pi/sx1302_hal/bin
# The user to be used by ssh/scp to copy the files on the gateway host
TARGET_USR=pi

View File

@ -0,0 +1,930 @@
[
{
"id": "bf64d63f.630868",
"type": "tab",
"label": "SX1302 register C header",
"disabled": false,
"info": ""
},
{
"id": "d60daf5.ed6835",
"type": "file in",
"z": "bf64d63f.630868",
"name": "",
"filename": "/home/mcoracin/SHARE/sx1302_reg/common.json",
"format": "utf8",
"chunk": false,
"sendError": true,
"x": 390,
"y": 160,
"wires": [
[
"ab571926.a8a0a8"
]
]
},
{
"id": "ab571926.a8a0a8",
"type": "json",
"z": "bf64d63f.630868",
"name": "",
"property": "payload",
"action": "",
"pretty": false,
"x": 660,
"y": 160,
"wires": [
[
"2efaa7ec.1e3158"
]
]
},
{
"id": "3e749686.017e32",
"type": "inject",
"z": "bf64d63f.630868",
"name": "common",
"topic": "common",
"payload": "true",
"payloadType": "bool",
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"x": 110,
"y": 160,
"wires": [
[
"d60daf5.ed6835"
]
]
},
{
"id": "a8c02e96.c069",
"type": "inject",
"z": "bf64d63f.630868",
"name": "Init",
"topic": "",
"payload": "true",
"payloadType": "bool",
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"x": 100,
"y": 40,
"wires": [
[
"13cca583.8beb3a"
]
]
},
{
"id": "13cca583.8beb3a",
"type": "function",
"z": "bf64d63f.630868",
"name": "Init global variables",
"func": "var reg_c = 'const struct lgw_reg_s loregs[LGW_TOTALREGS+1] = {' + '\\n';\n\nvar reg_desc;\nreg_desc = '\\n/* -------------------------------------------------------------------------- */\\n';\nreg_desc += '/* --- REGISTER DESCRIPTIONS ------------------------------------------------\\n';\n\nflow.set('tx_top_macro', '');\nflow.set('reg_h', '');\nflow.set('reg_c', reg_c);\nflow.set('reg_desc', reg_desc);\nflow.set('reg_count', 0);\n\nreturn msg;",
"outputs": 1,
"noerr": 0,
"x": 290,
"y": 40,
"wires": [
[
"d040198e.3a7208"
]
]
},
{
"id": "d040198e.3a7208",
"type": "debug",
"z": "bf64d63f.630868",
"name": "",
"active": false,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"x": 500,
"y": 40,
"wires": []
},
{
"id": "12128a9a.ea5e35",
"type": "file in",
"z": "bf64d63f.630868",
"name": "",
"filename": "/home/mcoracin/SHARE/sx1302_reg/agc_mcu.json",
"format": "utf8",
"chunk": false,
"sendError": true,
"x": 390,
"y": 220,
"wires": [
[
"d6c34db.5471fb"
]
]
},
{
"id": "d6c34db.5471fb",
"type": "json",
"z": "bf64d63f.630868",
"name": "",
"property": "payload",
"action": "",
"pretty": false,
"x": 660,
"y": 220,
"wires": [
[
"2efaa7ec.1e3158"
]
]
},
{
"id": "c7ba4afe.f6229",
"type": "inject",
"z": "bf64d63f.630868",
"name": "agc_mcu",
"topic": "agc_mcu",
"payload": "true",
"payloadType": "bool",
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"x": 110,
"y": 220,
"wires": [
[
"12128a9a.ea5e35"
]
]
},
{
"id": "917cf46c.ceaa48",
"type": "inject",
"z": "bf64d63f.630868",
"name": "Create files",
"topic": "",
"payload": "true",
"payloadType": "bool",
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"x": 110,
"y": 940,
"wires": [
[
"67447ab8.c38edc"
]
]
},
{
"id": "67447ab8.c38edc",
"type": "function",
"z": "bf64d63f.630868",
"name": "Write header file",
"func": "var reg_count = flow.get('reg_count') || 0;\n\nvar reg_h = flow.get('reg_h') || '';\nreg_h += '\\n#define LGW_TOTALREGS ' + reg_count + '\\n';\n\nvar reg_c = flow.get('reg_c') || '';\nreg_c += ' {0,0,0,0,0,0,0,0}\\n'\nreg_c += '};';\n\nvar reg_desc = flow.get('reg_desc') || '';\nreg_desc += \"\\n*/\";\n\nvar tx_top_macro = flow.get('tx_top_macro') || '';\n\n// write all to file\nvar file = reg_h + '\\n' + tx_top_macro + '\\n' + reg_c + '\\n' + reg_desc;\nmsg.payload = file;\n\nreturn msg;",
"outputs": 1,
"noerr": 0,
"x": 290,
"y": 940,
"wires": [
[
"b1b0d974.6ae61"
]
]
},
{
"id": "b1b0d974.6ae61",
"type": "file",
"z": "bf64d63f.630868",
"name": "",
"filename": "/home/mcoracin/SHARE/sx1302_reg/sx1302_reg.h",
"appendNewline": true,
"createDir": false,
"overwriteFile": "true",
"x": 620,
"y": 940,
"wires": []
},
{
"id": "aae4ac70.472438",
"type": "debug",
"z": "bf64d63f.630868",
"name": "",
"active": false,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"x": 1160,
"y": 280,
"wires": []
},
{
"id": "1450f6f8.1be259",
"type": "file in",
"z": "bf64d63f.630868",
"name": "",
"filename": "/home/mcoracin/SHARE/sx1302_reg/rif_top_mux.json",
"format": "utf8",
"chunk": false,
"sendError": true,
"x": 420,
"y": 100,
"wires": [
[
"5c712585.37aeec"
]
]
},
{
"id": "5c712585.37aeec",
"type": "json",
"z": "bf64d63f.630868",
"name": "",
"property": "payload",
"action": "",
"pretty": false,
"x": 700,
"y": 100,
"wires": [
[
"8bbc1d63.391468"
]
]
},
{
"id": "a48ebe9c.34b028",
"type": "inject",
"z": "bf64d63f.630868",
"name": "rif_top_mux",
"topic": "",
"payload": "true",
"payloadType": "bool",
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"x": 120,
"y": 100,
"wires": [
[
"1450f6f8.1be259"
]
]
},
{
"id": "8bbc1d63.391468",
"type": "function",
"z": "bf64d63f.630868",
"name": "JSON Parse Base Addresses",
"func": "var reg_h = flow.get('reg_h') || '';\n\nvar prefix = 'SX1302_REG_';\nvar postfix = '_BASE_ADDR';\nvar line = '';\n\nfor(var item in msg.payload.map)\n{\n var base_addr = msg.payload.map[item].addr;\n line += '#define ' + prefix + item.toUpperCase() + postfix + ' 0x' + base_addr.toString(16) + '\\n'\n}\n//console.log(line);\n\nreg_h += line + '\\n';\n\nflow.set('reg_h', reg_h);\n\nreturn msg;",
"outputs": 1,
"noerr": 0,
"x": 920,
"y": 100,
"wires": [
[
"9494d0f2.75b628"
]
]
},
{
"id": "9494d0f2.75b628",
"type": "debug",
"z": "bf64d63f.630868",
"name": "",
"active": false,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"x": 1160,
"y": 100,
"wires": []
},
{
"id": "2d9b9bf2.6b3bb4",
"type": "file in",
"z": "bf64d63f.630868",
"name": "",
"filename": "/home/mcoracin/SHARE/sx1302_reg/clk_ctrl.json",
"format": "utf8",
"chunk": false,
"sendError": true,
"x": 380,
"y": 280,
"wires": [
[
"7efa17e6.f8b68"
]
]
},
{
"id": "7efa17e6.f8b68",
"type": "json",
"z": "bf64d63f.630868",
"name": "",
"property": "payload",
"action": "",
"pretty": false,
"x": 660,
"y": 280,
"wires": [
[
"2efaa7ec.1e3158"
]
]
},
{
"id": "5883ebc9.2fede4",
"type": "inject",
"z": "bf64d63f.630868",
"name": "clk_ctrl",
"topic": "clk_ctrl",
"payload": "true",
"payloadType": "bool",
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"x": 100,
"y": 280,
"wires": [
[
"2d9b9bf2.6b3bb4"
]
]
},
{
"id": "54a46dd.945aa14",
"type": "file in",
"z": "bf64d63f.630868",
"name": "",
"filename": "/home/mcoracin/SHARE/sx1302_reg/tx_top.json",
"format": "utf8",
"chunk": false,
"sendError": true,
"x": 380,
"y": 340,
"wires": [
[
"5ae327f2.618788"
]
]
},
{
"id": "5ae327f2.618788",
"type": "json",
"z": "bf64d63f.630868",
"name": "",
"property": "payload",
"action": "",
"pretty": false,
"x": 660,
"y": 340,
"wires": [
[
"2efaa7ec.1e3158"
]
]
},
{
"id": "ca4d9728.057d68",
"type": "inject",
"z": "bf64d63f.630868",
"name": "tx_top_a",
"topic": "tx_top_a",
"payload": "true",
"payloadType": "bool",
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"x": 110,
"y": 340,
"wires": [
[
"54a46dd.945aa14"
]
]
},
{
"id": "50b73755.13ee",
"type": "file in",
"z": "bf64d63f.630868",
"name": "",
"filename": "/home/mcoracin/SHARE/sx1302_reg/tx_top.json",
"format": "utf8",
"chunk": false,
"sendError": true,
"x": 380,
"y": 400,
"wires": [
[
"ce78d3ce.e6bf18"
]
]
},
{
"id": "ce78d3ce.e6bf18",
"type": "json",
"z": "bf64d63f.630868",
"name": "",
"property": "payload",
"action": "",
"pretty": false,
"x": 660,
"y": 400,
"wires": [
[
"2efaa7ec.1e3158"
]
]
},
{
"id": "4791c4e5.60b12c",
"type": "inject",
"z": "bf64d63f.630868",
"name": "tx_top_b",
"topic": "tx_top_b",
"payload": "true",
"payloadType": "bool",
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"x": 110,
"y": 400,
"wires": [
[
"50b73755.13ee"
]
]
},
{
"id": "2efaa7ec.1e3158",
"type": "function",
"z": "bf64d63f.630868",
"name": "JSON to C header",
"func": "var reg_h = flow.get('reg_h') || '';\nvar reg_c = flow.get('reg_c') || '';\nvar reg_desc = flow.get('reg_desc') || '';\nvar reg_count = flow.get('reg_count') || 0;\nvar tx_top_macro = flow.get('tx_top_macro') || '';\n\nvar rif = msg.topic;\nvar prefix = 'SX1302_REG_';\nvar base_addr = prefix + rif.toUpperCase() + '_BASE_ADDR';\n\n// register definition\nvar page = '';\nvar addr = '';\nvar offs = '';\nvar sign = '';\nvar leng = '';\nvar rdon = '';\nvar dflt = '';\nvar chck = '';\nvar reg_comment = '';\nvar desc = '';\nvar flags;\n\nfor(var register in msg.payload)\n{\n //console.log(register);\n for(var field in msg.payload[register].fields)\n {\n //console.log(' ' + field);\n page = '0';\n addr = base_addr + '+' + msg.payload[register].addr;\n offs = msg.payload[register].fields[field].pos;\n if(msg.payload[register].fields[field].hasOwnProperty('signed'))\n {\n console.log(msg.payload[register].fields[field].signed);\n if(msg.payload[register].fields[field].signed === true)\n {\n sign = '1';\n }\n else\n {\n sign = '0';\n }\n }\n else\n {\n sign = '0';\n }\n leng = msg.payload[register].fields[field].width;\n rdon = (msg.payload[register].fields[field].readonly === false) ? '0' : '1';\n dflt = msg.payload[register].fields[field].value;\n flags = msg.payload[register].fields[field].flags;\n desc = msg.payload[register].fields[field].desc;\n //desc = desc.replace(/(?:\\r\\n|\\r|\\n)/g, ' / ');\n if (flags.length > 0)\n {\n for (var i_flag of flags) {\n console.log(i_flag);\n }\n //if(['pulse','w0clr','w1clr','interrupt'].includes(flags))\n if (flags.includes('pulse') || flags.includes('w0clr') || flags.includes('w1clr') || flags.includes('interrupt'))\n {\n console.log('non-checkable register');\n check = 0;\n }\n else\n {\n check = 1;\n }\n }\n else\n {\n check = 1;\n }\n reg_comment = (rif + '_' + register + '_' + field).toUpperCase();\n\n reg_h += '#define ' + prefix + reg_comment + ' ' + reg_count + '\\n';\n reg_c += ' '; // indent\n reg_c += '{' + page + ',' + addr + ',' + offs + ',' + sign + ',' + leng + ',' + rdon + ',' + check + ',' + dflt + '}, // ' + reg_comment + '\\n';\n reg_desc += '\\n' + reg_comment + ':\\n' + desc + '\\n';\n \n if (msg.topic === 'tx_top_a')\n {\n tx_top_macro += '#define ' + prefix + 'TX_TOP' + '_' + register.toUpperCase() + '_' + field.toUpperCase() + '(rf_chain) ((rf_chain == 0) ? \\\\\\n ' + prefix + 'TX_TOP_A' + '_' + register.toUpperCase() + '_' + field.toUpperCase() + ' : \\\\\\n ' + prefix + 'TX_TOP_B' + '_' + register.toUpperCase() + '_' + field.toUpperCase() + ')\\n';\n }\n \n reg_count += 1;\n }\n}\n\n//console.log(reg_h);\n//console.log(reg_c);\n\nflow.set('reg_count', reg_count);\nflow.set('reg_h', reg_h);\nflow.set('reg_c', reg_c);\nflow.set('reg_desc', reg_desc);\nflow.set('tx_top_macro', tx_top_macro);\n\nreturn msg;",
"outputs": 1,
"noerr": 0,
"x": 940,
"y": 280,
"wires": [
[
"aae4ac70.472438"
]
]
},
{
"id": "ab7535a2.47b2e8",
"type": "file in",
"z": "bf64d63f.630868",
"name": "",
"filename": "/home/mcoracin/SHARE/sx1302_reg/gpio.json",
"format": "utf8",
"chunk": false,
"sendError": true,
"x": 370,
"y": 460,
"wires": [
[
"6017e95e.37841"
]
]
},
{
"id": "6017e95e.37841",
"type": "json",
"z": "bf64d63f.630868",
"name": "",
"property": "payload",
"action": "",
"pretty": false,
"x": 660,
"y": 460,
"wires": [
[
"2efaa7ec.1e3158"
]
]
},
{
"id": "626f34eb.dac1f4",
"type": "inject",
"z": "bf64d63f.630868",
"name": "gpio",
"topic": "gpio",
"payload": "true",
"payloadType": "bool",
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"x": 100,
"y": 460,
"wires": [
[
"ab7535a2.47b2e8"
]
]
},
{
"id": "f6871b6f.167d",
"type": "file in",
"z": "bf64d63f.630868",
"name": "",
"filename": "/home/mcoracin/SHARE/sx1302_reg/timestamp.json",
"format": "utf8",
"chunk": false,
"sendError": true,
"x": 390,
"y": 520,
"wires": [
[
"d1c6c905.bbf6a"
]
]
},
{
"id": "d1c6c905.bbf6a",
"type": "json",
"z": "bf64d63f.630868",
"name": "",
"property": "payload",
"action": "",
"pretty": false,
"x": 660,
"y": 520,
"wires": [
[
"2efaa7ec.1e3158"
]
]
},
{
"id": "128c968f.34cc09",
"type": "inject",
"z": "bf64d63f.630868",
"name": "timestamp",
"topic": "timestamp",
"payload": "true",
"payloadType": "bool",
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"x": 110,
"y": 520,
"wires": [
[
"f6871b6f.167d"
]
]
},
{
"id": "38f6ab4d.eba4b4",
"type": "file in",
"z": "bf64d63f.630868",
"name": "",
"filename": "/home/mcoracin/SHARE/sx1302_reg/rx_top.json",
"format": "utf8",
"chunk": false,
"sendError": true,
"x": 380,
"y": 580,
"wires": [
[
"f464e634.1500a8"
]
]
},
{
"id": "f464e634.1500a8",
"type": "json",
"z": "bf64d63f.630868",
"name": "",
"property": "payload",
"action": "",
"pretty": false,
"x": 660,
"y": 580,
"wires": [
[
"2efaa7ec.1e3158"
]
]
},
{
"id": "4dd5087.c34c178",
"type": "inject",
"z": "bf64d63f.630868",
"name": "rx_top",
"topic": "rx_top",
"payload": "true",
"payloadType": "bool",
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"x": 100,
"y": 580,
"wires": [
[
"38f6ab4d.eba4b4"
]
]
},
{
"id": "28e533a1.3ef5ac",
"type": "file in",
"z": "bf64d63f.630868",
"name": "",
"filename": "/home/mcoracin/SHARE/sx1302_reg/arb_mcu.json",
"format": "utf8",
"chunk": false,
"sendError": true,
"x": 390,
"y": 640,
"wires": [
[
"3c2109da.3df84e"
]
]
},
{
"id": "3c2109da.3df84e",
"type": "json",
"z": "bf64d63f.630868",
"name": "",
"property": "payload",
"action": "",
"pretty": false,
"x": 660,
"y": 640,
"wires": [
[
"2efaa7ec.1e3158"
]
]
},
{
"id": "c3a46357.bf67c",
"type": "inject",
"z": "bf64d63f.630868",
"name": "arb_mcu",
"topic": "arb_mcu",
"payload": "true",
"payloadType": "bool",
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"x": 110,
"y": 640,
"wires": [
[
"28e533a1.3ef5ac"
]
]
},
{
"id": "b3ed4e4f.fbd358",
"type": "file in",
"z": "bf64d63f.630868",
"name": "",
"filename": "/home/mcoracin/SHARE/sx1302_reg/radio_fe.json",
"format": "utf8",
"chunk": false,
"sendError": true,
"x": 390,
"y": 700,
"wires": [
[
"37f7e33f.14dc6c"
]
]
},
{
"id": "37f7e33f.14dc6c",
"type": "json",
"z": "bf64d63f.630868",
"name": "",
"property": "payload",
"action": "",
"pretty": false,
"x": 660,
"y": 700,
"wires": [
[
"2efaa7ec.1e3158"
]
]
},
{
"id": "e9006ee1.8d154",
"type": "inject",
"z": "bf64d63f.630868",
"name": "radio_fe",
"topic": "radio_fe",
"payload": "true",
"payloadType": "bool",
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"x": 110,
"y": 700,
"wires": [
[
"b3ed4e4f.fbd358"
]
]
},
{
"id": "7c16420a.7b8634",
"type": "file in",
"z": "bf64d63f.630868",
"name": "",
"filename": "/home/mcoracin/SHARE/sx1302_reg/otp.json",
"format": "utf8",
"chunk": false,
"sendError": true,
"x": 360,
"y": 760,
"wires": [
[
"4e1e58e8.d72998"
]
]
},
{
"id": "4e1e58e8.d72998",
"type": "json",
"z": "bf64d63f.630868",
"name": "",
"property": "payload",
"action": "",
"pretty": false,
"x": 650,
"y": 760,
"wires": [
[
"2efaa7ec.1e3158"
]
]
},
{
"id": "cdd63a9c.be8f4",
"type": "inject",
"z": "bf64d63f.630868",
"name": "otp",
"topic": "otp",
"payload": "true",
"payloadType": "bool",
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"x": 100,
"y": 760,
"wires": [
[
"7c16420a.7b8634"
]
]
},
{
"id": "9ac5ddf6.e47048",
"type": "file in",
"z": "bf64d63f.630868",
"name": "",
"filename": "/home/mcoracin/SHARE/sx1302_reg/rx_top_lora_service_fsk.json",
"format": "utf8",
"chunk": false,
"sendError": true,
"x": 500,
"y": 820,
"wires": [
[
"5a5536dc.db687"
]
]
},
{
"id": "5a5536dc.db687",
"type": "json",
"z": "bf64d63f.630868",
"name": "",
"property": "payload",
"action": "",
"pretty": false,
"x": 800,
"y": 820,
"wires": [
[
"2efaa7ec.1e3158"
]
]
},
{
"id": "c5ffdcfa.19ea28",
"type": "inject",
"z": "bf64d63f.630868",
"name": "rx_top_lora_service_fsk",
"topic": "rx_top_lora_service_fsk",
"payload": "true",
"payloadType": "bool",
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"x": 150,
"y": 820,
"wires": [
[
"9ac5ddf6.e47048"
]
]
},
{
"id": "b5897d4d.0628c",
"type": "file in",
"z": "bf64d63f.630868",
"name": "",
"filename": "/home/mcoracin/SHARE/sx1302_reg/capture_ram.json",
"format": "utf8",
"chunk": false,
"sendError": true,
"x": 410,
"y": 880,
"wires": [
[
"753bfa61.c8068c"
]
]
},
{
"id": "753bfa61.c8068c",
"type": "json",
"z": "bf64d63f.630868",
"name": "",
"property": "payload",
"action": "",
"pretty": false,
"x": 680,
"y": 880,
"wires": [
[
"2efaa7ec.1e3158"
]
]
},
{
"id": "6ca97d0.d189b84",
"type": "inject",
"z": "bf64d63f.630868",
"name": "capture_ram",
"topic": "capture_ram",
"payload": "true",
"payloadType": "bool",
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"x": 120,
"y": 880,
"wires": [
[
"b5897d4d.0628c"
]
]
},
{
"id": "56ed912a.91d118",
"type": "trigger",
"z": "bf64d63f.630868",
"op1": "1",
"op2": "0",
"op1type": "val",
"op2type": "val",
"duration": "250",
"extend": "false",
"units": "ms",
"reset": "",
"bytopic": "all",
"name": "",
"x": 1020,
"y": 500,
"wires": [
[]
]
}
]

View File

@ -0,0 +1,61 @@
### get external defined data
include ../../target.cfg
### constant symbols
ARCH ?=
CROSS_COMPILE ?=
CC := $(CROSS_COMPILE)gcc
AR := $(CROSS_COMPILE)ar
CFLAGS := -O2 -Wall -Wextra -std=c99 -I. -I../../libtools/inc
### linking options
LIBS := -ltinymt32
### general build targets
all: payload_crc payload_diff payload_gen
clean:
rm -f payload_crc payload_diff payload_gen
rm -f *.o
install:
ifneq ($(strip $(TARGET_IP)),)
ifneq ($(strip $(TARGET_DIR)),)
ifneq ($(strip $(TARGET_USR)),)
@echo "---- Copying payload tools files to $(TARGET_IP):$(TARGET_DIR)"
@ssh $(TARGET_USR)@$(TARGET_IP) "mkdir -p $(TARGET_DIR)"
@scp payload_crc $(TARGET_USR)@$(TARGET_IP):$(TARGET_DIR)
@scp payload_diff $(TARGET_USR)@$(TARGET_IP):$(TARGET_DIR)
@scp payload_gen $(TARGET_USR)@$(TARGET_IP):$(TARGET_DIR)
else
@echo "ERROR: TARGET_USR is not configured in target.cfg"
endif
else
@echo "ERROR: TARGET_DIR is not configured in target.cfg"
endif
else
@echo "ERROR: TARGET_IP is not configured in target.cfg"
endif
### rules
%.o : %.c
$(CC) -c $(CFLAGS) $< -o $@
### test programs
payload_crc: payload_crc.o
$(CC) $(CFLAGS) -o $@ $^
payload_diff: payload_diff.o
$(CC) $(CFLAGS) -o $@ $^
payload_gen: payload_gen.o
$(CC) $(CFLAGS) -L../../libtools -o $@ $^ $(LIBS)
### EOF

View File

@ -0,0 +1,110 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
/* -------------------------------------------------------------------------- */
/* --- SUBFUNCTIONS DECLARATION --------------------------------------------- */
static void usage(void);
uint16_t sx1302_lora_payload_crc(const uint8_t * data, uint8_t size);
void remove_spaces(char *str);
/* -------------------------------------------------------------------------- */
/* --- MAIN FUNCTION -------------------------------------------------------- */
int main(int argc, char ** argv)
{
int j;
uint8_t payload[255];
uint8_t payload_size;
uint16_t crc;
char hexstr[1024];
if (argc < 2) {
usage();
return -1;
}
/* Get payload hex string from command line */
memcpy(hexstr, argv[1], strlen(argv[1]));
hexstr[strlen(argv[1])] = '\0';
printf("Input hex string: %s\n", hexstr);
/* Remove spaces from the string if any */
remove_spaces(hexstr);
hexstr[strlen(hexstr)] = '\0';
printf("Removing spaces: %s\n", hexstr);
/* Convert hex string to byte array */
payload_size = strlen(hexstr) / 2;
for (j = 0; j < payload_size; j++) {
sscanf(hexstr + 2*j, "%02hhx", &payload[j]);
}
/* Compute CRC */
crc = sx1302_lora_payload_crc(payload, payload_size);
printf("Payload CRC_16: %04X\n", crc);
return 0;
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
void usage(void) {
printf("Missing payload hex string\n");
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
void remove_spaces(char *str)
{
/* To keep track of non-space character count */
int count = 0;
/* Traverse the given string. If current character
is not space, then place it at index 'count++' */
for (int i = 0; str[i]; i++) {
if (str[i] != ' ') {
str[count++] = str[i]; /* here count is incremented */
}
}
str[count] = '\0';
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
void lora_crc16(const char data, int *crc) {
int next = 0;
next = (((data>>0)&1) ^ ((*crc>>12)&1) ^ ((*crc>> 8)&1) ) ;
next += ((((data>>1)&1) ^ ((*crc>>13)&1) ^ ((*crc>> 9)&1) )<<1 ) ;
next += ((((data>>2)&1) ^ ((*crc>>14)&1) ^ ((*crc>>10)&1) )<<2 ) ;
next += ((((data>>3)&1) ^ ((*crc>>15)&1) ^ ((*crc>>11)&1) )<<3 ) ;
next += ((((data>>4)&1) ^ ((*crc>>12)&1) )<<4 ) ;
next += ((((data>>5)&1) ^ ((*crc>>13)&1) ^ ((*crc>>12)&1) ^ ((*crc>> 8)&1))<<5 ) ;
next += ((((data>>6)&1) ^ ((*crc>>14)&1) ^ ((*crc>>13)&1) ^ ((*crc>> 9)&1))<<6 ) ;
next += ((((data>>7)&1) ^ ((*crc>>15)&1) ^ ((*crc>>14)&1) ^ ((*crc>>10)&1))<<7 ) ;
next += ((((*crc>>0)&1) ^ ((*crc>>15)&1) ^ ((*crc>>11)&1) )<<8 ) ;
next += ((((*crc>>1)&1) ^ ((*crc>>12)&1) )<<9 ) ;
next += ((((*crc>>2)&1) ^ ((*crc>>13)&1) )<<10) ;
next += ((((*crc>>3)&1) ^ ((*crc>>14)&1) )<<11) ;
next += ((((*crc>>4)&1) ^ ((*crc>>15)&1) ^ ((*crc>>12)&1) ^ ((*crc>> 8)&1))<<12) ;
next += ((((*crc>>5)&1) ^ ((*crc>>13)&1) ^ ((*crc>> 9)&1) )<<13) ;
next += ((((*crc>>6)&1) ^ ((*crc>>14)&1) ^ ((*crc>>10)&1) )<<14) ;
next += ((((*crc>>7)&1) ^ ((*crc>>15)&1) ^ ((*crc>>11)&1) )<<15) ;
(*crc) = next;
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
uint16_t sx1302_lora_payload_crc(const uint8_t * data, uint8_t size) {
int i;
int crc = 0;
for (i = 0; i < size; i++) {
lora_crc16(data[i], &crc);
}
//printf("CRC16: 0x%02X 0x%02X (%X)\n", (uint8_t)(crc >> 8), (uint8_t)crc, crc);
return (uint16_t)crc;
}

View File

@ -0,0 +1,116 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
/* -------------------------------------------------------------------------- */
/* --- MACROS --------------------------------------------------------------- */
#define TAKE_N_BITS_FROM(b, p, n) (((b) >> (p)) & ((1 << (n)) - 1))
/* -------------------------------------------------------------------------- */
/* --- SUBFUNCTIONS DECLARATION --------------------------------------------- */
static void usage(void);
void remove_spaces(char *str);
/* -------------------------------------------------------------------------- */
/* --- MAIN FUNCTION -------------------------------------------------------- */
int main(int argc, char ** argv)
{
int i, j;
uint8_t payload_a[255];
uint8_t payload_b[255];
uint8_t payload_diff[255];
uint8_t payload_size;
char hexstr[1024];
uint16_t nb_bits_diff = 0;
if (argc < 3) {
usage();
return -1;
}
if (strlen(argv[1]) != strlen(argv[2])) {
printf("ERROR: payloads A & B must have same size\n");
return -1;
}
/* Get payload A hex string from command line */
memcpy(hexstr, argv[1], strlen(argv[1]));
hexstr[strlen(argv[1])] = '\0';
printf("Input hex string: %s\n", hexstr);
/* Remove spaces from the string if any */
remove_spaces(hexstr);
hexstr[strlen(hexstr)] = '\0';
printf("Removing spaces: %s\n", hexstr);
/* Convert hex string to byte array */
payload_size = strlen(hexstr) / 2;
for (j = 0; j < payload_size; j++) {
sscanf(hexstr + 2*j, "%02hhx", &payload_a[j]);
}
/* Get payload B hex string from command line */
memcpy(hexstr, argv[2], strlen(argv[2]));
hexstr[strlen(argv[2])] = '\0';
printf("Input hex string: %s\n", hexstr);
/* Remove spaces from the string if any */
remove_spaces(hexstr);
hexstr[strlen(hexstr)] = '\0';
printf("Removing spaces: %s\n", hexstr);
/* Convert hex string to byte array */
for (j = 0; j < payload_size; j++) {
sscanf(hexstr + 2*j, "%02hhx", &payload_b[j]);
}
/* Count how many bits differs */
printf("Diff: ");
for (j = 0; j < payload_size; j++) {
payload_diff[j] = payload_a[j] ^ payload_b[j];
printf("%02X ", payload_diff[j]);
}
printf("\n");
for (j = 0; j < payload_size; j++) {
for (i = 7; i >= 0; i--) {
printf("%u", TAKE_N_BITS_FROM(payload_diff[j], i, 1));
if (TAKE_N_BITS_FROM(payload_diff[j], i, 1) == 1) {
nb_bits_diff += 1;
}
}
printf(" ");
}
printf("\n");
printf("%u bits flipped\n", nb_bits_diff);
return 0;
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
void usage(void) {
printf("Missing payload hex strings for a & b\n");
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
void remove_spaces(char *str)
{
/* To keep track of non-space character count */
int count = 0;
/* Traverse the given string. If current character
is not space, then place it at index 'count++' */
for (int i = 0; str[i]; i++) {
if (str[i] != ' ') {
str[count++] = str[i]; /* here count is incremented */
}
}
str[count] = '\0';
}

View File

@ -0,0 +1,124 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include "tinymt32.h"
/* -------------------------------------------------------------------------- */
/* --- SUBFUNCTIONS DECLARATION --------------------------------------------- */
static void usage(void);
void remove_spaces(char *str);
/* -------------------------------------------------------------------------- */
/* --- MAIN FUNCTION -------------------------------------------------------- */
int main(int argc, char ** argv)
{
int j;
uint8_t dev_id[4];
uint8_t payload[255];
uint8_t payload_size;
unsigned int packet_cnt;
tinymt32_t tinymt;
char hexstr[32];
if (argc < 4) {
usage();
return -1;
}
/* Get dev_id hex string from command line */
memcpy(hexstr, argv[1], strlen(argv[1]));
hexstr[strlen(argv[1])] = '\0';
/* Remove spaces from the string if any */
remove_spaces(hexstr);
hexstr[strlen(hexstr)] = '\0';
printf("Dev_id: %s\n", hexstr);
/* Convert hex string to byte array */
payload_size = strlen(hexstr) / 2;
for (j = 0; j < 4; j++) {
sscanf(hexstr + 2*j, "%02hhx", &dev_id[j]);
}
/* Get packet count from which generate the random payload */
packet_cnt = atoi(argv[2]);
/* Get packet payload size */
payload_size = (uint8_t)atoi(argv[3]);
/* Initialize the pseudo-random generator */
tinymt.mat1 = 0x8f7011ee;
tinymt.mat2 = 0xfc78ff1f;
tinymt.tmat = 0x3793fdff;
tinymt32_init(&tinymt, packet_cnt);
/* Construct packet */
payload[0] = dev_id[0];
payload[1] = dev_id[1];
payload[2] = dev_id[2];
payload[3] = dev_id[3];
payload[4] = (uint8_t)(packet_cnt >> 24);
payload[5] = (uint8_t)(packet_cnt >> 16);
payload[6] = (uint8_t)(packet_cnt >> 8);
payload[7] = (uint8_t)(packet_cnt >> 0);
for (j = 8; j < payload_size; j++) {
payload[j] = (uint8_t)tinymt32_generate_uint32(&tinymt);
}
for (j = 0; j < payload_size; j++) {
printf("%02X ", payload[j]);
}
printf("\n");
#if 0
for (packet_cnt = 0; packet_cnt < 10; packet_cnt++) {
tinymt32_init(&tinymt, (int)packet_cnt);
payload[0] = 0xCA;
payload[1] = 0xFE;
payload[2] = 0x12;
payload[3] = 0x34;
payload[4] = (uint8_t)(packet_cnt >> 24);
payload[5] = (uint8_t)(packet_cnt >> 16);
payload[6] = (uint8_t)(packet_cnt >> 8);
payload[7] = (uint8_t)(packet_cnt >> 0);
for (j = 8; j < 16; j++) {
payload[j] = (uint8_t)tinymt32_generate_uint32(&tinymt);
}
for (j = 0; j < 16; j++) {
printf("%02X ", payload[j]);
}
printf("\n");
}
#endif
return 0;
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
void usage(void) {
printf("Missing parameters: ./payload_gen dev_id pkt_cnt pkt_size\n");
printf(" dev_id: hex string for 4-bytes dev_id\n");
printf(" pkt_cnt: unsigned int used to initialize the pseudo-random generator\n");
printf(" pkt_size: paylaod size in bytes [0..255]\n");
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
void remove_spaces(char *str)
{
/* To keep track of non-space character count */
int count = 0;
/* Traverse the given string. If current character
is not space, then place it at index 'count++' */
for (int i = 0; str[i]; i++) {
if (str[i] != ' ') {
str[count++] = str[i]; /* here count is incremented */
}
}
str[count] = '\0';
}

70
tools/reset_lgw.sh Executable file
View File

@ -0,0 +1,70 @@
#!/bin/sh
# This script is intended to be used on SX1302 CoreCell platform, it performs
# the following actions:
# - export/unpexort GPIO23 and GPIO18 used to reset the SX1302 chip and to enable the LDOs
#
# Usage examples:
# ./reset_lgw.sh stop
# ./reset_lgw.sh start
# GPIO mapping has to be adapted with HW
#
SX1302_RESET_PIN=23
SX1302_POWER_EN_PIN=18
WAIT_GPIO() {
sleep 0.1
}
init() {
# setup GPIOs
echo "$SX1302_RESET_PIN" > /sys/class/gpio/export; WAIT_GPIO
echo "$SX1302_POWER_EN_PIN" > /sys/class/gpio/export; WAIT_GPIO
# set GPIOs as output
echo "out" > /sys/class/gpio/gpio$SX1302_RESET_PIN/direction; WAIT_GPIO
echo "out" > /sys/class/gpio/gpio$SX1302_POWER_EN_PIN/direction; WAIT_GPIO
}
reset() {
echo "CoreCell reset through GPIO$SX1302_RESET_PIN..."
echo "CoreCell power enable through GPIO$SX1302_POWER_EN_PIN..."
# write output for SX1302 CoreCell power_enable and reset
echo "1" > /sys/class/gpio/gpio$SX1302_POWER_EN_PIN/value; WAIT_GPIO
echo "1" > /sys/class/gpio/gpio$SX1302_RESET_PIN/value; WAIT_GPIO
echo "0" > /sys/class/gpio/gpio$SX1302_RESET_PIN/value; WAIT_GPIO
}
term() {
# cleanup all GPIOs
if [ -d /sys/class/gpio/gpio$SX1302_RESET_PIN ]
then
echo "$SX1302_RESET_PIN" > /sys/class/gpio/unexport; WAIT_GPIO
fi
if [ -d /sys/class/gpio/gpio$SX1302_POWER_EN_PIN ]
then
echo "$SX1302_POWER_EN_PIN" > /sys/class/gpio/unexport; WAIT_GPIO
fi
}
case "$1" in
start)
term # just in case
init
reset
;;
stop)
reset
term
;;
*)
echo "Usage: $0 {start|stop}"
exit 1
;;
esac
exit 0

83
util_chip_id/Makefile Normal file
View File

@ -0,0 +1,83 @@
### get external defined data
include ../target.cfg
### User defined build options
ARCH ?=
CROSS_COMPILE ?=
BUILD_MODE := release
OBJDIR = obj
### ----- AVOID MODIFICATIONS BELLOW ------ AVOID MODIFICATIONS BELLOW ----- ###
ifeq '$(BUILD_MODE)' 'alpha'
$(warning /\/\/\/ Building in 'alpha' mode \/\/\/\)
WARN_CFLAGS :=
OPT_CFLAGS := -O0
DEBUG_CFLAGS := -g
LDFLAGS :=
else ifeq '$(BUILD_MODE)' 'debug'
$(warning /\/\/\/ Building in 'debug' mode \/\/\/\)
WARN_CFLAGS := -Wall -Wextra
OPT_CFLAGS := -O2
DEBUG_CFLAGS := -g
LDFLAGS :=
else ifeq '$(BUILD_MODE)' 'release'
$(warning /\/\/\/ Building in 'release' mode \/\/\/\)
WARN_CFLAGS := -Wall -Wextra
OPT_CFLAGS := -O2 -ffunction-sections -fdata-sections
DEBUG_CFLAGS :=
LDFLAGS := -Wl,--gc-sections
else
$(error BUILD_MODE must be set to either 'alpha', 'debug' or 'release')
endif
### Application-specific variables
APP_NAME := chip_id
APP_LIBS := -lloragw -lm -ltinymt32 -lrt
### Environment constants
LIB_PATH := ../libloragw
### Expand build options
CFLAGS := -std=c99 $(WARN_CFLAGS) $(OPT_CFLAGS) $(DEBUG_CFLAGS)
CC := $(CROSS_COMPILE)gcc
AR := $(CROSS_COMPILE)ar
### General build targets
all: $(APP_NAME)
clean:
rm -f obj/*.o
rm -f $(APP_NAME)
install:
ifneq ($(strip $(TARGET_IP)),)
ifneq ($(strip $(TARGET_DIR)),)
ifneq ($(strip $(TARGET_USR)),)
@echo "---- Copying chip_id files to $(TARGET_IP):$(TARGET_DIR)"
@ssh $(TARGET_USR)@$(TARGET_IP) "mkdir -p $(TARGET_DIR)"
@scp chip_id $(TARGET_USR)@$(TARGET_IP):$(TARGET_DIR)
else
@echo "ERROR: TARGET_USR is not configured in target.cfg"
endif
else
@echo "ERROR: TARGET_DIR is not configured in target.cfg"
endif
else
@echo "ERROR: TARGET_IP is not configured in target.cfg"
endif
$(OBJDIR):
mkdir -p $(OBJDIR)
### Compile main program
$(OBJDIR)/$(APP_NAME).o: src/$(APP_NAME).c | $(OBJDIR)
$(CC) -c $< -o $@ $(CFLAGS) -Iinc -I../libloragw/inc
### Link everything together
$(APP_NAME): $(OBJDIR)/$(APP_NAME).o
$(CC) -L$(LIB_PATH) -L../libtools $^ -o $@ $(LDFLAGS) $(APP_LIBS)
### EOF

53
util_chip_id/readme.md Normal file
View File

@ -0,0 +1,53 @@
______ _
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(C)2019 Semtech
Utility to get SX1302 chip EUI
==============================
## 1. Introduction
This utility configures the SX1302 to be able to retrieve its EUI.
It can then be used as a Gateway ID.
## 2. Command line options
### 2.1. General options ###
`-h`
will display a short help and version informations.
### 2.2. SPI options ###
`-d filename`
use the Linux SPI device driver, but with an explicit path, for systems with
several SPI device drivers, or uncommon numbering scheme.
## 3. Legal notice
The information presented in this project documentation does not form part of
any quotation or contract, is believed to be accurate and reliable and may be
changed without notice. No liability will be accepted by the publisher for any
consequence of its use. Publication thereof does not convey nor imply any
license under patent or other industrial or intellectual property rights.
Semtech assumes no responsibility or liability whatsoever for any failure or
unexpected operation resulting from misuse, neglect improper installation,
repair or improper handling or unusual physical or electrical stress
including, but not limited to, exposure to parameters beyond the specified
maximum ratings or operation outside the specified range.
SEMTECH PRODUCTS ARE NOT DESIGNED, INTENDED, AUTHORIZED OR WARRANTED TO BE
SUITABLE FOR USE IN LIFE-SUPPORT APPLICATIONS, DEVICES OR SYSTEMS OR OTHER
CRITICAL APPLICATIONS. INCLUSION OF SEMTECH PRODUCTS IN SUCH APPLICATIONS IS
UNDERSTOOD TO BE UNDERTAKEN SOLELY AT THE CUSTOMER'S OWN RISK. Should a
customer purchase or use Semtech products for any such unauthorized
application, the customer shall indemnify and hold Semtech and its officers,
employees, subsidiaries, affiliates, and distributors harmless against all
claims, costs damages and attorney fees which could arise.
*EOF*

209
util_chip_id/src/chip_id.c Normal file
View File

@ -0,0 +1,209 @@
/*
______ _
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(C)2019 Semtech
Description:
Utility to get SX1302 chip EUI
License: Revised BSD License, see LICENSE.TXT file include in the project
*/
/* -------------------------------------------------------------------------- */
/* --- DEPENDANCIES --------------------------------------------------------- */
/* fix an issue between POSIX and C99 */
#if __STDC_VERSION__ >= 199901L
#define _XOPEN_SOURCE 600
#else
#define _XOPEN_SOURCE 500
#endif
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <math.h>
#include <signal.h> /* sigaction */
#include <getopt.h> /* getopt_long */
#include "loragw_hal.h"
#include "loragw_reg.h"
#include "loragw_aux.h"
/* -------------------------------------------------------------------------- */
/* --- PRIVATE MACROS ------------------------------------------------------- */
#define RAND_RANGE(min, max) (rand() % (max + 1 - min) + min)
/* -------------------------------------------------------------------------- */
/* --- PRIVATE CONSTANTS ---------------------------------------------------- */
#define LINUXDEV_PATH_DEFAULT "/dev/spidev0.0"
#define DEFAULT_CLK_SRC 0
#define DEFAULT_FREQ_HZ 868500000U
/* -------------------------------------------------------------------------- */
/* --- PRIVATE VARIABLES ---------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* --- PRIVATE FUNCTIONS ---------------------------------------------------- */
/* describe command line options */
void usage(void) {
printf("Library version information: %s\n", lgw_version_info());
printf("Available options:\n");
printf(" -h print this help\n");
printf(" -d [path] Path the spidev file (ex: /dev/spidev0.0)\n");
printf(" -k <uint> Concentrator clock source (Radio A or Radio B) [0..1]\n");
printf(" -r <uint> Radio type (1255, 1257, 1250)\n");
}
/* -------------------------------------------------------------------------- */
/* --- MAIN FUNCTION -------------------------------------------------------- */
int main(int argc, char **argv)
{
int i, x;
unsigned int arg_u;
uint8_t clocksource = 0;
lgw_radio_type_t radio_type = LGW_RADIO_TYPE_SX1250;
struct lgw_conf_board_s boardconf;
struct lgw_conf_rxrf_s rfconf;
uint64_t eui;
/* SPI interfaces */
const char spidev_path_default[] = LINUXDEV_PATH_DEFAULT;
const char * spidev_path = spidev_path_default;
/* Parameter parsing */
int option_index = 0;
static struct option long_options[] = {
{0, 0, 0, 0}
};
/* parse command line options */
while ((i = getopt_long (argc, argv, "hd:", long_options, &option_index)) != -1) {
switch (i) {
case 'h':
usage();
return -1;
break;
case 'd':
spidev_path = optarg;
break;
case 'r': /* <uint> Radio type */
i = sscanf(optarg, "%u", &arg_u);
if ((i != 1) || ((arg_u != 1255) && (arg_u != 1257) && (arg_u != 1250))) {
printf("ERROR: argument parsing of -r argument. Use -h to print help\n");
return EXIT_FAILURE;
} else {
switch (arg_u) {
case 1255:
radio_type = LGW_RADIO_TYPE_SX1255;
break;
case 1257:
radio_type = LGW_RADIO_TYPE_SX1257;
break;
default: /* 1250 */
radio_type = LGW_RADIO_TYPE_SX1250;
break;
}
}
break;
case 'k': /* <uint> Clock Source */
i = sscanf(optarg, "%u", &arg_u);
if ((i != 1) || (arg_u > 1)) {
printf("ERROR: argument parsing of -k argument. Use -h to print help\n");
return EXIT_FAILURE;
} else {
clocksource = (uint8_t)arg_u;
}
break;
default:
printf("ERROR: argument parsing\n");
usage();
return -1;
}
}
/* Board reset */
if (system("./reset_lgw.sh start") != 0) {
printf("ERROR: failed to reset SX1302, check your reset_lgw.sh script\n");
exit(EXIT_FAILURE);
}
/* Configure the gateway */
memset( &boardconf, 0, sizeof boardconf);
boardconf.lorawan_public = true;
boardconf.clksrc = clocksource;
boardconf.full_duplex = false;
strncpy(boardconf.spidev_path, spidev_path, sizeof boardconf.spidev_path);
if (lgw_board_setconf(&boardconf) != LGW_HAL_SUCCESS) {
printf("ERROR: failed to configure board\n");
return EXIT_FAILURE;
}
memset( &rfconf, 0, sizeof rfconf);
rfconf.enable = true; /* rf chain 0 needs to be enabled for calibration to work on sx1257 */
rfconf.freq_hz = 868500000; /* dummy */
rfconf.type = radio_type;
rfconf.tx_enable = false;
if (lgw_rxrf_setconf(0, &rfconf) != LGW_HAL_SUCCESS) {
printf("ERROR: failed to configure rxrf 0\n");
return EXIT_FAILURE;
}
memset( &rfconf, 0, sizeof rfconf);
rfconf.enable = (clocksource == 1) ? true : false;
rfconf.freq_hz = 868500000; /* dummy */
rfconf.type = radio_type;
rfconf.tx_enable = false;
if (lgw_rxrf_setconf(1, &rfconf) != LGW_HAL_SUCCESS) {
printf("ERROR: failed to configure rxrf 1\n");
return EXIT_FAILURE;
}
x = lgw_start();
if (x != 0) {
printf("ERROR: failed to start the gateway\n");
return EXIT_FAILURE;
}
/* get the concentrator EUI */
x = lgw_get_eui(&eui);
if (x != LGW_HAL_SUCCESS) {
printf("ERROR: failed to get concentrator EUI\n");
} else {
printf("\nINFO: concentrator EUI: 0x%016llX\n\n", eui);
}
/* Stop the gateway */
x = lgw_stop();
if (x != 0) {
printf("ERROR: failed to stop the gateway\n");
return EXIT_FAILURE;
}
/* Board reset */
if (system("./reset_lgw.sh stop") != 0) {
printf("ERROR: failed to reset SX1302, check your reset_lgw.sh script\n");
exit(EXIT_FAILURE);
}
return 0;
}
/* --- EOF ------------------------------------------------------------------ */

View File

@ -0,0 +1,83 @@
### get external defined data
include ../target.cfg
### User defined build options
ARCH ?=
CROSS_COMPILE ?=
BUILD_MODE := release
OBJDIR = obj
### ----- AVOID MODIFICATIONS BELLOW ------ AVOID MODIFICATIONS BELLOW ----- ###
ifeq '$(BUILD_MODE)' 'alpha'
$(warning /\/\/\/ Building in 'alpha' mode \/\/\/\)
WARN_CFLAGS :=
OPT_CFLAGS := -O0
DEBUG_CFLAGS := -g
LDFLAGS :=
else ifeq '$(BUILD_MODE)' 'debug'
$(warning /\/\/\/ Building in 'debug' mode \/\/\/\)
WARN_CFLAGS := -Wall -Wextra
OPT_CFLAGS := -O2
DEBUG_CFLAGS := -g
LDFLAGS :=
else ifeq '$(BUILD_MODE)' 'release'
$(warning /\/\/\/ Building in 'release' mode \/\/\/\)
WARN_CFLAGS := -Wall -Wextra
OPT_CFLAGS := -O2 -ffunction-sections -fdata-sections
DEBUG_CFLAGS :=
LDFLAGS := -Wl,--gc-sections
else
$(error BUILD_MODE must be set to either 'alpha', 'debug' or 'release')
endif
### Application-specific variables
APP_NAME := net_downlink
APP_LIBS := -lparson -lbase64 -lpthread
### Environment constants
LIB_PATH := ../libtools
### Expand build options
CFLAGS := -std=c99 $(WARN_CFLAGS) $(OPT_CFLAGS) $(DEBUG_CFLAGS)
CC := $(CROSS_COMPILE)gcc
AR := $(CROSS_COMPILE)ar
### General build targets
all: $(APP_NAME)
clean:
rm -f obj/*.o
rm -f $(APP_NAME)
install:
ifneq ($(strip $(TARGET_IP)),)
ifneq ($(strip $(TARGET_DIR)),)
ifneq ($(strip $(TARGET_USR)),)
@echo "---- Copying net_downlink files to $(TARGET_IP):$(TARGET_DIR)"
@ssh $(TARGET_USR)@$(TARGET_IP) "mkdir -p $(TARGET_DIR)"
@scp net_downlink $(TARGET_USR)@$(TARGET_IP):$(TARGET_DIR)
else
@echo "ERROR: TARGET_USR is not configured in target.cfg"
endif
else
@echo "ERROR: TARGET_DIR is not configured in target.cfg"
endif
else
@echo "ERROR: TARGET_IP is not configured in target.cfg"
endif
$(OBJDIR):
mkdir -p $(OBJDIR)
### Compile main program
$(OBJDIR)/$(APP_NAME).o: src/$(APP_NAME).c | $(OBJDIR)
$(CC) -c $< -o $@ $(CFLAGS) -Iinc -I../libtools/inc
### Link everything together
$(APP_NAME): $(OBJDIR)/$(APP_NAME).o
$(CC) -L$(LIB_PATH) $^ -o $@ $(LDFLAGS) $(APP_LIBS)
### EOF

View File

@ -0,0 +1,62 @@
______ _
/ _____) _ | |
( (____ _____ ____ _| |_ _____ ____| |__
\____ \| ___ | (_ _) ___ |/ ___) _ \
_____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
(C)2019 Semtech
Utility: Downlink server
========================
## 1. Introduction
This utility allows to send regular downlink requests to the packet forwarder
running on the gateway.
The downlinks are sent in 'immediate' mode, meaning that the SX1302 will send
the incoming packet over the air as soon as it receives it.
So, the net_downlink utility will construct a JSON 'txpk' object based on given
command line arguments, and send it on a UDP socket on the given port. Then the
packet forwarder receives it on its downlink socket, parses the JSON object to
build the packet buffer to be sent to the concentrator board.
This utility can be compiled and run on the gateway itself, or on a PC.
Optionally, the net_downlink utility can forward the received uplinks
(PUSH_DATA) to another UDP server, which can be useful for uplink Packet Error
Rate measurement while performing downlink testing (full-duplex testing etc...)
In can also be used as a UDP packet logger, logging all uplinks in a CSV file.
## 2. Dependencies
A packet forwarder must be running to receive downlink packets and send it to
the concentrator board.
## 3. Usage
### 3.1. Packet Forwarder configuration
The 'global_conf.json' file provided with the packet forwarder can be used, only
the 'server_address' must be set to 'localhost' if net_downlink is running on
the gateway itself, or set to the IP address of the PC on which the utility is
running.
### 3.2. Launching the packet forwarder
The packet forwarder has to be launched with the global_conf.json described in
3.1.
`./lora_pkt_fwd -c global_conf.json`
### 3.3. Launching net_downlink
The net_downlink utility can be started with various command line arguments.
In order to get the available options, and some examples, run:
`./net_downlink -h`
To stop the application, press Ctrl+C.

File diff suppressed because it is too large Load Diff