diff --git a/CBUF.h b/CBUF.h
new file mode 100644
index 0000000..06ce9c0
--- /dev/null
+++ b/CBUF.h
@@ -0,0 +1,238 @@
+/****************************************************************************
+*
+* Since this code originated from code which is public domain, I
+* hereby declare this code to be public domain as well.
+*
+* Dave Hylands - dhylands@gmail.com
+*
+****************************************************************************/
+/**
+*
+* @file CBUF.h
+*
+* @defgroup CBUF Circular Buffer
+* @{
+*
+* @brief A simple and efficient set of circular buffer manipulations.
+*
+* These macros implement a circular buffer which employs get and put
+* pointers, in such a way that mutual exclusion is not required
+* (assumes one reader & one writer).
+*
+* It requires that the circular buffer size be a power of two, and the
+* size of the buffer needs to smaller than the index. So an 8 bit index
+* supports a circular buffer upto ( 1 << 7 ) = 128 entries, and a 16 bit index
+* supports a circular buffer upto ( 1 << 15 ) = 32768 entries.
+*
+* The basis for these routines came from an article in Jack Ganssle's
+* Embedded Muse: http://www.ganssle.com/tem/tem110.pdf
+*
+* In order to offer the most amount of flexibility for embedded environments
+* you need to define a macro for the size.
+*
+* First, you need to name your circular buffer. For this example, we'll
+* call it @c myQ.
+*
+* The size macro that needs to be defined will be the name of the
+* circular buffer followed by @c _SIZE. The size must be a power of two
+* and it needs to fit in the get/put indicies. i.e. if you use an
+* 8 bit index, then the maximum supported size would be 128.
+*
+* The structure which defines the circular buffer needs to have 3 members
+* m_getIdx, m_putIdx, and @c m_entry.
+*
+* @c m_getIdx and @c m_putIdx need to be unsigned integers of the same size.
+*
+* @c m_entry needs to be an array of @c xxx_SIZE entries, or a pointer to an
+* array of @c xxx_SIZE entries. The type of each entry is entirely up to the
+* caller.
+*
+* @code
+* #define myQ_SIZE 64
+*
+* volatile struct
+* {
+* uint8_t m_getIdx;
+* uint8_t m_putIdx;
+* uint8_t m_entry[ myQ_SIZE ];
+*
+* } myQ;
+* @endcode
+*
+* You could then use CBUF_Push to add a character to the circular buffer:
+*
+* @code
+* CBUF_Push( myQ, 'x' );
+* @endcode
+*
+* And CBUF_Pop to retrieve an element from the buffer:
+*
+* @code
+* ch = CBUF_Pop( myQ );
+* @endcode
+*
+* If you happen to prefer to use C++ instead, there is a templatized
+* version which requires no macros. You just declare 3 template parameters:
+*
+* - The type that should be used for the index
+* - The size of the circular buffer
+* - The type that should be used for the entry
+*
+* For example:
+* @code
+* volatile CBUF< uint8_t, 64, char > myQ;
+* @endcode
+*
+****************************************************************************/
+
+#if !defined( CBUF_H )
+#define CBUF_H /**< Include Guard */
+
+/* ---- Include Files ---------------------------------------------------- */
+
+/* ---- Constants and Types ---------------------------------------------- */
+
+/**
+* Initializes the circular buffer for use.
+*/
+
+#define CBUF_Init( cbuf ) cbuf.m_getIdx = cbuf.m_putIdx = 0
+
+/**
+* Returns the number of elements which are currently
+* contained in the circular buffer.
+*/
+
+//#define CBUF_Len( cbuf ) ((typeof( cbuf.m_putIdx ))(( cbuf.m_putIdx ) - ( cbuf.m_getIdx )))
+#define CBUF_Len( cbuf ) (( cbuf.m_putIdx ) - ( cbuf.m_getIdx ))
+
+/**
+* Appends an element to the end of the circular buffer. The
+* element is expected to be of the same type as the @c m_entry
+* member.
+*/
+
+#define CBUF_Push( cbuf, elem ) (cbuf.m_entry)[ cbuf.m_putIdx++ & (( cbuf##_SIZE ) - 1 )] = (elem)
+
+/**
+* Retrieves an element from the beginning of the circular buffer
+*/
+
+#define CBUF_Pop( cbuf ) (cbuf.m_entry)[ cbuf.m_getIdx++ & (( cbuf##_SIZE ) - 1 )]
+
+/**
+* Returns a pointer to the last spot that was pushed.
+*/
+
+#define CBUF_GetLastEntryPtr( cbuf ) &(cbuf.m_entry)[ ( cbuf.m_putIdx - 1 ) & (( cbuf##_SIZE ) - 1 )]
+
+/**
+* Returns a pointer to the next spot to push. This can be used
+* in conjunction with CBUF_AdvancePushIdx to fill out an entry
+* before indicating that it's available. It is the caller's
+* responsibility to enure that space is available, and that no
+* other items are pushed to overwrite the entry returned.
+*/
+
+#define CBUF_GetPushEntryPtr( cbuf ) &(cbuf.m_entry)[ cbuf.m_putIdx & (( cbuf##_SIZE ) - 1 )]
+
+/**
+* Advances the put index. This is useful if you need to
+* reserve space for an item but can't fill in the contents
+* yet. CBUG_GetLastEntryPtr can be used to get a pointer to
+* the item. It is the caller's responsibility to ensure that
+* the item isn't popped before the contents are filled in.
+*/
+
+#define CBUF_AdvancePushIdx( cbuf ) cbuf.m_putIdx++
+
+/**
+* Advances the get index. This is slightly more efficient than
+* popping and tossing the result.
+*/
+
+#define CBUF_AdvancePopIdx( cbuf ) cbuf.m_getIdx++
+
+/**
+* Retrieves the idx'th element from the beginning of
+* the circular buffer
+*/
+
+#define CBUF_Get( cbuf, idx ) (cbuf.m_entry)[( cbuf.m_getIdx + idx ) & (( cbuf##_SIZE ) - 1 )]
+
+/**
+* Retrieves the idx'th element from the end of the
+* circular buffer.
+*/
+
+#define CBUF_GetEnd( cbuf, idx ) (cbuf.m_entry)[( cbuf.m_putIdx - idx - 1 ) & (( cbuf##_SIZE ) - 1 )]
+
+/**
+* Returns a pointer to the next spot to push.
+*/
+
+#define CBUF_GetPopEntryPtr( cbuf ) &(cbuf.m_entry)[ cbuf.m_getIdx & (( cbuf##_SIZE ) - 1 )]
+
+/**
+* Determines if the circular buffer is empty.
+*/
+
+#define CBUF_IsEmpty( cbuf ) ( CBUF_Len( cbuf ) == 0 )
+
+/**
+* Determines if the circular buffer is full.
+*/
+
+#define CBUF_IsFull( cbuf ) ( CBUF_Len( cbuf ) == ( cbuf##_SIZE ))
+
+/**
+* Determines if the circular buffer is currenly overflowed or underflowed.
+*/
+
+#define CBUF_Error( cbuf ) ( CBUF_Len( cbuf ) > cbuf##_SIZE )
+
+#if defined( __cplusplus )
+
+template < class IndexType, unsigned Size, class EntryType >
+class CBUF
+{
+public:
+
+ CBUF()
+ {
+ m_getIdx = m_putIdx = 0;
+ }
+
+ IndexType Len() const { return m_putIdx - m_getIdx; }
+
+ bool IsEmpty() const { return Len() == 0; }
+ bool IsFull() const { return Len() == Size; }
+ bool Error() const { return Len() > Size; }
+
+ void Push( EntryType val )
+ {
+ m_entry[ m_putIdx++ & ( Size - 1 )] = val;
+ }
+
+ EntryType Pop()
+ {
+ return m_entry[ m_getIdx++ & ( Size - 1 )];
+ }
+
+private:
+
+ volatile IndexType m_getIdx;
+ volatile IndexType m_putIdx;
+ EntryType m_entry[ Size ];
+
+};
+
+#endif // __cplusplus
+
+/* ---- Variable Externs ------------------------------------------------- */
+/* ---- Function Prototypes ---------------------------------------------- */
+
+/** @} */
+
+#endif // CBUF_H
+
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..b4d886f
--- /dev/null
+++ b/README.md
@@ -0,0 +1,12 @@
+# 433T30D-LoRa-KISS-TNC
+
+This project aims to make a better firmware for E32-DTU 433L30 LoRa data transceiver devices that can be actually used by radio amateurs as a cheap replacement for traditional 1200 baud AFSK TNCs.
+
+LoRa employs a chirp modulation which delivers a better path power budget.
+
+To install new firmware you will need to have an ST-LINK flasher and make some hardware changes to E32-DTU device that will void your warranty.
+
+More details are on Wiki.
+
+All hardware manipulations are on your own risk!
+
diff --git a/STM8L-433T30D-TNC.hex b/STM8L-433T30D-TNC.hex
new file mode 100644
index 0000000..00321cf
--- /dev/null
+++ b/STM8L-433T30D-TNC.hex
@@ -0,0 +1,1184 @@
+:1080000082009100820097A8820097A8820097A81A
+:10801000820097A8820097A8820097A8820097A85C
+:10802000820097A8820097A8820097A8820097A84C
+:10803000820097A8820097A8820097A88200C66154
+:108040008200C9D1820097A8820097A8820097A8D1
+:10805000820097A88200C3B9820097A88200C7AFA8
+:10806000820097A88200C4F0820097A8820097A897
+:10807000820097A8820097A88200C001820097A87A
+:1080800000100000978A00977200977800977E0092
+:10809000975600975D00976400976B0096350095A2
+:1080A000E10095ED0095F9009464009472009480CD
+:1080B0000091170091DD00912D0092590097160054
+:1080C00096C50096CE0096D70096610096E0009681
+:1080D000E900971E00972600972E0093EC0095521A
+:1080E00000955F0093FB00940A0094190094280007
+:1080F000948E00949C0094AA0094B80094C60094B6
+:10810000D40094E20094F00094FE008F9700966BE8
+:1081100000967500967F009689008FB0008C480409
+:108120000400100100041401000514003C061400B2
+:0D8130000000888C009C008080065000003C
+:10813D008D008BF12000B604A480B800B700CD836C
+:10814D00642071206D206E2071207472F90172A26D
+:10815D00007E17015F894B0090AE00034B0090E647
+:10816D0004272C3D032710BE024272FB031F03247C
+:10817D00060C0226020C013D02270CBE014272FBC9
+:10818D00021F0224020C01BE004272FB011F01906E
+:10819D005A26C95B02320003849085905D2702AA9E
+:1081AD000190855D2B0648390359905ABF0193CC38
+:1081BD0083ED27038725FDCC844B5B02CC843F5F89
+:1081CD00380056BF005FBF025B02878726FDCC8457
+:1081DD004B8D008BF12000B604A480B800B700CD04
+:1081ED00836420EA20E520E420D520CE72F2011729
+:1081FD00013F04BE02905FB6019097B306B205240D
+:10820D00095890590C0226020C0172B00006240286
+:10821D00905A72B200044FB70235010003589059BD
+:10822D0090B3042602B306250C72B000062402900A
+:10823D005A72B20004390339024924E1330333027F
+:10824D0043509059AE007F72F0015B022D26A300C2
+:10825D00FF2503CC843F02905049380056BF0036AD
+:10826D00023603240C4D27093C0326053C0226014A
+:10827D005CBF0087B70190504F46993601360236E4
+:10828D000346CC83EDBE01B6005849A07F2529A138
+:10829D00102509AE7FFF3D002A015C87995640AB42
+:1082AD000FA1082509884F0184A008200154A001C1
+:1082BD0024FB3D002A0150875F87B60148B600496F
+:1082CD002542A07F253EA12025075F5ABF00BF0292
+:1082DD0087721E00014501004502014503023F035F
+:1082ED0040AB1F200D4502034501024500013F0033
+:1082FD00A008A10824EF2008340036013602360309
+:10830D004A2AF5875FBF00BF02874FBE002604BE15
+:10831D00022743AE009E3D0020095A380339023929
+:10832D000139002AF572030002047210000372581D
+:10833D000000489F463100004631000131000231F6
+:10834D000003AB7F24103C03260C3C0226083C01A5
+:10835D0026043C002600879085B60148B6004927C3
+:10836D0030A1FF271C721E0001884B00B60548B6D0
+:10837D000449273EA1FF2733721E00055F9751EC7C
+:10838D000AAD4B2648AD4F2240250290FC5D90EC86
+:10839D0002AD3B270E5F5C5A3803390239012AF7CB
+:1083AD008920C9AD3122225D90EC04AD29221890AF
+:1083BD00EC06AD225D270D5F5C5A3807390639058D
+:1083CD002AF720BA90EC085B02AC00874387B60110
+:1083DD0048BA02BA0381BE0650BE0459A3FF0081FC
+:1083ED005D9C2C24A3FFE72C0D4FB703B702B701FB
+:1083FD00380046B70087340136023603462402AAF8
+:10840D00015CA3000126EF5F7201000302AA01A324
+:10841D0000FE2C1E380138004146B700360141A13F
+:10842D0081250E3C03260A3C0226063C0126023C11
+:10843D000087AEFF00380056BF005FBF02875F5A4E
+:10844D00BF00BF0287BE005089BE0250BF02852704
+:10845D00015ABF0087AE0004B603EB03B703B602A3
+:10846D00E902B702B601E901B701B600F9B7008715
+:10847D00B600BE06429FB700B601BE05429FBB00C7
+:10848D00B700B602BE04429FBB00B700B603BE03E1
+:10849D00429FBB00B700B601BE06423F0172BB0052
+:1084AD0000BF00B602BE054272BB0000BF00B6039E
+:1084BD00BE044272BB0000BF00B602BE06423F02C0
+:1084CD0072BB0001BF0124023C00B603BE0542721F
+:1084DD00BB0001BF0124023C00B603BE06423F03B0
+:1084ED0072BB0002BF022405BE005CBF0087BE0048
+:1084FD00261E90BE04260CBE0290BE0665BF0290DD
+:10850D00BF0687BF04450206450307BF00BF0287AC
+:10851D00A6203D00260DBE01BF004503023F033AD4
+:10852D0003A0085F905F992017905959B304260353
+:10853D0090B306250B72B2000624015A72B00004E6
+:10854D0039033902390139004A2ADEBF0490BF06CA
+:10855D00BE0053BF00BE0253BF02878D008BF120BA
+:10856D00048D008BF14B004B003D002A0BAE00003B
+:10857D008D0085AD030103023D042A09AE00048D73
+:10858D000085AD03018D0084FB844D2707AE0000EF
+:10859D008D0085AD844D2707AE00048D0085AD8718
+:1085AD0060034FE202E7024FE201E7014FF2F78766
+:1085BD00BE00B3042610BE02B3068A84A47BA501B7
+:1085CD002702AA04888687AE0004B600FAB700B663
+:1085DD0001EA01B701B602EA02B702B603EA03B730
+:1085ED000387B600F8B700B601E801B701B602E897
+:1085FD0002B702B603E803B70387AE00004D270AA2
+:10860D00680369026901794A26F687AE00004D2795
+:10861D000A746601660266034A26F687BF025F3D4D
+:10862D00022A0153BF008788A6082000887B028894
+:10863D007B02891E061F045F977B086B06FE1F07D2
+:10864D00858487891E06BF0820001E041F061E0292
+:10865D001F04855B0287887B03887B03887B0388E7
+:10866D007B076B04B6096B05B60A6B06B60B6B0779
+:10867D008487887B03887B03887B03887B076B0457
+:10868D00B60D6B05B60E6B06B60F6B078487887B30
+:10869D0005B7097B06B70A7B07B70B200F887B054B
+:1086AD00B70D7B06B70E7B07B70F20007B046B075A
+:1086BD00846B03846B03846B03848788A600200F6F
+:1086CD0088A604200A88A608200588A60C20008804
+:1086DD00897B04887B04891E081F045F977B0A6BC6
+:1086ED00061F09FE1F071E095C5CFE1F0985848796
+:1086FD00891E06BF001E08BF022021891E06BF0469
+:10870D001E08BF062016891E06BF081E08BF0A20B8
+:10871D000B891E06BF0C1E08BF0E20001E031F076F
+:10872D001E041F08855B04878D00871E8D0087132F
+:10873D00878D008650874504004505014506024595
+:10874D00070387450800450901450A02450B0387C4
+:10875D00450D01450E02450F0387450004450105F2
+:10876D0045020645030787450804450905450A06E0
+:10877D00450B0787450C04450D05450E06450F07AE
+:10878D008745000845010945020A45030B87450445
+:10879D000845050945060A45070B8745010D4502A4
+:1087AD000E45030F8745090D450A0E450B0F874EE4
+:1087BD006F746520746861742070203D203120280D
+:1087CD0050203D2032353529206D65616E7320278F
+:1087DD007472616E736D697420617320736F6F6E47
+:1087ED0020617320746865206368616E6E656C200E
+:1087FD00636C656172732E27002854686973207746
+:10880D00696C6C206F766572777269746520746817
+:10881D00652070726576696F75736C792073746FEE
+:10882D007265642073657474696E677320696E2058
+:10883D00544E4320454550524F4D2E2900456E74E0
+:10884D0065722052656750706D436F727265637407
+:10885D00696F6E20696E2072616E676520302E2EF5
+:10886D00323535206F7220686974205B454E544552
+:10887D00525D20746F2072657475726E3A2000BF60
+:10888D000092CE00BF02BE001C000290BE02260167
+:10889D00878D008F3A1C0003BF0092CE009093BECF
+:1088AD00001C0002BF0092BC000590F75F5C72BB1C
+:1088BD000006BF06905CBE025ABF0227C420E731F6
+:1088CD00202D20456E61626C65643B206D616E6488
+:1088DD006174656420666F72207768656E207468B8
+:1088ED00652073796D626F6C206C656E677468209E
+:1088FD00657863656564732031366D7300456E74FC
+:10890D00657220502D706172616D6574657220699C
+:10891D006E2072616E676520302E2E323535206FD8
+:10892D007220686974205B454E5445525D20746F0A
+:10893D002072657475726E3A2000456E74657220F2
+:10894D00507265616D626C6520696E2072616E6733
+:10895D006520302E2E3635353335206F72206869FF
+:10896D0074205B454E5445525D20746F20726574C2
+:10897D0075726E3A2000456E74657220536C6F747B
+:10898D0054696D6520696E2072616E676520302EA9
+:10899D002E323535206F7220686974205B454E5438
+:1089AD0045525D20746F2072657475726E3A2000A9
+:1089BD00456E7465722053796E63576F72642069CA
+:1089CD006E2072616E676520302E2E323535206F28
+:1089DD007220686974205B454E5445525D20746F5A
+:1089ED002072657475726E3A2000456E7465722042
+:1089FD00547844656C617920696E2072616E67658B
+:108A0D0020302E2E323535206F7220686974205B30
+:108A1D00454E5445525D20746F2072657475726EAB
+:108A2D003A2000456E746572204F63705472696D03
+:108A3D0020696E2072616E676520302E2E333120D5
+:108A4D006F7220686974205B454E5445525D2074E9
+:108A5D006F2072657475726E3A2000456E74657282
+:108A6D00206E6577206672657175656E6379206914
+:108A7D006E206B487A206F7220686974205B454EBA
+:108A8D005445525D20746F2072657475726E3A2074
+:108A9D0000506F70756C61746520544E43204545D0
+:108AAD0050524F4D2064617461206279207265616E
+:108ABD0064696E67206D6F64656D2072656769739B
+:108ACD00746572730031202D20456E61626C656492
+:108ADD002E204C4E41206761696E207365742062B3
+:108AED00792074686520696E7465726E616C2041C1
+:108AFD004743206C6F6F700031202D204F766572CB
+:108B0D00777269746520544E4320454550524F4D40
+:108B1D002073657474696E67732077697468206D4E
+:108B2D006F64656D206461746100416476616E638C
+:108B3D0065642073657474696E6773202873656549
+:108B4D002064617461736865657420666F72206D51
+:108B5D006F726520696E666F293A0030202D2044B2
+:108B6D00697361626C65642E204C4E4120676169AA
+:108B7D006E20736574206279207265676973746500
+:108B8D0072204C6E614761696E00546865206465A2
+:108B9D006661756C742076616C7565206973205003
+:108BAD00203D2036332028692E652E2C2070203D47
+:108BBD0020302E3235292E004D6F64656D20737077
+:108BCD0072656164696E6720666163746F72206996
+:108BDD006E206368697073207065722073796D62A1
+:108BED006F6C3A00887B05B7051E06BF061C000496
+:108BFD001F0624020C055F92AF0005885CA30004DC
+:108C0D0026F5320007320006320005320004848753
+:108C1D004B49535320544E4320666F7220535831A5
+:108C2D00323778206F72205246284D293978204CE2
+:108C3D006F5261206D6F64656D7300BF0092CE0041
+:108C4D008D0096565D2603BE008791CE0090BF0223
+:108C5D008D0096564F92C70290BE02905C90BF0257
+:108C6D005A27DA20EF205B454E5445525D202D20CA
+:108C7D0052657475726E20746F2074686520707201
+:108C8D006576696F7573206D656E7565005B454E14
+:108C9D005445525D202D2052657475726E20746F8F
+:108CAD00207468652070726576696F7573206D65C7
+:108CBD006E75650089FEBF001E01EE02BF0285873D
+:108CCD0089FEBF041E01EE02BF068587908990BE06
+:108CDD0000FF90BE02EF0290858788895EB6014243
+:108CED00891E03B6004272FB014F02891E05B601B3
+:108CFD004272FB015B0684874D2704574A26FC8789
+:108D0D0034202D2044756D70205358313237782022
+:108D1D0068617264776172652072656769737465E5
+:108D2D0072730031202D204920616E642051207313
+:108D3D0069676E616C732061726520696E7665720C
+:108D4D0074656400526567446574656374696F6E1C
+:108D5D005468726573686F6C6420283078333729D6
+:108D6D003A2030780043757272656E7420502D7004
+:108D7D006172616D657465722028646563696D61EA
+:108D8D006C293A200031202D205472616E73706170
+:108D9D0072656E742052462055415254206272699C
+:108DAD006467650033202D20426F6F7374206F6EE2
+:108DBD002C2031353025204C4E4120637572726563
+:108DCD006E74004D6F64656D20636F64696E67200E
+:108DDD007261746520696E2073796D626F6C733A80
+:108DED00002020205265674D6F64656D436F6E6680
+:108DFD00696733202830783236293A203078009050
+:108E0D00AE811C2011931C000389938D008F3A8530
+:108E1D00928D0005909390A3813D26E98736202DF4
+:108E2D00204D6F64656D20737072656164696E6746
+:108E3D0020666163746F723A200043757272656EBD
+:108E4D007420536C6F7454696D6520286465636973
+:108E5D006D616C293A2000202020526567507265A3
+:108E6D00616D626C654D7362202830783230293A1D
+:108E7D0020307800202020526567507265616D6248
+:108E8D006C654C7362202830783231293A20307865
+:108E9D00005265674465746563744F7074696D69DC
+:108EAD007A65202830783331293A2030780043759F
+:108EBD007272656E742053796E63576F72642028D9
+:108ECD00646563696D616C293A200031202D205451
+:108EDD004E432073657269616C20706F72742073DC
+:108EED00706565643A200043757272656E74205426
+:108EFD007844656C61792028646563696D616C29BE
+:108F0D003A20004F6E626F6172642053656D746517
+:108F1D00636820636869702069643A20307800883E
+:108F2D00F6B701E601B702E602B703848788F6B704
+:108F3D0005E601B706E602B707848738202D204DD8
+:108F4D006F64756C6174696F6E2062616E647769B0
+:108F5D006474683A2000507265737320616E7920D5
+:108F6D006B657920746F20636F6E74696E75650023
+:108F7D0033202D20496E697469616C6973652054C5
+:108F8D004E4320454550524F4D00303031202D3E3F
+:108F9D00204731203D206D6178696D756D206761C9
+:108FAD00696E00313130202D3E204736203D206D39
+:108FBD00696E696D756D206761696E0030202D20B9
+:108FCD00544E43206F7065726174696F6E206D6FC2
+:108FDD0064653A20005265675061436F6E6669673C
+:108FED00204F7574707574506F7765723A005265C5
+:108FFD006750706D436F7272656374696F6E202870
+:10900D0030783237293A0037202D204D6F64656D49
+:10901D0020636F64696E6720726174653A20003554
+:10902D00202D204C6F7744617461726174654F70AF
+:10903D0074696D697A650030202D2044656661750F
+:10904D006C74204C4E412063757272656E740043D2
+:10905D00757272656E742050706D436F72726563B8
+:10906D0074696F6E3A200030202D20307830332017
+:10907D002D3E2053463720746F2053463132003039
+:10908D00202D2030783041202D3E2053463720743E
+:10909D006F205346313200544E432073657269611F
+:1090AD006C20706F72742073706565643A00612076
+:1090BD002D20446574656374696F6E5468726573B1
+:1090CD00686F6C64005265675061436F6E666967C7
+:1090DD00202830783039293A203078005265674998
+:1090ED006E766572744951202830783333293A20D1
+:1090FD00307800AE07FF948D0097BC5D27048D007E
+:10910D008E0C8D00B2B3AC0097AC342F352028787F
+:10911D0020312E3235206F766572686561642900C5
+:10912D00342F3720287820312E3735206F76657211
+:10913D006865616429004D6F64756C6174696F6E4B
+:10914D002062616E6477696474683A0035202D2061
+:10915D004D6F64656D206672657175656E63793AE4
+:10916D0020000D0A4F7065726174696F6E20636F18
+:10917D006D706C65746564000D0A4E6F20616374CB
+:10918D00696F6E20706572666F726D6564005265F1
+:10919D00675061436F6E66696720506153656C65FA
+:1091AD0063743A005265675061436F6E666967205C
+:1091BD004D6178506F7765723A0032202D204164F1
+:1091CD0076616E6365642073657474696E67730090
+:1091DD00342F3620287820312E35206F7665726831
+:1091ED006561642900526567506152616D70202878
+:1091FD0030783041293A203078004C6F77446174D3
+:10920D0061726174654F7074696D697A653A005366
+:10921D00656C65637420544E432073657474696E78
+:10922D0067733A0043757272656E7420667265716C
+:10923D0075656E63793A200052656753796E635791
+:10924D006F7264202830783339293A00342F382052
+:10925D0028782032206F7665726865616429003840
+:10926D00202D204465746563744F7074696D697A3F
+:10927D0065005265674C6E61204C6E61426F6F7375
+:10928D007448663A0043757272656E74205072654B
+:10929D00616D626C653A200031202D204261736949
+:1092AD00632073657474696E67730088BF045F9281
+:1092BD00AF000172D700045C905A26F384873320E7
+:1092CD002D20502D706172616D657465723A2000AC
+:1092DD0037202D2050706D436F7272656374696F06
+:1092ED006E0033202D204F7574707574506F776537
+:1092FD00723A20005265674F6370202830783042F3
+:10930D00293A2030780043757272656E74204F6370
+:10931D00705472696D3A20005265674C6E61202859
+:10932D0030783043293A20307800303132333435BB
+:10933D0036373839414243444546004D6F64656DBB
+:10934D00206672657175656E63793A0030202D2047
+:10935D004F43502064697361626C65640032202D47
+:10936D00204C6E61426F6F73744C663A200033204F
+:10937D002D204C6E61426F6F737448663A20004227
+:10938D00617369632073657474696E67733A003134
+:10939D00202D204F435020656E61626C6564005234
+:1093AD0065674C6E61204C6E614761696E3A0031A4
+:1093BD00202D2030783035202D3E2053463600307C
+:1093CD00202D206E6F726D616C206D6F64650031A4
+:1093DD00202D2030783043202D3E2053463600304E
+:1093ED00303030202D3E20332E34206D7300303040
+:1093FD003131202D3E203530302075730030313025
+:10940D0030202D3E20323530207573003031303113
+:10941D00202D3E2031323520757300303131302012
+:10942D002D3E203130302075730034202D20536CAB
+:10943D006F7454696D653A200031202D20506153B1
+:10944D00656C6563743A200032202D204D61785093
+:10945D006F7765723A200031302028313032342058
+:10946D0043505329003131202832303438204350B5
+:10947D0053290031322028343039362043505329B6
+:10948D000030313131202D3E2036322075730031C0
+:10949D00303030202D3E2035302075730031303086
+:1094AD0031202D3E20343020757300313031302085
+:1094BD002D3E2033312075730031303131202D3E5A
+:1094CD002032352075730031313030202D3E203261
+:1094DD00302075730031313031202D3E2031352053
+:1094ED0075730031313130202D3E203132207573AE
+:1094FD000031313131202D3E203130207573003255
+:10950D00202D20547844656C61793A200036202D49
+:10951D00204167634175746F4F6E0032202D204FCF
+:10952D0063705472696D3A20005265674F63702005
+:10953D004F63704F6E3A0031202D204C6E614761A4
+:10954D00696E3A200030303031202D3E2032206DB2
+:10955D00730030303130202D3E2031206D730032BC
+:10956D00202D204B49535320544E4300502D7061F4
+:10957D0072616D657465723A0030202D2050614323
+:10958D006F6E6669670034202D20507265616D62C3
+:10959D006C650039202D20496E7665727449710015
+:1095AD0062202D2053796E63576F72640031202D28
+:1095BD002050415F424F4F5354004F435020646938
+:1095CD007361626C65640030202D204469736162A3
+:1095DD006C65640037202831323820435053290000
+:1095ED00382028323536204350532900392028356C
+:1095FD0031322043505329004D4F44455F4252496B
+:10960D004447450031202D204F63704F6E3A2000A6
+:10961D004F435020656E61626C65640048484EA4EE
+:10962D00F08744444EA40F8736202836342043500B
+:10963D0053290031202D20506152616D700041671A
+:10964D00634175746F4F6E3A0090BE0072A90002AF
+:10965D0090BF008733312E3235206B487A00303180
+:10966D0030202D3E20473200303131202D3E204715
+:10967D003300313030202D3E204734003130312041
+:10968D002D3E20473500544E43206D6F64653A00E2
+:10969D004D4F44455F4B49535300536C6F745469A0
+:1096AD006D653A00507265616D626C653A00F6B198
+:1096BD00012604EE01B3028731302E34206B487A37
+:1096CD000031352E36206B487A0032302E38206B23
+:1096DD00487A0034312E37206B487A0036322E35D9
+:1096ED00206B487A002078203130206D730054783B
+:1096FD0044656C61793A0050415F424F4F535400BD
+:10970D004F63705472696D3A00372E38206B487A6A
+:10971D0000313235206B487A00323530206B487A73
+:10972D0000353030206B487A00756E6B6E6F776E3A
+:10973D000032202D204F63700033202D204C6E61A0
+:10974D000030202D2052464F00313135323030005F
+:10975D003233303430300034363038303000393236
+:10976D00313630300031393230300033383430302A
+:10977D000035373630300089858D0097D239363037
+:10978D003000206B487A005B324A00202D200020EB
+:10979D0030620052464F00207C2000AC0097BFACD9
+:1097AD000097845B48003A20000D0A000D0A005F07
+:1097BD005C879D20FD20002B00610062006300642A
+:1097CD00006500660020FE008D00866352118D003D
+:1097DD0087918D00C845968D00C9A94EA40F3F02F3
+:1097ED00B703968D00C69C968D00C6C272BB000A4B
+:1097FD00BF0A4F92BD00095B11AC0098B98D008670
+:10980D00D28D00867F8D0087A88D00879BAEFFF7D8
+:10981D00BF065F5ABF048D0087508D0085BD2E1881
+:10982D008D00856E0000000A8D0087678D00875DB5
+:10983D008D00980A8D0087A88D0087508D00856852
+:10984D000000000AA630B0078D00989F8D00875D3F
+:10985D008D0086AAAC0087398D0086638D00867FCA
+:10986D008D0087918D0087B23D042A08A62D8D00AD
+:10987D00989F200C8D0087438D0084528D00876743
+:10988D008D00875D8D00980A8D00C8BE8D0086AA5B
+:10989D00201A92BD000D5F5C72BB000EBF0E8702D9
+:1098AD008D0086298D0098BE8D00C92F8D00869B59
+:1098BD00878D0087678D0098C7878D00A0DE878D07
+:1098CD000086343B000A8D00A7A9350000013590B4
+:1098DD00000235A400038D009C94AE0653BF089280
+:1098ED00BC06505F9701A40702905FB60A9097BF20
+:1098FD000090B3002604A62B2002A6208D009B2DE0
+:10990D00A10825DB8D009C8F32000AAC00873E8DAF
+:10991D000098CC8F72070646198D009CD9B700A10F
+:10992D0008240F92BC0650A4F8BA00B7004F8D0062
+:10993D00C4188D00A99B26068D00A85226D1725FF2
+:10994D00064D873B00088D00A7A93500000135960F
+:10995D000002359300038D00A25E92BC0650444474
+:10996D0044A403B7084A2604A62B2002A6208D0086
+:10997D00C90635000001358D0002359200038D00BA
+:10998D00C96BA1022604A62B2002A6208D00C906B4
+:10999D003500000135950002356C00038D00C8C7F8
+:1099AD00320008878D0099508F72070646248D006E
+:1099BD00AA6D27054A270C201992BC0650A4E7AAC8
+:1099CD0008200892BC0650A4E7AA10B7004F8D00DE
+:1099DD00C4188D00A99B26068D00A85226C6725F5D
+:1099ED00064D878D0086D28D00867FAE06383F0DE1
+:1099FD00BF0E8D00A7A93500000135930002354833
+:109A0D0000038D00A25E3500000135920002353154
+:109A1D0000038D009F488D0087678D00875D8D0049
+:109A2D0098658D00875D8D009F6835000001358A32
+:109A3D000002356800038D00C49DA60A8D00875D68
+:109A4D008D00BE068D00AAA7201C5F97BF065FBFC5
+:109A5D00048D0086CDAE000ABF068D00847D8D007D
+:109A6D0087088D00C6095FB608978D0087848D0025
+:109A7D00C87192BC000526D2BE002602BE02273B4D
+:109A8D008D0083178D00813D447A00008D0081DEAD
+:109A9D00427424008D0082C78D00878E8D00C472A4
+:109AAD00450900A6058D00C418450A00A6068D00BF
+:109ABD00C418450B00A6078D00C4188D00A99B2660
+:109ACD0009C606382704AC0099FF8D0086AAAC00A4
+:109ADD0087398D0086343B000A8D00A7A93500001B
+:109AED0001358B000235C500038D009C94AE066BCD
+:109AFD00BF08A61E8D00C4468D00C91F5FB60A970C
+:109B0D001C0006B3002604A62B2002A6208D009B68
+:109B1D002DA10725DD8D009C8F32000AAC00873EFC
+:109B2D008D00C906B60AAB318D00C9068D00A5F2B0
+:109B3D00BE088D008F2C8D00BFA9B60A4CB70ABE8A
+:109B4D00081C0003BF08873B00088D009ADF8F7249
+:109B5D0006064604AC009C068D00C9AEA61E8D00FF
+:109B6D00C446A40FB708A631C1064D2622B608AAD1
+:109B7D00648D009C1E8D009C89AA018D009C2EAACF
+:109B8D00058D00C7048D00C8A2350C00002064A609
+:109B9D00CECB064DA1062461AE064DF6A03227119F
+:109BAD004A27144A27174A271A4A271D4A272020D1
+:109BBD0026B608AA74201CB608AA842016B608AAD0
+:109BCD00942010B608AAA4200AB608AAB42004B698
+:109BDD0008AAC48D009C1E8D009C89A4FE8D009C3E
+:109BED002EAA038D00C704350A00008D00C8A635C6
+:109BFD000A0000A6148D00C4188D00A99B260A8D9D
+:109C0D0000A8522704AC009B57725F064D32000826
+:109C1D00878D00C98BA61E8D00C927A60DAC00C46B
+:109C2D00188D009DAFA6318D00C446A4F8878D0018
+:109C3D0086343B000A8D00A7A935000001358D0043
+:109C4D000235D000038D009C94AE0680BF088D00B8
+:109C5D009C89445F9701A40702BF00B60A975CB3C5
+:109C6D00002604A62B2002A6208D009B2DA10425E5
+:109C7D00DD8D009C8F32000AAC00873EA61DAC0026
+:109C8D00C4468D00C6F4878D00A2633F0A873B0052
+:109C9D00088D009C3B8F720706461D8D009CD9A137
+:109CAD000424158D009C898D00A6604888B600A4FB
+:109CBD00F1B700848D009DA88D00A99B26068D000F
+:109CCD00A85226CD725F064D320008878D00C91748
+:109CDD00A6CFCB064D878D0086343B000A8D00A79D
+:109CED00A93500000135910002354300038D009C1C
+:109CFD0094AE068CBF08A61D8D00A945B10A260499
+:109D0D00A62B2002A6208D00C906B60AAB308D0009
+:109D1D009B35A10A25E08D009C8F32000AAC00878F
+:109D2D003E3B00088D009CE38F72070646598D005F
+:109D3D00A64E2453A61D8D00A52E88B600A40FB7E0
+:109D4D0000848D009DA8A639C1064D26263502003A
+:109D5D0000A6368D00C53935020000A6168D00C44B
+:109D6D0018357F0000A63A8D00C539357F0000A655
+:109D7D0017201035030000A6368D00C539350300B8
+:109D8D0000A6168D00C4188D00A99B26068D00A86F
+:109D9D00522691725F064D32000887BA008D009DE4
+:109DAD00AF87B708450800A61D8D00C539450800C9
+:109DBD00A60CAC00C4188D0086638D00A7A93500D4
+:109DCD00000135930002358C00038D00A25E350035
+:109DDD000001358F000235C900038D00C49D92BC72
+:109DED000650444444A4034A27054A2710201C3535
+:109DFD0000000135960002350500032012350000E4
+:109E0D000135960002359D000320048D009F318D94
+:109E1D0000BFA935000001358E000235D800038D35
+:109E2D0000C49D92BC0650A407A108240D8D009F6F
+:109E3D007C1C06538D008F2C20048D009F318D00CE
+:109E4D00BFA93500000135950002350C00038D00CA
+:109E5D009FE68D009F8A350000013592000235CBBB
+:109E6D0000038D00C49D8D00A0B635000001359412
+:109E7D000002353700038D00C49D8D00A0968D0026
+:109E8D009F8A3500000135910002355900038D0080
+:109E9D009F488D0098BE8D009F6835000001358E5E
+:109EAD000002352A00038D00C49DA61E8D00A94514
+:109EBD00ABFAA107240D8D009F7C1C066B8D008FC6
+:109ECD002C20048D009F318D00BFA9350000013578
+:109EDD00900002351400038D009F3E44A4074AA153
+:109EED0004240D8D009F7C1C06808D008F2C20047A
+:109EFD008D009F318D00BFA935000001358F000207
+:109F0D00354800038D009F3E4EA40FA10A240D8DF0
+:109F1D00009F7C1C068C8D008F2C20048D009F31A2
+:109F2D00AC0098B5350000013597000235360003B9
+:109F3D00878D00C49DA61DAC00C4468D00C49D8DAB
+:109F4D0000C1868D0083178D00813D427424008DE4
+:109F5D000081DE447A0000AC0082C78D00C49D35BF
+:109F6D0000000135970002358F0003AC00BFA990AA
+:109F7D005F9097AE0003BF0093AC008CE78D00A0FF
+:109F8D00C98D00A0D28D00C49D350000013596000D
+:109F9D000235F20003AC00BFA98D0086638D00A7CA
+:109FAD00A9350000013596000235FB00038D00A296
+:109FBD005E35000001358E000235F400038D009FE3
+:109FCD00E68D00A0BD350000013589000235F70092
+:109FDD00038D00C49DAC0098B98D00A0AA8D00A082
+:109FED009EAE0002878D0086638D00A7A935000007
+:109FFD000135950002357900038D00BFA9350000AC
+:10A00D00013587000235BC00038D00BFA935000066
+:10A01D0001358B0002359700038D00A25E350000DF
+:10A02D0001358D0002357200038D00A0A58D00A0B5
+:10A03D00B63500000135890002350A00038D00C4D4
+:10A04D009DAC0098B98D0086638D00A7A9350000E1
+:10A05D00013596000235A700038D00A25E35000084
+:10A06D0001358E0002354700038D00A0A58D00A09F
+:10A07D00968D00A0BD3500000135890002358300A5
+:10A08D00038D00C49DAC0098B98D00A09EAE000458
+:10A09D0087AE0650AC008F2C8D00A0AA878D00C412
+:10A0AD009DAE06383F09BF0A878D00A09EAE000306
+:10A0BD008D00A0C98D00A0D2AC00BFA98D00C83AFB
+:10A0CD0092BC0001875F97BF065FBF048D00A0DEC5
+:10A0DD00878D0087538D009865AC0087538D008662
+:10A0ED00638D00C9B38D009DC38F7206064604AC07
+:10A0FD0000A1998D00C972271A4A271D4A27204AA7
+:10A10D00273A4A27544A276E4A27714A27744A2705
+:10A11D007720798D0099B120738D00991C206D8D5C
+:10A12D00009FA68D00AA8827638D00A1B1A6028D80
+:10A13D0000A9972657C6063826E520508D009FF2B8
+:10A14D008D00AA8827468D00A1B1A6038D00A99781
+:10A15D00263AC6063826E520338D00A0528D00AA7A
+:10A16D008827298D00A1B1A6048D00A997261DC6AB
+:10A17D00063826E520168D0099F020108D009B5491
+:10A18D00200A8D009C9B20048D009D2E8D00A99B87
+:10A19D00260A8D00A8522704AC00A0F2725F064D6E
+:10A1AD00AC0098B98D0087538D00C55A8D00876717
+:10A1BD00450700878D0086343F093F088D00A7A90C
+:10A1CD0035000001358A0002359E00038D00BFA9C0
+:10A1DD003500000135880002350600038D00A25EB2
+:10A1ED0035000001358B0002350500038D00C92FA8
+:10A1FD003D0927243F093D08270E35000001359103
+:10A20D000002356F0003200C35000001359100026E
+:10A21D00358500038D00BFA98F720706461B3501DA
+:10A22D0000098D00C9AEA631C1064D260A8D00B3B9
+:10A23D00A03501000820023F088D00A99B260A8D3C
+:10A24D0000A8522704AC00A1C9725F064DAC00876F
+:10A25D003E8D00A263878D00BFA98D00C6E4878D5A
+:10A26D0000A7A935000001358B0002353700038D9D
+:10A27D0000A25E3500000135950002358600038D84
+:10A28D0000BFA93500000135960002354000038D51
+:10A29D0000BFA93500000135970002353E00038D42
+:10A2AD0000BFA93500000135970002354600038D2A
+:10A2BD0000BFA93500000135950002359300038DCF
+:10A2CD0000BFA93500000135900002352C00038D2B
+:10A2DD0000BFA93500000135950002351A00038D28
+:10A2ED0000BFA9350000013592000235DD00038D58
+:10A2FD0000BFA93500000135920002356C00038DB9
+:10A30D0000BFA9350000013595000235A000038D71
+:10A31D0000BFA9350000013590000235BB00038D4B
+:10A32D0000BFA9350000013595000235AD00038D44
+:10A33D0000A344AC00BFA98D00BFA98D00A34D871C
+:10A34D0035000001358C0002359A0003878D00869B
+:10A35D00638D00A7A9350000013590000235D200AC
+:10A36D00038D00C49DA6098D00A8C6A6098D00C841
+:10A37D00D03500000135940002354600038D00A351
+:10A38D00E5A580270E350000013597000235040044
+:10A39D0003200C350000013597000235A000038D18
+:10A3AD0000BFA93500000135940002355500038D1D
+:10A3BD0000A3E54EA40F5F9701A4078D00AA963563
+:10A3CD000000013592000235EF00038D00A3E55F1B
+:10A3DD009701A40FAC0098AC8D00C49D8D00A4E82E
+:10A3ED00873B00088D00A7A93500000135910002BB
+:10A3FD00359B00038D00A4DF484F49B70827068D14
+:10A40D0000C7E320048D00C7D68D00C49D35000024
+:10A41D000135970002354E00038D00BFA93D082779
+:10A42D00068D00C7D620048D00C7E38D00C49D3571
+:10A43D000000013595000235BA00038D00C92F3299
+:10A44D000008873B00088D00A3EE8F720706461D9E
+:10A45D008D00C97227054A270A20128D00A4E8A491
+:10A46D007F20068D00A4E8AA808D00A53D8D00A952
+:10A47D009B26068D00A85226CD725F064D32000830
+:10A48D00878D0086D23B000D8D00A7A935000001F8
+:10A49D003591000235B100038D00A4DF4EA40FA449
+:10A4AD00078D00A6028D00A7E0B10826068D00C716
+:10A4BD00D620048D00C7E38D00C49D8D00C9378D56
+:10A4CD0000A5E0A10825DE8D00A5D832000DAC0059
+:10A4DD0087398D00A2638D00A4E887A609AC00C45E
+:10A4ED00463B00088D00A48E8F720706461F8D0017
+:10A4FD00C9AEA6D0CB064DA1082412A6098D00A584
+:10A50D002E88B600A48FB700848D00A53B8D00A9C1
+:10A51D009B26068D00A85226CB725F064D32000891
+:10A52D00878D00C446B700C6064D4EA4F087BA000D
+:10A53D008D00C98BA6098D00C927A608AC00C418CB
+:10A54D008D0086D23B000D8D00A7A9350000013589
+:10A55D008F000235E200038D00A4DFA40F8D00A64D
+:10A56D0002B60DB10826068D00C7D620048D00C792
+:10A57D00E38D00A7D524068D0087532037A00A2729
+:10A58D00114A27144A27174A271A4A271D4A2720F6
+:10A59D0020268D00A79C201C8D00A78F20168D00D6
+:10A5AD00A78220108D00A775200A8D00A7682004B2
+:10A5BD008D00A75B8D00C49D8D00A5E0A11025A188
+:10A5CD008D00A5D832000DAC0087398D00A34DACA0
+:10A5DD0000BFA98D00A5F28D0087538D00BFA9B6D0
+:10A5ED000D4CB70D873500000135970002359800E9
+:10A5FD0003AC00C49DB7083F0D8D00A0AE873B0096
+:10A60D00088D00A54D8F72070646228D00A64E259A
+:10A61D0009A69FCB064DA1062413A6098D00A65AA7
+:10A62D00A10A2505A6A9CB064D8D00A53B8D00A938
+:10A63D009B26068D00A85226C8725F064D32000873
+:10A64D00878D00C917A6D0CB064DA10A878D00C4F2
+:10A65D0046A4F0B700A6D0CB064D878D00A35A8F28
+:10A66D00720706461E8D00AA6D27084A270B4A273A
+:10A67D000E20108D00A450200A8D00A4EE20048D14
+:10A68D0000A60B8D00A99B26068D00A85226CC7224
+:10A69D005F064D878D0086D28D00867F8D00A7A920
+:10A6AD00350000013591000235F200038D00C49D87
+:10A6BD00A60A8D00A8C6A60A8D00C8D0A60A8D00D0
+:10A6CD00C446A40FB7083F0DAE06AABF0EB60DB116
+:10A6DD000826068D00C7D620048D00C7E38D00A780
+:10A6ED00D524068D0087532037A00A27114A271439
+:10A6FD004A27174A271A4A271D4A272020268D0048
+:10A70D00A79C201C8D00A78F20168D00A7822010DE
+:10A71D008D00A775200A8D00A76820048D00A75B0A
+:10A72D008D00C49D8D00A5F2BE0E8D008F2C8D0069
+:10A73D00BFA9B60D4CB70DBE0E1C0003BF0EA11068
+:10A74D00258B8D00A5D88D0086AAAC0087393500E4
+:10A75D0000013597000235D0000387350000013523
+:10A76D0097000235CE0003873500000135970002B2
+:10A77D0035CC000387350000013597000235CA003E
+:10A78D000387350000013597000235C80003873572
+:10A79D000000013597000235C6000387A61B8D000A
+:10A7AD00C9063500000135970002359400038D0070
+:10A7BD00C49DA61B8D00C90635000001359700020A
+:10A7CD0035B00003AC00C49D8D00C49D8D00A7E085
+:10A7DD00A10A875FB60D97BF025FBF008D0084523F
+:10A7ED008D0087678D0087538D00980A4F92BD00AD
+:10A7FD0001B60D873B00088D00A6A18F7207064696
+:10A80D00308D00A64E2509A69FCB064DA10624210D
+:10A81D00A60A8D00A65AA10A2505A6A9CB064DBAF2
+:10A82D00008D00C98BA60A8D00C9B8A6098D00C47C
+:10A83D00188D00A99B26068D00A85226BA725F06B8
+:10A84D004D32000887A60DC1064D878D0086638D9C
+:10A85D0000A7A93500000135930002350100038DD5
+:10A86D0000C49DA60B8D00A8C6A60B8D00C8D035C3
+:10A87D0000000135960002351100038D00C49D8D39
+:10A88D0000A991A520270E3500000135960002354F
+:10A89D001D0003200C350000013595000235C70061
+:10A8AD00038D00BFA9350000013595000235280044
+:10A8BD00038D00AA74AC0098AC8D00C4468D00A029
+:10A8CD00AE8D0087538D0097D58D00C9378D00C78C
+:10A8DD0024873B00088D00A7A935000001359500A0
+:10A8ED0002353600038D00A25EA60B8D00A94544EE
+:10A8FD00B708A50127068D00C7E320048D00C7D634
+:10A90D008D00C49D350000013593000235590003BB
+:10A91D008D00C8E227068D00C7D620048D00C7E341
+:10A92D008D00C49D3500000135930002359C000358
+:10A93D008D00C92F320008878D00C4464EA40F87A5
+:10A94D003B00088D00A8DF8F72070646238D00C9D6
+:10A95D007227054A270A20188D00A991A4DF200629
+:10A96D008D00A991AA20B7088D00AA5B8D00AA645D
+:10A97D008D00A99B26068D00A85226C7725F064D35
+:10A98D0032000887A60BAC00C4468D00C418A6C0C3
+:10A99D00C50646878D0086D28D0086D752018D0063
+:10A9AD00C9B38D00A8588F7206064604AC00AA41A3
+:10A9BD008D00AA6D27054A270820798D00A94D2005
+:10A9CD00738D00A7A93500000135970002350D00E4
+:10A9DD00038D00A25E350000013593000235130092
+:10A9ED00038D00AA748D00AA9635000001358A00EA
+:10A9FD0002353000038D00AA8427398D00A9916B93
+:10AA0D00018D00AAA720048D00C5D48D00C689260E
+:10AA1D00F6B603A41F887B02A4E0B70184BA01B780
+:10AA2D00088D00AA648D00AA5B8D00A99B2605C622
+:10AA3D000638268D8D00A99B260A8D00A852270465
+:10AA4D00AC00A9AF725F064D5B01AC0087354508C0
+:10AA5D0000A60BAC00C539450800A60AAC00C41809
+:10AA6D008D00C917A031878D00C49DA60B8D00C424
+:10AA7D00465F9701A41F878D00C49DA60A8D008790
+:10AA8D00538D00BE06C6063887028D0086298D00BF
+:10AA9D0087678D0098C7AC00BFA95FBF02BF003F9D
+:10AAAD0008878D0086638D00A7A9350000013593B9
+:10AABD000002352500038D00C49DA60C8D00A8C68F
+:10AACD00A60C8D00C8D03500000135950002354427
+:10AADD0000038D00C8EB4EA40F448D00A0C13500BE
+:10AAED00000135930002356A00038D00C8EB444424
+:10AAFD00445F9701A4038D00AA963500000135939C
+:10AB0D000002357B00038D00C8EB5F9701A403ACF9
+:10AB1D000098AC8D0086D78D00866335FF000DA69D
+:10AB2D0030C506462604AC00AC2AC6064E2704AC3A
+:10AB3D0000AC5F72190646720B064620721B064664
+:10AB4D00A6128D00C446A5502704AC00AC5FA61814
+:10AB5D008D00C446A5012704AC00AC5F5506470027
+:10AB6D00008D00C9562404AC00AC21CE00105CCF82
+:10AB7D000010CE06495ABF0EB600A540270435FE7B
+:10AB8D00000D5FB60D97C30649B600240CAA80C709
+:10AB9D000647B60D4AB70D2005A47FC70647A61078
+:10ABAD008D00C94FAE04143F09BF0A2011B600A491
+:10ABBD00EFC7064735020000B60D8D00AC7A5FB6C3
+:10ABCD000D97B30E24193F0CBE0E72B0000CBF0EC4
+:10ABDD00C60647B700A51026D43501000020D93F81
+:10ABED0000B60F8D00AC7A8D00C8AC8D00C5B735A1
+:10ABFD00080000A6128D00C539720D0647048D00A0
+:10AC0D00C898A6058D00C675A6018D00C446A18500
+:10AC1D00274020F4B600A4F7C7064720358D00C5A0
+:10AC2D007AB7008D00AC73AE00038D00AC68B10037
+:10AC3D00250D721806468D00AC73AE0002200B7206
+:10AC4D001A06468D00AC73AE00048D00AC688D0005
+:10AC5D00AF298D00869B8D00871E8772BB0002BFBA
+:10AC6D000292BC000187AE0650AC008F2C8D00B84F
+:10AC7D00F38D008753AC00BCA2CE001002A40301DB
+:10AC8D001C0014F6270B4A270C4A27184A272420A4
+:10AC9D0038AC00AB208D00C956262955041500008F
+:10ACAD00A602201C8D00C956261A5504150000A6B3
+:10ACBD0003200D8D00C956260B5504150000A60462
+:10ACCD008D00C4188D00C8AC878D00C8ACCE064968
+:10ACDD0072BB0010CF0010878D0086D28D0086D7F5
+:10ACED008D00C9B38D00A26C8F72070646048D00CE
+:10ACFD00C9AEAE064DF6A030273A4A273F4A274443
+:10AD0D004A27494A274E4A27534A27584A2604AC10
+:10AD1D0000ADA44A2604AC00ADEE4A2604AC00AD4D
+:10AD2D00F4A0282604AC00ADFA4A2604AC00AE000F
+:10AD3D00AC00AE698D00A668AC00AE698D00A801AF
+:10AD4D00AC00AE698D00A9A1AC00AE698D00C382C7
+:10AD5D00AC00AE698D00BE72AC00AE698D00C293C1
+:10AD6D00AC00AE698D00C2D1AC00AE698D00C5D40A
+:10AD7D008D00C68926F6450308450800A6278D00D7
+:10AD8D00C9B8A6118D00A9972704AC00AE69C606F7
+:10AD9D00382604AC00AE698D00A7A9350000013539
+:10ADAD008F000235FB00038D00A25E3500000135DA
+:10ADBD00900002355C00038D00C49DA6278D00C454
+:10ADCD00468D00A0C13500000135880002354A00CE
+:10ADDD00038D00AA842604AC00AE698D00AAA720BD
+:10ADED008F8D00C20F20758D00C0A3206F8D00C206
+:10ADFD005120698D00A7A93500000135920002355B
+:10AE0D004500038D00A25E35000001358E00023530
+:10AE1D00BB00038D00C49DA6398D00C4468D00A0D6
+:10AE2D00C1350000013589000235BD00038D00AA32
+:10AE3D008427298D00AAA720048D00C5D48D00C6B6
+:10AE4D008926F6450308450800A6398D00C9B8A620
+:10AE5D00158D00A9972605C6063826978D00A99B46
+:10AE6D00260A8D00A8522704AC00ACF1725F064D86
+:10AE7D00AC008735720250C434721250C4721F5226
+:10AE8D00B0721152B6353952C1353A52C235FF52F0
+:10AE9D00C335F952C4725F52BF350152C07214529C
+:10AEAD00B0721052B5721052B8721052B0A6FDC4E5
+:10AEBD000648C7064887721152B0721152B5721109
+:10AECD0052B6721350C487721152B0725F52BF35B1
+:10AEDD000152C0721052B0A6FDC40648C70648877D
+:10AEED00721050C3721F5250721152563506525E77
+:10AEFD003509525F35C45260721052557210525856
+:10AF0D00721052508772115250721150C3874D27D3
+:10AF1D000A8D00AF2E8FC6064E26FA878D00AF2EF6
+:10AF2D008772115250725F525C3501525DC7064EE9
+:10AF3D007210525087721250C3721F52807211528A
+:10AF4D00863507528E35F4528F3524529072145235
+:10AF5D0080721052857210528872105280A604CAE7
+:10AF6D000648C706488772115280721152867211B7
+:10AF7D005285721350C38720018F72040648FA87D9
+:10AF8D00720306480D8D00AF428D00AF848D00AF6A
+:10AF9D007387AC00AE818D0086D28D0086D752139B
+:10AFAD008D00A7A94F6B018D00C9B3AE00028D00B6
+:10AFBD0087B272BB000EBF0E4F6B027B014848B7C4
+:10AFCD00088D00C9C2961C00038D00C9BDB6088D41
+:10AFDD0000C91F968D00C791B6088D00C8B5968D16
+:10AFED0000C5F0350000013597000235B300038D23
+:10AFFD0000C49DB6088D00C446B70C8D00C9C2961D
+:10B00D001C00038D00C9BD8D00C91F968D00C79111
+:10B01D00B60C8D00C8B5968D00C5F0A6208D00C963
+:10B02D00068D008777350700008D0087465F5C8DA4
+:10B03D0000C8715FB60C97B6008D008D059FA501F8
+:10B04D002704A6312002A63092BD0001B600B7013B
+:10B05D004AB7003D0126D24F92BD00058D00C9377C
+:10B06D00350000013597000235A400038D00C49D05
+:10B07D007B024C6B02B6084CB7087B02A10424047A
+:10B08D00AC00AFCE8D00C7447B014C6B01A11024E9
+:10B09D0004AC00AFC58D00C74435000001358F00ED
+:10B0AD0002356300038D00C49D8F5B13AC008735A3
+:10B0BD00A6018D00C6758D00C82FAE00058D00C987
+:10B0CD0064A6068D00C82BAE00068D00C964A607C8
+:10B0DD008D00C82BAE00078D00C964A6088D00C871
+:10B0ED002BAE00088D00C964A6098D00C82BAE00DB
+:10B0FD00098D00C964A60A8D00C82BAE000A8D000B
+:10B10D00C964A60B8D00C82BAE000B8D00C964A6BB
+:10B11D000C8D00C82BAE000C8D00C964A61D8D00D2
+:10B12D00C82BAE000D8D00C964A61E8D00C82BAEB8
+:10B13D00000E8D00C964A6268D00C82BAE000F8DA4
+:10B14D0000C964A6208D00C82BAE00108D00C96407
+:10B15D00A6218D00C82BAE00118D00C964A6278DC8
+:10B16D0000C82BAE00128D00C964A6318D00C82B0E
+:10B17D00AE00138D00C964A6338D00C82BAE00142C
+:10B18D008D00C964A6378D00C82BAE00168D00C981
+:10B19D0064A6368D00C539A6368D00C446A102269B
+:10B1AD00118D00C82FAE00178D00C964A63A8D0011
+:10B1BD00C539878D0086343B000A8D00AE818D0028
+:10B1CD00AEED5FCF0012CF00108D00C51792BC06FB
+:10B1DD0050A4078D00B633A601C40648A8014490BB
+:10B1ED0011500FA6018D00C67535FF0000A6128DFA
+:10B1FD0000C5393F00A60F8D00C539A61D8D00C4B1
+:10B20D0046A501270EA640CA0647C706478D00C8AA
+:10B21D008E2008A6BFC40647C70647A6058D00C6E3
+:10B22D0075A6018D00C446A18526F68D00C59A8DA3
+:10B23D0000C979A54027248D00AED48D00BD1CB763
+:10B24D000A2718AE0514BF0892C6088D00C906BEA0
+:10B25D00085CBF08B60A4AB70A26EDCE061672B0CC
+:10B26D000614270F720006460A8D00C4C88D00C350
+:10B27D00E920E8720106461A8D00AED48D00C97919
+:10B28D00A550260EA6188D00C446A50126048D00D6
+:10B29D00BBA28D00AF8DA6C0C40646A1402790323B
+:10B2AD00000AAC00873E8D00B488C60648720D506A
+:10B2BD000B04AA012002A4FEC706487218500F8D78
+:10B2CD0000C64C8D00AEED8D00C7738D00B0BDA6D0
+:10B2DD00058D00C6758D00C97F8D00C82F5F5C8DF3
+:10B2ED0000C8155F97BF025FBF00A6108D008607CF
+:10B2FD00AE06428D008CD9A61B8D00C4465F974FBC
+:10B30D00028D0086298D00C820A62C8D00C4465FB5
+:10B31D0097BF025FBF008D00C8204F8D00C675AE70
+:10B32D0006428D008CC1BE002602BE02260C5FCFE8
+:10B33D000642AE0007CF064420043D032710AE069B
+:10B34D00428D008CCD450700A6018D00C418A63F87
+:10B35D00C40646B700720B500B0F92BC0650444406
+:10B36D0044A4038D009629BA00C7064620048D001B
+:10B37D00C054C606468D00962F4D27F24A27054A22
+:10B38D002708200C8D00B1C020E88D00B56220E2A9
+:10B39D008F20DF8D00C773A6068D00C95DA6058DB4
+:10B3AD0000C418A6078D00C95DA6068D00C418A699
+:10B3BD00088D00C95DA6078D00C418A6098D00C9AA
+:10B3CD005DA6088D00C418A60A8D00C95DA6098D5D
+:10B3DD0000C418A60B8D00C95DA60A8D00C418A661
+:10B3ED000C8D00C95DA60B8D00C418A61D8D00C95E
+:10B3FD005DA60C8D00C418A61E8D00C95DA60D8D11
+:10B40D0000C418A6268D00C95DA60E8D00C418A611
+:10B41D00208D00C95DA60F8D00C418A6218D00C911
+:10B42D005DA6108D00C418A6278D00C95DA6118DCF
+:10B43D0000C418A6318D00C95DA6128D00C418A6D2
+:10B44D00338D00C95DA6138D00C418A6378D00C9B4
+:10B45D005DA6148D00C418A6398D00C95DA6158D85
+:10B46D0000C418A6368D00C95DA6168D00C418A699
+:10B47D003A8D00C95DA617AC00C418725F50C03577
+:10B48D00FF500235FF5003725F500035FF500735F6
+:10B49D00FF5008725F500535FF500C35FF500D728F
+:10B4AD005F500A35FF501135FF5012725F500F7209
+:10B4BD00135002721250037218500772185008720E
+:10B4CD00185005721A5007721A5008721A500972E4
+:10B4DD001B5005721C5007721C5008721C500972CB
+:10B4ED001D5005721F5007721E50087214500772BE
+:10B4FD001450087216500C7216500D7219500C72B1
+:10B50D0019500D721B500C721B500D721A500EA655
+:10B51D000CCA50A1C750A1721D500C721D500D7256
+:10B52D001C500EA630CA50A1C750A17210501172F6
+:10B53D001050127212501172125012721450117268
+:10B54D001450127216501172165012721850117248
+:10B55D001850129A873B0008A6CFC40646C7064668
+:10B56D00A6DF8D00C8AE8D00AE818D00AEED5FCF34
+:10B57D000012CF00108D00C51792BC0650A4078D88
+:10B58D0000B633A601C40648A801449011500FA679
+:10B59D00018D00C67535FF0000A6128D00C5393F1F
+:10B5AD0000A60F8D00C539A61D8D00C446A5012727
+:10B5BD000CA6408D00C94F8D00C8982006A6BF8DE2
+:10B5CD0000C8AEA6058D00C675A6018D00C446A1A6
+:10B5DD008526F68D00C5B7A6128D00C446A5402759
+:10B5ED00128D00BD1C8D00B86220088D00C4C88D61
+:10B5FD0000BA0CCE0616550647000072B006142789
+:10B60D0006B600A50827E4B600A50827088D00AEEC
+:10B61D00D48D00AC868D00AF8DA6C0C40646A1802A
+:10B62D0027B532000887B700721750027217500302
+:10B63D007217500472145002721450037214500495
+:10B64D0072145000721A50C3A6CFC4509EAA10C7D0
+:10B65D00509E72195234B6004A27144A271B4A27A6
+:10B66D00224A27294A27304A27374A273E2046AE05
+:10B67D007555BF02AE44502044AE9555BF02AE4342
+:10B68D00D0203AAE238EBF02AE438B2030AE638EF8
+:10B69D00BF02AE430B2026AEE38EBF02AE428B201F
+:10B6AD001CAEE38EBF02AE420C2012AEE38EBF0283
+:10B6BD00AE418E2008AE6555BF02AE44D0BF008DA1
+:10B6CD0000829290938D0082929FA40F88909EA4E9
+:10B6DD00F0B70184BB01C7523393575757579FC7D4
+:10B6ED0052327216523572145235721A52358752C1
+:10B6FD00118D00A7A935000001358C0002351D0004
+:10B70D00038D00A25E35000001358F00023510005B
+:10B71D00038D00C49DA6428D00C841968D00C9A918
+:10B72D00AE06383F01BF024EA40F3F04B705965C2D
+:10B73D0072BB0004F692BD00018D00C6A6965C900A
+:10B74D00BF0472BB0004F65F5C8D00C86D92BD0036
+:10B75D0005AE00028D00C86D4F92BD00058D00A293
+:10B76D005E3500000135920002351C00038D00A2EC
+:10B77D005E350000013592000235A500038D00BF36
+:10B78D00A9350000013591000235C700038D00BFBA
+:10B79D00A935000001358F0002357D00038D00BFF6
+:10B7AD00A935000001358D0002350D00038D00BF58
+:10B7BD00A95B11878D0086D28D00A7A935000001E8
+:10B7CD003596000235B100038D00BFA9350000018B
+:10B7DD00358E0002356400038D00C49DA6208D00BA
+:10B7ED00A8C6A6208D00C4468D00C71435000001E3
+:10B7FD00358E0002358100038D00C49DA6218D007C
+:10B80D00C4468D00A8CEA6218D00C8D035000001FC
+:10B81D0035920002359200038D00C49DA6208D0047
+:10B82D00C446B708A6218D00C446B7015FB6089778
+:10B83D004FBA0102BF065FBF048D0098C28D00BFD5
+:10B84D00A93500000135890002354700038D00C47C
+:10B85D009DAC0087398D0086D2B70A3F0BAE05141B
+:10B86D00F6A102251C720B064706A6C08D00C9065F
+:10B87D00A6C08D00C9064F8D00C906A6208D00C932
+:10B88D004FAE0514F6A10324043501000BB60BB120
+:10B89D000A243A5F971C0514BF08B60AB00BB70A05
+:10B8AD0092C608A1C0270EA1DB2612A6DB8D00C90A
+:10B8BD0006A6DD2008A6DB8D00C906A6DC8D00C915
+:10B8CD0006BE085CBF08B60A4AB70A26D3C60514D9
+:10B8DD002704A103250CA6C08D00C906A6DF8D0087
+:10B8ED00C8AEAC008739B7043F03C60647B702A5FB
+:10B8FD0040270435010003B602A480B7012705B621
+:10B90D00034CB703B602A5402705B604C704145F60
+:10B91D00B603973D012709B6001C0413F71D041348
+:10B92D00450302C60648A401A801B705905FB604F9
+:10B93D009097BF0072B9000090BF0620298D00C8F6
+:10B94D00782407B605449011500FCE001090935CEB
+:10B95D00CF00109302A403011C0014F6BE00D704FF
+:10B96D0014B6024CB702450201BE00B3062FCEB687
+:10B97D0003BB04878D0086D252118D00A7A9350017
+:10B98D000001358E0002359E00038D00C49D8D0093
+:10B99D00C99D8D00C845968D00C754968D00C69CD7
+:10B9AD00968D00C6378D00C99D8D00C8D48D00C9F8
+:10B9BD009DA407B708A10326068D00C7D620048DC8
+:10B9CD0000C7E38D00C49D350000013590000235A0
+:10B9DD007400038D00C96BA10526068D00C7D62006
+:10B9ED00048D00C7E38D00C49D3500000135930023
+:10B9FD000235BC00038D00C92F5B11AC008739B730
+:10BA0D0001A1C02706A1DB2725202F8D00C90F27F7
+:10BA1D0013A6F88D00C8AEA6088D00C94FCE064BF3
+:10BA2D00CF0649875FCF064BB600AA0120558D0082
+:10BA3D00C90F2752B600AA042049720106470CA669
+:10BA4D00FE8D00C8AEA6028D00C94F8D00C90F270F
+:10BA5D0035B600A5042604B6012018B600A4FBB720
+:10BA6D0000C70647B601A0DC27074A2612A6DB2031
+:10BA7D0002A6C08D00C3E9CE064B5CCF064B87B640
+:10BA8D0000A4F0C70647878D0086D252118D00A7FE
+:10BA9D00A9350000013590000235E900038D00C481
+:10BAAD009DA6338D00C841968D00C754968D00C656
+:10BABD009C968D00C637A6338D00C8D0A6338D0059
+:10BACD00C446A440B70827068D00C7E320048D00A7
+:10BADD00C7D68D00C49D350000013593000235CCCD
+:10BAED0000038D00BFA93D0827068D00C7D6200491
+:10BAFD008D00C7E38D00C49D35000001358D00021A
+:10BB0D00353000038D00C92F5B11AC0087398D00D6
+:10BB1D0086D252118D00A7A935000001358D000286
+:10BB2D00355100038D00C49DA6378D00C841968DFB
+:10BB3D0000C754968D00C69C968D00C637A6378DCE
+:10BB4D0000C8D0A6378D00C446B708A10A26068DB9
+:10BB5D0000C7D620048D00C7E38D00C49D350000BD
+:10BB6D000135900002358C00038D00C96BA10C26A8
+:10BB7D00068D00C7D620048D00C7E38D00C49D350A
+:10BB8D000000013593000235DC00038D00C92F5BE9
+:10BB9D0011AC0087398D0086343B000A35FF000A51
+:10BBAD00CE001272B00010BF08720D06470435FEAC
+:10BBBD00000A275B5FB60A97B3082418BF00BE08BA
+:10BBCD0072B00000BF088D00C7648D00C979A5084B
+:10BBDD0026E220F6BE0827103F00B6098D00C76487
+:10BBED008D00C979A50827F88D00C59A3508000084
+:10BBFD00A6128D00C539720D0647048D00C88EA69C
+:10BC0D00058D00C675A6018D00C446A18526F67268
+:10BC1D0011064632000AAC00873E8D0086D25211C5
+:10BC2D008D00A7A93500000135960002354B0003A4
+:10BC3D008D00C6208D00C841968D00C754968D008D
+:10BC4D00C69C968D00C6378D00C6AFB708A50127D7
+:10BC5D00068D00C7E320048D00C7D68D00C49D3529
+:10BC6D00000001358B0002356800038D00C8E22706
+:10BC7D00068D00C7D620048D00C7E38D00C49D3509
+:10BC8D00000001358A000235D200038D00C92F5BFB
+:10BC9D0011AC0087398D0086D28D008791B7088D44
+:10BCAD0000C8FD35080000A6128D00C5393F00A65D
+:10BCBD000E8D00C5398D00C93F720D06470635FF43
+:10BCCD0000002003450800A6228D00C53945080057
+:10BCDD008D0087534F8D00C13E7215500572155062
+:10BCED000F7217500F7214500F7217500F721250AF
+:10BCFD000F7216500A8D00C9CC350300008D00C996
+:10BD0D00A3A6128D00C446A50827F6AC0087398D71
+:10BD1D000086D23F0BA6128D00C446A52026428D6B
+:10BD2D0000C8FDA6108D00C446B7008D00C9417234
+:10BD3D000D0647034F2002A6138D00C446B70B27EF
+:10BD4D001CAE0514BF08450B0A4F8D00C44692C7A3
+:10BD5D0008BE085CBF08B60A4AB70A26EC8D00C9B2
+:10BD6D003F35700000A6128D00C5398D00C9CC3548
+:10BD7D000500008D00C9A3A6018D00C446A185262E
+:10BD8D00F6B60BAC0087393B00088D00C0F18F7201
+:10BD9D00070646518D009CD9A10624498D00C991F5
+:10BDAD00A41FB708AE064DF6A03127114A27144A35
+:10BDBD0027174A271A4A271D4A27202024B608AAE2
+:10BDCD0020201CB608AA402016B608AA602010B67E
+:10BDDD0008AA80200AB608AAA02004B608AAC0B7EF
+:10BDED00088D00C7828D00A99B26068D00A85226BE
+:10BDFD0099725F064D320008878D0086638D00872E
+:10BE0D006AB7023F008F720706462C721706465519
+:10BE1D00064F0001720F5230FBB601C752315FB6AB
+:10BE2D0000978D00879E72BB000ABF0AB60192BDB6
+:10BE3D000009B6004CB700B601A10D2706B600B13A
+:10BE4D000225C2B600B1025F2502B6029772BB0091
+:10BE5D0006BF065F5A72BB0006BF064F92BD0005B6
+:10BE6D008D00869B878D0086D28D0086D752028DE0
+:10BE7D0000C9B38D00B7C18D00AA88274B8D00AACC
+:10BE8D00A720048D00C5D48D00C68926F6BE021FDD
+:10BE9D0001965C3F018D00C862A6208D00C9B8A631
+:10BEAD000F8D00C418965C3F01BF025F5C72BB0032
+:10BEBD00028D00C862A6218D00C9B8A6108D00A9FB
+:10BECD00972605C6063826AB5B02AC0087358D007C
+:10BEDD0086D28D00A7A935000001359200023507E5
+:10BEED0000038D00C6208D00A8C68D00C6AF44B7D7
+:10BEFD0008A50127068D00C7E320048D00C7D68D48
+:10BF0D0000C49D350000013595000235D400038D28
+:10BF1D0000C8E227068D00C7D620048D00C7E38D2B
+:10BF2D0000C49D350000013588000235CC00038D1D
+:10BF3D0000C92FAC0087393B00088D00A7A935003B
+:10BF4D00000135920002357F00038D00A25E8D0049
+:10BF5D00C991A403B70826068D00C7D620048D000D
+:10BF6D00C7E38D00C49D35000001359000023544B6
+:10BF7D0000038D00C96BA10326068D00C7D62004D2
+:10BF8D008D00C7E38D00C49D35000001358D000285
+:10BF9D0035B100038D00C8C7320008878D00876A50
+:10BFAD002009720F5230FB8D00C8093F015FBF029F
+:10BFBD00BE06893B0005965C8D0096BB84852706E1
+:10BFCD0092BC000526DC350000053597000635B915
+:10BFDD0000072009720F5230FB8D00C809BE06897B
+:10BFED003B0005965C8D0096BB8485270692BC00B0
+:10BFFD000526E1873B0000720B5230407216064653
+:10C00D00555231064FCE061672B00614A3001625F2
+:10C01D000F5506480000B600A40134009011500FD2
+:10C02D0090CE0616935CCF061651024F02A41F0246
+:10C03D005172A90618C6064F90F72003C6523172E9
+:10C04D0012064632000080A6018D00C6758D00AF28
+:10C05D00128D00C5174F8D00B633725F064D8D00E2
+:10C06D00B6FC8F72070646278D00AA6D270B4A274F
+:10C07D000E4A27114A271420168D00A0EA20108D94
+:10C08D0000ACE5200A8D00A1C120048D00AFA38D69
+:10C09D0000A99B27C9873B00088D00BA948F7207B2
+:10C0AD0006462D8D00C97227054A270C2022A6337E
+:10C0BD008D00C446A4BF2008A6338D00C446AA40F7
+:10C0CD008D00C98BA6338D00C9B8A6138D00C41879
+:10C0DD008D00A99B26068D00A85226BD725F064DC8
+:10C0ED00320008878D0086343B000A8D00A7A935E4
+:10C0FD000000013593000235AC00038D009C94AE19
+:10C10D0006DABF08A60C8D00A945443F00B7015FB4
+:10C11D00B60A975CB3002604A62B2002A6208D003C
+:10C12D009B2DA10625DE8D009C8F32000AAC008769
+:10C13D003E72195005AA8072035203FBC752047256
+:10C14D00015203FBC652043D00272992BC0001B7E2
+:10C15D0004B6004AB70072035203FBB604C752047B
+:10C16D0072015203FBC652045F5C72BB0002BF0238
+:10C17D003D0026D772185005878D0086D28D00C8D8
+:10C18D00FDA6068D00C446B708A6078D00C7FDA6FF
+:10C19D00088D0086078D0087675FB60897BF02A6DA
+:10C1AD00108D0086078D0085D48D00878EA6088D95
+:10C1BD0000C7FD8D0087748D0085D4AC0087393B99
+:10C1CD0000088D00BF448F72070646228D00C9AE50
+:10C1DD008D00C991A4FCB708AE064DF6A130270A13
+:10C1ED00A133260AB608AA03B7088D00C7828D00B1
+:10C1FD00A99B26068D00A85226C8725F064D3200F7
+:10C20D0008873B00088D00B9818F72070646218D86
+:10C21D0000C97227054A270C20168D00C99DA4F868
+:10C22D00AA0320088D00C99DA4F8AA058D00C70496
+:10C23D008D00A99B26068D00A85226C9725F064D5A
+:10C24D00320008878D00BB1B8F72070646278D00B5
+:10C25D00C97227054A2710201C350A00008D00C819
+:10C26D00A6350A000020088D00C8A2350C0000A6D6
+:10C27D00148D00C4188D00A99B26068D00A852268A
+:10C28D00C3725F064D873B00088D00BEDB8F7207C2
+:10C29D0006461D8D00C97227054A270A20128D00FA
+:10C2AD00C997A4F720068D00C997AA088D00C73439
+:10C2BD008D00A99B26068D00A85226CD725F064DD6
+:10C2CD00320008873B00088D00BC278F7207064699
+:10C2DD001D8D00C97227054A270A20128D00C997A6
+:10C2ED00A4FB20068D00C997AA048D00C7348D00CC
+:10C2FD00A99B26068D00A85226CD725F064D3200F1
+:10C30D0008873B000855525000088D00AEED8D009A
+:10C31D00C7BC8D00C97F7218500C7218500D721960
+:10C32D00500A8D00C97F7218500A8D00C7BCB6081F
+:10C33D00A50126048D00AF123200088735070004D1
+:10C34D00905F90978D00876A5F5C8D00C83AB60448
+:10C35D00938D008D059FA5012704A6312002A630DF
+:10C36D0092BD0005B604B7004AB7043D0026D54F6F
+:10C37D0092BD0001878D00AAAF8F72070646048D0E
+:10C38D0000C9AEAE064DF6A1312706A13327082010
+:10C39D000A8D00BD9420048D00C1CC8D00A99B2673
+:10C3AD00068D00A85226CE725F064D877211525629
+:10C3BD00C6064E2708A6FFCB064EC7064EA606C4D8
+:10C3CD000646A1042608721506467210064672032B
+:10C3DD00064608721306467214064680B701CE0053
+:10C3ED001272B00010A303F6250F5506480000B6D3
+:10C3FD0000A40134009011500F90CE0012935CCF29
+:10C40D0000128D00C883B60190F787A11824293535
+:10C41D00AE505335565053905F9097AE06508D00E9
+:10C42D008F2C9372BB0002BF02B60092BD00017249
+:10C43D00055054FB721750548772195005A47F7222
+:10C44D00035203FBC7520472015203FBC65204721E
+:10C45D00035203FB725F520472015203FBC6520476
+:10C46D0072185005878D0086D28D00878E8D00C87D
+:10C47D00FD450900A6068D00C539450A00A6078DA4
+:10C48D0000C539450B00A6088D00C539AC008739AC
+:10C49D008D00876A2009720F5230FB8D00C8093F4D
+:10C4AD00015FBF02BE06893B0005965C8D0096BB01
+:10C4BD008485270692BC000526DC87CE061672B051
+:10C4CD000614A3000324048D00C7F090CE06149328
+:10C4DD005CCF061451024F02A41F025172A9061817
+:10C4ED0090F6878A84A4BF88868D0086C88D0086C5
+:10C4FD00CD721152B6A602CA0648C706488D00AEC7
+:10C50D00C38D0087088D0086FD80720D5230FB7241
+:10C51D0017523572155235721B5235721B50C3723C
+:10C52D001650027217500372155003877219500579
+:10C53D00AA808D00C6D372035203FBB600C7520406
+:10C54D0072015203FBC6520472185005878D008686
+:10C55D00D28D0086D78D0087918D00AAA720048DDE
+:10C56D0000C5D48D00C68926F6AC008735AE0642CF
+:10C57D008D008CC1A60D8D00C7A0A6118D0086184B
+:10C58D008D00C7A4A6058D00C7A0B60287721350F3
+:10C59D000F7217500A7215500F7217500F721650F6
+:10C5AD000F7215500F72145005877213500F7217BA
+:10C5BD00500A7215500F7217500F7216500F7215D8
+:10C5CD00500F72145005875F97BF0E5FBF0CAE0002
+:10C5DD000ABF065FBF048D00847D8D0087818D00AD
+:10C5ED00C609871C00038D00C84C8D00C9C792BDBC
+:10C5FD0000014F92BD000D8D00C937878D008462FB
+:10C60D00AEFFD0BF065F5ABF048D008462B6084CE2
+:10C61D00B708878D00BFA935000001358D000235A3
+:10C62D00EE00038D00C49DA626878D00C6C28D0029
+:10C63D00C9C78D00C8BE8D00C49D8D00C7248772EB
+:10C64D001850C3A603CA5201C75201A644CA5200CC
+:10C65D00C7520087721A50A3720B500B027580A639
+:10C66D003FC40646C70646803B0008B7088D00C983
+:10C67D00CC4508008D00C9A3320008875FB6089726
+:10C68D008D00877772BB0006BF0692BC0005875CE4
+:10C69D0072BB0002F692BD0009905FB6008D00C816
+:10C6AD00B587A6268D00C4468D00C8D9A6268D0057
+:10C6BD00C4464444875C8D00C84C8D00C83692BD7D
+:10C6CD000001AE00028772035203FBC752047201D0
+:10C6DD005203FBC652048735000001359700023521
+:10C6ED00D40003AC00BFA935000001358C00023524
+:10C6FD00720003AC00BFA98D00C98BA6318D00C996
+:10C70D0027A612AC00C4188D0087538D00C3498D28
+:10C71D00008753AC00BFA9350000013597000235E5
+:10C72D009C0003AC00C49D8D00C98BA6268D00C94D
+:10C73D0027A60EAC00C41835000001359700023550
+:10C74D00B60003AC00C49D8D00C9858D00A0AE4E12
+:10C75D00A40F3F02B703878D00B8F3AE04143F0159
+:10C76D00BF02AC00BCA28D00C30F4F8D00C675A6D5
+:10C77D0080AC00C675450800A60C8D00C927A60B18
+:10C78D00AC00C4181C000372BB0000F692BD00097A
+:10C79D00905F878D008607AE06428D0085EFAC0059
+:10C7AD008CD972115286A6FBC40648C70648808DE7
+:10C7BD0000C97F7219500C7219500D873500000198
+:10C7CD00359300023537000387350000013597009A
+:10C7DD000235C4000387350000013597000235C2CC
+:10C7ED00000387A601C40648A801449011500F8785
+:10C7FD008D00C4465F97BF025FBF0087C752315F90
+:10C80D005C72BB0006BF068772BB0002BF0292BC02
+:10C81D00000187AE06428D0085D7AC008CD98D0006
+:10C82D00C539AE0650AC008F2C8D00875372BB00FE
+:10C83D0002BF02878D00C446B7008D00C7C987901F
+:10C84D00BF0072BB0000F65F5C878990AE00118D52
+:10C85D000092B88587BF0292BC00018D00C98B87FD
+:10C86D008D00876A72BB0006BF0687CE001272B0BC
+:10C87D000010A30066875102A403015172A9001490
+:10C88D008735FF0000A622AC00C53935FF0000A694
+:10C89D0022AC00C539350C0000A637AC00C539A651
+:10C8AD00F7C40647C706478790975101A40F025159
+:10C8BD00874F92BD0001AC0087538D00BFA98D003D
+:10C8CD00C6F4878D00C4468D00C8D9878D00C71466
+:10C8DD008D00C6E4878D00BFA9B608A501878D0020
+:10C8ED00C49D8D00C99187350100008D00C9A387B6
+:10C8FD008D00C8F48D00C8F487720F5230FBC752FB
+:10C90D003187C60647B700A502878D00C947C70600
+:10C91D004D874EA40F3F00B701878D00C5394508DF
+:10C92D0000878D00A344AC00BFA98D008753AC00D8
+:10C93D00C49D3F00A60DAC00C53972170646C6064C
+:10C94D004F87CA0647C7064787CE0649A300028709
+:10C95D008D00C446B700878D00C815B700878D00C0
+:10C96D00BFA9B608878D00C917A03087A612AC00E5
+:10C97D00C446A601AC00AF1B5C8D00C85787B70835
+:10C98D0045080087A60CAC00C446A626AC00C446DC
+:10C99D00A631AC00C446A601AC00C5398D00C985D1
+:10C9AD00878D00C917878D00A0AE878D00C9278799
+:10C9BD008D00C857878D00C7C9878D00C836878DF4
+:06C9CD0000C8F48775802C
+:040000050000910066
+:00000001FF
diff --git a/bridge.c b/bridge.c
new file mode 100644
index 0000000..0edf156
--- /dev/null
+++ b/bridge.c
@@ -0,0 +1,115 @@
+#include "config.h"
+#include "bridge.h"
+#include "spi.h"
+#include "rfm98w.h"
+#include "uart.h"
+#include "timer.h"
+
+#define IRQ_CHECK() ( spi_readreg(RF98_REG_12_IRQ_FLAGS) & \
+ (RF98_RX_DONE | RF98_VALID_HEADER) )
+
+#define MODEMSTAT_CHECK() ( spi_readreg(RF98_REG_18_MODEM_STAT) & \
+ RF98_MODEM_STATUS_SIGNAL_DETECTED )
+
+static void slice_and_transmit(void);
+
+void bridge()
+{
+ uint8_t len;
+
+ tim1_start();
+ tim2_start();
+ CBUF_Init(urxcbuf);
+ uart_stop();
+ uart_init(cfg[CFG_TNC_IDX] & CFG_TNC_SSP_MASK);
+ RTS_SET_LOW;
+
+ rf_setopmode(RF98_MODE_STDBY);
+ spi_writereg(RF98_REG_12_IRQ_FLAGS, 0xFF); // clear interrupts
+ spi_writereg(RF98_REG_0F_FIFO_RX_BASE_ADDR, RF_FIFO_RXBASE);
+
+ /* Set header implicit flag */
+ if ( spi_readreg(RF98_REG_1D_MODEM_CONFIG1) &
+ RF98_IMPLICIT_HEADER_MODE_ON )
+ {
+ flag.header_implicit = true;
+ /* max PayloadLength for receiving */
+ spi_writereg(RF98_REG_22_PAYLOAD_LENGTH, 0xFF);
+ } else
+ {
+ flag.header_implicit = false;
+ }
+
+ rf_setopmode(RF98_MODE_RXCONTINUOUS);
+ while ( spi_readreg(RF98_REG_01_OP_MODE) != (RF98_LONG_RANGE_MODE | RF98_MODE_RXCONTINUOUS) );
+ RX_ON;
+
+ do
+ {
+ /* process incoming Radio data */
+ if ( spi_readreg(RF98_REG_12_IRQ_FLAGS) & RF98_RX_DONE )
+ {
+ tim1_restart();
+ len = rf_recv();
+ for (uint8_t i = 0; i < len; i++)
+ {
+ uart_putchar(rf_rxbuf[i]);
+ }
+ }
+
+ /* process incoming UART data */
+ while ( CBUF_Len(utmpbuf) && !flag.uart_rx_complete )
+ {
+ cbuf_push(cbuf_pop2());
+ }
+
+ if ( flag.uart_rx_complete )
+ {
+ tim1_restart();
+ if ( !IRQ_CHECK() && !MODEMSTAT_CHECK() ) // transmit if not receiving
+ {
+ slice_and_transmit();
+ }
+ }
+ adaptive_sleep();
+ } while (flag.tnc_mode == MODE_BRIDGE);
+}
+
+static void slice_and_transmit()
+{
+ uint16_t len;
+ uint8_t payload_sz = RF96_MTU;
+
+ len = CBUF_Len(urxcbuf);
+ if ( flag.header_implicit )
+ payload_sz--;
+ if ( len )
+ {
+ while ( len > payload_sz )
+ {
+ len -= payload_sz;
+ rf_send(rf_txbuf, assemble(payload_sz, 0));
+ while ( ! (spi_readreg(RF98_REG_12_IRQ_FLAGS) & RF98_TX_DONE) ); // block
+ }
+
+ if ( len > 0 )
+ {
+ rf_send(rf_txbuf, assemble(len, 0));
+ while ( ! (spi_readreg(RF98_REG_12_IRQ_FLAGS) & RF98_TX_DONE) ); // block
+ }
+
+ /* Return to RX */
+ RX_ON;
+ //rf_setopmode(RF98_MODE_STDBY); /* <-- after TX already in STDBY */
+ spi_writereg(RF98_REG_12_IRQ_FLAGS, RF98_TX_DONE); /* clear interrupt */
+ /* set maximum PayloadLength for receiving in implicit mode */
+ if ( flag.header_implicit )
+ {
+ spi_writereg(RF98_REG_22_PAYLOAD_LENGTH, 0xFF);
+ }
+ rf_setopmode(RF98_MODE_RXCONTINUOUS);
+ while ( spi_readreg(RF98_REG_01_OP_MODE) != (RF98_LONG_RANGE_MODE | RF98_MODE_RXCONTINUOUS) );
+ }
+
+ flag.uart_rx_complete = false;
+}
diff --git a/bridge.h b/bridge.h
new file mode 100644
index 0000000..d959c61
--- /dev/null
+++ b/bridge.h
@@ -0,0 +1,7 @@
+// bridge.h
+#ifndef BRIDGE_H
+#define BRIDGE_H
+
+void bridge(void);
+
+#endif
\ No newline at end of file
diff --git a/config.h b/config.h
new file mode 100644
index 0000000..0aad262
--- /dev/null
+++ b/config.h
@@ -0,0 +1,187 @@
+// config.h
+#ifndef CONFIG_H
+#define CONFIG_H
+
+#include
+#include
+#include
+#include "CBUF.h"
+#include "util.h"
+
+#define HIGH 1
+#define LOW 0
+
+
+#define F_MASTER 16 /* MCU clock */
+#define RF_XTAL 32000000.0 /* SX1278 crystal frq */
+#define RF_FIFO_TXBASE 0x00
+#define RF_FIFO_RXBASE 0x00
+#define RF96_MTU 255 /* LoRa MTU */
+#define RFBUF_SZ RF96_MTU + 1 /* radio buf size */
+#define urxcbuf_SIZE 1024
+#define utmpbuf_SIZE 32
+#define TTBL_SZ ( utmpbuf_SIZE / 10 ) /* tmp TX buffer low watermark*/
+#define TTBH_SZ ( utmpbuf_SIZE - 10 ) /* tmp TX buffer high watermark*/
+#define TBL_SZ ( urxcbuf_SIZE / 10 ) /* TX buffer low watermark*/
+#define TBH_SZ ( urxcbuf_SIZE - 10 ) /* TX buffer high watermark*/
+#define CFG_SZ 24
+#define MODE_SETUP 0 /* TNC SETUP MODE */
+#define MODE_BRIDGE 1 /* TNC TRANSPARENT MODE */
+#define MODE_KISS 2 /* TNC KISS MODE */
+#define P_PARAMETER cfg[CFG_PPR_IDX]
+#define TXDELAY cfg[CFG_TXD_IDX] /* in 10 ms units */
+#define SLOTTIME cfg[CFG_SLT_IDX] /* in 10 ms units */
+
+#define LNA_CTL PB_ODR_ODR2
+#define BIAS_CTL PC_ODR_ODR3
+#define NRST_CTL PC_ODR_ODR4
+#define M0_SW PC_IDR_IDR5
+#define M1_SW PC_IDR_IDR6
+#define AUX PD_ODR_ODR0
+#define PA_CTL PD_ODR_ODR1
+#define TR_SW10 PD_ODR_ODR2 /* TX/RX switch, STM8 pin 10 */
+#define TR_SW11 PD_ODR_ODR3 /* TX/RX switch, STM8 pin 11 */
+#define TCXO_CTL PD_ODR_ODR4
+
+#define PA_ENABLED HIGH
+#define PA_DISABLED LOW
+#define LNA_ENABLED HIGH
+#define LNA_DISABLED LOW
+#define TCXO_ENABLED HIGH
+#define TCXO_DISABLED LOW
+#define BIAS_ON HIGH
+#define BIAS_OFF LOW
+
+#define CFG_TNC_IDX 0 /* TNC parameters: mode, serial speed */
+#define CFG_RND_IDX 1 /* salt for random generator */
+#define CFG_TXD_IDX 2 /* TXDelay CSMA parameter */
+#define CFG_PPR_IDX 3 /* P-persistence CSMA parameter */
+#define CFG_SLT_IDX 4 /* SlotTime CSMA parameter */
+#define CFG_FRU_IDX 5 /* freq. MSByte */
+#define CFG_FRM_IDX 6 /* freq. mid byte */
+#define CFG_FRL_IDX 7 /* freq. LSbyte */
+#define CFG_PAC_IDX 8 /* PA config */
+#define CFG_PAR_IDX 9 /* PA ramp */
+#define CFG_OCP_IDX 10 /* overcurrent protection */
+#define CFG_LNA_IDX 11 /* LNA cfg */
+#define CFG_MC1_IDX 12 /* RegModemConfig1 */
+#define CFG_MC2_IDX 13 /* RegModemConfig2 */
+#define CFG_MC3_IDX 14 /* RegModemConfig3 */
+#define CFG_PRU_IDX 15 /* preamble length MSByte */
+#define CFG_PRL_IDX 16 /* -//- LSByte */
+#define CFG_PPM_IDX 17 /* PPM correction */
+#define CFG_DEO_IDX 18 /* DetectOptimize */
+#define CFG_IIQ_IDX 19 /* InvertIq */
+#define CFG_DTT_IDX 20 /* DetectionThreshold */
+#define CFG_SCW_IDX 21 /* SyncWord */
+#define CFG_R36_IDX 22 /* Register 0x36 (See SX127x Errata note, section 2.1.) */
+#define CFG_R3A_IDX 23 /* Register 0x3A (See SX127x Errata note, section 2.1.) */
+
+#define CFG_TNC_SSP_MASK 0x07
+#define CFG_TNC_MOD_MASK 0x18
+
+//-------------- Macros -----------------
+#define BIT(n) ( 1<<(n) )
+#define BIT_SET(y, mask) ( y |= (mask) )
+#define BIT_CLELAR(y, mask) ( y &= ~(mask) )
+#define BIT_FLIP(y, mask) ( y ^= (mask) )
+//! Create a bitmask of length
+#define BIT_MASK(len) ( BIT(len)-1 )
+//! Create a bitfield mask of length starting at bit
+#define BF_MASK(start, len) ( BIT_MASK(len)<<(start) )
+//! Prepare a bitmask for insertion or combining
+#define BF_PREP(x, start, len) ( ((x)&BIT_MASK(len)) << (start) )
+//! Extract a bitfield of length starting at bit from y
+#define BF_GET(y, start, len) ( ((y)>>(start)) & BIT_MASK(len) )
+//! Insert a new bitfield value x into y
+#define BF_SET(y, x, start, len) \
+ ( y= ((y) &~ BF_MASK(start, len)) | BF_PREP(x, start, len) )
+//! Massage x for use in bitfield
+#define BFN_PREP(x, name) ( ((x)<>name##_SHIFT )
+//! Set bitfield name from y to x: y.name= x.
+#define BFN_SET(y, x, name) (y = ((y)&~name##_MASK) | BFN_PREP(x,name) )
+
+#define __wait_for_interrupt() asm("wfi")
+#define __disable_interrupt() asm("sim")
+#define __enable_interrupt() asm("rim")
+#define __halt() asm("halt")
+#define __reset() asm("dc8 $75") /* Initiate MCU RESET by illegal opcode */
+#define HIBYTE(x) ((char*)(&(x)))[0]
+#define LOBYTE(x) ((char*)(&(x)))[1]
+
+#define TX_OFF \
+ PA_CTL = PA_DISABLED; \
+ BIAS_CTL = BIAS_OFF; \
+ TR_SW10 = LOW; \
+ TR_SW11 = LOW
+#define TX_ON \
+ RX_OFF; \
+ TR_SW10 = HIGH; \
+ TR_SW11 = LOW; \
+ PA_CTL = PA_ENABLED; \
+ BIAS_CTL = BIAS_ON
+#define RX_OFF \
+ LNA_CTL = LNA_DISABLED; \
+ TR_SW10 = LOW; \
+ TR_SW11 = LOW
+#define RX_ON \
+ TX_OFF; \
+ TR_SW11 = HIGH; \
+ TR_SW10 = LOW; \
+ LNA_CTL = LNA_ENABLED
+
+#define RTS_SET_HIGH \
+ AUX = flag.rts_inverted ? HIGH : LOW;
+#define RTS_SET_LOW \
+ AUX = flag.rts_inverted ? LOW : HIGH;
+
+extern uint32_t seed;
+extern struct flag_t flag;
+extern volatile struct urxcbuf_t urxcbuf;
+extern volatile struct utmpbuf_t utmpbuf;
+extern char rf_rxbuf[RFBUF_SZ];
+extern char rf_txbuf[RFBUF_SZ];
+extern uint8_t *cfg;
+
+struct flag_t {
+ unsigned char uart_rx_complete : 1; /* UART IDLE char received */
+ unsigned char uart_timer_restarted : 1; /* serial symbol timeout */
+ unsigned char uart_timer_pending : 1; /* serial symbol timeout */
+ unsigned char uart_rxne : 1; /* UART RX buf not empty */
+ unsigned char txdelay_engaged : 1;
+ unsigned char slottime_engaged : 1;
+ unsigned char tnc_mode : 2;
+ unsigned char kiss_sof : 1; /* Start of Frame */
+ unsigned char kiss_payload : 1;
+ unsigned char kiss_escaped : 1;
+ unsigned char kiss_frame_ready : 1;
+ unsigned char kiss_frame_first : 1;
+ unsigned char kiss_start_sent : 1;
+ unsigned char header_implicit : 1;
+ unsigned char rf_multiframe : 1;
+ unsigned char rts_inverted : 1; /* RTS output inverted */
+ unsigned char tfc_timeout : 1; /* traffic timeout */
+ unsigned char t1s_reset : 1; /* 1s timer reset */
+};
+
+/* UART RX circular buffer */
+struct urxcbuf_t {
+ uint16_t m_getIdx;
+ uint16_t m_putIdx;
+ uint8_t m_entry[ urxcbuf_SIZE ];
+};
+
+/* tmp UART RX circular buffer */
+struct utmpbuf_t {
+ uint16_t m_getIdx;
+ uint16_t m_putIdx;
+ uint8_t m_entry[ utmpbuf_SIZE ];
+};
+
+void reset_radio(void);
+void eeprom_populate(void);
+void eeprom_write_byte(uint8_t, uint8_t);
+
+#endif
diff --git a/intr.c b/intr.c
new file mode 100644
index 0000000..e75ac72
--- /dev/null
+++ b/intr.c
@@ -0,0 +1,78 @@
+#include "config.h"
+#include "uart.h"
+#include "timer.h"
+#include "kiss.h"
+
+#pragma vector=USART_R_RXNE_vector
+__interrupt void USART_R_RXNE_handler(void) {
+ if (USART1_SR & MASK_USART1_SR_RXNE) {
+ flag.uart_rxne = true;
+ uart_dr = USART1_DR;
+ /* Hardware flow control */
+ if ( CBUF_Len(utmpbuf) >= TTBH_SZ )
+ {
+ RTS_SET_HIGH;
+ }
+ CBUF_Push(utmpbuf, uart_dr);
+ }
+ else {
+ USART1_DR; // implicit read
+ }
+ flag.uart_timer_restarted = true;
+}
+
+#pragma vector=EXTI5_vector // Pin 5 interrupts, see datasheet
+__interrupt void EXTI5_handler(void)
+{
+ /* M0 Switch handler */
+ EXTI_SR1_P5F = 1; // Clear the interrupt flag
+ if ( M0_SW == HIGH )
+ __reset();
+ else
+ flag.tnc_mode = MODE_SETUP;
+}
+
+#pragma vector=EXTI6_vector // Pin 6 interrupts, see datasheet
+__interrupt void EXTI6_handler(void)
+{
+ /* M1 Switch handler */
+ __reset();
+}
+
+#pragma vector=TIM1_OVR_UIF_vector
+__interrupt void TIM1_OVR_UIF_handler(void)
+{
+ TIM1_SR1_UIF = 0; //clear interrupt flag
+ flag.tfc_timeout = true;
+ tim1_stop();
+}
+
+#pragma vector=TIM2_OVR_UIF_vector
+__interrupt void TIM2_OVR_UIF_handler(void)
+{
+ TIM2_SR1_UIF = 0; //clear interrupt flag
+
+ if (tim2_cnt)
+ {
+ tim2_cnt--;
+ }
+
+ if ( flag.uart_timer_pending && !flag.uart_timer_restarted )
+ {
+ flag.uart_timer_pending = false;
+ flag.uart_rx_complete = true;
+ }
+
+ if ( flag.uart_timer_restarted )
+ {
+ flag.uart_timer_restarted = false;
+ flag.uart_timer_pending = true;
+ }
+}
+
+#pragma vector=TIM3_OVR_UIF_vector
+__interrupt void TIM3_OVR_UIF_handler(void)
+{
+ TIM3_SR1_UIF = 0; //clear interrupt flag
+ flag.t1s_reset = false;
+}
diff --git a/intr.h b/intr.h
new file mode 100644
index 0000000..eb4b944
--- /dev/null
+++ b/intr.h
@@ -0,0 +1,5 @@
+// intr.h
+#ifndef INTR_H
+#define INTR_H
+
+#endif
diff --git a/kiss.c b/kiss.c
new file mode 100644
index 0000000..0e09766
--- /dev/null
+++ b/kiss.c
@@ -0,0 +1,335 @@
+#include "config.h"
+#include "kiss.h"
+#include "spi.h"
+#include "rfm98w.h"
+#include "uart.h"
+#include "timer.h"
+
+// Radio frame first byte:
+// 2 - first multiframe
+// 1 - intermediate multiframe
+// 0 - last multiframe
+// other value - single frame
+
+
+#define IRQ_CHECK() ( spi_readreg(RF98_REG_12_IRQ_FLAGS) & \
+ (RF98_RX_DONE | RF98_VALID_HEADER) )
+
+#define MODEMSTAT_CHECK() ( spi_readreg(RF98_REG_18_MODEM_STAT) & \
+ RF98_MODEM_STATUS_SIGNAL_DETECTED )
+
+static uint16_t pkt_len; // length of packet to transmit via RF
+static uint16_t tmp_len;
+
+static void kiss_in(uint8_t);
+static void kiss_out(uint8_t);
+static void kiss_process_cmd(void);
+static void csma_transmit(void);
+static void cmd_set_txdelay(void);
+static void cmd_set_p(void);
+static void cmd_set_slottime(void);
+
+void kiss()
+{
+ flag.txdelay_engaged = false;
+ flag.slottime_engaged = false;
+ flag.kiss_start_sent = false;
+
+ tim1_start();
+ tim2_start();
+ CBUF_Init(urxcbuf);
+ uart_stop();
+ uart_init(cfg[CFG_TNC_IDX] & CFG_TNC_SSP_MASK);
+ RTS_SET_LOW;
+
+ rf_setopmode(RF98_MODE_STDBY);
+ spi_writereg(RF98_REG_12_IRQ_FLAGS, 0xFF); // clear interrupts
+ spi_writereg(RF98_REG_0F_FIFO_RX_BASE_ADDR, RF_FIFO_RXBASE);
+
+ /* Set header implicit flag */
+ if ( spi_readreg(RF98_REG_1D_MODEM_CONFIG1) &
+ RF98_IMPLICIT_HEADER_MODE_ON )
+ {
+ flag.header_implicit = true;
+ /* max PayloadLength for receiving */
+ spi_writereg(RF98_REG_22_PAYLOAD_LENGTH, 0xFF);
+ } else
+ {
+ flag.header_implicit = false;
+ }
+
+ rf_setopmode(RF98_MODE_RXCONTINUOUS);
+ while ( spi_readreg(RF98_REG_01_OP_MODE) != (RF98_LONG_RANGE_MODE | RF98_MODE_RXCONTINUOUS) );
+ RX_ON;
+
+ do
+ {
+ /* process incoming radio data */
+ if ( spi_readreg(RF98_REG_12_IRQ_FLAGS) & RF98_RX_DONE )
+ {
+ kiss_out(rf_recv());
+ }
+
+ /* process incoming UART data */
+ while ( CBUF_Len(utmpbuf) && !flag.kiss_frame_ready )
+ {
+ kiss_in(cbuf_pop2());
+ }
+
+ if ( flag.kiss_frame_ready )
+ {
+ tim1_restart();
+ kiss_process_cmd();
+ }
+ adaptive_sleep();
+ } while (flag.tnc_mode == MODE_KISS);
+}
+
+static void kiss_in(uint8_t byte)
+{
+ switch(byte)
+ {
+ case FEND:
+ if ( flag.kiss_payload )
+ {
+ /* EOF received */
+ flag.kiss_sof = false;
+ flag.kiss_payload = false;
+ flag.kiss_escaped = false;
+ flag.kiss_frame_ready = true;
+ pkt_len = tmp_len;
+ }
+ else {
+ /* SOF received */
+ tmp_len = 0;
+ flag.kiss_sof = true;
+ }
+ break;
+ case FESC:
+ if ( flag.kiss_payload )
+ {
+ flag.kiss_escaped = true;
+ }
+ break;
+ default:
+ if ( flag.kiss_sof )
+ {
+ flag.kiss_sof = false;
+ flag.kiss_payload = true;
+ }
+
+ if ( flag.kiss_payload )
+ {
+ if ( !flag.kiss_escaped )
+ {
+ cbuf_push(byte);
+ tmp_len++;
+ }
+ else {
+ flag.kiss_escaped = false;
+
+ switch ( byte )
+ {
+ case TFESC:
+ cbuf_push(FESC);
+ tmp_len++;
+ break;
+ case TFEND:
+ cbuf_push(FEND);
+ tmp_len++;
+ break;
+ default:
+ // framing error, drop the frame
+ flag.kiss_sof = false;
+ flag.kiss_payload = false;
+ flag.kiss_escaped = false;
+ flag.kiss_frame_ready = false;
+ } // switch
+
+ } // else kiss_escaped
+ } // if kiss_payload
+
+ } // switch
+}
+
+static void kiss_out(const uint8_t len)
+{
+ uint8_t idx = 0;
+
+ if ( rf_rxbuf[0] >= FRAME_FIRST )
+ {
+ if ( flag.kiss_start_sent ) // error in framing, start over
+ uart_putchar(FEND);
+
+ uart_putchar(FEND);
+ uart_putchar(CMD_DATA_FRAME);
+ flag.kiss_start_sent = true;
+ }
+
+ /* check for multiframe */
+ if ( rf_rxbuf[0] <= FRAME_FIRST )
+ idx++;
+
+ for (uint8_t i = idx; i < len; i++)
+ {
+ switch ( rf_rxbuf[i] )
+ {
+ case FESC:
+ uart_putchar(FESC);
+ uart_putchar(TFESC);
+ break;
+ case FEND:
+ uart_putchar(FESC);
+ uart_putchar(TFEND);
+ break;
+ default:
+ uart_putchar(rf_rxbuf[i]);
+ }
+ }
+
+ /* last or single frame end */
+ if ( rf_rxbuf[0] == FRAME_LAST || rf_rxbuf[0] > FRAME_FIRST )
+ {
+ uart_putchar(FEND);
+ flag.kiss_start_sent = false;
+ }
+}
+
+static void csma_transmit()
+{
+ uint16_t len;
+ uint8_t payload_sz = RF96_MTU;
+
+ if ( (flag.txdelay_engaged || flag.slottime_engaged) && tim2_cnt )
+ {
+ /* TxDelay or SlotTime is not expired */
+ return;
+ }
+
+ /* Delay is expired */
+ if ( flag.txdelay_engaged || flag.slottime_engaged )
+ {
+ flag.txdelay_engaged = false;
+
+ if ( flag.slottime_engaged )
+ {
+ flag.slottime_engaged = false;
+
+ if ( IRQ_CHECK() || MODEMSTAT_CHECK() )
+ {
+ /* Oops, are we still receiving? */
+ return;
+ }
+ }
+
+ /* first byte is KISS CMD */
+ if ( pkt_len > 1 ) /* frame size >0 */
+ {
+ /* get rid of KISS CMD byte */
+ CBUF_AdvancePopIdx( urxcbuf );
+ len = pkt_len-1;
+ if ( flag.header_implicit )
+ payload_sz--;
+
+ if ( pkt_len > payload_sz )
+ {
+ flag.rf_multiframe = true;
+ payload_sz--;
+ }
+ else
+ flag.rf_multiframe = false;
+
+ flag.kiss_frame_first = true;
+ while ( len > payload_sz )
+ {
+ len -= payload_sz;
+ if ( flag.kiss_frame_first )
+ {
+ flag.kiss_frame_first = false;
+ rf_send(rf_txbuf, assemble(payload_sz, FRAME_FIRST)); // blocking op
+ }
+ else
+ rf_send(rf_txbuf, assemble(payload_sz, FRAME_INTER)); // blocking op
+ }
+ rf_send(rf_txbuf, assemble(len, FRAME_LAST)); // blocking op
+ /* Return to RX */
+ flag.kiss_frame_ready = false;
+ RX_ON;
+ //rf_setopmode(RF98_MODE_STDBY); /* <-- after TX already in STDBY */
+ spi_writereg(RF98_REG_12_IRQ_FLAGS, RF98_TX_DONE); /* clear interrupt */
+ /* set maximum PayloadLength for receiving in implicit mode */
+ if ( flag.header_implicit )
+ {
+ spi_writereg(RF98_REG_22_PAYLOAD_LENGTH, 0xFF);
+ }
+ rf_setopmode(RF98_MODE_RXCONTINUOUS);
+ while ( spi_readreg(RF98_REG_01_OP_MODE) != (RF98_LONG_RANGE_MODE | RF98_MODE_RXCONTINUOUS) );
+ }
+ else {
+ flag.kiss_frame_ready = false;
+ }
+ }
+ else {
+ // p-persistence CSMA
+ if ( random() <= P_PARAMETER )
+ {
+ flag.txdelay_engaged = true;
+ tim2_restart(TXDELAY);
+ } else
+ {
+ flag.slottime_engaged = true;
+ tim2_restart(SLOTTIME);
+ }
+ }
+}
+
+static void kiss_process_cmd()
+{
+ // first byte is KISS cmd
+ switch ( CBUF_Get( urxcbuf, 0 ) )
+ {
+ case CMD_DATA_FRAME:
+ csma_transmit();
+ break;
+ case CMD_TX_DELAY:
+ cmd_set_txdelay();
+ flag.kiss_frame_ready = false;
+ break;
+ case CMD_P:
+ cmd_set_p();
+ flag.kiss_frame_ready = false;
+ break;
+ case CMD_SLOT_TIME:
+ cmd_set_slottime();
+ flag.kiss_frame_ready = false;
+ break;
+ default:
+ flag.kiss_frame_ready = false;
+ /* Pop out unprocessed packet data */
+ urxcbuf.m_getIdx += pkt_len;
+ }
+}
+
+static void cmd_set_txdelay()
+{
+ if ( pkt_len == 2 )
+ {
+ eeprom_write_byte(CFG_TXD_IDX, rf_txbuf[1]);
+ }
+}
+
+static void cmd_set_p()
+{
+ if ( pkt_len == 2 )
+ {
+ eeprom_write_byte(CFG_PPR_IDX, rf_txbuf[1]);
+ }
+}
+
+static void cmd_set_slottime()
+{
+ if ( pkt_len == 2 )
+ {
+ eeprom_write_byte(CFG_SLT_IDX, rf_txbuf[1]);
+ }
+}
diff --git a/kiss.h b/kiss.h
new file mode 100644
index 0000000..59f4919
--- /dev/null
+++ b/kiss.h
@@ -0,0 +1,27 @@
+// kiss.h
+#ifndef KISS_H
+#define KISS_H
+
+#define FEND 0xC0
+#define FESC 0xDB
+#define TFEND 0xDC
+#define TFESC 0xDD
+
+#define CMD_DATA_FRAME 0x00
+#define CMD_TX_DELAY 0x01
+#define CMD_P 0x02
+#define CMD_SLOT_TIME 0x03
+#define CMD_SET_HARDWARE 0x06
+#define CMD_SET_FEC 0x08
+
+#define CMD_SF_MASK 0xA0
+#define CMD_BW_MASK 0xB0
+#define CMD_PREAMBLE_MASK 0xC0
+
+#define FRAME_FIRST 2 /* First frame */
+#define FRAME_INTER 1 /* Intermediate frame */
+#define FRAME_LAST 0 /* last frame */
+
+void kiss(void);
+
+#endif
\ No newline at end of file
diff --git a/main.c b/main.c
new file mode 100644
index 0000000..08fb27e
--- /dev/null
+++ b/main.c
@@ -0,0 +1,268 @@
+// STM8L 433T30D KISS TNC
+// Version 1.0: June-July 2018 (c) UR5VIB
+// * initial release for RFM98
+// Version 1.1 September 2018
+// * added UART hardware flow control
+// * improved performance
+//
+// E32-TTL-1W model 433T30D
+// Due to poor system design it requires UART hardware flow ctl.
+// DIOx are unpopulated on the PCB which impacts performance.
+//
+// STM8L pinout:
+// 1 - NRST 8 - AUX 15 - 22 -
+// 2 - TX 9 - PA_ON 16 - NSS 23 -
+// 3 - RX 10 - RX_ON 17 - SCK 24 - BIAS
+// 4 - 11 - TX_ON 18 - MOSI 25 - RST
+// 5 - 12 - 19 - MISO 26 - M0
+// 6 - 13 - 20 - TCX_ON 27 - M1
+// 7 - 14 - LNA_ON 21 - 28 - SWIM
+
+#include "config.h"
+#include "timer.h"
+#include "uart.h"
+#include "spi.h"
+#include "rfm98w.h"
+#include "menue.h"
+#include "bridge.h"
+#include "kiss.h"
+
+struct flag_t flag;
+volatile struct urxcbuf_t urxcbuf;
+volatile struct utmpbuf_t utmpbuf;
+char rf_rxbuf[RFBUF_SZ];
+char rf_txbuf[RFBUF_SZ];
+
+uint8_t *cfg = (uint8_t *) 0x1000; // EEPROM base address.
+
+static void mcu_init(void);
+static void modemcfg_populate(void);
+void reset_radio(void);
+
+uint32_t seed;
+
+void main( void )
+{
+ mcu_init();
+
+ // TTL or RS-232 flow ctl
+ if ( M1_SW == HIGH )
+ flag.rts_inverted = true;
+ else
+ flag.rts_inverted = false;
+
+ TCXO_CTL = TCXO_ENABLED;
+ spi_init();
+ tim2_start();
+
+ // Reset SX1278
+ reset_radio();
+ // Set LoRa mode
+ rf_setopmode(RF98_MODE_SLEEP);
+ rf_setopmode(RF98_LONG_RANGE_MODE);
+
+ // Load config into the radio
+ modemcfg_populate();
+
+ /* Update random() salt */
+ rf_setopmode(RF98_MODE_RXCONTINUOUS);
+ delay_10ms(1);
+ seed = ((uint32_t)cfg[CFG_RND_IDX] << 16);
+ seed |= (spi_readreg(RF98_REG_1B_RSSI_VALUE) << 8);
+ seed |= spi_readreg(RF98_REG_2C_RSSI_WIDEBAND);
+ rf_setopmode(RF98_MODE_SLEEP);
+ if ( seed == 0 )
+ seed = 0x07; /* kludge */
+ /* save it for next use */
+ if ( (uint8_t)seed )
+ {
+ eeprom_write_byte(CFG_RND_IDX, (uint8_t)seed);
+ }
+
+ /* Set initial TNC mode according to switch positon */
+ if ( M0_SW == HIGH )
+ {
+ flag.tnc_mode = ((cfg[CFG_TNC_IDX] & CFG_TNC_MOD_MASK) >> 3);
+ } else
+ {
+ flag.tnc_mode = MODE_SETUP;
+ }
+
+ /* Main programme loop */
+ while(1)
+ {
+ switch ( flag.tnc_mode )
+ {
+ case MODE_SETUP:
+ menue();
+ break;
+ case MODE_BRIDGE:
+ bridge();
+ break;
+ case MODE_KISS:
+ kiss();
+ break;
+ default:
+ __wait_for_interrupt();
+ }
+ }
+}
+
+void mcu_init()
+{
+ CLK_CKDIVR = 0; // 16 MHz
+
+ /* Configure unused pins as Out Push-Pull, Low. See RefMan. sect. 10.5. */
+ PA_DDR = 0xFF; // OUT
+ PA_CR1 = 0xFF; // P-P
+ PA_ODR = 0x00; // LOW
+ PB_DDR = 0xFF; // OUT
+ PB_CR1 = 0xFF; // P-P
+ PB_ODR = 0x00; // LOW
+ PC_DDR = 0xFF; // OUT
+ PC_CR1 = 0xFF; // P-P
+ PC_ODR = 0x00; // LOW
+ PD_DDR = 0xFF; // OUT
+ PD_CR1 = 0xFF; // P-P
+ PD_ODR = 0x00; // LOW
+
+ /* PA1 = RESET */
+ PA_DDR_DDR1 = 0; // IN
+ PA_CR1_C11 = 1; // P-U
+
+ /* PB4 = CS */
+ PB_DDR_DDR4 = 1; // OUT
+ PB_CR1_C14 = 1; // PUSH-PULL
+ PB_ODR_ODR4 = HIGH; // HIGH
+ /* PB5 = SCK */
+ PB_DDR_DDR5 = 1; // OUT
+ PB_CR1_C15 = 1; // PUSH-PULL
+ PB_CR2_C25 = 1; // 10 MHz
+ PB_ODR_ODR5 = LOW; // LOW
+ /* PB6 = MOSI */
+ PB_DDR_DDR6 = 1; // OUT
+ PB_CR1_C16 = 1; // PUSH-PULL
+ PB_CR2_C26 = 1; // 10 MHz
+ PB_ODR_ODR6 = LOW; // LOW
+ /* PB7 = MISO */
+ PB_DDR_DDR7 = 0; // IN
+ PB_CR1_C17 = 1; // PULL-UP
+
+ /* PB2 = LNA_CTL */
+ PB_DDR_DDR2 = 1; // OUT
+ PB_CR1_C12 = 1; // PUSH-PULL
+ /* PC3 = PA BIAS_CTL */
+ PC_DDR_DDR3 = 1; // OUT
+ PC_CR1_C13 = 1; // PUSH-PULL
+ /* PC4 = NRST_CTL */
+ PC_DDR_DDR4 = 0; // IN
+ PC_CR1_C14 = 0; // FLOAT
+ /* PC5 = M0_SW */
+ PC_DDR_DDR5 = 0; // IN
+ PC_CR1_C15 = 0; // FLOAT
+ PC_CR2_C25 = 1; // EXTI
+ EXTI_CR2_P5IS = 3; // int on falling & rising edge
+ /* PC6 = M1_SW */
+ PC_DDR_DDR6 = 0; // IN
+ PC_CR1_C16 = 0; // FLOAT
+ PC_CR2_C26 = 1; // EXTI
+ EXTI_CR2_P6IS = 3; // int on falling & rising edge
+ /* PD0 = AUX */
+ PD_DDR_DDR0 = 1; // OUT
+ PD_CR1_C10 = 1; // PUSH-PULL
+ /* PD1 = PA_CTL */
+ PD_DDR_DDR1 = 1; // OUT
+ PD_CR1_C11 = 1; // PUSH-PULL
+ /* PD2 = TR_SW10 TX/RX Switch */
+ PD_DDR_DDR2 = 1; // OUT
+ PD_CR1_C12 = 1; // PUSH-PULL
+ /* PD3 = TR_SW11 TX/RX Switch */
+ PD_DDR_DDR3 = 1; // OUT
+ PD_CR1_C13 = 1; // PUSH-PULL
+ /* PD4 = TCXO_CTL */
+ PD_DDR_DDR4 = 1; // OUT
+ PD_CR1_C14 = 1; // PUSH-PULL
+
+ __enable_interrupt();
+}
+
+void eeprom_populate()
+{
+ reset_radio();
+ rf_setopmode(RF98_MODE_SLEEP);
+ rf_setopmode(RF98_LONG_RANGE_MODE);
+
+ eeprom_write_byte(CFG_FRU_IDX, spi_readreg(RF98_REG_06_FRF_MSB));
+ eeprom_write_byte(CFG_FRM_IDX, spi_readreg(RF98_REG_07_FRF_MID));
+ eeprom_write_byte(CFG_FRL_IDX, spi_readreg(RF98_REG_08_FRF_LSB));
+ eeprom_write_byte(CFG_PAC_IDX, spi_readreg(RF98_REG_09_PA_CONFIG));
+ eeprom_write_byte(CFG_PAR_IDX, spi_readreg(RF98_REG_0A_PA_RAMP));
+ eeprom_write_byte(CFG_OCP_IDX, spi_readreg(RF98_REG_0B_OCP));
+ eeprom_write_byte(CFG_LNA_IDX, spi_readreg(RF98_REG_0C_LNA));
+ eeprom_write_byte(CFG_MC1_IDX, spi_readreg(RF98_REG_1D_MODEM_CONFIG1));
+ eeprom_write_byte(CFG_MC2_IDX, spi_readreg(RF98_REG_1E_MODEM_CONFIG2));
+ eeprom_write_byte(CFG_MC3_IDX, spi_readreg(RF98_REG_26_MODEM_CONFIG3));
+ eeprom_write_byte(CFG_PRU_IDX, spi_readreg(RF98_REG_20_PREAMBLE_MSB));
+ eeprom_write_byte(CFG_PRL_IDX, spi_readreg(RF98_REG_21_PREAMBLE_LSB));
+ eeprom_write_byte(CFG_PPM_IDX, spi_readreg(RF98_REG_27_PPM_CORRECTION));
+ eeprom_write_byte(CFG_DEO_IDX, spi_readreg(RF98_REG_31_DETECT_OPTIMIZ));
+ eeprom_write_byte(CFG_IIQ_IDX, spi_readreg(RF98_REG_33_INVERT_IQ));
+ eeprom_write_byte(CFG_DTT_IDX, spi_readreg(RF98_REG_37_DETECTION_THRESHOLD));
+ eeprom_write_byte(CFG_SCW_IDX, spi_readreg(RF98_REG_39_SYNC_WORD));
+ eeprom_write_byte(CFG_R36_IDX, spi_readreg(RF98_REG_36));
+ eeprom_write_byte(CFG_R3A_IDX, spi_readreg(RF98_REG_3A));
+}
+
+void modemcfg_populate()
+{
+ rf_setopmode(RF98_MODE_STDBY);
+ spi_writereg(RF98_REG_06_FRF_MSB, cfg[CFG_FRU_IDX]);
+ spi_writereg(RF98_REG_07_FRF_MID, cfg[CFG_FRM_IDX]);
+ spi_writereg(RF98_REG_08_FRF_LSB, cfg[CFG_FRL_IDX]);
+ spi_writereg(RF98_REG_09_PA_CONFIG, cfg[CFG_PAC_IDX]);
+ spi_writereg(RF98_REG_0A_PA_RAMP, cfg[CFG_PAR_IDX]);
+ spi_writereg(RF98_REG_0B_OCP, cfg[CFG_OCP_IDX]);
+ spi_writereg(RF98_REG_0C_LNA, cfg[CFG_LNA_IDX]);
+ spi_writereg(RF98_REG_1D_MODEM_CONFIG1, cfg[CFG_MC1_IDX]);
+ spi_writereg(RF98_REG_1E_MODEM_CONFIG2, cfg[CFG_MC2_IDX]);
+ spi_writereg(RF98_REG_26_MODEM_CONFIG3, cfg[CFG_MC3_IDX]);
+ spi_writereg(RF98_REG_20_PREAMBLE_MSB, cfg[CFG_PRU_IDX]);
+ spi_writereg(RF98_REG_21_PREAMBLE_LSB, cfg[CFG_PRL_IDX]);
+ spi_writereg(RF98_REG_27_PPM_CORRECTION, cfg[CFG_PPM_IDX]);
+ spi_writereg(RF98_REG_31_DETECT_OPTIMIZ, cfg[CFG_DEO_IDX]);
+ spi_writereg(RF98_REG_33_INVERT_IQ, cfg[CFG_IIQ_IDX]);
+ spi_writereg(RF98_REG_37_DETECTION_THRESHOLD, cfg[CFG_DTT_IDX]);
+ spi_writereg(RF98_REG_36, cfg[CFG_R36_IDX]);
+ if ( spi_readreg(RF98_REG_36) == RF98_REG_36_500KHZ )
+ {
+ spi_writereg(RF98_REG_3A, cfg[CFG_R3A_IDX]);
+ }
+}
+
+void reset_radio()
+{
+ uint8_t tim2_state = TIM2_CR1_CEN;
+
+ tim2_start();
+ delay_10ms(1);
+ PC_DDR_DDR4 = 0; // IN
+ PC_CR1_C14 = 0; // FLOAT
+
+ delay_10ms(1);
+ PC_DDR_DDR4 = 1; // OUT
+ PC_CR1_C14 = 1; // P-P
+
+ NRST_CTL = LOW; // PULL LOW
+ delay_10ms(1);
+ NRST_CTL = HIGH; // PULL HIGH
+ delay_10ms(1);
+
+ PC_DDR_DDR4 = 0; // IN
+ PC_CR1_C14 = 0; // FLOAT
+
+ /* return TIM2 previous state */
+ if ( tim2_state == 0 )
+ {
+ tim2_stop();
+ }
+}
diff --git a/menue.c b/menue.c
new file mode 100644
index 0000000..676cd32
--- /dev/null
+++ b/menue.c
@@ -0,0 +1,1800 @@
+#include "config.h"
+#include "menue.h"
+#include "uart.h"
+#include "rfm98w.h"
+#include "spi.h"
+#include "timer.h"
+
+#define SIO_SP_SZ 8
+#define DIG_SZ 10
+#define SF_SZ 7
+#define CR_SZ 4
+#define BW_SZ 10
+#define RAMP_SZ 16
+#define LNA_SZ 6
+
+static char input;
+static char dig[DIG_SZ];
+
+char *sio_speeds[SIO_SP_SZ] = {
+ "9600",
+ "19200",
+ "38400",
+ "57600",
+ "115200",
+ "230400",
+ "460800",
+ "921600" };
+
+static char *sf_levels[SF_SZ] = {
+ "6 (64 CPS)",
+ "7 (128 CPS)",
+ "8 (256 CPS)",
+ "9 (512 CPS)",
+ "10 (1024 CPS)",
+ "11 (2048 CPS)",
+ "12 (4096 CPS)" };
+
+static char *cr_levels[CR_SZ] = {
+ "4/5 (x 1.25 overhead)",
+ "4/6 (x 1.5 overhead)",
+ "4/7 (x 1.75 overhead)",
+ "4/8 (x 2 overhead)" };
+
+static char *bw_levels[BW_SZ] = {
+ "7.8 kHz",
+ "10.4 kHz",
+ "15.6 kHz",
+ "20.8 kHz",
+ "31.25 kHz",
+ "41.7 kHz",
+ "62.5 kHz",
+ "125 kHz",
+ "250 kHz",
+ "500 kHz" };
+
+static char *ramp_levels[RAMP_SZ] = {
+ "0000 -> 3.4 ms",
+ "0001 -> 2 ms",
+ "0010 -> 1 ms",
+ "0011 -> 500 us",
+ "0100 -> 250 us",
+ "0101 -> 125 us",
+ "0110 -> 100 us",
+ "0111 -> 62 us",
+ "1000 -> 50 us",
+ "1001 -> 40 us",
+ "1010 -> 31 us",
+ "1011 -> 25 us",
+ "1100 -> 20 us",
+ "1101 -> 15 us",
+ "1110 -> 12 us",
+ "1111 -> 10 us" };
+
+static char *lna_levels[LNA_SZ] = {
+ "001 -> G1 = maximum gain",
+ "010 -> G2",
+ "011 -> G3",
+ "100 -> G4",
+ "101 -> G5",
+ "110 -> G6 = minimum gain" };
+
+uint32_t atoi_simple(char *str)
+{
+ uint32_t res = 0;
+
+ for (uint8_t i = 0; str[i] != '\0'; ++i)
+ res = res*10 + str[i] - '0';
+ return res;
+}
+
+void itob_simple(char *dst, uint8_t x)
+{
+ uint8_t i=7;
+ do
+ {
+ *dst++ = (x >> i & 1) ? '1' : '0';
+ } while(i--);
+ *dst = '\0';
+}
+/*
+static void itox_simple(char *s, uint8_t i)
+{
+ uint8_t n;
+ s += 4;
+ *s = '\0';
+ for (n = 4; n != 0; --n) {
+ *--s = "0123456789ABCDEF"[i & 0x0F];
+ i >>= 4;
+ }
+}
+*/
+
+static void itox_simple(char *s, uint8_t i)
+{
+ char hex[] = "0123456789ABCDEF";
+
+ s[0] = hex[i >> 4];
+ s[1] = hex[i & 0x0F];
+ s[2] = 0;
+}
+
+static char *itoa_simple_helper(char *dest, long int i) {
+ if (i <= -10) {
+ dest = itoa_simple_helper(dest, i/10);
+ }
+ *dest++ = '0' - i%10;
+ return dest;
+}
+
+char *itoa_simple(char *dest, long int i) {
+ char *s = dest;
+ if (i < 0) {
+ *s++ = '-';
+ } else {
+ i = -i;
+ }
+ *itoa_simple_helper(s, i) = '\0';
+ return dest;
+}
+
+static void clearScreen()
+{
+ uart_putchar(27); // ESC
+ uart_puts("[2J"); // clear screen
+}
+
+static void goHome()
+{
+ uart_putchar(27); // ESC
+ uart_puts("[H"); // cursor to home
+}
+
+static void menue_basic_serial_draw()
+{
+ uint8_t i;
+
+ clearScreen();
+ goHome();
+ uart_println("TNC serial port speed:");
+ uart_println("");
+ for (i=0;i= '1') && (input < ('1' + SIO_SP_SZ)) )
+ {
+ t = cfg[CFG_TNC_IDX] & ~(CFG_TNC_SSP_MASK);
+ t |= (uint8_t)(input - '1');
+ eeprom_write_byte(CFG_TNC_IDX, t);
+ }
+ }
+ } while ( (flag.tnc_mode == MODE_SETUP) && (input != '\r') );
+ input = '\0';
+}
+
+static void menue_basic_mode_draw()
+{
+ uint8_t mode;
+
+ clearScreen();
+ goHome();
+ uart_println("TNC mode:");
+ uart_println("");
+ mode = (cfg[CFG_TNC_IDX] & CFG_TNC_MOD_MASK) >> 3;
+ ( mode == MODE_BRIDGE ) ? uart_putchar('+') : uart_putchar(' ');
+ uart_println("1 - Transparent RF UART bridge");
+ ( mode == MODE_KISS ) ? uart_putchar('+') : uart_putchar(' ');
+ uart_println("2 - KISS TNC");
+ uart_println(" [ENTER] - Return to the previous menue");
+}
+
+static void menue_basic_mode()
+{
+ uint8_t mode;
+
+ do
+ {
+ menue_basic_mode_draw();
+ __wait_for_interrupt();
+ if ( flag.uart_rxne )
+ {
+ input = uart_getc();
+ switch ( input )
+ {
+ case '1':
+ mode = cfg[CFG_TNC_IDX] & ~(CFG_TNC_MOD_MASK);
+ mode |= (MODE_BRIDGE << 3);
+ eeprom_write_byte(CFG_TNC_IDX, mode);
+ break;
+ case '2':
+ mode = cfg[CFG_TNC_IDX] & ~(CFG_TNC_MOD_MASK);
+ mode |= (MODE_KISS << 3);
+ eeprom_write_byte(CFG_TNC_IDX, mode);
+ break;
+ }
+ }
+ } while ( (flag.tnc_mode == MODE_SETUP) && (input != '\r') );
+ input = '\0';
+}
+
+static void menue_basic_freq_draw()
+{
+ uint32_t frq_i;
+
+ clearScreen();
+ goHome();
+ uart_println("Modem frequency:");
+ uart_println("");
+ uart_puts("Current frequency: ");
+ frq_i = (uint32_t)(rf_getfreq() * RF_STEP / 1000.0 );
+ itoa_simple(dig, frq_i);
+ uart_puts(dig);
+ uart_println(" kHz");
+ uart_puts("Enter new frequency in kHz or hit [ENTER] to return: ");
+}
+
+static void menue_basic_freq()
+{
+ uint32_t frq_i, frq_s;
+
+ do
+ {
+ menue_basic_freq_draw();
+ uart_readln(dig, DIG_SZ);
+ frq_i = atoi_simple(dig);
+ if ( frq_i != 0 )
+ {
+ frq_s = (uint32_t)(frq_i * 1000.0 / RF_STEP);
+ rf_setfreq(frq_s);
+ eeprom_write_byte(CFG_FRU_IDX, (uint8_t)(frq_s >> 16));
+ eeprom_write_byte(CFG_FRM_IDX, (uint8_t)(frq_s >> 8));
+ eeprom_write_byte(CFG_FRL_IDX, (uint8_t)frq_s);
+ }
+ } while ( (flag.tnc_mode == MODE_SETUP) && (dig[0] != '\0') );
+}
+
+static void menue_basic_sf_draw()
+{
+ uint8_t i;
+
+ clearScreen();
+ goHome();
+ uart_println("Modem spreading factor in chips per symbol:");
+ uart_println("");
+ for (i=0;i> 4) & 0x0F) == (i+6) )
+ uart_putchar('+');
+ else
+ uart_putchar(' ');
+ uart_putchar(i+'1');
+ uart_puts(" - ");
+ uart_println(sf_levels[i]);
+ }
+ uart_println(" [ENTER] - Return to the previous menue");
+}
+
+static void menue_basic_sf()
+{
+ uint8_t t;
+
+ do
+ {
+ menue_basic_sf_draw();
+ __wait_for_interrupt();
+ if ( flag.uart_rxne )
+ {
+ input = uart_getc();
+
+ t = spi_readreg(RF98_REG_1E_MODEM_CONFIG2) &
+ ~(RF98_SPREADING_FACTOR_MASK);
+ if ( input == '1' )
+ {
+ /* SF6 is a special case, see datasheet section 4.1.1.2. */
+ /* set SF */
+ t |= RF98_SPREADING_FACTOR_64CPS;
+ t |= RF98_PAYLOAD_CRC_ON; // we need CRC
+ spi_writereg(RF98_REG_1E_MODEM_CONFIG2, t);
+ eeprom_write_byte(CFG_MC2_IDX, t);
+ /* set implicit header */
+ t = spi_readreg(RF98_REG_1D_MODEM_CONFIG1) &
+ ~(RF98_IMPLICIT_HEADER_MODE_ON_MASK);
+ t |= RF98_IMPLICIT_HEADER_MODE_ON;
+ spi_writereg(RF98_REG_1D_MODEM_CONFIG1, t);
+ eeprom_write_byte(CFG_MC1_IDX, t);
+ /* set DetectOptimize */
+ t = spi_readreg(RF98_REG_31_DETECT_OPTIMIZ);
+ t &= ~(RF98_DETOPTIMIZE_MASK);
+ t |= RF98_DETOPTIMIZE_05;
+ spi_writereg(RF98_REG_31_DETECT_OPTIMIZ, t);
+ eeprom_write_byte(CFG_DEO_IDX, t);
+ /* set DetectionThreshold */
+ spi_writereg(RF98_REG_37_DETECTION_THRESHOLD,
+ RF98_DETTH_0C);
+ eeprom_write_byte(CFG_DTT_IDX, RF98_DETTH_0C);
+ } else if ( (input >= '2') && (input <= '7') )
+ {
+ /* set SF */
+ switch ( input )
+ {
+ case '2':
+ t |= RF98_SPREADING_FACTOR_128CPS;
+ t |= RF98_PAYLOAD_CRC_ON; // we need CRC
+ spi_writereg(RF98_REG_1E_MODEM_CONFIG2, t);
+ eeprom_write_byte(CFG_MC2_IDX, t);
+ break;
+ case '3':
+ t |= RF98_SPREADING_FACTOR_256CPS;
+ t |= RF98_PAYLOAD_CRC_ON; // we need CRC
+ spi_writereg(RF98_REG_1E_MODEM_CONFIG2, t);
+ eeprom_write_byte(CFG_MC2_IDX, t);
+ break;
+ case '4':
+ t |= RF98_SPREADING_FACTOR_512CPS;
+ t |= RF98_PAYLOAD_CRC_ON; // we need CRC
+ spi_writereg(RF98_REG_1E_MODEM_CONFIG2, t);
+ eeprom_write_byte(CFG_MC2_IDX, t);
+ break;
+ case '5':
+ t |= RF98_SPREADING_FACTOR_1024CPS;
+ t |= RF98_PAYLOAD_CRC_ON; // we need CRC
+ spi_writereg(RF98_REG_1E_MODEM_CONFIG2, t);
+ eeprom_write_byte(CFG_MC2_IDX, t);
+ break;
+ case '6':
+ t |= RF98_SPREADING_FACTOR_2048CPS;
+ t |= RF98_PAYLOAD_CRC_ON; // we need CRC
+ spi_writereg(RF98_REG_1E_MODEM_CONFIG2, t);
+ eeprom_write_byte(CFG_MC2_IDX, t);
+ break;
+ case '7':
+ t |= RF98_SPREADING_FACTOR_4096CPS;
+ t |= RF98_PAYLOAD_CRC_ON; // we need CRC
+ spi_writereg(RF98_REG_1E_MODEM_CONFIG2, t);
+ eeprom_write_byte(CFG_MC2_IDX, t);
+ break;
+ }
+ /* set explicit header */
+ t = spi_readreg(RF98_REG_1D_MODEM_CONFIG1) &
+ ~(RF98_IMPLICIT_HEADER_MODE_ON_MASK);
+ spi_writereg(RF98_REG_1D_MODEM_CONFIG1, t);
+ eeprom_write_byte(CFG_MC1_IDX, t);
+ /* set DetectOptimize */
+ t = spi_readreg(RF98_REG_31_DETECT_OPTIMIZ) &
+ ~(RF98_DETOPTIMIZE_MASK);
+ t |= RF98_DETOPTIMIZE_03;
+ spi_writereg(RF98_REG_31_DETECT_OPTIMIZ, t);
+ eeprom_write_byte(CFG_DEO_IDX, t);
+ /* set DetectionThreshold */
+ spi_writereg(RF98_REG_37_DETECTION_THRESHOLD,
+ RF98_DETTH_0A);
+ eeprom_write_byte(CFG_DTT_IDX, RF98_DETTH_0A);
+ }
+ }
+ } while ( (flag.tnc_mode == MODE_SETUP) && (input != '\r') );
+ input = '\0';
+}
+
+static void menue_basic_cr_draw()
+{
+ uint8_t i;
+
+ clearScreen();
+ goHome();
+ uart_println("Modem coding rate in symbols:");
+ uart_println("");
+ for (i=0;i> 1) & 0x07) == (i+1) )
+ uart_putchar('+');
+ else
+ uart_putchar(' ');
+ uart_putchar(i+'1');
+ uart_puts(" - ");
+ uart_println(cr_levels[i]);
+ }
+ uart_println(" [ENTER] - Return to the previous menue");
+}
+
+static void menue_basic_cr()
+{
+ uint8_t t;
+
+ do
+ {
+ menue_basic_cr_draw();
+ __wait_for_interrupt();
+ if ( flag.uart_rxne )
+ {
+ input = uart_getc();
+ if ( (input >= '1') && (input < ('1' + CR_SZ)) )
+ {
+ t = spi_readreg(RF98_REG_1D_MODEM_CONFIG1) & 0xF1;
+ t |= ( ((uint8_t)(input - '1' + 1)) << 1 ); // CR starting with 1
+ spi_writereg(RF98_REG_1D_MODEM_CONFIG1, t);
+ eeprom_write_byte(CFG_MC1_IDX, t);
+ }
+ }
+ } while ( (flag.tnc_mode == MODE_SETUP) && (input != '\r') );
+ input = '\0';
+}
+
+static void menue_basic_bw_draw()
+{
+ uint8_t i;
+
+ clearScreen();
+ goHome();
+ uart_println("Modulation bandwidth:");
+ uart_println("");
+ for (i=0;i> 4) & 0x0F) == i )
+ uart_putchar('+');
+ else
+ uart_putchar(' ');
+ uart_putchar(i+'0');
+ uart_puts(" - ");
+ uart_println(bw_levels[i]);
+ }
+ uart_println(" [ENTER] - Return to the previous menue");
+}
+
+static void menue_basic_bw()
+{
+ uint8_t t;
+
+ do
+ {
+ menue_basic_bw_draw();
+ __wait_for_interrupt();
+ if ( flag.uart_rxne )
+ {
+ input = uart_getc();
+ if ( (input >= '0') && (input < ('0' + BW_SZ)) )
+ {
+ t = spi_readreg(RF98_REG_1D_MODEM_CONFIG1) & 0x0F;
+ t |= ( ((uint8_t)(input - '0')) << 4 ); // BW starting with 0
+ spi_writereg(RF98_REG_1D_MODEM_CONFIG1, t);
+ eeprom_write_byte(CFG_MC1_IDX, t);
+ if ( input == '9' )
+ {
+ /*
+ For 500 kHz bandwidth we need to apply some corrections
+ to shadow registers. See SX127x Errata Note, Section 2.1.
+ */
+ spi_writereg(RF98_REG_36, RF98_REG_36_500KHZ);
+ eeprom_write_byte(CFG_R36_IDX, RF98_REG_36_500KHZ);
+ spi_writereg(RF98_REG_3A, RF98_REG_3A_500KHZ);
+ eeprom_write_byte(CFG_R3A_IDX, RF98_REG_3A_500KHZ);
+ } else
+ {
+ spi_writereg(RF98_REG_36, RF98_REG_36_DEFAULT);
+ eeprom_write_byte(CFG_R36_IDX, RF98_REG_36_DEFAULT);
+ //spi_writereg(RF98_REG_3A, RF98_REG_3A_DEFAULT); // not needed as per errata
+ //eeprom_write_byte(CFG_R3A_IDX, RF98_REG_3A_DEFAULT);
+ }
+ }
+ }
+ } while ( (flag.tnc_mode == MODE_SETUP) && (input != '\r') );
+ input = '\0';
+}
+
+static void menue_basic_draw()
+{
+ uint8_t t;
+
+ clearScreen();
+ goHome();
+ uart_println("Basic settings:");
+ uart_println("");
+ uart_puts("0 - TNC operation mode: ");
+ t = (cfg[CFG_TNC_IDX] & CFG_TNC_MOD_MASK) >> 3;
+ switch ( t )
+ {
+ case MODE_BRIDGE:
+ uart_println("MODE_BRIDGE");
+ break;
+ case MODE_KISS:
+ uart_println("MODE_KISS");
+ break;
+ default:
+ uart_println("unknown");
+ }
+ uart_puts("1 - TNC serial port speed: ");
+ t = cfg[CFG_TNC_IDX] & CFG_TNC_SSP_MASK;
+ if ( t < SIO_SP_SZ )
+ uart_println(sio_speeds[t]);
+ else
+ uart_println("unknown");
+ uart_puts("2 - TxDelay: ");
+ itoa_simple(dig, TXDELAY);
+ uart_puts(dig);
+ uart_println(" x 10 ms");
+ uart_puts("3 - P-parameter: ");
+ itoa_simple(dig, P_PARAMETER);
+ uart_println(dig);
+ uart_puts("4 - SlotTime: ");
+ itoa_simple(dig, SLOTTIME);
+ uart_puts(dig);
+ uart_println(" x 10 ms");
+ uart_puts("5 - Modem frequency: ");
+ itoa_simple(dig, (uint32_t)(rf_getfreq() * RF_STEP / 1000.0));
+ uart_puts(dig);
+ uart_println(" kHz");
+ uart_puts("6 - Modem spreading factor: ");
+ t = spi_readreg(RF98_REG_1E_MODEM_CONFIG2);
+ t &= RF98_SPREADING_FACTOR;
+ t >>= 4;
+ t -= 6;
+ //t = ((spi_readreg(RF98_REG_1E_MODEM_CONFIG2) & RF98_SPREADING_FACTOR) >> 4) - 6;
+ if ( t < SF_SZ )
+ uart_println(sf_levels[t]);
+ else
+ uart_println("unknown");
+ uart_puts("7 - Modem coding rate: ");
+ t = ((spi_readreg(RF98_REG_1D_MODEM_CONFIG1) & RF98_CODING_RATE) >> 1) - 1;
+ if ( t < CR_SZ )
+ uart_println(cr_levels[t]);
+ else
+ uart_println("unknown");
+ uart_puts("8 - Modulation bandwidth: ");
+ t = (spi_readreg(RF98_REG_1D_MODEM_CONFIG1) & RF98_BW) >> 4;
+ if ( t < BW_SZ )
+ uart_println(bw_levels[t]);
+ else
+ uart_println("unknown");
+ uart_println("[ENTER] - Return to the previous menue");
+}
+
+static void menue_basic_txdelay_draw()
+{
+ clearScreen();
+ goHome();
+ uart_println("TxDelay:");
+ uart_println("");
+ uart_puts("Current TxDelay (decimal): ");
+ itoa_simple(dig, TXDELAY);
+ uart_println(dig);
+ uart_puts("Enter TxDelay in range 0..255 or hit [ENTER] to return: ");
+}
+
+static void menue_basic_txdelay()
+{
+ do
+ {
+ menue_basic_txdelay_draw();
+ uart_readln(dig, DIG_SZ);
+ if ( dig[0] != '\0' )
+ {
+ eeprom_write_byte(CFG_TXD_IDX, (uint8_t)atoi_simple(dig));
+ }
+ } while ( (flag.tnc_mode == MODE_SETUP) && (dig[0] != '\0') );
+}
+
+static void menue_basic_ppar_draw()
+{
+ clearScreen();
+ goHome();
+ uart_println("P-parameter:");
+ uart_println("Note that p = 1 (P = 255) means 'transmit as soon as the channel clears.'");
+ uart_println("The default value is P = 63 (i.e., p = 0.25).");
+ uart_println("");
+ uart_puts("Current P-parameter (decimal): ");
+ itoa_simple(dig, P_PARAMETER);
+ uart_println(dig);
+ uart_puts("Enter P-parameter in range 0..255 or hit [ENTER] to return: ");
+}
+
+static void menue_basic_ppar()
+{
+ do
+ {
+ menue_basic_ppar_draw();
+ uart_readln(dig, DIG_SZ);
+ if ( dig[0] != '\0' )
+ {
+ eeprom_write_byte(CFG_PPR_IDX, (uint8_t)atoi_simple(dig));
+ }
+ } while ( (flag.tnc_mode == MODE_SETUP) && (dig[0] != '\0') );
+}
+
+static void menue_basic_slottime_draw()
+{
+ clearScreen();
+ goHome();
+ uart_println("SlotTime:");
+ uart_println("");
+ uart_puts("Current SlotTime (decimal): ");
+ itoa_simple(dig, SLOTTIME);
+ uart_println(dig);
+ uart_puts("Enter SlotTime in range 0..255 or hit [ENTER] to return: ");
+}
+
+static void menue_basic_slottime()
+{
+ do
+ {
+ menue_basic_slottime_draw();
+ uart_readln(dig, DIG_SZ);
+ if ( dig[0] != '\0' )
+ {
+ eeprom_write_byte(CFG_SLT_IDX, (uint8_t)atoi_simple(dig));
+ }
+ } while ( (flag.tnc_mode == MODE_SETUP) && (dig[0] != '\0') );
+}
+
+static void menue_basic()
+{
+ do
+ {
+ menue_basic_draw();
+ __wait_for_interrupt();
+ if ( flag.uart_rxne )
+ {
+ input = uart_getc();
+ switch ( input ) {
+ case '0':
+ menue_basic_mode();
+ break;
+ case '1':
+ menue_basic_serial();
+ break;
+ case '2':
+ menue_basic_txdelay();
+ break;
+ case '3':
+ menue_basic_ppar();
+ break;
+ case '4':
+ menue_basic_slottime();
+ break;
+ case '5':
+ menue_basic_freq();
+ break;
+ case '6':
+ menue_basic_sf();
+ break;
+ case '7':
+ menue_basic_cr();
+ break;
+ case '8':
+ menue_basic_bw();
+ break;
+ case '\r':
+ continue;
+ break;
+ }
+ }
+ } while ( (flag.tnc_mode == MODE_SETUP) && (input != '\r') );
+ input = '\0';
+}
+
+static void menue_readmodem_draw()
+{
+ clearScreen();
+ goHome();
+ uart_println("Populate TNC EEPROM data by reading modem registers");
+ uart_println("(This will overwrite the previously stored settings in TNC EEPROM.)");
+ uart_println("");
+ uart_println("1 - Overwrite TNC EEPROM settings with modem data");
+ uart_println("[ENTER] - Return to the previous menue");
+}
+
+static void menue_readmodem()
+{
+ bool pressed = false;
+ bool m_read = false;
+
+ do
+ {
+ menue_readmodem_draw();
+ if ( pressed )
+ {
+ pressed = false;
+ if ( m_read )
+ uart_println("\r\nOperation completed");
+ else
+ uart_println("\r\nNo action performed");
+ }
+ __wait_for_interrupt();
+ if ( flag.uart_rxne )
+ {
+ pressed = true;
+ input = uart_getc();
+ if ( input == '1' )
+ {
+ eeprom_populate();
+ m_read = true;
+ } else {
+ m_read = false;
+ }
+ }
+ } while ( (flag.tnc_mode == MODE_SETUP) && (input != '\r') );
+ input = '\0';
+}
+
+static void menue_adv_draw()
+{
+ clearScreen();
+ goHome();
+ uart_println("Advanced settings (see datasheet for more info):");
+ uart_println("");
+ uart_println("0 - PaConfig");
+ uart_println("1 - PaRamp");
+ uart_println("2 - Ocp");
+ uart_println("3 - Lna");
+ uart_println("4 - Preamble");
+ uart_println("5 - LowDatarateOptimize");
+ uart_println("6 - AgcAutoOn");
+ uart_println("7 - PpmCorrection");
+ uart_println("8 - DetectOptimize");
+ uart_println("9 - InvertIq");
+ uart_println("a - DetectionThreshold");
+ uart_println("b - SyncWord");
+ uart_println("[ENTER] - Return to the previous menue");
+}
+
+static void menue_adv_paconfig_draw()
+{
+ clearScreen();
+ goHome();
+ uart_puts("RegPaConfig (0x09): 0x");
+ itox_simple(dig, spi_readreg(RF98_REG_09_PA_CONFIG));
+ uart_puts(dig);
+ uart_puts(" 0b");
+ itob_simple(dig, spi_readreg(RF98_REG_09_PA_CONFIG));
+ uart_println(dig);
+ uart_println("");
+ uart_puts("1 - PaSelect: ");
+ if ( spi_readreg(RF98_REG_09_PA_CONFIG) >> 7 )
+ uart_println("PA_BOOST");
+ else
+ uart_println("RFO");
+ uart_puts("2 - MaxPower: ");
+ itoa_simple(dig, (spi_readreg(RF98_REG_09_PA_CONFIG) & 0x70) >> 4);
+ uart_println(dig);
+ uart_puts("3 - OutputPower: ");
+ itoa_simple(dig, spi_readreg(RF98_REG_09_PA_CONFIG) & 0x0F);
+ uart_println(dig);
+ uart_println("[ENTER] - Return to the previous menue");
+}
+
+static void menue_adv_paconfig_paselect_draw()
+{
+ uint8_t t;
+ clearScreen();
+ goHome();
+ uart_println("RegPaConfig PaSelect:");
+ uart_println("");
+ t = spi_readreg(RF98_REG_09_PA_CONFIG) >> 7;
+ t ? uart_puts(" ") : uart_puts("+");
+ uart_println("0 - RFO");
+ t ? uart_puts("+") : uart_puts(" ");
+ uart_println("1 - PA_BOOST");
+ uart_println("[ENTER] - Return to the previous menue");
+}
+
+static void menue_adv_paconfig_paselect()
+{
+ uint8_t t;
+
+ do
+ {
+ menue_adv_paconfig_paselect_draw();
+ __wait_for_interrupt();
+ if ( flag.uart_rxne )
+ {
+ input = uart_getc();
+ switch ( input ) {
+ case '0':
+ t = spi_readreg(RF98_REG_09_PA_CONFIG) & ~(RF98_PA_SELECT);
+ spi_writereg(RF98_REG_09_PA_CONFIG, t);
+ eeprom_write_byte(CFG_PAC_IDX, t);
+ break;
+ case '1':
+ t = spi_readreg(RF98_REG_09_PA_CONFIG) & ~(RF98_PA_SELECT);
+ t |= RF98_PA_SELECT;
+ spi_writereg(RF98_REG_09_PA_CONFIG, t);
+ eeprom_write_byte(CFG_PAC_IDX, t);
+ break;
+ }
+ }
+ } while ( (flag.tnc_mode == MODE_SETUP) && (input != '\r') );
+ input = '\0';
+}
+
+static void menue_adv_paconfig_mpower_draw()
+{
+ uint8_t i, t;
+ clearScreen();
+ goHome();
+ uart_println("RegPaConfig MaxPower:");
+ uart_println("");
+ t = ( spi_readreg(RF98_REG_09_PA_CONFIG) & RF98_MAX_POWER ) >> 4;
+ for (i = 0; i <= 7; i++) {
+ itoa_simple(dig, i);
+ (t == i) ? uart_puts("+") : uart_puts(" ");
+ uart_puts(dig);
+ uart_puts(" - ");
+ uart_println(dig);
+ }
+ uart_println("[ENTER] - Return to the previous menue");
+}
+
+static void menue_adv_paconfig_mpower()
+{
+ uint8_t t;
+
+ do
+ {
+ menue_adv_paconfig_mpower_draw();
+ __wait_for_interrupt();
+ if ( flag.uart_rxne )
+ {
+ input = uart_getc();
+ if ( (input >= '0') && (input <= '7') )
+ {
+ t = spi_readreg(RF98_REG_09_PA_CONFIG) & ~(RF98_MAX_POWER);
+ t |= ( input - '0' ) << 4;
+ spi_writereg(RF98_REG_09_PA_CONFIG, t);
+ eeprom_write_byte(CFG_PAC_IDX, t);
+ }
+ }
+ } while ( (flag.tnc_mode == MODE_SETUP) && (input != '\r') );
+ input = '\0';
+}
+
+static void menue_adv_paconfig_opower_draw()
+{
+ uint8_t i, t;
+
+ clearScreen();
+ goHome();
+ uart_println("RegPaConfig OutputPower:");
+ uart_println("");
+ t = spi_readreg(RF98_REG_09_PA_CONFIG) & RF98_OUTPUT_POWER;
+ for (i = 0; i <= 15; i++) {
+ (t == i) ? uart_puts("+") : uart_puts(" ");
+ itoa_simple(dig, i);
+ if ( i <=9 )
+ {
+ uart_puts(dig);
+ } else {
+ switch ( i )
+ {
+ case 10:
+ uart_puts("a");
+ break;
+ case 11:
+ uart_puts("b");
+ break;
+ case 12:
+ uart_puts("c");
+ break;
+ case 13:
+ uart_puts("d");
+ break;
+ case 14:
+ uart_puts("e");
+ break;
+ case 15:
+ uart_puts("f");
+ break;
+ }
+ }
+ uart_puts(" - ");
+ uart_println(dig);
+ }
+ uart_println("[ENTER] - Return to the previous menue");
+}
+
+static void menue_adv_paconfig_opower()
+{
+ uint8_t t;
+
+ do
+ {
+ menue_adv_paconfig_opower_draw();
+ __wait_for_interrupt();
+ if ( flag.uart_rxne )
+ {
+ input = uart_getc();
+ if ( (input >= '0') && (input <= '9') || (input >= 'a') && (input <= 'f') )
+ {
+ t = spi_readreg(RF98_REG_09_PA_CONFIG) & ~(RF98_OUTPUT_POWER);
+ if ( (input >= '0') && (input <= '9') )
+ t |= ( input - '0' );
+ else
+ t |= ( input - 'a' + 10 );
+ spi_writereg(RF98_REG_09_PA_CONFIG, t);
+ eeprom_write_byte(CFG_PAC_IDX, t);
+ }
+ }
+ } while ( (flag.tnc_mode == MODE_SETUP) && (input != '\r') );
+ input = '\0';
+}
+
+static void menue_adv_paconfig()
+{
+ do
+ {
+ menue_adv_paconfig_draw();
+ __wait_for_interrupt();
+ if ( flag.uart_rxne )
+ {
+ input = uart_getc();
+ switch ( input ) {
+ case '1':
+ menue_adv_paconfig_paselect();
+ break;
+ case '2':
+ menue_adv_paconfig_mpower();
+ break;
+ case '3':
+ menue_adv_paconfig_opower();
+ break;
+ }
+ }
+ } while ( (flag.tnc_mode == MODE_SETUP) && (input != '\r') );
+ input = '\0';
+}
+
+static void menue_adv_paramp_draw()
+{
+ uint8_t i, t;
+
+ clearScreen();
+ goHome();
+ uart_puts("RegPaRamp (0x0A): 0x");
+ itox_simple(dig, spi_readreg(RF98_REG_0A_PA_RAMP));
+ uart_puts(dig);
+ uart_puts(" 0b");
+ itob_simple(dig, spi_readreg(RF98_REG_0A_PA_RAMP));
+ uart_println(dig);
+ uart_println("");
+ t = spi_readreg(RF98_REG_0A_PA_RAMP) & RF98_PA_RAMP;
+ for (i = 0; i <= 15; i++) {
+ (t == i) ? uart_puts("+") : uart_puts(" ");
+ itoa_simple(dig, i);
+ if ( i <=9 )
+ {
+ uart_puts(dig);
+ } else {
+ switch ( i )
+ {
+ case 10:
+ uart_puts("a");
+ break;
+ case 11:
+ uart_puts("b");
+ break;
+ case 12:
+ uart_puts("c");
+ break;
+ case 13:
+ uart_puts("d");
+ break;
+ case 14:
+ uart_puts("e");
+ break;
+ case 15:
+ uart_puts("f");
+ break;
+ }
+ }
+ uart_puts(" - ");
+ uart_println(ramp_levels[i]);
+ }
+ uart_println("[ENTER] - Return to the previous menue");
+}
+
+static void menue_adv_paramp()
+{
+ uint8_t t;
+
+ do
+ {
+ menue_adv_paramp_draw();
+ __wait_for_interrupt();
+ if ( flag.uart_rxne )
+ {
+ input = uart_getc();
+ if ( (input >= '0') && (input <= '9') || (input >= 'a') && (input <= 'f') )
+ {
+ t = spi_readreg(RF98_REG_0A_PA_RAMP) & ~(RF98_PA_RAMP);
+ if ( (input >= '0') && (input <= '9') )
+ t |= ( input - '0' );
+ else
+ t |= ( input - 'a' + 10 );
+ spi_writereg(RF98_REG_0A_PA_RAMP, t);
+ eeprom_write_byte(CFG_PAR_IDX, t);
+ }
+ }
+ } while ( (flag.tnc_mode == MODE_SETUP) && (input != '\r') );
+ input = '\0';
+}
+
+static void menue_adv_ocp_draw()
+{
+ clearScreen();
+ goHome();
+ uart_puts("RegOcp (0x0B): 0x");
+ itox_simple(dig, spi_readreg(RF98_REG_0B_OCP));
+ uart_puts(dig);
+ uart_puts(" 0b");
+ itob_simple(dig, spi_readreg(RF98_REG_0B_OCP));
+ uart_println(dig);
+ uart_println("");
+ uart_puts("1 - OcpOn: ");
+ if ( (spi_readreg(RF98_REG_0B_OCP) & RF98_OCP_ON ) >> 5 )
+ uart_println("OCP enabled");
+ else
+ uart_println("OCP disabled");
+ uart_puts("2 - OcpTrim: ");
+ itoa_simple(dig, (spi_readreg(RF98_REG_0B_OCP) & RF98_OCP_TRIM) );
+ uart_println(dig);
+ uart_println("[ENTER] - Return to the previous menue");
+}
+
+static void menue_adv_ocp_ocpon_draw()
+{
+ uint8_t t;
+
+ clearScreen();
+ goHome();
+ uart_println("RegOcp OcpOn:");
+ uart_println("");
+ t = ( spi_readreg(RF98_REG_0B_OCP) & RF98_OCP_ON ) >> 5;
+ t ? uart_puts(" ") : uart_puts("+");
+ uart_println("0 - OCP disabled");
+ t ? uart_puts("+") : uart_puts(" ");
+ uart_println("1 - OCP enabled");
+ uart_println("[ENTER] - Return to the previous menue");
+}
+
+static void menue_adv_ocp_ocpon()
+{
+ uint8_t t;
+
+ do
+ {
+ menue_adv_ocp_ocpon_draw();
+ __wait_for_interrupt();
+ if ( flag.uart_rxne )
+ {
+ input = uart_getc();
+ switch ( input ) {
+ case '0':
+ t = spi_readreg(RF98_REG_0B_OCP) & ~(RF98_OCP_ON);
+ spi_writereg(RF98_REG_0B_OCP, t);
+ eeprom_write_byte(CFG_OCP_IDX, t);
+ break;
+ case '1':
+ t = spi_readreg(RF98_REG_0B_OCP) & ~(RF98_OCP_ON);
+ t |= RF98_OCP_ON;
+ spi_writereg(RF98_REG_0B_OCP, t);
+ eeprom_write_byte(CFG_OCP_IDX, t);
+ break;
+ }
+ }
+ } while ( (flag.tnc_mode == MODE_SETUP) && (input != '\r') );
+ input = '\0';
+}
+
+static void menue_adv_ocp_ocptrim_draw()
+{
+ clearScreen();
+ goHome();
+ uart_println("OcpTrim:");
+ uart_println("");
+ uart_puts("Current OcpTrim: ");
+ itoa_simple(dig, spi_readreg(RF98_REG_0B_OCP) & RF98_OCP_TRIM);
+ uart_println(dig);
+ uart_puts("Enter OcpTrim in range 0..31 or hit [ENTER] to return: ");
+}
+
+static void menue_adv_ocp_ocptrim()
+{
+ uint8_t t;
+
+ do
+ {
+ menue_adv_ocp_ocptrim_draw();
+ uart_readln(dig, DIG_SZ);
+ if ( dig[0] != '\0' )
+ {
+ t = spi_readreg(RF98_REG_0B_OCP) & ~(RF98_OCP_TRIM);
+ t |= (uint8_t)(atoi_simple(dig) % 32);
+ eeprom_write_byte(CFG_OCP_IDX, t);
+ spi_writereg(RF98_REG_0B_OCP, t);
+ }
+ } while ( (flag.tnc_mode == MODE_SETUP) && (dig[0] != '\0') );
+}
+
+static void menue_adv_ocp()
+{
+ do
+ {
+ menue_adv_ocp_draw();
+ __wait_for_interrupt();
+ if ( flag.uart_rxne )
+ {
+ input = uart_getc();
+ switch ( input ) {
+ case '1':
+ menue_adv_ocp_ocpon();
+ break;
+ case '2':
+ menue_adv_ocp_ocptrim();
+ break;
+ }
+ }
+ } while ( (flag.tnc_mode == MODE_SETUP) && (input != '\r') );
+ input = '\0';
+}
+
+
+static void menue_adv_lna_draw()
+{
+ clearScreen();
+ goHome();
+ uart_puts("RegLna (0x0C): 0x");
+ itox_simple(dig, spi_readreg(RF98_REG_0C_LNA));
+ uart_puts(dig);
+ uart_puts(" 0b");
+ itob_simple(dig, spi_readreg(RF98_REG_0C_LNA));
+ uart_println(dig);
+ uart_println("");
+ uart_puts("1 - LnaGain: ");
+ itoa_simple(dig, BFN_GET(spi_readreg(RF98_REG_0C_LNA), RF98_LNA_GAIN));
+ uart_println(dig);
+ uart_puts("2 - LnaBoostLf: ");
+ itoa_simple(dig, BFN_GET(spi_readreg(RF98_REG_0C_LNA), RF98_LNA_BOOST_LF));
+ uart_println(dig);
+ uart_puts("3 - LnaBoostHf: ");
+ itoa_simple(dig, BFN_GET(spi_readreg(RF98_REG_0C_LNA), RF98_LNA_BOOST_HF));
+ uart_println(dig);
+ uart_println("[ENTER] - Return to the previous menue");
+}
+
+static void menue_adv_lna_lnagain_draw()
+{
+ uint8_t i;
+
+ clearScreen();
+ goHome();
+ uart_println("RegLna LnaGain:");
+ uart_println("");
+ for (i=0;i= '1') && (input <= '6') )
+ {
+ t = spi_readreg(RF98_REG_0C_LNA) & ~(RF98_LNA_GAIN_MASK);
+ switch ( input )
+ {
+ case '1':
+ t |= RF98_LNA_GAIN_G1;
+ break;
+ case '2':
+ t |= RF98_LNA_GAIN_G2;
+ break;
+ case '3':
+ t |= RF98_LNA_GAIN_G3;
+ break;
+ case '4':
+ t |= RF98_LNA_GAIN_G4;
+ break;
+ case '5':
+ t |= RF98_LNA_GAIN_G5;
+ break;
+ case '6':
+ t |= RF98_LNA_GAIN_G6;
+ break;
+ }
+ spi_writereg(RF98_REG_0C_LNA, t);
+ eeprom_write_byte(CFG_LNA_IDX, t);
+ }
+ }
+ } while ( (flag.tnc_mode == MODE_SETUP) && (input != '\r') );
+ input = '\0';
+}
+
+static void menue_adv_lna_lnabhf_draw()
+{
+ uint8_t t;
+
+ clearScreen();
+ goHome();
+ uart_println("RegLna LnaBoostHf:");
+ uart_println("");
+ t = spi_readreg(RF98_REG_0C_LNA) & RF98_LNA_BOOST_HF_MASK;
+ t == RF98_LNA_BOOST_HF_DEFAULT ? uart_puts("+") : uart_puts(" ");
+ uart_println("0 - Default LNA current");
+ t == RF98_LNA_BOOST_HF_150PC ? uart_puts("+") : uart_puts(" ");
+ uart_println("3 - Boost on, 150% LNA current");
+ uart_println(" [ENTER] - Return to the previous menue");
+}
+
+static void menue_adv_lna_lnabhf()
+{
+ uint8_t t;
+
+ do
+ {
+ menue_adv_lna_lnabhf_draw();
+ __wait_for_interrupt();
+ if ( flag.uart_rxne )
+ {
+ input = uart_getc();
+ t = spi_readreg(RF98_REG_0C_LNA) & ~(RF98_LNA_BOOST_HF_MASK);
+ switch ( input )
+ {
+ case '0':
+ t |= RF98_LNA_BOOST_HF_DEFAULT;
+ spi_writereg(RF98_REG_0C_LNA, t);
+ eeprom_write_byte(CFG_LNA_IDX, t);
+ break;
+ case '3':
+ t |= RF98_LNA_BOOST_HF_150PC;
+ spi_writereg(RF98_REG_0C_LNA, t);
+ eeprom_write_byte(CFG_LNA_IDX, t);
+ break;
+ }
+ }
+ } while ( (flag.tnc_mode == MODE_SETUP) && (input != '\r') );
+ input = '\0';
+}
+
+static void menue_adv_lna()
+{
+ do
+ {
+ menue_adv_lna_draw();
+ __wait_for_interrupt();
+ if ( flag.uart_rxne )
+ input = uart_getc();
+ switch ( input ) {
+ case '1':
+ menue_adv_lna_lnagain();
+ break;
+ case '2':
+ //menue_adv_lna_lnablf();
+ break;
+ case '3':
+ menue_adv_lna_lnabhf();
+ break;
+ }
+ } while ( (flag.tnc_mode == MODE_SETUP) && (input != '\r') );
+ input = '\0';
+}
+
+static void menue_adv_preamble_draw()
+{
+ uint16_t t;
+
+ clearScreen();
+ goHome();
+ uart_println("Preamble:");
+ uart_puts(" RegPreambleMsb (0x20): 0x");
+ itox_simple(dig, spi_readreg(RF98_REG_20_PREAMBLE_MSB));
+ uart_puts(dig);
+ uart_puts(" 0b");
+ itob_simple(dig, spi_readreg(RF98_REG_20_PREAMBLE_MSB));
+ uart_println(dig);
+ uart_puts(" RegPreambleLsb (0x21): 0x");
+ itox_simple(dig, spi_readreg(RF98_REG_21_PREAMBLE_LSB));
+ uart_puts(dig);
+ uart_puts(" 0b");
+ itob_simple(dig, spi_readreg(RF98_REG_21_PREAMBLE_LSB));
+ uart_println(dig);
+ uart_println("");
+ uart_puts("Current Preamble: ");
+ t = spi_readreg(RF98_REG_20_PREAMBLE_MSB) << 8;
+ t |= spi_readreg(RF98_REG_21_PREAMBLE_LSB);
+ itoa_simple(dig, t);
+ uart_println(dig);
+ uart_puts("Enter Preamble in range 0..65535 or hit [ENTER] to return: ");
+}
+
+static void menue_adv_preamble()
+{
+ uint8_t t;
+ uint16_t m;
+
+ do
+ {
+ menue_adv_preamble_draw();
+ uart_readln(dig, DIG_SZ);
+ if ( dig[0] != '\0' )
+ {
+ m = (uint16_t)atoi_simple(dig);
+ t = HIBYTE(m);
+ spi_writereg(RF98_REG_20_PREAMBLE_MSB, t);
+ eeprom_write_byte(CFG_PRU_IDX, t);
+ t = LOBYTE(m);
+ spi_writereg(RF98_REG_21_PREAMBLE_LSB, t);
+ eeprom_write_byte(CFG_PRL_IDX, t);
+ }
+ } while ( (flag.tnc_mode == MODE_SETUP) && (dig[0] != '\0') );
+}
+
+static void menue_adv_lowdropt_draw()
+{
+ uint8_t t;
+
+ clearScreen();
+ goHome();
+ uart_println("LowDatarateOptimize:");
+ uart_puts(" RegModemConfig3 (0x26): 0x");
+ itox_simple(dig, spi_readreg(RF98_REG_26_MODEM_CONFIG3));
+ uart_puts(dig);
+ uart_puts(" 0b");
+ itob_simple(dig, spi_readreg(RF98_REG_26_MODEM_CONFIG3));
+ uart_println(dig);
+ uart_println("");
+ t = ( spi_readreg(RF98_REG_26_MODEM_CONFIG3) & RF98_LDOPTIMIZE ) >> 3;
+ t ? uart_puts(" ") : uart_puts("+");
+ uart_println("0 - Disabled");
+ t ? uart_puts("+") : uart_puts(" ");
+ uart_println("1 - Enabled; mandated for when the symbol length exceeds 16ms");
+ uart_println("[ENTER] - Return to the previous menue");
+}
+
+static void menue_adv_lowdropt()
+{
+ uint8_t t;
+
+ do
+ {
+ menue_adv_lowdropt_draw();
+ __wait_for_interrupt();
+ if ( flag.uart_rxne )
+ {
+ input = uart_getc();
+ switch ( input ) {
+ case '0':
+ t = spi_readreg(RF98_REG_26_MODEM_CONFIG3) & ~(RF98_LDOPTIMIZE);
+ spi_writereg(RF98_REG_26_MODEM_CONFIG3, t);
+ eeprom_write_byte(CFG_MC3_IDX, t);
+ break;
+ case '1':
+ t = spi_readreg(RF98_REG_26_MODEM_CONFIG3) & ~(RF98_LDOPTIMIZE);
+ t |= RF98_LDOPTIMIZE;
+ spi_writereg(RF98_REG_26_MODEM_CONFIG3, t);
+ eeprom_write_byte(CFG_MC3_IDX, t);
+ break;
+ }
+ }
+ } while ( (flag.tnc_mode == MODE_SETUP) && (input != '\r') );
+ input = '\0';
+}
+
+static void menue_adv_agcaon_draw()
+{
+ uint8_t t;
+
+ clearScreen();
+ goHome();
+ uart_println("AgcAutoOn:");
+ uart_puts(" RegModemConfig3 (0x26): 0x");
+ itox_simple(dig, spi_readreg(RF98_REG_26_MODEM_CONFIG3));
+ uart_puts(dig);
+ uart_puts(" 0b");
+ itob_simple(dig, spi_readreg(RF98_REG_26_MODEM_CONFIG3));
+ uart_println(dig);
+ uart_println("");
+ t = ( spi_readreg(RF98_REG_26_MODEM_CONFIG3) & RF98_AGCAUTOON ) >> 2;
+ t ? uart_puts(" ") : uart_puts("+");
+ uart_println("0 - Disabled. LNA gain set by register LnaGain");
+ t ? uart_puts("+") : uart_puts(" ");
+ uart_println("1 - Enabled. LNA gain set by the internal AGC loop");
+ uart_println("[ENTER] - Return to the previous menue");
+}
+
+static void menue_adv_agcaon()
+{
+ uint8_t t;
+
+ do
+ {
+ menue_adv_agcaon_draw();
+ __wait_for_interrupt();
+ if ( flag.uart_rxne )
+ {
+ input = uart_getc();
+ switch ( input ) {
+ case '0':
+ t = spi_readreg(RF98_REG_26_MODEM_CONFIG3) & ~(RF98_AGCAUTOON);
+ spi_writereg(RF98_REG_26_MODEM_CONFIG3, t);
+ eeprom_write_byte(CFG_MC3_IDX, t);
+ break;
+ case '1':
+ t = spi_readreg(RF98_REG_26_MODEM_CONFIG3) & ~(RF98_AGCAUTOON);
+ t |= RF98_AGCAUTOON;
+ spi_writereg(RF98_REG_26_MODEM_CONFIG3, t);
+ eeprom_write_byte(CFG_MC3_IDX, t);
+ break;
+ }
+ }
+ } while ( (flag.tnc_mode == MODE_SETUP) && (input != '\r') );
+ input = '\0';
+}
+
+static void menue_adv_ppmcorr_draw()
+{
+ clearScreen();
+ goHome();
+ uart_println("RegPpmCorrection (0x27):");
+ uart_println("");
+ uart_puts("Current PpmCorrection: ");
+ itoa_simple(dig, spi_readreg(RF98_REG_27_PPM_CORRECTION));
+ uart_println(dig);
+ uart_puts("Enter RegPpmCorrection in range 0..255 or hit [ENTER] to return: ");
+}
+
+static void menue_adv_ppmcorr()
+{
+ uint8_t t;
+
+ do
+ {
+ menue_adv_ppmcorr_draw();
+ uart_readln(dig, DIG_SZ);
+ if ( dig[0] != '\0' )
+ {
+ t = (uint8_t)atoi_simple(dig);
+ spi_writereg(RF98_REG_27_PPM_CORRECTION, t);
+ eeprom_write_byte(CFG_PPM_IDX, t);
+ }
+ } while ( (flag.tnc_mode == MODE_SETUP) && (dig[0] != '\0') );
+}
+
+static void menue_adv_detectopt_draw()
+{
+ uint8_t t;
+
+ clearScreen();
+ goHome();
+ uart_puts("RegDetectOptimize (0x31): 0x");
+ itox_simple(dig, spi_readreg(RF98_REG_31_DETECT_OPTIMIZ));
+ uart_puts(dig);
+ uart_puts(" 0b");
+ itob_simple(dig, spi_readreg(RF98_REG_31_DETECT_OPTIMIZ));
+ uart_println(dig);
+ uart_println("");
+ t = spi_readreg(RF98_REG_31_DETECT_OPTIMIZ) & RF98_DETOPTIMIZE;
+ (t == RF98_DETOPTIMIZE_03) ? uart_puts("+") : uart_puts(" ");
+ uart_println("0 - 0x03 -> SF7 to SF12");
+ (t == RF98_DETOPTIMIZE_05) ? uart_puts("+") : uart_puts(" ");
+ uart_println("1 - 0x05 -> SF6");
+ uart_println("[ENTER] - Return to the previous menue");
+}
+
+static void menue_adv_detectopt()
+{
+ uint8_t t;
+
+ do
+ {
+ menue_adv_detectopt_draw();
+ __wait_for_interrupt();
+ if ( flag.uart_rxne )
+ {
+ input = uart_getc();
+ switch ( input ) {
+ case '0':
+ t = spi_readreg(RF98_REG_31_DETECT_OPTIMIZ) & ~(RF98_DETOPTIMIZE);
+ t |= RF98_DETOPTIMIZE_03;
+ spi_writereg(RF98_REG_31_DETECT_OPTIMIZ, t);
+ eeprom_write_byte(CFG_DEO_IDX, t);
+ break;
+ case '1':
+ t = spi_readreg(RF98_REG_31_DETECT_OPTIMIZ) & ~(RF98_DETOPTIMIZE);
+ t |= RF98_DETOPTIMIZE_05;
+ spi_writereg(RF98_REG_31_DETECT_OPTIMIZ, t);
+ eeprom_write_byte(CFG_DEO_IDX, t);
+ break;
+ }
+ }
+ } while ( (flag.tnc_mode == MODE_SETUP) && (input != '\r') );
+ input = '\0';
+}
+
+static void menue_adv_invertiq_draw()
+{
+ uint8_t t;
+
+ clearScreen();
+ goHome();
+ uart_puts("RegInvertIQ (0x33): 0x");
+ itox_simple(dig, spi_readreg(RF98_REG_33_INVERT_IQ));
+ uart_puts(dig);
+ uart_puts(" 0b");
+ itob_simple(dig, spi_readreg(RF98_REG_33_INVERT_IQ));
+ uart_println(dig);
+ uart_println("");
+ t = spi_readreg(RF98_REG_33_INVERT_IQ) & RF98_INVERT_IQ;
+ t ? uart_puts(" ") : uart_puts("+");
+ uart_println("0 - normal mode");
+ t ? uart_puts("+") : uart_puts(" ");
+ uart_println("1 - I and Q signals are inverted");
+ uart_println("[ENTER] - Return to the previous menue");
+}
+
+static void menue_adv_invertiq()
+{
+ uint8_t t;
+
+ do
+ {
+ menue_adv_invertiq_draw();
+ __wait_for_interrupt();
+ if ( flag.uart_rxne )
+ {
+ input = uart_getc();
+ switch ( input ) {
+ case '0':
+ t = spi_readreg(RF98_REG_33_INVERT_IQ) & ~(RF98_INVERT_IQ);
+ spi_writereg(RF98_REG_33_INVERT_IQ, t);
+ eeprom_write_byte(CFG_IIQ_IDX, t);
+ break;
+ case '1':
+ t = spi_readreg(RF98_REG_33_INVERT_IQ) & ~(RF98_INVERT_IQ);
+ t |= RF98_INVERT_IQ;
+ spi_writereg(RF98_REG_33_INVERT_IQ, t);
+ eeprom_write_byte(CFG_IIQ_IDX, t);
+ break;
+ }
+ }
+ } while ( (flag.tnc_mode == MODE_SETUP) && (input != '\r') );
+ input = '\0';
+}
+
+static void menue_adv_detth_draw()
+{
+ uint8_t t;
+
+ clearScreen();
+ goHome();
+ uart_puts("RegDetectionThreshold (0x37): 0x");
+ itox_simple(dig, spi_readreg(RF98_REG_37_DETECTION_THRESHOLD));
+ uart_puts(dig);
+ uart_puts(" 0b");
+ itob_simple(dig, spi_readreg(RF98_REG_37_DETECTION_THRESHOLD));
+ uart_println(dig);
+ uart_println("");
+ t = spi_readreg(RF98_REG_37_DETECTION_THRESHOLD);
+ (t == RF98_DETTH_0A) ? uart_puts("+") : uart_puts(" ");
+ uart_println("0 - 0x0A -> SF7 to SF12");
+ (t == RF98_DETTH_0C) ? uart_puts("+") : uart_puts(" ");
+ uart_println("1 - 0x0C -> SF6");
+ uart_println("[ENTER] - Return to the previous menue");
+}
+
+static void menue_adv_detth()
+{
+ do
+ {
+ menue_adv_detth_draw();
+ __wait_for_interrupt();
+ if ( flag.uart_rxne )
+ {
+ input = uart_getc();
+ switch ( input ) {
+ case '0':
+ spi_writereg(RF98_REG_37_DETECTION_THRESHOLD, RF98_DETTH_0A);
+ eeprom_write_byte(CFG_DTT_IDX, RF98_DETTH_0A);
+ break;
+ case '1':
+ spi_writereg(RF98_REG_37_DETECTION_THRESHOLD, RF98_DETTH_0C);
+ eeprom_write_byte(CFG_DTT_IDX, RF98_DETTH_0C);
+ break;
+ }
+ }
+ } while ( (flag.tnc_mode == MODE_SETUP) && (input != '\r') );
+ input = '\0';
+}
+
+static void menue_adv_syncword_draw()
+{
+ clearScreen();
+ goHome();
+ uart_println("RegSyncWord (0x39):");
+ uart_println("");
+ uart_puts("Current SyncWord (decimal): ");
+ itoa_simple(dig, spi_readreg(RF98_REG_39_SYNC_WORD));
+ uart_println(dig);
+ uart_puts("Enter SyncWord in range 0..255 or hit [ENTER] to return: ");
+}
+
+static void menue_adv_syncword()
+{
+ uint8_t t;
+
+ do
+ {
+ menue_adv_syncword_draw();
+ uart_readln(dig, DIG_SZ);
+ if ( dig[0] != '\0' )
+ {
+ t = (uint8_t)atoi_simple(dig);
+ spi_writereg(RF98_REG_39_SYNC_WORD, t);
+ eeprom_write_byte(CFG_SCW_IDX, t);
+ }
+ } while ( (flag.tnc_mode == MODE_SETUP) && (dig[0] != '\0') );
+}
+
+static void menue_adv()
+{
+ do
+ {
+ menue_adv_draw();
+ __wait_for_interrupt();
+ if ( flag.uart_rxne )
+ input = uart_getc();
+ switch ( input ) {
+ case '0':
+ menue_adv_paconfig();
+ break;
+ case '1':
+ menue_adv_paramp();
+ break;
+ case '2':
+ menue_adv_ocp();
+ break;
+ case '3':
+ menue_adv_lna();
+ break;
+ case '4':
+ menue_adv_preamble();
+ break;
+ case '5':
+ menue_adv_lowdropt();
+ break;
+ case '6':
+ menue_adv_agcaon();
+ break;
+ case '7':
+ menue_adv_ppmcorr();
+ break;
+ case '8':
+ menue_adv_detectopt();
+ break;
+ case '9':
+ menue_adv_invertiq();
+ break;
+ case 'a':
+ menue_adv_detth();
+ break;
+ case 'b':
+ menue_adv_syncword();
+ break;
+ }
+ } while ( (flag.tnc_mode == MODE_SETUP) && (input != '\r') );
+ input = '\0';
+}
+
+static void menue_regdump()
+{
+ uint8_t n, t;
+
+ clearScreen();
+ goHome();
+
+ for (uint8_t i=0; i <= 15; i++)
+ {
+ for (uint8_t j=0;j<=3;j++)
+ {
+ n = j+i*4;
+ itox_simple(dig, n);
+ uart_puts(dig);
+ uart_puts(": ");
+ t = spi_readreg(n);
+ itox_simple(dig, t);
+ uart_puts(dig);
+ uart_putchar(' ');
+ itob_simple(dig, t);
+ uart_puts(dig);
+ uart_puts(" | ");
+ }
+ uart_puts("\r\n");
+ }
+ uart_puts("\r\n");
+ uart_puts("Press any key to continue");
+ __wait_for_interrupt();
+}
+
+static void menue_draw()
+{
+ clearScreen();
+ goHome();
+ uart_println("KISS TNC for SX127x or RF(M)9x LoRa modems");
+ uart_println("");
+ uart_puts("Onboard Semtech chip id: 0x");
+ itox_simple(dig, spi_readreg(RF98_REG_42_VERSION));
+ uart_println(dig);
+ uart_println("");
+ uart_println("Select TNC settings:");
+ uart_println("");
+ uart_println("1 - Basic settings");
+ uart_println("2 - Advanced settings");
+ uart_println("3 - Initialise TNC EEPROM");
+ uart_println("4 - Dump SX127x hardware registers");
+}
+
+void menue()
+{
+ rf_setopmode(RF98_MODE_STDBY);
+ tim2_stop();
+ uart_stop();
+ uart_init(BD9600);
+ input = '\0';
+ do
+ {
+ menue_draw();
+ __wait_for_interrupt();
+ if ( flag.uart_rxne ) {
+ input = uart_getc();
+ switch ( input ) {
+ case '1':
+ menue_basic();
+ break;
+ case '2':
+ menue_adv();
+ break;
+ case '3':
+ menue_readmodem();
+ break;
+ case '4':
+ menue_regdump();
+ break;
+ }
+ }
+ } while (flag.tnc_mode == MODE_SETUP);
+}
diff --git a/menue.h b/menue.h
new file mode 100644
index 0000000..5db6f50
--- /dev/null
+++ b/menue.h
@@ -0,0 +1,7 @@
+// menue.h
+#ifndef MENUE_H
+#define MENUE_H
+
+void menue(void);
+
+#endif
diff --git a/rfm98w.c b/rfm98w.c
new file mode 100644
index 0000000..c074c3f
--- /dev/null
+++ b/rfm98w.c
@@ -0,0 +1,83 @@
+#include "config.h"
+#include "rfm98w.h"
+#include "spi.h"
+#include "uart.h"
+
+void rf_setopmode(uint8_t opmode)
+{
+ spi_writereg(RF98_REG_01_OP_MODE, RF98_MODE_STDBY);
+ spi_writereg(RF98_REG_01_OP_MODE, opmode);
+}
+
+void rf_setfreq(uint32_t freq)
+{
+ rf_setopmode(RF98_MODE_STDBY);
+ spi_writereg(RF98_REG_06_FRF_MSB, (uint8_t)(freq >> 16));
+ spi_writereg(RF98_REG_07_FRF_MID, (uint8_t)(freq >> 8));
+ spi_writereg(RF98_REG_08_FRF_LSB, (uint8_t)(freq));
+}
+
+uint32_t rf_getfreq()
+{
+ uint32_t frq;
+
+ rf_setopmode(RF98_MODE_STDBY);
+ frq = (uint32_t)spi_readreg(RF98_REG_06_FRF_MSB) << 16;
+ frq |= (uint32_t)spi_readreg(RF98_REG_07_FRF_MID) << 8;
+ frq |= spi_readreg(RF98_REG_08_FRF_LSB);
+
+ return frq;
+}
+
+void rf_send(char *data, uint8_t len)
+{
+ rf_setopmode(RF98_MODE_STDBY);
+ spi_writereg(RF98_REG_12_IRQ_FLAGS, RF98_TX_DONE); /* clear interrupt */
+ //spi_writereg(RF98_REG_12_IRQ_FLAGS, 0xFF); /* clear interrupt */
+ spi_writereg(RF98_REG_0E_FIFO_TX_BASE_ADDR, RF_FIFO_TXBASE);
+ spi_writereg(RF98_REG_0D_FIFO_ADDR_PTR, RF_FIFO_TXBASE);
+ if ( flag.header_implicit )
+ spi_writereg(RF98_REG_22_PAYLOAD_LENGTH, 0xFF);
+ else
+ spi_writereg(RF98_REG_22_PAYLOAD_LENGTH, len);
+ spi_writeburst(RF98_REG_00_FIFO, data, len);
+ TX_ON;
+ rf_setopmode(RF98_MODE_TX);
+ while ( ! (spi_readreg(RF98_REG_12_IRQ_FLAGS) & RF98_TX_DONE) ); // block
+}
+
+uint8_t rf_recv(void)
+{
+ uint8_t i, num = 0;
+
+ if ( !(spi_readreg(RF98_REG_12_IRQ_FLAGS) &
+ RF98_PAYLOAD_CRC_ERROR) )
+ {
+ rf_setopmode(RF98_MODE_STDBY);
+ spi_writereg(RF98_REG_0D_FIFO_ADDR_PTR, spi_readreg(RF98_REG_10_FIFO_RX_CURRENT_ADDR));
+
+ /* In implicit mode 1st byte is packet length */
+ if ( flag.header_implicit )
+ {
+ num = spi_readreg(RF98_REG_00_FIFO);
+ } else
+ {
+ num = spi_readreg(RF98_REG_13_RX_NB_BYTES);
+ }
+
+ for (i=0; i> 8) + (UART_DIV(baud) & 0x000F)); // set
+ USART1_BRR1 = (char)((UART_DIV(baud) & 0x0FF0) >> 4); // baudrate
+
+ USART1_CR2_TEN = 1; // TX enable
+ USART1_CR2_REN = 1; // RX enable
+ USART1_CR2_RIEN = 1; // Enable RX interrupt
+}
+
+void uart_stop(void)
+{
+ while (USART1_SR_TC == 0); // disable only after tx complete
+ USART1_CR2_TEN = 0; // TX disable
+ USART1_CR2_REN = 0; // RX disable
+ USART1_CR2_RIEN = 0; // Disable RX interrupt
+ CLK_PCKENR1_PCKEN15 = 0;
+
+ // Configure PD6 (USART1_RX) for output
+ PA_DDR_DDR3 = 1; // out
+ PA_CR1_C13 = 0; // open drain PA3
+
+ PA_CR1_C12 = 0; // open drain PA2
+}
+
+void uart_putchar(char c)
+{
+ while (USART1_SR_TXE == 0);
+ USART1_DR = c;
+}
+
+char uart_getchar(void)
+{
+ while(USART1_SR_RXNE == 0); // Wait until char is available
+ return (USART1_DR); // Get it
+}
+
+char uart_getc(void)
+{
+ flag.uart_rxne = false;
+ return uart_dr;
+}
+
+void uart_puts(const char *s)
+{
+ while (s && *s)
+ uart_putchar (*s++);
+}
+
+void uart_putsn(const char *s, uint16_t len)
+{
+ if ( len == 0 ) return;
+ while (len--)
+ uart_putchar (*s++);
+}
+
+void uart_println(const char *s)
+{
+ while (s && *s)
+ uart_putchar (*s++);
+ uart_puts("\r\n");
+}
+
+void uart_readln(char *buf, uint8_t sz)
+{
+ char input;
+ uint8_t n = 0;
+
+ do
+ {
+ __wait_for_interrupt();
+ if ( flag.uart_rxne )
+ {
+ input = uart_getc();
+ uart_putchar(input);
+ buf[n++] = input;
+ }
+ } while (input != '\r' && n < sz);
+ if ( n < sz )
+ buf[n-1] = '\0';
+ else
+ buf[sz-1] = '\0';
+}
\ No newline at end of file
diff --git a/uart.h b/uart.h
new file mode 100644
index 0000000..66a9f69
--- /dev/null
+++ b/uart.h
@@ -0,0 +1,30 @@
+// uart.h
+#ifndef UART_H
+#define UART_H
+
+#include "config.h"
+
+#define BD9600 0
+#define BD19200 1
+#define BD38400 2
+#define BD57600 3
+#define BD115200 4
+#define BD230400 5
+#define BD460800 6
+#define BD921600 7
+
+#define UART_DIV(x) (int)((( F_MASTER * 1000000.0f ) / ( x ) ) + 0.5f )
+
+extern char uart_dr;
+
+void uart_init(uint8_t baudspec);
+void uart_stop(void);
+void uart_putchar(char);
+void uart_puts(const char *);
+void uart_putsn(const char *, uint16_t);
+void uart_println(const char *);
+void uart_readln(char *, uint8_t);
+char uart_getchar(void);
+char uart_getc(void);
+
+#endif
diff --git a/util.c b/util.c
new file mode 100644
index 0000000..a3aca0e
--- /dev/null
+++ b/util.c
@@ -0,0 +1,71 @@
+#include "config.h"
+#include "util.h"
+
+uint8_t random()
+{
+ seed ^= seed << 13;
+ seed ^= seed >> 17;
+ seed ^= seed << 5;
+ return (uint8_t)(seed >> 8);
+}
+
+void eeprom_write_byte(uint8_t idx, uint8_t val)
+{
+ if ( idx < CFG_SZ )
+ {
+ FLASH_DUKR = 0xAE;
+ FLASH_DUKR = 0x56;
+ cfg[idx] = val;
+ while(!FLASH_IAPSR_EOP);
+ FLASH_IAPSR_DUL = 0;
+ }
+}
+
+uint8_t assemble(uint8_t len, uint8_t id)
+{
+ uint8_t i, idx = 0;
+
+ if ( flag.header_implicit )
+ idx++;
+ if ( flag.rf_multiframe )
+ idx++;
+
+ if ( flag.header_implicit )
+ rf_txbuf[0] = len;
+
+ if ( flag.rf_multiframe )
+ {
+ rf_txbuf[idx-1] = id;
+ }
+
+ for(i = idx; i < len+idx; i++)
+ {
+ rf_txbuf[i] = cbuf_pop();
+ }
+
+ return len+idx;
+}
+
+char cbuf_pop2(void)
+{
+ if ( CBUF_Len(utmpbuf) < TTBL_SZ )
+ RTS_SET_LOW;
+ return CBUF_Pop(utmpbuf);
+}
+
+char cbuf_pop(void)
+{
+ if ( CBUF_Len(urxcbuf) < TBL_SZ )
+ RTS_SET_LOW;
+ return CBUF_Pop(urxcbuf);
+}
+
+void cbuf_push(char c)
+{
+ /* Hardware flow control */
+ if ( CBUF_Len(urxcbuf) >= TBH_SZ )
+ {
+ RTS_SET_HIGH;
+ }
+ CBUF_Push(urxcbuf, c);
+}
diff --git a/util.h b/util.h
new file mode 100644
index 0000000..53bf1c6
--- /dev/null
+++ b/util.h
@@ -0,0 +1,12 @@
+// util.h
+#ifndef UTIL_H
+#define UTIL_H
+
+uint8_t random(void);
+uint8_t assemble(uint8_t len, uint8_t id);
+char cbuf_pop2(void);
+char cbuf_pop(void);
+void cbuf_push(char c);
+void eeprom_write_byte(uint8_t idx, uint8_t val);
+
+#endif
\ No newline at end of file