end node on synchronous star LoRa network.

Dependencies:   SX127x sx12xx_hal TSL2561

radio chip selection

Radio chip driver is not included, allowing choice of radio device.
If you're using SX1272 or SX1276, then import sx127x driver into your program.
if you're using SX1261 or SX1262, then import sx126x driver into your program.
if you're using SX1280, then import sx1280 driver into your program.
If you're using NAmote72 or Murata discovery, then you must import only sx127x driver.

This project for use with LoRaWAN_singlechannel_gateway project.

Alternately gateway running on raspberry pi can be used as gateway.

LoRaWAN on single radio channel

Network description is at gateway project page. Synchronous star network.

Hardware Support

This project supports SX1276 and SX1272, sx126x kit, sx126x shield, and sx128x 2.4GHz. The ST board B-L072Z-LRWAN1 is also supported (TypeABZ module). When B-L072Z-LRWAN1 target is selected, TARGET_DISCO_L072CZ_LRWAN1 is defined by tools, allowing correct radio driver configuration for this platform. Alternately, any mbed board that can use LoRa radio shield board should work, but NUCLEO boards are tested.

End-node Unique ID

DevEUI is created from CPU serial number. AppEUI and AppKey are declared as software constants.

End-node Configuration

Data rate definition LORAMAC_DEFAULT_DATARATE configured in LoRaMac-definitions.h. See gateway project page for configuration of gateway.
LoRaWAN addressing is configured in Comissioning.h; only OTA mode is functional.
Header file board/lora_config.h, selects application layer options (i.e. sensors) to be compiled in.

Serial Interface

Serial port operates at 115200bps.
Application layer single_us915_main.cpp User button triggers uplink (i.e. blue button on nucleo board), or jumper enables continuously sends repeated uplink packets. The MAC layer holds each uplink request until the allocated timeslot.

commandargumentsdescription
?-print available commands
. (period)-print status (DevEUI, DevAddr, etc)
ullength integerset payload length of test uplink packets

sensor demo

Selected grove sensors may be plugged into SX1272 shield.
To enable, edit lora_config.h to define SENSORS.

Sensor connections on SX1272MB2xAS:

D8 D9: buttonRX TX: (unused)A3 A4: Rotary Angle Sensor
D6 D7: RGB LEDSCL SDA: digital light sensorA1 A2: Rotary Angle Sensor

Digital input pin, state reported via uplink: PC8
Digital output pin, controlled via downlink: PC6
PWM out: PB_10

Jumper enables auto-repeated transmit: PC10 and PC12 on NUCLEO board, located on end of morpho headers nearby JP4.

Revision:
29:ad409c68c0a6
Parent:
25:fed9d5b77183
Child:
30:1c35c4f56e50
--- a/mac/LoRaMac.cpp	Mon Aug 28 13:13:56 2017 -0700
+++ b/mac/LoRaMac.cpp	Fri Dec 07 17:57:41 2018 -0800
@@ -15,19 +15,26 @@
 
 License: Revised BSD License, see LICENSE.TXT file include in the project
 
-Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jäckle ( STACKFORCE )
 */
 #include <math.h>
 #include "board.h"
+#include "radio.h"
 
 #include "LoRaMacCrypto.h"
 #include "LoRaMac.h"
 #include "LoRaMacTest.h"
-
-#define TX_DEBUG
+#include "LowPowerTimeoutAbs.h"
+
+//DigitalOut pc2(PC_2);
+#define BEACONS_MISSED_LIMIT            16
 
 #define PING_SLOT_RESOLUTION_us         30000
 
+#define PREAMBLE_SYMBS                  8
+
+#define TARGET_PRECESSION_us            3000
+#define BEACON_RX_TIMEOUT_LOCKED_us     8000
+
 /*!
  * Maximum PHY layer payload size
  */
@@ -38,29 +45,10 @@
  */
 #define LORA_MAC_COMMAND_MAX_LENGTH                 15
 
-/*!
- * FRMPayload overhead to be used when setting the Radio.SetMaxPayloadLength
- * in RxWindowSetup function.
- * Maximum PHYPayload = MaxPayloadOfDatarate/MaxPayloadOfDatarateRepeater + LORA_MAC_FRMPAYLOAD_OVERHEAD
- */
-#define LORA_MAC_FRMPAYLOAD_OVERHEAD                13 // MHDR(1) + FHDR(7) + Port(1) + MIC(4)
-
-/*!
- * LoRaMac duty cycle for the back-off procedure during the first hour.
- */
-#define BACKOFF_DC_1_HOUR                           100
-
-/*!
- * LoRaMac duty cycle for the back-off procedure during the next 10 hours.
- */
-#define BACKOFF_DC_10_HOURS                         1000
-
-/*!
- * LoRaMac duty cycle for the back-off procedure during the next 24 hours.
- */
-#define BACKOFF_DC_24_HOURS                         10000
-
-#define DIO0_LAG_us         1000
+/*! radio timer started later by this much: */
+static volatile int lpt_offset;
+
+static volatile us_timestamp_t txDoneAt;
 
 /*!
  * Device IEEE EUI
@@ -117,34 +105,13 @@
 static MulticastParams_t *MulticastChannels = NULL;
 
 /*!
- * Actual device class
- */
-static DeviceClass_t LoRaMacDeviceClass;
-
-/*!
  * Indicates if the node is connected to a private or public network
  */
 static bool PublicNetwork;
 
-/*!
- * Buffer containing the data to be sent or received.
- */
-static uint8_t LoRaMacBuffer[LORAMAC_PHY_MAXPAYLOAD];
-
-/*!
- * Length of packet in LoRaMacBuffer
- */
-static uint16_t LoRaMacBufferPktLen = 0;
-
-/*!
- * Length of the payload in LoRaMacBuffer
- */
-static uint8_t LoRaMacTxPayloadLen = 0;
-
-/*!
- * Buffer containing the upper layer data.
- */
-static uint8_t LoRaMacRxPayload[LORAMAC_PHY_MAXPAYLOAD];
+uint8_t tx_buf_len;
+
+static uint8_t rxFRMPayload[244];
 
 /*!
  * LoRaMAC frame counter. Each time a packet is sent the counter is incremented.
@@ -168,24 +135,8 @@
  * Used for test purposes. Disables the opening of the reception windows.
  */
 static bool IsRxWindowsEnabled = true;
-LowPowerTimeout rx_timeout;
-
-/*!
- * Indicates if the MAC layer has already joined a network.
- */
-static bool IsLoRaMacNetworkJoined = false;
-
-/*!
- * If the node has sent a FRAME_TYPE_DATA_CONFIRMED_UP this variable indicates
- * if the nodes needs to manage the server acknowledgement.
- */
-static bool NodeAckRequested = false;
-
-/*!
- * If the server has sent a FRAME_TYPE_DATA_CONFIRMED_DOWN this variable indicates
- * if the ACK bit must be set for the next transmission
- */
-static bool SrvAckRequested = false;
+LowPowerTimeoutAbs _rx_timeout;
+volatile us_timestamp_t _rx_timeout_setAt;
 
 /*!
  * Indicates if the MAC layer wants to send MAC commands
@@ -216,24 +167,46 @@
 #define BEACON_CHANNEL_DR           LORAMAC_DEFAULT_DATARATE
 
 
-#if defined( USE_BAND_915_SINGLE )
+#ifdef SX128x_H 
+
+    #define LORA_MAX_NB_CHANNELS                        8
+
+    //                             0    1   2  3  4  5  6  7
+    const uint8_t Datarates[]  = { 12, 11, 10, 9, 8, 7, 6, 5 };
+    #define SF_FROM_DR0     12  /* DR0 is sf12 */
+    #define SF_FROM_DR1     11  /* DR1 is sf11 */
+    #define SF_FROM_DR2     10  /* DR2 is sf10 */
+    #define SF_FROM_DR3      9  /* DR3 is sf9 */
+    #define SF_FROM_DR4      8  /* DR4 is sf8 */
+    #define SF_FROM_DR5      7  /* DR5 is sf7 */
+    #define SF_FROM_DR6      6  /* DR5 is sf6 */
+    #define SF_FROM_DR7      5  /* DR7 is sf5 */
+
+
+    const int8_t TxPowers[] = { 12, 5 };
+    #define LORAMAC_FIRST_CHANNEL           ( (uint32_t)2486.9e6 )
+    #define LORAMAC_STEPWIDTH_CHANNEL       ( (uint32_t)300e3 )
+
+    #define LORA_BANDWIDTH_KHZ           200
+
+/*
+#define STR_HELPER(x) #x
+#define STR(x) STR_HELPER(x)
+#pragma message "content of BEACON_RXDONE_LATENCY_us: " STR(BEACON_RXDONE_LATENCY_us)
+*/
+
+    /* end sx1280 */
+#elif defined( USE_BAND_915_SINGLE )
     /*!
      * Data rates table definition
-     */
+                    DR:             0  1  2   3   4   5   6  7   8  9   10  11 12 13 14 15*/
     const uint8_t Datarates[]  = { 10, 9, 8,  7,  8,  0,  0, 0, 12, 11, 10, 9, 8, 7, 0, 0 };
-
-    /*!
-     * Bandwidths table definition in Hz
-     */
-    const uint32_t Bandwidths[] = { 125000, 125000, 125000, 125000, 500000, 0, 0, 0, 500000, 500000, 500000, 500000, 500000, 500000, 0, 0 };
-
-    /*!
-     * LoRaMac bands
-     */
-    /*static Band_t Bands[LORA_MAX_NB_BANDS] =
-    {
-        BAND0,
-    };*/
+    #define SF_FROM_DR8        12
+    #define SF_FROM_DR9        11
+    #define SF_FROM_DR10       10
+    #define SF_FROM_DR11        9
+    #define SF_FROM_DR12        8
+    #define SF_FROM_DR13        7
 
     /*!
      * Tx output powers table definition
@@ -244,88 +217,109 @@
     #define LORAMAC_STEPWIDTH_CHANNEL       ( (uint32_t)800e3 )
     #define LORA_MAX_NB_CHANNELS                        8
 
-    #define LORA_BANDWIDTH           2   /* 2=500KHz */
-
-    /* measured beacon duration (all at bw500, 6 byte fixed payload length)
-     * latency assigned for correct rx-before-tx measurement */
-    #if (LORAMAC_DEFAULT_DATARATE == DR_8)
-        #define BEACON_RXDONE_LATENCY_us        6000
-        #define BEACON_TOA_us                   209000
-    #elif (LORAMAC_DEFAULT_DATARATE == DR_9)
-        #define BEACON_RXDONE_LATENCY_us        3500
-        #define BEACON_TOA_us                   105000
-    #elif (LORAMAC_DEFAULT_DATARATE == DR_10)
-        #define BEACON_RXDONE_LATENCY_us        1460
-        #define BEACON_TOA_us                   52800
-    #elif (LORAMAC_DEFAULT_DATARATE == DR_11)
-        #define BEACON_RXDONE_LATENCY_us        2000
-        #define BEACON_TOA_us                   26000
-    #elif (LORAMAC_DEFAULT_DATARATE == DR_12)
-        #define BEACON_RXDONE_LATENCY_us        1500
-        #define BEACON_TOA_us                   12800
-    #elif (LORAMAC_DEFAULT_DATARATE == DR_13)
-        #define BEACON_RXDONE_LATENCY_us        1300
-        #define BEACON_TOA_us                   6560
-    #else
-        #error datarate
-    #endif
+    #define LORA_BANDWIDTH_KHZ           500
+/*
+#define STR_HELPER(x) #x
+#define STR(x) STR_HELPER(x)
+#pragma message "content of BEACON_RXDONE_LATENCY_us: " STR(BEACON_RXDONE_LATENCY_us)
+#pragma message "content of BEACON_TOA_us: " STR(BEACON_TOA_us)
+*/
 
     /* end us915 */
-#elif defined(USE_BAND_433) && defined(ENABLE_SX1276)
+#elif defined(USE_BAND_433)
+                       //   DR:    0   1   2   3  4  5  6  7
     const uint8_t Datarates[]  = { 12, 11, 10, 9, 8, 7, 7, 50 };
-    const uint32_t Bandwidths[] = { 125000, 125000, 125000, 125000, 125000, 125000, 250000, 0 };
+    #define SF_FROM_DR0        12
+    #define SF_FROM_DR1        11
+    #define SF_FROM_DR2        10
+    #define SF_FROM_DR3         9
+    #define SF_FROM_DR4         8
+    #define SF_FROM_DR5         7
+
     const int8_t TxPowers[] = { 10, 7, 4, 1, -2, -5 };
-    #define LORA_BANDWIDTH           0   /* 0=125KHz */
+    #define LORA_BANDWIDTH_KHZ           125
 
     #define LORAMAC_FIRST_CHANNEL           ( (uint32_t)433.32e6 )
     #define LORAMAC_STEPWIDTH_CHANNEL       ( (uint32_t)200e3 )
     #define LORA_MAX_NB_CHANNELS                        7
 
-    #if (LORAMAC_DEFAULT_DATARATE == DR_0)
-        #define BEACON_RXDONE_LATENCY_us        27500
-        #define BEACON_TOA_us                   828100
-    #elif (LORAMAC_DEFAULT_DATARATE == DR_1)
-        #define BEACON_RXDONE_LATENCY_us        12500
-        #define BEACON_TOA_us                   414400
-    #elif (LORAMAC_DEFAULT_DATARATE == DR_2)
-        #define BEACON_RXDONE_LATENCY_us        6000
-        #define BEACON_TOA_us                   207500
-    #elif (LORAMAC_DEFAULT_DATARATE == DR_3)
-        #define BEACON_RXDONE_LATENCY_us        3840
-        #define BEACON_TOA_us                   103000
-    #elif (LORAMAC_DEFAULT_DATARATE == DR_4)
-        #define BEACON_RXDONE_LATENCY_us        2800
-        #define BEACON_TOA_us                   51000
-    #elif (LORAMAC_DEFAULT_DATARATE == DR_5)
-        #define BEACON_RXDONE_LATENCY_us        1400
-        #define BEACON_TOA_us                   25800
-    #else
-        #error datarate
-    #endif
-
     /* end USE_BAND_433 */
 #else
     #error "Please define a frequency band in the compiler options."
 #endif
 
+#define _SF_FROM_DR(dr)         SF_FROM_DR ## dr
+#define SF_FROM_DR_(x)          _SF_FROM_DR(x)
+
+#ifdef SX128x_H 
+
+    #define LATENCY_FROM_1600KHZ_SF12       17300
+    #define LATENCY_FROM_1600KHZ_SF11       16900
+    #define LATENCY_FROM_1600KHZ_SF10       4300
+    #define LATENCY_FROM_1600KHZ_SF9        4000
+    #define LATENCY_FROM_1600KHZ_SF8        1050
+    #define LATENCY_FROM_1600KHZ_SF7        1000
+    #define LATENCY_FROM_1600KHZ_SF6        504
+    #define LATENCY_FROM_1600KHZ_SF5        256
+
+    #define _LATENCY_FROM_SF(sf)    LATENCY_FROM_1600KHZ_SF ## sf
+    #define LATENCY_FROM_SF_(x)     _LATENCY_FROM_SF(x)
+
+    /* 6 byte implict packet */
+    #define BEACON_TOA_FROM_1600KHZ_SF12     66000
+    #define BEACON_TOA_FROM_1600KHZ_SF11     33100
+    #define BEACON_TOA_FROM_1600KHZ_SF10     16400
+    #define BEACON_TOA_FROM_1600KHZ_SF9      8260
+    #define BEACON_TOA_FROM_1600KHZ_SF8      5100
+    #define BEACON_TOA_FROM_1600KHZ_SF7      2560
+    #define BEACON_TOA_FROM_1600KHZ_SF6      1350
+    #define BEACON_TOA_FROM_1600KHZ_SF5      800
+
+    #define BEACON_RXDONE_LATENCY_us        (LATENCY_FROM_SF_( SF_FROM_DR_(LORAMAC_DEFAULT_DATARATE) ) * (1600/LORA_BANDWIDTH_KHZ))
+
+    #define _BEACON_TOA_FROM_SF(sf)    BEACON_TOA_FROM_1600KHZ_SF ## sf
+    #define BEACON_TOA_FROM_SF_(x)     _BEACON_TOA_FROM_SF(x)
+    #define BEACON_TOA_us        (BEACON_TOA_FROM_SF_( SF_FROM_DR_(LORAMAC_DEFAULT_DATARATE) ) * (1600/LORA_BANDWIDTH_KHZ))
+
+#elif defined(SX126x_H) || defined(SX127x_H)
+
+    /* 6 byte implict packet */
+    #define BEACON_TOA_FROM_500KHZ_SF12      198000
+    #define BEACON_TOA_FROM_500KHZ_SF11       97750
+    #define BEACON_TOA_FROM_500KHZ_SF10       49630
+    #define BEACON_TOA_FROM_500KHZ_SF9        24500
+    #define BEACON_TOA_FROM_500KHZ_SF8        12500
+    #define BEACON_TOA_FROM_500KHZ_SF7         6250
+    #define BEACON_TOA_FROM_500KHZ_SF6         3320
+    #define BEACON_TOA_FROM_500KHZ_SF5         1940
+
+    #define LATENCY_FROM_500KHZ_SF12       7750
+    #define LATENCY_FROM_500KHZ_SF11       3600
+    #define LATENCY_FROM_500KHZ_SF10       1675
+    #define LATENCY_FROM_500KHZ_SF9        7875
+    #define LATENCY_FROM_500KHZ_SF8        385
+    #define LATENCY_FROM_500KHZ_SF7        195
+    #define LATENCY_FROM_500KHZ_SF6        103
+    #define LATENCY_FROM_500KHZ_SF5        48
+
+    #define _LATENCY_FROM_SF(sf)    LATENCY_FROM_500KHZ_SF ## sf
+    #define LATENCY_FROM_SF_(x)     _LATENCY_FROM_SF(x)
+
+    #define BEACON_RXDONE_LATENCY_us        (LATENCY_FROM_SF_( SF_FROM_DR_(LORAMAC_DEFAULT_DATARATE) ) * (500/LORA_BANDWIDTH_KHZ))
+
+    #define _BEACON_TOA_FROM_SF(sf)    BEACON_TOA_FROM_500KHZ_SF ## sf
+    #define BEACON_TOA_FROM_SF_(x)     _BEACON_TOA_FROM_SF(x)
+    #define BEACON_TOA_us        (BEACON_TOA_FROM_SF_( SF_FROM_DR_(LORAMAC_DEFAULT_DATARATE) ) * (500/LORA_BANDWIDTH_KHZ))
+#endif /* SX126x_H || SX127x_H */
+
+const us_timestamp_t beaconDuration = BEACON_TOA_us + BEACON_RXDONE_LATENCY_us;
+
 static ChannelParams_t Channels[LORA_MAX_NB_CHANNELS];  // populated in init
 
 #define BEACON_GUARD_us         2000000     // pre-beacon start
 #define BEACON_RESERVED_us      2120000     // post-beacon start
 
-#define BEACON_MIN_SYMBOL_TIMEOUT       8
-
-LowPowerTimer lp_timer;
-
-#ifdef DEBUG_GWTX_JUMPER
-InterruptIn gwtx_pin(DEBUG_GWTX_JUMPER);
-unsigned int gwtx_rise_us;
-
-void gwtx_pin_callback()
-{
-    gwtx_rise_us = lp_timer.read_us();
-}
-#endif
+#define MIN_SYMBOL_TIMEOUT       8   // number of symbols to keep receiver open for
 
 /*!
  * LoRaMac parameters
@@ -342,21 +336,12 @@
  */
 static uint8_t ChannelsNbRepCounter = 0;
 
-static uint32_t AggregatedLastTxDoneTime_us;
-
 /*!
  * Current channel index
  */
 static uint8_t Channel;
 
 /*!
- * Stores the time at LoRaMac initialization.
- *
- * \remark Used for the BACKOFF_DC computation.
- */
-//static TimerTime_t LoRaMacInitializationTime = 0;
-
-/*!
  * LoRaMac upper layer event functions
  */
 static LoRaMacPrimitives_t *LoRaMacPrimitives;
@@ -367,15 +352,9 @@
 static LoRaMacCallback_t *LoRaMacCallbacks;
 
 /*!
- * Radio events function pointer
- */
-static RadioEvents_t RadioEvents;
-
-/*!
  * LoRaMac duty cycle delayed Tx timer
  */
-LowPowerTimeout tx_timeout;
-
+LowPowerTimeoutAbs _tx_timeout;
 
 /*!
  * LoRaMac reception windows delay
@@ -393,29 +372,30 @@
 
 static struct beacon_struct {
     int rx_precession_us; // positive: rxing before tx start, negative: rxing after tx start
-    unsigned int RxBeaconSetupAt_us;
-    unsigned int LastBeaconRx_us;   // updated only at beacon reception
+    us_timestamp_t rx_setup_at;
+    us_timestamp_t LastBeaconRx_us;   // updated only at beacon reception
+    us_timestamp_t sendAt;
+    int lastSendAtErr;
     int last_BeaconRxTimerError_us;
     int known_working_BeaconRxTimerError_us;
 
-    float symbol_period_secs;
+    unsigned symbol_period_us;
 
     uint8_t Precess_symbols;    // how many symbols we want to start receiver before expected transmitter
-    uint16_t SymbolTimeout;
-    float SymbolTimeout_sec;
+    uint16_t nSymbsTimeout;
+    unsigned SymbolTimeout_us;
     uint8_t num_missed;
 
     beacon_state_e state;
 
     uint16_t tx_slot_offset;
     uint16_t periodicity_slots;
-
-    LowPowerTimeout timeout_rx;
-    LowPowerTimeout timeout_guard;
+    uint16_t sendOpportunities;
+
+    LowPowerTimeoutAbs _timeout_rx;
+    LowPowerTimeoutAbs _timeout_guard;
     bool guard;
-    unsigned guard_at;
 } BeaconCtx;
-bool expecting_beacon;
 
 /*!
  * Rx window parameters
@@ -423,24 +403,19 @@
 typedef struct
 {
     int8_t Datarate;
-    uint8_t Bandwidth;
     uint32_t RxWindowTimeout;
-    int32_t RxOffset;
-}RxConfigParams_t;
+} RxConfigParams_t;
 
 /*!
  * Rx windows params
  */
 static RxConfigParams_t RxWindowsParam;
 
-
 /*!
  * Acknowledge timeout timer. Used for packet retransmissions.
  */
 static LowPowerTimeout AckTimeoutTimer;
 
-uint32_t TxTimeOnAir = 0;
-
 /*!
  * Number of trials for the Join Request
  */
@@ -474,19 +449,7 @@
 /*!
  * LoRaMac tx/rx operation state
  */
-LoRaMacFlags_t LoRaMacFlags;
-static volatile struct {
-    uint8_t rx_win        : 1;
-    uint8_t join_send     : 1;
-    uint8_t beacon_setup  : 1;
-    uint8_t send          : 1;
-} flags;
-
-/*!
- * \brief Function to be executed on Radio Tx Done event
- */
-static void OnRadioTxDone( unsigned int tx_done_us );
-unsigned int TxDone_us;
+volatile LoRaMacFlags_t LoRaMacFlags;
 
 /*!
  * \brief This function prepares the MAC to abort the execution of function
@@ -495,11 +458,6 @@
 static void PrepareRxDoneAbort( void );
 
 /*!
- * \brief Function to be executed on Radio Rx Done event
- */
-static void OnRadioRxDone(unsigned rx_us, uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr );
-
-/*!
  * \brief Function executed on Radio Tx Timeout event
  */
 static void OnRadioTxTimeout( void );
@@ -514,26 +472,11 @@
  */
 static void OnRadioRxTimeout( void );
 
-static void OnRxWindowTimerEvent( void );
-
 /*!
  * \brief Function executed on AckTimeout timer event
  */
 static void OnAckTimeoutTimerEvent( void );
 
-
-/*!
- * \brief Initializes and opens the reception window
- *
- * \param [IN] freq window channel frequency
- * \param [IN] datarate window channel datarate
- * \param [IN] bandwidth window channel bandwidth
- * \param [IN] timeout window channel timeout
- *
- * \retval status Operation status [true: Success, false: Fail]
- */
-static bool RxWindowSetup( uint32_t freq, int8_t datarate, uint32_t bandwidth, uint16_t timeout, bool rxContinuous );
-
 /*!
  * \brief Adds a new MAC command to be sent.
  *
@@ -632,6 +575,60 @@
  */
 LoRaMacStatus_t SetTxContinuousWave1( uint16_t timeout, uint32_t frequency, uint8_t power );
 
+#ifdef SX127x_H
+void printLoraIrqs(bool clear)
+{
+    pc.printf("\r\nIrqFlags:");
+    if (Radio::lora.RegIrqFlags.bits.CadDetected)
+        pc.printf("CadDetected ");
+    if (Radio::lora.RegIrqFlags.bits.FhssChangeChannel) {
+        pc.printf("FhssChangeChannel:%d ", Radio::lora.RegHopChannel.bits.FhssPresentChannel);
+    }
+    if (Radio::lora.RegIrqFlags.bits.CadDone)
+        pc.printf("CadDone ");
+    if (Radio::lora.RegIrqFlags.bits.TxDone)
+        pc.printf("TxDone-dio0:%d ", Radio::radio.dio0.read());
+    if (Radio::lora.RegIrqFlags.bits.ValidHeader)
+        pc.printf("ValidHeader ");
+    if (Radio::lora.RegIrqFlags.bits.PayloadCrcError)
+        pc.printf("PayloadCrcError ");
+    if (Radio::lora.RegIrqFlags.bits.RxDone)
+        pc.printf("RxDone ");  
+    if (Radio::lora.RegIrqFlags.bits.RxTimeout)
+        pc.printf("RxTimeout ");
+
+    pc.printf("\r\n");
+
+    if (clear)
+        Radio::radio.write_reg(REG_LR_IRQFLAGS, Radio::lora.RegIrqFlags.octet);
+}
+
+void printOpMode()
+{
+    Radio::radio.RegOpMode.octet = Radio::radio.read_reg(REG_OPMODE);
+    switch (Radio::radio.RegOpMode.bits.Mode) {
+        case RF_OPMODE_SLEEP: pc.printf("sleep"); break;
+        case RF_OPMODE_STANDBY: pc.printf("stby"); break;
+        case RF_OPMODE_SYNTHESIZER_TX: pc.printf("fstx"); break;
+        case RF_OPMODE_TRANSMITTER: pc.printf("tx"); break;
+        case RF_OPMODE_SYNTHESIZER_RX: pc.printf("fsrx"); break;
+        case RF_OPMODE_RECEIVER: pc.printf("rx"); break;
+        case 6:
+            if (Radio::radio.RegOpMode.bits.LongRangeMode)
+                pc.printf("rxs");
+            else
+                pc.printf("-6-");
+            break;  // todo: different lora/fsk
+        case 7:
+            if (Radio::radio.RegOpMode.bits.LongRangeMode)
+                pc.printf("cad");
+            else
+                pc.printf("-7-");
+            break;  // todo: different lora/fsk
+    }
+}
+#endif /* SX127x_H */
+
 /*!
  * \brief Resets MAC specific parameters to default
  */
@@ -640,70 +637,143 @@
 void
 loramac_print_status()
 {
-    isr_printf("DR%u=sf%u guard:%d\r\n",
+    int until_beacon = BeaconCtx.rx_setup_at - BeaconCtx._timeout_rx.read_us();
+    mac_printf("until_beacon:%d ", until_beacon);
+    mac_printf("DR%u=sf%u guard:%d\r\n",
         LoRaMacParams.ChannelsDatarate_fixed,
         Datarates[LoRaMacParams.ChannelsDatarate_fixed],
         BeaconCtx.guard
     );
+#ifdef SX128x_H 
+    status_t status;
+    Radio::radio.xfer(OPCODE_GET_STATUS, 0, 1, &status.octet);
+    switch (status.bits.cmdStatus) {
+        case 1: pc.printf("success"); break;
+        case 2: pc.printf("dataAvail"); break;
+        case 3: pc.printf("cmdTimeout"); break;
+        case 4: pc.printf("cmdErr"); break;
+        case 5: pc.printf("exeFail"); break;
+        case 6: pc.printf("txdone"); break;
+        default: pc.printf("cmdStatus:<%u>", status.bits.cmdStatus); break;
+    }
+    pc.printf(" ");
+    switch (status.bits.chipMode) {
+        case 2: pc.printf("stdby_rc"); break;
+        case 3: pc.printf("stdby_xosc"); break;
+        case 4: pc.printf("fs"); break;
+        case 5: pc.printf("\e[32mrx\e[0m"); break;
+        case 6: pc.printf("\e[31mtx\e[0m"); break;
+        default: pc.printf("chipMode:<%u>", status.bits.chipMode); break;
+    }
+    LoRaPktPar0_t LoRaPktPar0; 
+    LoRaPktPar0.octet = Radio::radio.readReg(REG_ADDR_LORA_PKTPAR0, 1);
+    pc.printf(" bw:%u sf%u ", LoRaPktPar0.bits.modem_bw, LoRaPktPar0.bits.modem_sf);
+    mac_printf("loraSync:%04x\r\n", Radio::radio.readReg(REG_ADDR_LORA_SYNC, 2));
+#elif defined(SX127x_H)
+    Radio::radio.RegPaConfig.octet = Radio::radio.read_reg(REG_PACONFIG);
+    if (Radio::radio.RegPaConfig.bits.PaSelect)
+        pc.printf("PA_BOOST ");
+    else
+        pc.printf("RFO ");
+
+    Radio::radio.RegOpMode.octet = Radio::radio.read_reg(REG_OPMODE);
+    pc.printf("%.3fMHz sf%ubw%u ", Radio::radio.get_frf_MHz(), Radio::lora.getSf(), Radio::lora.getBw());
+    pc.printf("dio0pin:%u ", Radio::radio.dio0.read());
+    printOpMode();
+    if (!Radio::radio.RegOpMode.bits.LongRangeMode) {
+        pc.printf("FSK\r\n");
+        return;
+    }
+
+    Radio::lora.RegIrqFlags.octet = Radio::radio.read_reg(REG_LR_IRQFLAGS);
+    printLoraIrqs(false);
+
+    Radio::lora.RegTest33.octet = Radio::radio.read_reg(REG_LR_TEST33);     // invert_i_q
+    Radio::lora.RegDriftInvert.octet = Radio::radio.read_reg(REG_LR_DRIFT_INVERT);
+    pc.printf("modemstat:%02x, rxinv:%x,%x\r\n", Radio::radio.read_reg(REG_LR_MODEMSTAT), Radio::lora.RegTest33.octet, Radio::lora.RegDriftInvert.octet);
+    Radio::radio.RegDioMapping1.octet = Radio::radio.read_reg(REG_DIOMAPPING1);
+    pc.printf("\r\ndio0map:%u\r\n", Radio::radio.RegDioMapping1.bits.Dio0Mapping);
+    pc.printf("FIfoAddrPtr:%02x RxBase:%02x\r\n", Radio::radio.read_reg(REG_LR_FIFOADDRPTR), Radio::radio.read_reg(REG_LR_FIFORXBASEADDR));
+#elif defined(SX126x_H)
+    status_t status;
+    Radio::radio.xfer(OPCODE_GET_STATUS, 0, 1, &status.octet);
+    switch (status.bits.chipMode) {
+        case 2: mac_printf("STBY_RC"); break;
+        case 3: mac_printf("STBY_XOSC"); break;
+        case 4: mac_printf("FS"); break;
+        case 5: mac_printf("RX"); break;
+        case 6: mac_printf("TX"); break;
+        default: mac_printf("%u", status.bits.chipMode); break;
+    }
+    pc.printf(" ");
+    switch (status.bits.cmdStatus) {
+        case 1: mac_printf("rfu"); break;
+        case 2: mac_printf("dataAvail"); break;
+        case 3: mac_printf("timeout"); break;
+        case 4: mac_printf("err"); break;
+        case 5: mac_printf("fail"); break;
+        case 6: mac_printf("txdone"); break;
+        default: mac_printf("%u", status.bits.cmdStatus); break;
+    }
+    loraConfig0_t conf0;
+    conf0.octet = Radio::radio.readReg(REG_ADDR_LORA_CONFIG0, 1);
+    // bw7=125 bw8=250 b9=500
+    mac_printf(" bw:%u sf%u\r\n", conf0.bits.modem_bw, conf0.bits.modem_sf);
+    loraConfig1_t conf1;
+    conf1.octet = Radio::radio.readReg(REG_ADDR_LORA_CONFIG1, 1);
+    mac_printf("inviq:%u cr%u\r\n", conf1.bits.rx_invert_iq, conf1.bits.tx_coding_rate);
+    mac_printf("loraSync:%04x\r\n", Radio::radio.readReg(REG_ADDR_LORA_SYNC, 2));
+#endif /* ..SX126x_H */
 }
 
-/*
- * Rx window precise timing
- *
- * For more details please consult the following document, chapter 3.1.2.
- * http://www.semtech.com/images/datasheet/SX1272_settings_for_LoRaWAN_v2.0.pdf
- * or
- * http://www.semtech.com/images/datasheet/SX1276_settings_for_LoRaWAN_v2.0.pdf
- *
- *                 Downlink start: T = Tx + 1s (+/- 20 us)
- *                            |
- *             TRxEarly       |        TRxLate
- *                |           |           |
- *                |           |           +---+---+---+---+---+---+---+---+
- *                |           |           |       Latest Rx window        |
- *                |           |           +---+---+---+---+---+---+---+---+
- *                |           |           |
- *                +---+---+---+---+---+---+---+---+
- *                |       Earliest Rx window      |
- *                +---+---+---+---+---+---+---+---+
- *                            |
- *                            +---+---+---+---+---+---+---+---+
- *Downlink preamble 8 symbols |   |   |   |   |   |   |   |   |
- *                            +---+---+---+---+---+---+---+---+
- *
- *                     Worst case Rx window timings
- *
- * TRxLate  = DEFAULT_MIN_RX_SYMBOLS * tSymbol - RADIO_WAKEUP_TIME
- * TRxEarly = 8 - DEFAULT_MIN_RX_SYMBOLS * tSymbol - RxWindowTimeout - RADIO_WAKEUP_TIME
- *
- * TRxLate - TRxEarly = 2 * DEFAULT_SYSTEM_MAX_RX_ERROR
- *
- * RxOffset = ( TRxLate + TRxEarly ) / 2
- *
- * RxWindowTimeout = ( 2 * DEFAULT_MIN_RX_SYMBOLS - 8 ) * tSymbol + 2 * DEFAULT_SYSTEM_MAX_RX_ERROR
- * RxOffset = 4 * tSymbol - RxWindowTimeout / 2 - RADIO_WAKE_UP_TIME
- *
- * Minimal value of RxWindowTimeout must be 5 symbols which implies that the system always tolerates at least an error of 1.5 * tSymbol
- */
-/*!
- * Computes the Rx window parameters.
- *
- * \param [IN] datarate     Rx window datarate to be used
- * \param [IN] rxError      Maximum timing error of the receiver. in milliseconds
- *                          The receiver will turn on in a [-rxError : +rxError] ms
- *                          interval around RxOffset
- *
- * \retval rxConfigParams   Returns a RxConfigParams_t structure.
- */
+static void RxWindowSetup( uint32_t freq, int8_t datarate, uint16_t timeout, bool rxContinuous )
+{
+    uint8_t downlinkDatarate = Datarates[datarate];
+
+    Radio::SetChannel( freq );
+
+    // Store downlink datarate
+    McpsIndication.RxDatarate = ( uint8_t ) datarate;
+
+    Radio::LoRaModemConfig(LORA_BANDWIDTH_KHZ, downlinkDatarate, 1);
+
+                        //    preambleLen, fixLen, crcOn, invIQ
+    Radio::LoRaPacketConfig(PREAMBLE_SYMBS, false, false, true);
+    Radio::SetLoRaSymbolTimeout(timeout);
+    Radio::SetRxMaxPayloadLength(LORAMAC_PHY_MAXPAYLOAD);
+
+} // ..RxWindowSetup()
+
+static void OnRxWindowTimerEvent( void )
+{
+    RxWindowSetup(Channels[Channel].Frequency, RxWindowsParam.Datarate, RxWindowsParam.RxWindowTimeout, false);
+    Radio::Rx( LoRaMacParams.MaxRxWindow );
+    //mac_printf("rxwinTo:%u\r\n", RxWindowsParam.RxWindowTimeout);
+#ifdef SX128x_H
+    {
+        LoRaPktPar1_t LoRaPktPar1;                                                                                  
+        LoRaPktPar1.octet = Radio::radio.readReg(REG_ADDR_LORA_PKTPAR1, 1);
+        mac_printf("dnRxiq:");
+        if (LoRaPktPar1.bits.rxinvert_iq) {
+            mac_printf("IQ_STD\r\n");
+        } else {
+            mac_printf("IQ_INV\r\n");
+        }
+    }
+#endif /* SX128x_H */
+}
+
 static RxConfigParams_t ComputeRxWindowParameters( int8_t datarate, uint32_t rxError );
 
-static void OnRadioTxDone( unsigned int tx_done_us )
+static void OnRadioTxDone_bh()
 {
     // Setup timers
     if (IsRxWindowsEnabled)
     {
-        rx_timeout.attach_us(&OnRxWindowTimerEvent, RxWindowDelay_us);
-        if (NodeAckRequested)
+        int delay = lpt_offset + RxWindowDelay_us;
+        _rx_timeout_setAt = Radio::irqAt + delay;
+        _rx_timeout.attach_us(&OnRxWindowTimerEvent, _rx_timeout_setAt);
+        if (LoRaMacFlags.Bits.NodeAckRequested)
         {
             AckTimeoutTimer.attach_us(&OnAckTimeoutTimerEvent, (RxWindowDelay_us/1000) + ACK_TIMEOUT_us + randr(-ACK_TIMEOUT_RND_us, ACK_TIMEOUT_RND_us));
         }
@@ -719,22 +789,20 @@
         }
         LoRaMacFlags.Bits.MacDone = 1;
     }
-    Radio.Sleep( );   
-    TxDone_us = tx_done_us; 
-
-    // Update Aggregated last tx done time
-    AggregatedLastTxDoneTime_us = tx_done_us;
-    // Update Backoff
-
-    if (!NodeAckRequested)
+
+    if (!LoRaMacFlags.Bits.NodeAckRequested)
     {
         McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_OK;
         ChannelsNbRepCounter++;
     }
-    
+
     MlmeIndication.MlmeIndication = MLME_TXDONE;
     MlmeIndication.Status = LORAMAC_EVENT_INFO_STATUS_OK;
-    LoRaMacPrimitives->MacMlmeIndication( &MlmeIndication );  
+    LoRaMacPrimitives->MacMlmeIndication( &MlmeIndication );
+
+    Radio::Standby( );
+
+    txDoneAt = Radio::irqAt + lpt_offset;
 }
 
 
@@ -744,192 +812,134 @@
         LoRaMacFlags.Bits.McpsInd = 0;
         LoRaMacPrimitives->MacMcpsIndication( &McpsIndication );
     }
-    
+
     if (LoRaMacFlags.Bits.McpsReq) {
         LoRaMacPrimitives->MacMcpsConfirm( &McpsConfirm );
         LoRaMacFlags.Bits.McpsReq = 0;
-    }    
+    }
 }
 
 static void PrepareRxDoneAbort( void )
 {
-    if (NodeAckRequested)
+    if (LoRaMacFlags.Bits.NodeAckRequested)
     {
         OnAckTimeoutTimerEvent( );
     }
 
     LoRaMacFlags.Bits.McpsInd = 1;
     LoRaMacFlags.Bits.MacDone = 1;
-    
+
     application_callbacks();
 }
 
 void send_bh()
-{      
-    int8_t datarate = Datarates[LoRaMacParams.ChannelsDatarate_fixed];
-    int8_t txPowerIndex = 0;
-    int8_t txPower = 0;
-
-    txPowerIndex = LoRaMacParams.ChannelsTxPower;
-    txPower = TxPowers[txPowerIndex];
-
+{
     MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_ERROR_SEND;
     McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_ERROR_SEND;
-    McpsConfirm.TxPower = txPowerIndex;
+    McpsConfirm.TxPower = LoRaMacParams.ChannelsTxPower;
     McpsConfirm.UpLinkFrequency = Channels[Channel].Frequency;
 
-    if (!IsLoRaMacNetworkJoined)    // joining is channel hunting
-        Radio.SetChannel( Channels[Channel].Frequency );
-
-#if defined( USE_BAND_915_SINGLE )
-    // High speed LoRa channel BW500 kHz
-    Radio.SetTxConfig( MODEM_LORA, txPower, 0, 2, datarate, 1, 8, false, true, 0, 0, false, 3e3 );
-    TxTimeOnAir = Radio.TimeOnAir( MODEM_LORA, LoRaMacBufferPktLen );
-#elif defined(USE_BAND_433) && defined (ENABLE_SX1276)
-    if( LoRaMacParams.ChannelsDatarate_fixed == DR_7 )
-    { // High Speed FSK channel
-        //Radio.SetMaxPayloadLength( MODEM_FSK, LoRaMacBufferPktLen );
-        Radio.SetTxConfig( MODEM_FSK, txPower, 25e3, 0, datarate * 1e3, 0, 5, false, true, 0, 0, false, 3e3 );
-        TxTimeOnAir = Radio.TimeOnAir( MODEM_FSK, LoRaMacBufferPktLen );
-
-    }
-    else if( LoRaMacParams.ChannelsDatarate_fixed == DR_6 )
-    { // High speed LoRa channel
-        //Radio.SetMaxPayloadLength( MODEM_LORA, LoRaMacBufferPktLen );
-        Radio.SetTxConfig( MODEM_LORA, txPower, 0, 1, datarate, 1, 8, false, true, 0, 0, false, 3e3 );
-        TxTimeOnAir = Radio.TimeOnAir( MODEM_LORA, LoRaMacBufferPktLen );
-    }
-    else
-    { // Normal LoRa channel
-        //Radio.SetMaxPayloadLength( MODEM_LORA, LoRaMacBufferPktLen );
-        Radio.SetTxConfig( MODEM_LORA, txPower, 0, 0, datarate, 1, 8, false, true, 0, 0, false, 3e3 );
-        TxTimeOnAir = Radio.TimeOnAir( MODEM_LORA, LoRaMacBufferPktLen );
-    }
-#endif
-
-    // Store the time on air
-    McpsConfirm.TxTimeOnAir = TxTimeOnAir;
-    MlmeConfirm.TxTimeOnAir = TxTimeOnAir;
-
-    if (!IsLoRaMacNetworkJoined)
+    if (!LoRaMacFlags.Bits.IsLoRaMacNetworkJoined)    // joining is channel hunting
+        Radio::SetChannel( Channels[Channel].Frequency );
+
+    if (!LoRaMacFlags.Bits.IsLoRaMacNetworkJoined)
     {
         JoinRequestTrials++;
-        isr_printf("join %luhz try%u DR%u\r\n", Channels[Channel].Frequency, JoinRequestTrials, LoRaMacParams.ChannelsDatarate_fixed);
+        mac_printf("join %luhz try%u DR%u\r\n", Channels[Channel].Frequency, JoinRequestTrials, LoRaMacParams.ChannelsDatarate_fixed);
     }
 
-    Radio.SetTxConfig(
-        /* RadioModems_t modem */ MODEM_LORA,
-        /* int8_t power */ TxPowers[LoRaMacParams.ChannelsTxPower],
-        /* uint32_t fdev */ 0,
-        /* uint32_t bandwidth */ LORA_BANDWIDTH,
-        /* uint32_t datarate */ Datarates[LoRaMacParams.ChannelsDatarate_fixed],
-        /* uint8_t coderate */ 1,
-        /* uint16_t preambleLen */ 8,
-        /* bool fixLen */ false,
-        /* bool crcOn */ true,
-        /* bool freqHopOn */ 0,
-        /* uint8_t hopPeriod */ 0,
-        /* bool iqInverted */ false,
-        /* uint32_t timeout */ 3e3
-    );
-    Radio.Send( LoRaMacBuffer, LoRaMacBufferPktLen );        
-        
+    Radio::set_tx_dbm(TxPowers[LoRaMacParams.ChannelsTxPower]);
+    Radio::LoRaModemConfig(LORA_BANDWIDTH_KHZ, Datarates[LoRaMacParams.ChannelsDatarate_fixed], 1);
+
+                        //    preambleLen, fixLen, crcOn, invIQ
+    Radio::LoRaPacketConfig(PREAMBLE_SYMBS, false, true, false);
+    Radio::Send(tx_buf_len, 0, 0, 0);
+
     LoRaMacFlags.Bits.uplink_pending = 0;   // sent
 
     // Compute Rx1 windows parameters, taken at TxDone
-    if (!IsLoRaMacNetworkJoined)
-        RxWindowDelay_us = LoRaMacParams.JoinAcceptDelay_us + RxWindowsParam.RxOffset;
+    if (!LoRaMacFlags.Bits.IsLoRaMacNetworkJoined)
+        RxWindowDelay_us = LoRaMacParams.JoinAcceptDelay_us - TARGET_PRECESSION_us;
     else
-        RxWindowDelay_us = LoRaMacParams.ReceiveDelay_us + RxWindowsParam.RxOffset;
-
-    RxWindowDelay_us -= DIO0_LAG_us;
-}
+        RxWindowDelay_us = LoRaMacParams.ReceiveDelay_us - TARGET_PRECESSION_us;
+
+} // ..send_bh()
 
 void send_callback()
-{      
+{
+    int err = 0;
+    us_timestamp_t now = BeaconCtx.sendAt + BeaconCtx.lastSendAtErr;
+
     if (BeaconCtx.guard) {
         /* last send, this will be restarted after beacon sent */
         return;
     }
-    
-    tx_timeout.attach_us(&send_callback, BeaconCtx.periodicity_slots * 30000);
-                
+    BeaconCtx.sendOpportunities++;
+
+    BeaconCtx.sendAt += BeaconCtx.periodicity_slots * PING_SLOT_RESOLUTION_us;
+    if (BeaconCtx.state == BEACON_STATE_LOCKED) {
+        float sinceBeacon = now - BeaconCtx.LastBeaconRx_us;
+        float ratio = sinceBeacon / BEACON_INTERVAL_us;
+        err = BeaconCtx.last_BeaconRxTimerError_us * ratio;
+    }
+    _tx_timeout.attach_us(&send_callback, BeaconCtx.sendAt + err);
+    BeaconCtx.lastSendAtErr = err;
+
     if (LoRaMacFlags.Bits.uplink_pending)
-        flags.send = 1;
+        LoRaMacFlags.Bits.send = 1;
 }
 
 void OnRxBeaconSetup()
 {
-    flags.beacon_setup = 1;
-}
-
-void OnRxBeaconSetupBH()
-{
-    unsigned int prevRxBeaconSetupAt_us;
-    unsigned now_us = lp_timer.read_us();
-
-    prevRxBeaconSetupAt_us = BeaconCtx.RxBeaconSetupAt_us;
-    BeaconCtx.RxBeaconSetupAt_us = now_us;
-    expecting_beacon = true;
-    
-    Radio.SetRxConfig(
-        /* RadioModems_t */ MODEM_LORA,
-        /* uint32_t bandwidth */ LORA_BANDWIDTH,
-        /* uint32_t datarate */ Datarates[BEACON_CHANNEL_DR],
-        /* uint8_t coderate */ 1,
-        /* uint32_t bandwidthAfc */ 0,
-        /* uint16_t preambleLen */ 10,
-        /* uint16_t symbTimeout */ BeaconCtx.SymbolTimeout,
-        /* bool fixLen */ true,
-        /* uint8_t payloadLen */ BEACON_SIZE,
-        /* bool crcOn */ false,
-        /* bool freqHopOn */ 0,
-        /* uint8_t hopPeriod */ 0,
-        /* bool iqInverted */ false,
-        /* bool rxContinuous */false
-    );
-
-    Radio.Rx(2000);
-    
-    unsigned int us_since_last_beacon_start = BeaconCtx.RxBeaconSetupAt_us - prevRxBeaconSetupAt_us;
-    isr_printf("OnRxBeaconSetup() %u since-last:%u\r\n", BeaconCtx.SymbolTimeout, us_since_last_beacon_start);
+    Radio::Standby( );
+
+    LoRaMacFlags.Bits.expecting_beacon = true;
+
+    Radio::LoRaModemConfig(LORA_BANDWIDTH_KHZ, Datarates[BEACON_CHANNEL_DR], 1);
+
+                        //    preambleLen, fixLen, crcOn, invIQ
+    Radio::LoRaPacketConfig(PREAMBLE_SYMBS, true, false, false);
+    Radio::SetFixedPayloadLength(BEACON_SIZE);
+    Radio::SetLoRaSymbolTimeout(BeaconCtx.nSymbsTimeout);
+
+    Radio::Rx(2000);
+
+    BeaconCtx.lastSendAtErr = 0;
+
+#ifdef SX128x_H
+    {
+        LoRaPktPar1_t LoRaPktPar1;                                                                                  
+        LoRaPktPar1.octet = Radio::radio.readReg(REG_ADDR_LORA_PKTPAR1, 1);
+        mac_printf("beaconRxiq:");
+        if (LoRaPktPar1.bits.rxinvert_iq) {
+            mac_printf("IQ_STD\r\n");
+        } else {
+            mac_printf("IQ_INV\r\n");
+        }
+    }
+#endif /* SX128x_H */
 }
 
 void guard_callback()
 {
-    unsigned now_us = lp_timer.read_us();
-    unsigned since_last = now_us - BeaconCtx.guard_at;
-
-    if (BeaconCtx.state != BEACON_STATE_FIRST_ACQ && since_last < (BEACON_INTERVAL_us - 1000000)) {
-        isr_printf("guard-fail %u\r\n", since_last);
-        BeaconCtx.timeout_rx.detach();
-        BeaconCtx.state = BEACON_STATE_NONE;
-        IsLoRaMacNetworkJoined = false;
-
-        /*McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_STOP;
-        MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_STOP;
-        LoRaMacFlags.Bits.MacDone = 1;
-        LoRaMacPrimitives->MacMcpsConfirm( &McpsConfirm );*/
-        return;
-    }
-    
-    /* occurs BEACON_GUARD_us prior to OnRxBeaconSetup() */
     BeaconCtx.guard = true;
 
-    isr_printf("guard since-last:%u\r\n", since_last);
-    
-    BeaconCtx.guard_at = now_us;
+    _tx_timeout.detach();
+    mac_printf("sendOpportunities:%u\r\n", BeaconCtx.sendOpportunities);
+    BeaconCtx.sendOpportunities = 0;
 }
 
-static void set_beacon_symbol_timeout(float secs)
+static void us_to_nSymbTimeout(unsigned us)
 {
-    isr_printf("symTo:%.1f ", secs);
-    BeaconCtx.SymbolTimeout = secs / BeaconCtx.symbol_period_secs;
-    if (BeaconCtx.SymbolTimeout < (BEACON_MIN_SYMBOL_TIMEOUT+BeaconCtx.Precess_symbols)) {
-        BeaconCtx.SymbolTimeout = BEACON_MIN_SYMBOL_TIMEOUT+BeaconCtx.Precess_symbols;  
-    }
-    BeaconCtx.SymbolTimeout_sec = BeaconCtx.SymbolTimeout * BeaconCtx.symbol_period_secs;
-    isr_printf("%u\r\n", BeaconCtx.SymbolTimeout);
+    mac_printf("symTo:%u ", us);
+    BeaconCtx.nSymbsTimeout = us / BeaconCtx.symbol_period_us;
+    if (BeaconCtx.nSymbsTimeout < (MIN_SYMBOL_TIMEOUT+BeaconCtx.Precess_symbols)) {
+        BeaconCtx.nSymbsTimeout = MIN_SYMBOL_TIMEOUT+BeaconCtx.Precess_symbols;
+    } else if (BeaconCtx.nSymbsTimeout > 255)
+        BeaconCtx.nSymbsTimeout = 255;
+
+    BeaconCtx.SymbolTimeout_us = BeaconCtx.nSymbsTimeout * BeaconCtx.symbol_period_us;
+    mac_printf("%u\r\n", BeaconCtx.nSymbsTimeout);
 }
 
 static uint16_t beacon_crc( uint8_t *buffer, uint16_t length )
@@ -956,78 +966,73 @@
     return crc;
 }
 
-/* low power timer needs larger value due to poor resolution */
-#define TARGET_PRECESSION_US        3000
-#define BEACON_RX_TIMEOUT_LOCKED    0.008
-
-void rx_beacon(unsigned int rx_us, uint8_t* payload, uint16_t size)
+void rx_beacon(uint16_t size)
 {
     static bool compensate_precession = false;
     int32_t compensation = 0;
-    unsigned ThisBeaconRx_us = rx_us - (BEACON_TOA_us + BEACON_RXDONE_LATENCY_us);
-
+    us_timestamp_t ThisBeaconRx_us = Radio::irqAt + lpt_offset - beaconDuration;
+
+    mac_printf("rx_beacon %llu ", Radio::irqAt);
     BeaconCtx.guard = false;
-    BeaconCtx.rx_precession_us = ThisBeaconRx_us - BeaconCtx.RxBeaconSetupAt_us;
+    BeaconCtx.rx_precession_us = ThisBeaconRx_us - BeaconCtx.rx_setup_at;
     if (BeaconCtx.state != BEACON_STATE_FIRST_ACQ) {
-        unsigned int us_since_last = ThisBeaconRx_us - BeaconCtx.LastBeaconRx_us;
-        unsigned int intervals_since_last = us_since_last / BEACON_INTERVAL_us;
         BeaconCtx.known_working_BeaconRxTimerError_us = BeaconCtx.last_BeaconRxTimerError_us;
-        /* get average of error history */
-        BeaconCtx.last_BeaconRxTimerError_us = (ThisBeaconRx_us - BeaconCtx.LastBeaconRx_us) % BEACON_INTERVAL_us;
-        /* BeaconRxTimerError: positive means our clock is fast
-         * negative means our clock is slow */
-        if (BeaconCtx.last_BeaconRxTimerError_us > (BEACON_INTERVAL_us/2))
-            BeaconCtx.last_BeaconRxTimerError_us -= BEACON_INTERVAL_us; // negative value representing slow crystal
-
-        if (intervals_since_last > 1) {
-            /* timer error is measured over more than one beacon period */
-            BeaconCtx.last_BeaconRxTimerError_us /= intervals_since_last;
+        BeaconCtx.last_BeaconRxTimerError_us = (ThisBeaconRx_us - BeaconCtx.LastBeaconRx_us) - (BEACON_INTERVAL_us * (BeaconCtx.num_missed+1));
+
+        if (BeaconCtx.num_missed > 0) {
+            /* Timer error is measured over more than one beacon period.
+             * Scale to error seen over single beacon period */
+            BeaconCtx.last_BeaconRxTimerError_us /= BeaconCtx.num_missed + 1;
         }
 
         if (BeaconCtx.state == BEACON_STATE_ACQ_ERROR) {
-            isr_printf("-->LOCKED ");
+            mac_printf("-->LOCKED ");
             BeaconCtx.state = BEACON_STATE_LOCKED;
             compensate_precession = true;
-            set_beacon_symbol_timeout(BEACON_RX_TIMEOUT_LOCKED);
+            us_to_nSymbTimeout(BEACON_RX_TIMEOUT_LOCKED_us);
         }
     } else {
         /* ignore precession at first acquisition because it has slot resolution added */
-        isr_printf("-->ACQ_ERROR ");
+        mac_printf("-->ACQ_ERROR ");
         // next beacon will give us our crystal error
         BeaconCtx.state = BEACON_STATE_ACQ_ERROR;
     }
 
-#ifdef DEBUG_GWTX_JUMPER
-    isr_printf("rx-before-gwtx:%d ", gwtx_rise_us - BeaconCtx.RxBeaconSetupAt_us);
-#endif /* DEBUG_GWTX_JUMPER */    
-    isr_printf("err%d=%u-%u ", BeaconCtx.last_BeaconRxTimerError_us, ThisBeaconRx_us, BeaconCtx.LastBeaconRx_us);
-    isr_printf(" rx-before-tx:%d ", BeaconCtx.rx_precession_us);
+    mac_printf("err%d=%llu-%llu ", BeaconCtx.last_BeaconRxTimerError_us, ThisBeaconRx_us, BeaconCtx.LastBeaconRx_us);
+    if (BeaconCtx.num_missed > 0)
+        mac_printf("missed%u ", BeaconCtx.num_missed);
+
+    mac_printf(" rx-before-tx:%d ", BeaconCtx.rx_precession_us);
+    if (BeaconCtx.last_BeaconRxTimerError_us > 40000 || BeaconCtx.last_BeaconRxTimerError_us < -40000) {
+        BeaconCtx._timeout_rx.detach();
+        BeaconCtx._timeout_guard.detach();
+        mac_printf("halt\r\n");
+        for (;;) asm("nop");
+    }
     BeaconCtx.LastBeaconRx_us = ThisBeaconRx_us;
 
     if (BeaconCtx.state == BEACON_STATE_LOCKED) {
         if (compensate_precession) {
-            compensation = BeaconCtx.rx_precession_us - TARGET_PRECESSION_US + BeaconCtx.last_BeaconRxTimerError_us;
-            isr_printf(" comp%ld", compensation);
+            compensation = BeaconCtx.rx_precession_us - TARGET_PRECESSION_us + BeaconCtx.last_BeaconRxTimerError_us;
+            mac_printf(" comp%ld", compensation);
         }
     }
 
-    unsigned int next_beacon_expected_us = BEACON_INTERVAL_us + compensation;
-    unsigned now_us = lp_timer.read_us();
-    unsigned us_since_rx_setup = now_us - BeaconCtx.RxBeaconSetupAt_us;
-    unsigned timeout_us = next_beacon_expected_us - us_since_rx_setup;
-    BeaconCtx.timeout_rx.attach_us(&OnRxBeaconSetup, timeout_us);
-    BeaconCtx.timeout_guard.attach_us(&guard_callback, timeout_us - BEACON_GUARD_us);
-    
-    /***************************************/
-    unsigned us_since_beacon_start = now_us - ThisBeaconRx_us;
-    timeout_us = BEACON_RESERVED_us + (BeaconCtx.tx_slot_offset * 30000) - us_since_beacon_start;
-    tx_timeout.attach_us(&send_callback, timeout_us);
-    /***************************************/    
-    isr_printf(" nextTx:%u\r\n", timeout_us);
+    // reference tick for uplink schedule: when gateway started beacon
+    BeaconCtx.sendAt = BeaconCtx.rx_setup_at + BeaconCtx.rx_precession_us;
+    BeaconCtx.sendAt += BeaconCtx.tx_slot_offset * PING_SLOT_RESOLUTION_us;
+    _tx_timeout.attach_us(&send_callback, BeaconCtx.sendAt);
+    mac_printf("sendAt:%llu ", BeaconCtx.sendAt);
+
+    BeaconCtx.rx_setup_at += BEACON_INTERVAL_us;
+    BeaconCtx.rx_setup_at += compensation;
+
+    BeaconCtx._timeout_rx.attach_us(&OnRxBeaconSetup, BeaconCtx.rx_setup_at);
+    BeaconCtx._timeout_guard.attach_us(&guard_callback, BeaconCtx.rx_setup_at - BEACON_GUARD_us);
 
     if (BeaconCtx.num_missed > 0) {
         /* restore rx symbol timeout */
-        set_beacon_symbol_timeout(BEACON_RX_TIMEOUT_LOCKED);
+        us_to_nSymbTimeout(BEACON_RX_TIMEOUT_LOCKED_us);
     }
 
     BeaconCtx.num_missed = 0;
@@ -1037,82 +1042,32 @@
     LoRaMacPrimitives->MacMlmeIndication( &MlmeIndication );
 
     /* check beacon payload */
-    uint16_t calc_crc = beacon_crc(payload, 4);
-    uint16_t rx_crc = payload[4];
-    rx_crc |= payload[5] << 8;
+    uint16_t calc_crc = beacon_crc(Radio::radio.rx_buf, 4);
+    uint16_t rx_crc = Radio::radio.rx_buf[4];
+    rx_crc |= Radio::radio.rx_buf[5] << 8;
     if (rx_crc == calc_crc) {
-        unsigned int rx = payload[0];
-        rx |= payload[1] << 8;
-        rx |= payload[2] << 16;
-        rx |= payload[3] << 24;
+        unsigned int rx = Radio::radio.rx_buf[0];
+        rx |= Radio::radio.rx_buf[1] << 8;
+        rx |= Radio::radio.rx_buf[2] << 16;
+        rx |= Radio::radio.rx_buf[3] << 24;
         if (rx != 0) {
-            //isr_printf("beacon payload:%08x\r\n", rx);
-            LoRaMacRxPayload[0] = payload[0];
-            LoRaMacRxPayload[1] = payload[1];
-            LoRaMacRxPayload[2] = payload[2];
-            LoRaMacRxPayload[3] = payload[3];
+            //mac_printf("beacon payload:%08x\r\n", rx);
             McpsIndication.McpsIndication = MCPS_MULTICAST;
             McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_OK;
-            McpsIndication.Buffer = LoRaMacRxPayload;
+            McpsIndication.Buffer = Radio::radio.rx_buf;
             McpsIndication.BufferSize = 4;
             McpsIndication.RxData = true;
             LoRaMacPrimitives->MacMcpsIndication( &McpsIndication );
         }
     } else
-        isr_printf("calc_crc:%04x rx_crc:%04x\r\n", calc_crc, rx_crc);
-}
-
-float get_symbol_period(uint8_t bw, uint8_t sf)
+        mac_printf("calc_crc:%04x rx_crc:%04x\r\n", calc_crc, rx_crc);
+} // ..rx_beacon()
+
+
+#define JOIN_ACCEPT_MAX_SIZE        34
+static void OnRadioRxDone(uint8_t size, float rssi, float snr )
 {
-        //bw:,   // 0=125kHz, 1=250kHz, 2=500KHz
-    float hz;
-    switch( bw )
-    {
-    //case 0: // 7.8 kHz
-    //    bw = 78e2;
-    //    break;
-    //case 1: // 10.4 kHz
-    //    bw = 104e2;
-    //    break;
-    //case 2: // 15.6 kHz
-    //    bw = 156e2;
-    //    break;
-    //case 3: // 20.8 kHz
-    //    bw = 208e2;
-    //    break;
-    //case 4: // 31.2 kHz
-    //    bw = 312e2;
-    //    break;
-    //case 5: // 41.4 kHz
-    //    bw = 414e2;
-    //    break;
-    //case 6: // 62.5 kHz
-    //    bw = 625e2;
-    //    break;
-    case 0: // 125 kHz
-        hz = 125e3;
-        break;
-    case 1: // 250 kHz
-        hz = 250e3;
-        break;
-    case 2: // 500 kHz
-        hz = 500e3;
-        break;
-    default:
-        return 0;
-    }
-
-    // return symbol period in seconds
-    return (1 << sf) / hz;
-}
-
-static uint32_t GetRxBandwidth( int8_t datarate )
-{
-    return 2;   /* always 500KHz */
-}
-
-static void OnRadioRxDone(unsigned rx_us, uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr )
-{
+    uint8_t _jaDecrypted[JOIN_ACCEPT_MAX_SIZE];
     LoRaMacHeader_t macHdr;
     LoRaMacFrameCtrl_t fCtrl;
     bool skipIndication = false;
@@ -1151,106 +1106,118 @@
     McpsIndication.DownLinkCounter = 0;
     McpsIndication.McpsIndication = MCPS_UNCONFIRMED;
 
-    Radio.Sleep( );
-
-    if (expecting_beacon) {
-        rx_beacon(rx_us, payload, size);
-        expecting_beacon = false;
+    Radio::Sleep( );
+
+    //mac_printf("OnRadioRxDone %u", size);
+    if (LoRaMacFlags.Bits.expecting_beacon) {
+        rx_beacon(size);
+        LoRaMacFlags.Bits.expecting_beacon = false;
         return;
     }
 
-    macHdr.Value = payload[pktHeaderLen++];
+    macHdr.Value = Radio::radio.rx_buf[pktHeaderLen++];
+    //mac_printf("mtype:%x\r\n", macHdr.Bits.MType);
 
     switch( macHdr.Bits.MType )
     {
         case FRAME_TYPE_JOIN_ACCEPT:
-            if (IsLoRaMacNetworkJoined)
+            if (LoRaMacFlags.Bits.IsLoRaMacNetworkJoined)
             {
                 McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR_JOIN_ACCEPT;
                 PrepareRxDoneAbort( );
                 return;
             }
-            LoRaMacJoinDecrypt( payload + 1, size - 1, LoRaMacAppKey, LoRaMacRxPayload + 1 );
-
-            LoRaMacRxPayload[0] = macHdr.Value;
-
-            LoRaMacJoinComputeMic( LoRaMacRxPayload, size - LORAMAC_MFR_LEN, LoRaMacAppKey, &mic );
-
-            micRx |= ( uint32_t )LoRaMacRxPayload[size - LORAMAC_MFR_LEN];
-            micRx |= ( ( uint32_t )LoRaMacRxPayload[size - LORAMAC_MFR_LEN + 1] << 8 );
-            micRx |= ( ( uint32_t )LoRaMacRxPayload[size - LORAMAC_MFR_LEN + 2] << 16 );
-            micRx |= ( ( uint32_t )LoRaMacRxPayload[size - LORAMAC_MFR_LEN + 3] << 24 );
+            LoRaMacJoinDecrypt( Radio::radio.rx_buf + 1, size - 1, LoRaMacAppKey, &_jaDecrypted[1]);
+
+            _jaDecrypted[0] = macHdr.Value;
+
+            LoRaMacJoinComputeMic( _jaDecrypted, size - LORAMAC_MFR_LEN, LoRaMacAppKey, &mic );
+
+            micRx |= ( uint32_t )_jaDecrypted[size - LORAMAC_MFR_LEN];
+            micRx |= ( ( uint32_t )_jaDecrypted[size - LORAMAC_MFR_LEN + 1] << 8 );
+            micRx |= ( ( uint32_t )_jaDecrypted[size - LORAMAC_MFR_LEN + 2] << 16 );
+            micRx |= ( ( uint32_t )_jaDecrypted[size - LORAMAC_MFR_LEN + 3] << 24 );
 
             if( micRx == mic )
             {
-                LoRaMacJoinComputeSKeys( LoRaMacAppKey, LoRaMacRxPayload + 1, LoRaMacDevNonce, LoRaMacNwkSKey, LoRaMacAppSKey );
-
-                LoRaMacNetID = ( uint32_t )LoRaMacRxPayload[4];
-                LoRaMacNetID |= ( ( uint32_t )LoRaMacRxPayload[5] << 8 );
-                LoRaMacNetID |= ( ( uint32_t )LoRaMacRxPayload[6] << 16 );
-
-                LoRaMacDevAddr = ( uint32_t )LoRaMacRxPayload[7];
-                LoRaMacDevAddr |= ( ( uint32_t )LoRaMacRxPayload[8] << 8 );
-                LoRaMacDevAddr |= ( ( uint32_t )LoRaMacRxPayload[9] << 16 );
-                LoRaMacDevAddr |= ( ( uint32_t )LoRaMacRxPayload[10] << 24 );
+                LoRaMacJoinComputeSKeys( LoRaMacAppKey, _jaDecrypted + 1, LoRaMacDevNonce, LoRaMacNwkSKey, LoRaMacAppSKey );
+
+                LoRaMacNetID = ( uint32_t )_jaDecrypted[4];
+                LoRaMacNetID |= ( ( uint32_t )_jaDecrypted[5] << 8 );
+                LoRaMacNetID |= ( ( uint32_t )_jaDecrypted[6] << 16 );
+
+                LoRaMacDevAddr = ( uint32_t )_jaDecrypted[7];
+                LoRaMacDevAddr |= ( ( uint32_t )_jaDecrypted[8] << 8 );
+                LoRaMacDevAddr |= ( ( uint32_t )_jaDecrypted[9] << 16 );
+                LoRaMacDevAddr |= ( ( uint32_t )_jaDecrypted[10] << 24 );
 
                 // DLSettings
-                LoRaMacParams.Rx1DrOffset = ( LoRaMacRxPayload[11] >> 4 ) & 0x07;
-
-                LoRaMacParams.ReceiveDelay_us = ( LoRaMacRxPayload[12] & 0x0F );
+                LoRaMacParams.Rx1DrOffset = ( _jaDecrypted[11] >> 4 ) & 0x07;
+
+                LoRaMacParams.ReceiveDelay_us = ( _jaDecrypted[12] & 0x0F );
                 if( LoRaMacParams.ReceiveDelay_us == 0 )
                     LoRaMacParams.ReceiveDelay_us = RECEIVE_DELAY_us;
                 else
                     LoRaMacParams.ReceiveDelay_us *= 10;
 
-                uint16_t beaconTimingDelay = LoRaMacRxPayload[13] & 0xff;
-                beaconTimingDelay |= LoRaMacRxPayload[14] << 8;
-
-                isr_printf("%lx slots:%x (rxdelay %lu)", LoRaMacDevAddr, beaconTimingDelay, LoRaMacParams.ReceiveDelay_us);
-                unsigned int now_us = lp_timer.read_us();
-                unsigned int us_since_TxDone = now_us - TxDone_us;
-                unsigned us_to_beacon = ( PING_SLOT_RESOLUTION_us * beaconTimingDelay );
-                unsigned timeout_us = us_to_beacon - us_since_TxDone;
-                BeaconCtx.timeout_rx.attach_us(&OnRxBeaconSetup, timeout_us);
-                BeaconCtx.timeout_guard.attach_us(&guard_callback, timeout_us - BEACON_GUARD_us);
-                
-                BeaconCtx.tx_slot_offset = LoRaMacRxPayload[15];
-                BeaconCtx.tx_slot_offset |= LoRaMacRxPayload[16] << 8;
-                BeaconCtx.periodicity_slots = LoRaMacRxPayload[17];
-                BeaconCtx.periodicity_slots |= LoRaMacRxPayload[18] << 8;                     
-                
-                BeaconCtx.LastBeaconRx_us = (us_to_beacon + TxDone_us) - BEACON_INTERVAL_us;
-                unsigned us_since_beacon_start = now_us - BeaconCtx.LastBeaconRx_us;
-                int now_slot = (us_since_beacon_start - BEACON_RESERVED_us) / 30000;
-                int use_slot = BeaconCtx.tx_slot_offset;
-                while (use_slot < now_slot)
-                    use_slot += BeaconCtx.periodicity_slots;                
-                int slots_now_to_next = use_slot - now_slot;
-                timeout_us = slots_now_to_next * 30000;
-                tx_timeout.attach_us(&send_callback, timeout_us);
-
-                isr_printf(" now_slot:%d use_slot:%d timeout_us:%u\r\n", now_slot, use_slot, timeout_us);
-                isr_printf("us_to_beacon:%u, since_txDone:%u\r\n", us_to_beacon, us_since_TxDone);
+                uint16_t beaconTimingDelay = _jaDecrypted[13] & 0xff;
+                beaconTimingDelay |= _jaDecrypted[14] << 8;
+
+                mac_printf("%lx slots:%x (rxdelay %lu)", LoRaMacDevAddr, beaconTimingDelay, LoRaMacParams.ReceiveDelay_us);
+                {
+                    unsigned us_to_beacon = ( PING_SLOT_RESOLUTION_us * beaconTimingDelay );
+                    mac_printf(" us_to_beacon:%u ", us_to_beacon);
+                    // time to beacon given as referenced to end of join request uplink
+                    BeaconCtx.rx_setup_at = txDoneAt + us_to_beacon - PPM_BEACON_INTERVAL;
+                    BeaconCtx._timeout_rx.attach_us(&OnRxBeaconSetup, BeaconCtx.rx_setup_at);
+                    BeaconCtx._timeout_guard.attach_us(&guard_callback, BeaconCtx.rx_setup_at - BEACON_GUARD_us);
+
+                    mac_printf("beaconIn:%llu\r\n", BeaconCtx.rx_setup_at - BeaconCtx._timeout_rx.read_us());
+                }
+
+                BeaconCtx.tx_slot_offset = _jaDecrypted[15];
+                BeaconCtx.tx_slot_offset |= _jaDecrypted[16] << 8;
+                BeaconCtx.periodicity_slots = _jaDecrypted[17];
+                BeaconCtx.periodicity_slots |= _jaDecrypted[18] << 8;
+
+                /* nowSlot: now vs previous beacon */
+                BeaconCtx.LastBeaconRx_us = BeaconCtx.rx_setup_at - BEACON_INTERVAL_us;
+                unsigned us_since_last_beacon = _rx_timeout.read_us() - BeaconCtx.LastBeaconRx_us;
+                unsigned nowSlot = us_since_last_beacon / PING_SLOT_RESOLUTION_us;
+                unsigned useSlot = BeaconCtx.tx_slot_offset;
+                while (useSlot < nowSlot)
+                    useSlot += BeaconCtx.periodicity_slots;
+
+                mac_printf("useSlot:%u nowSlot:%u ", useSlot, nowSlot);
+                BeaconCtx.sendAt = BeaconCtx.LastBeaconRx_us + (useSlot * PING_SLOT_RESOLUTION_us);
+                mac_printf("sendIn:%u\r\n", BeaconCtx.sendAt - _tx_timeout.read_us());
+                _tx_timeout.attach_us(send_callback, BeaconCtx.sendAt);
+                BeaconCtx.sendOpportunities = 0;
+
                 BeaconCtx.state = BEACON_STATE_FIRST_ACQ;
                 BeaconCtx.guard = false;
                 BeaconCtx.num_missed = 0;
                 BeaconCtx.rx_precession_us = 0;
-                BeaconCtx.last_BeaconRxTimerError_us = -PPM_100_BEACON_INTERVAL;
-                BeaconCtx.known_working_BeaconRxTimerError_us = -PPM_100_BEACON_INTERVAL;
-                BeaconCtx.symbol_period_secs = get_symbol_period(GetRxBandwidth(LORAMAC_DEFAULT_DATARATE), Datarates[LORAMAC_DEFAULT_DATARATE]);
-                // N-ms: slot resolution + minimum for preamble detector + 100ppm fast crystal rxing 12ms early
-                BeaconCtx.Precess_symbols = TARGET_PRECESSION_US * (BeaconCtx.symbol_period_secs);
-                BeaconCtx.SymbolTimeout_sec = 0.1 + (BEACON_MIN_SYMBOL_TIMEOUT * BeaconCtx.symbol_period_secs);
-                BeaconCtx.SymbolTimeout = BeaconCtx.SymbolTimeout_sec / BeaconCtx.symbol_period_secs;
+                BeaconCtx.last_BeaconRxTimerError_us = -PPM_BEACON_INTERVAL;
+                BeaconCtx.known_working_BeaconRxTimerError_us = -PPM_BEACON_INTERVAL;
+                /* first beacon reception needs to open for 30ms timing resolution */
+                BeaconCtx.Precess_symbols = ceil(TARGET_PRECESSION_us / BeaconCtx.symbol_period_us);
+                BeaconCtx.SymbolTimeout_us = PING_SLOT_RESOLUTION_us + (PPM_BEACON_INTERVAL * 2);   // error unknown at start
+                BeaconCtx.nSymbsTimeout = BeaconCtx.SymbolTimeout_us / BeaconCtx.symbol_period_us;
+                if (BeaconCtx.nSymbsTimeout < MIN_SYMBOL_TIMEOUT)
+                    BeaconCtx.nSymbsTimeout = MIN_SYMBOL_TIMEOUT;
+                else if (BeaconCtx.nSymbsTimeout > 255)
+                    BeaconCtx.nSymbsTimeout = 255;
+                mac_printf("startSymbTo:%u ", BeaconCtx.nSymbsTimeout);
 
                 MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_OK;
-                IsLoRaMacNetworkJoined = true;
+                LoRaMacFlags.Bits.IsLoRaMacNetworkJoined = true;
                 LoRaMacParams.ChannelsDatarate_fixed = LoRaMacParamsDefaults.ChannelsDatarate_fixed;
             }
             else
             {
                 MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_JOIN_FAIL;
-                isr_printf("join-mic-fail\r\n");
+                mac_printf("join-mic-fail\r\n");
                 JoinRequestTrials = MaxJoinRequestTrials; // stop trying
             }
             LoRaMacPrimitives->MacMlmeConfirm( &MlmeConfirm );
@@ -1259,10 +1226,10 @@
         case FRAME_TYPE_DATA_CONFIRMED_DOWN:
         case FRAME_TYPE_DATA_UNCONFIRMED_DOWN:
             {
-                address = payload[pktHeaderLen++];
-                address |= ( (uint32_t)payload[pktHeaderLen++] << 8 );
-                address |= ( (uint32_t)payload[pktHeaderLen++] << 16 );
-                address |= ( (uint32_t)payload[pktHeaderLen++] << 24 );
+                address = Radio::radio.rx_buf[pktHeaderLen++];
+                address |= ( (uint32_t)Radio::radio.rx_buf[pktHeaderLen++] << 8 );
+                address |= ( (uint32_t)Radio::radio.rx_buf[pktHeaderLen++] << 16 );
+                address |= ( (uint32_t)Radio::radio.rx_buf[pktHeaderLen++] << 24 );
 
                 if( address != LoRaMacDevAddr )
                 {
@@ -1295,17 +1262,17 @@
                     downLinkCounter = DownLinkCounter;
                 }
 
-                fCtrl.Value = payload[pktHeaderLen++];
-
-                sequenceCounter = ( uint16_t )payload[pktHeaderLen++];
-                sequenceCounter |= ( uint16_t )payload[pktHeaderLen++] << 8;
+                fCtrl.Value = Radio::radio.rx_buf[pktHeaderLen++];
+
+                sequenceCounter = ( uint16_t )Radio::radio.rx_buf[pktHeaderLen++];
+                sequenceCounter |= ( uint16_t )Radio::radio.rx_buf[pktHeaderLen++] << 8;
 
                 appPayloadStartIndex = 8 + fCtrl.Bits.FOptsLen;
 
-                micRx |= ( uint32_t )payload[size - LORAMAC_MFR_LEN];
-                micRx |= ( ( uint32_t )payload[size - LORAMAC_MFR_LEN + 1] << 8 );
-                micRx |= ( ( uint32_t )payload[size - LORAMAC_MFR_LEN + 2] << 16 );
-                micRx |= ( ( uint32_t )payload[size - LORAMAC_MFR_LEN + 3] << 24 );
+                micRx |= ( uint32_t )Radio::radio.rx_buf[size - LORAMAC_MFR_LEN];
+                micRx |= ( ( uint32_t )Radio::radio.rx_buf[size - LORAMAC_MFR_LEN + 1] << 8 );
+                micRx |= ( ( uint32_t )Radio::radio.rx_buf[size - LORAMAC_MFR_LEN + 2] << 16 );
+                micRx |= ( ( uint32_t )Radio::radio.rx_buf[size - LORAMAC_MFR_LEN + 3] << 24 );
 
                 sequenceCounterPrev = ( uint16_t )downLinkCounter;
                 sequenceCounterDiff = ( sequenceCounter - sequenceCounterPrev );
@@ -1313,7 +1280,7 @@
                 if( sequenceCounterDiff < ( 1 << 15 ) )
                 {
                     downLinkCounter += sequenceCounterDiff;
-                    LoRaMacComputeMic( payload, size - LORAMAC_MFR_LEN, nwkSKey, address, DOWN_LINK, downLinkCounter, &mic );
+                    LoRaMacComputeMic( Radio::radio.rx_buf, size - LORAMAC_MFR_LEN, nwkSKey, address, DOWN_LINK, downLinkCounter, &mic );
                     if( micRx == mic )
                     {
                         isMicOk = true;
@@ -1323,7 +1290,7 @@
                 {
                     // check for sequence roll-over
                     uint32_t  downLinkCounterTmp = downLinkCounter + 0x10000 + ( int16_t )sequenceCounterDiff;
-                    LoRaMacComputeMic( payload, size - LORAMAC_MFR_LEN, nwkSKey, address, DOWN_LINK, downLinkCounterTmp, &mic );
+                    LoRaMacComputeMic( Radio::radio.rx_buf, size - LORAMAC_MFR_LEN, nwkSKey, address, DOWN_LINK, downLinkCounterTmp, &mic );
                     if( micRx == mic )
                     {
                         isMicOk = true;
@@ -1372,7 +1339,7 @@
                     {
                         if( macHdr.Bits.MType == FRAME_TYPE_DATA_CONFIRMED_DOWN )
                         {
-                            SrvAckRequested = true;
+                            LoRaMacFlags.Bits.SrvAckRequested = true;
                             McpsIndication.McpsIndication = MCPS_CONFIRMED;
 
                             if( ( DownLinkCounter == downLinkCounter ) &&
@@ -1388,7 +1355,7 @@
                         }
                         else
                         {
-                            SrvAckRequested = false;
+                            LoRaMacFlags.Bits.SrvAckRequested = false;
                             McpsIndication.McpsIndication = MCPS_UNCONFIRMED;
 
                             if( ( DownLinkCounter == downLinkCounter ) &&
@@ -1422,7 +1389,7 @@
                     // Process payload and MAC commands
                     if( ( ( size - 4 ) - appPayloadStartIndex ) > 0 )
                     {
-                        port = payload[appPayloadStartIndex++];
+                        port = Radio::radio.rx_buf[appPayloadStartIndex++];
                         frameLen = ( size - 4 ) - appPayloadStartIndex;
 
                         McpsIndication.Port = port;
@@ -1432,16 +1399,17 @@
                             // Only allow frames which do not have fOpts
                             if( fCtrl.Bits.FOptsLen == 0 )
                             {
-                                LoRaMacPayloadDecrypt( payload + appPayloadStartIndex,
+                                uint8_t macDecrypt[16];
+                                LoRaMacPayloadDecrypt( Radio::radio.rx_buf + appPayloadStartIndex,
                                                        frameLen,
                                                        nwkSKey,
                                                        address,
                                                        DOWN_LINK,
                                                        downLinkCounter,
-                                                       LoRaMacRxPayload );
+                                                       macDecrypt);
 
                                 // Decode frame payload MAC commands
-                                ProcessMacCommands( LoRaMacRxPayload, 0, frameLen, snr );
+                                ProcessMacCommands( macDecrypt, 0, frameLen, snr );
                             }
                             else
                             {
@@ -1453,20 +1421,20 @@
                             if( fCtrl.Bits.FOptsLen > 0 )
                             {
                                 // Decode Options field MAC commands. Omit the fPort.
-                                ProcessMacCommands( payload, 8, appPayloadStartIndex - 1, snr );
+                                ProcessMacCommands( Radio::radio.rx_buf, 8, appPayloadStartIndex - 1, snr );
                             }
 
-                            LoRaMacPayloadDecrypt( payload + appPayloadStartIndex,
+                            LoRaMacPayloadDecrypt( Radio::radio.rx_buf + appPayloadStartIndex,
                                                    frameLen,
                                                    appSKey,
                                                    address,
                                                    DOWN_LINK,
                                                    downLinkCounter,
-                                                   LoRaMacRxPayload );
+                                                   rxFRMPayload);
 
                             if( skipIndication == false )
                             {
-                                McpsIndication.Buffer = LoRaMacRxPayload;
+                                McpsIndication.Buffer = rxFRMPayload;
                                 McpsIndication.BufferSize = frameLen;
                                 McpsIndication.RxData = true;
                             }
@@ -1477,7 +1445,7 @@
                         if( fCtrl.Bits.FOptsLen > 0 )
                         {
                             // Decode Options field MAC commands
-                            ProcessMacCommands( payload, 8, appPayloadStartIndex, snr );
+                            ProcessMacCommands( Radio::radio.rx_buf, 8, appPayloadStartIndex, snr );
                         }
                     }
 
@@ -1513,11 +1481,9 @@
             break;
         case FRAME_TYPE_PROPRIETARY:
             {
-                memcpy1( LoRaMacRxPayload, &payload[pktHeaderLen], size );
-
                 McpsIndication.McpsIndication = MCPS_PROPRIETARY;
                 McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_OK;
-                McpsIndication.Buffer = LoRaMacRxPayload;
+                McpsIndication.Buffer = Radio::radio.rx_buf;
                 McpsIndication.BufferSize = size - pktHeaderLen;
 
                 LoRaMacFlags.Bits.McpsInd = 1;
@@ -1526,7 +1492,7 @@
         default:
             McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR_RX_MTYPE;
             McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_ERROR_RX_MTYPE;
-            isr_printf("%d:macHdr:%02x(%d,%d) ", size, macHdr.Value, rssi, snr);
+            mac_printf("%d:macHdr:%02x(%d,%d) ", size, macHdr.Value, rssi, snr);
             PrepareRxDoneAbort( );
             break;
     }
@@ -1534,13 +1500,11 @@
 
     application_callbacks();
 
-} // ..OnRadioRxDone();
-
-
+} // ..OnRadioRxDone()
 
 static void OnRadioTxTimeout( void )
 {
-    Radio.Sleep( );
+    Radio::Sleep( );
 
     McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT;
     MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT;
@@ -1549,30 +1513,27 @@
 
 static void OnRadioRxError( void )
 {
-    Radio.Sleep( );
-
-    if (NodeAckRequested)
+    Radio::Sleep( );
+
+    if (LoRaMacFlags.Bits.NodeAckRequested)
     {
         McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_RX_ERROR;
     }
     MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_RX_ERROR;
 
-    if ((lp_timer.read_us() - AggregatedLastTxDoneTime_us) >= RxWindowDelay_us )
-    {
-        LoRaMacFlags.Bits.MacDone = 1;
-    }
+    LoRaMacFlags.Bits.MacDone = 1;
 }
 
 static void
 join_send()
 {
-    flags.join_send = 1;
+    LoRaMacFlags.Bits.join_send = 1;
 }
 
 static void ScheduleTx( void )
 {
-    if (!IsLoRaMacNetworkJoined)
-        flags.send = 1; // immediately send join asychronously
+    if (!LoRaMacFlags.Bits.IsLoRaMacNetworkJoined)
+        LoRaMacFlags.Bits.send = 1; // immediately send join asychronously
     else
         LoRaMacFlags.Bits.uplink_pending = 1;   // send synchronously
 }
@@ -1583,149 +1544,81 @@
     if (JoinRequestTrials < MaxJoinRequestTrials) {
         LoRaMacHeader_t macHdr;
         LoRaMacFrameCtrl_t fCtrl;
-        
+
         if (++Channel == LORA_MAX_NB_CHANNELS)
             Channel = 0;
-        isr_printf("<join-ch%u>", Channel);
-    
+        mac_printf("<join-ch%u>", Channel);
+
         macHdr.Value = 0;
         macHdr.Bits.MType = FRAME_TYPE_JOIN_REQ;
-    
+
         fCtrl.Value = 0;
         fCtrl.Bits.Adr = 0;
-    
+
         /* In case of join request retransmissions, the stack must prepare
          * the frame again, because the network server keeps track of the random
          * LoRaMacDevNonce values to prevent reply attacks. */
         PrepareFrame( &macHdr, &fCtrl, 0, NULL, 0 );
-        
+
         ScheduleTx();
     } else {
         MlmeConfirm.MlmeRequest = MLME_JOIN;
         MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_JOIN_FAIL;
         LoRaMacPrimitives->MacMlmeConfirm( &MlmeConfirm );
     }
-}
+} // ..join_send_bh()
 
 static void OnRadioRxTimeout( void )
 {
-    static unsigned LastBeaconRx_us;
-    
-    Radio.Sleep( );
-
-    if (expecting_beacon) {
-        unsigned next_beacon_expected_us = BEACON_INTERVAL_us;
-        if (BeaconCtx.state == BEACON_STATE_FIRST_ACQ) {
-            next_beacon_expected_us -= 1000000;
-            set_beacon_symbol_timeout(1.000);
-        } else {
-            next_beacon_expected_us += BeaconCtx.known_working_BeaconRxTimerError_us;
-            // for measurement resolution and temperature drift while missing beacons:
-            next_beacon_expected_us -= 10000;
-            set_beacon_symbol_timeout(BeaconCtx.SymbolTimeout_sec + 0.025);
-        }
-        unsigned now_us = lp_timer.read_us();
-        unsigned us_since_rx_setup = now_us - BeaconCtx.RxBeaconSetupAt_us;
-        unsigned b_timeout_us = next_beacon_expected_us - us_since_rx_setup;
-        BeaconCtx.timeout_rx.attach_us(&OnRxBeaconSetup, b_timeout_us);
-        BeaconCtx.timeout_guard.attach_us(&guard_callback, b_timeout_us - BEACON_GUARD_us);
-
-        if (BeaconCtx.num_missed == 0)  /* first missed beacon, init our local LastBeaconRx_us */
-            LastBeaconRx_us = BeaconCtx.LastBeaconRx_us;
-
-        BeaconCtx.num_missed++;
-        /***************************************/
-        unsigned fake_ThisBeaconRx_us = LastBeaconRx_us + BEACON_INTERVAL_us + BeaconCtx.known_working_BeaconRxTimerError_us;
-        unsigned us_since_beacon_start = now_us - fake_ThisBeaconRx_us;
-        unsigned timeout_us = BEACON_RESERVED_us + (BeaconCtx.tx_slot_offset * 30000) - us_since_beacon_start;
-        tx_timeout.attach_us(&send_callback, timeout_us);
-        isr_printf("fake %u\r\n", fake_ThisBeaconRx_us - LastBeaconRx_us);
-        LastBeaconRx_us = fake_ThisBeaconRx_us; // update our local for next missed beacon
-        /***************************************/         
-
-#ifdef DEBUG_GWTX_JUMPER
-        isr_printf("rx-before-gwtx:%d ", gwtx_rise_us - BeaconCtx.RxBeaconSetupAt_us);
-#endif /* DEBUG_GWTX_JUMPER */    
-            
-       isr_printf("beacon-rx-timeout %u %u next in %uus (rxing for %u)", BeaconCtx.num_missed, BeaconCtx.SymbolTimeout, b_timeout_us, us_since_rx_setup);
-       isr_printf(" %u,nextTx:%u\r\n", us_since_beacon_start, timeout_us);
-
-        MlmeIndication.MlmeIndication = MLME_BEACON;
-        MlmeIndication.Status = LORAMAC_EVENT_INFO_STATUS_BEACON_LOST;
-        LoRaMacPrimitives->MacMlmeIndication( &MlmeIndication );
+    Radio::Sleep( );
+
+    if (LoRaMacFlags.Bits.expecting_beacon) {
+        float ourErrSecs = BeaconCtx.known_working_BeaconRxTimerError_us / 1000000.0;
 
         BeaconCtx.guard = false;
-        expecting_beacon = false;
+        LoRaMacFlags.Bits.expecting_beacon = false;
+
+        BeaconCtx.rx_setup_at += BEACON_INTERVAL_us + BeaconCtx.known_working_BeaconRxTimerError_us;
+        BeaconCtx.rx_setup_at -= (PPM_BEACON_INTERVAL / 4);
+        us_to_nSymbTimeout(BeaconCtx.SymbolTimeout_us + (PPM_BEACON_INTERVAL / 2));
+        mac_printf("beacon timeout ourErr:%f SymbTo:%u\r\n", ourErrSecs, BeaconCtx.nSymbsTimeout);
+
+        BeaconCtx._timeout_rx.attach_us(&OnRxBeaconSetup, BeaconCtx.rx_setup_at);
+        BeaconCtx._timeout_guard.attach_us(&guard_callback, BeaconCtx.rx_setup_at - BEACON_GUARD_us);
+
+        if (++BeaconCtx.num_missed > BEACONS_MISSED_LIMIT) {
+            LoRaMacFlags.Bits.reJoin = 1;
+        } else {
+            MlmeIndication.MlmeIndication = MLME_BEACON;
+            MlmeIndication.Status = LORAMAC_EVENT_INFO_STATUS_BEACON_LOST;
+            LoRaMacPrimitives->MacMlmeIndication( &MlmeIndication );
+        }
+
+        BeaconCtx.sendAt = BeaconCtx.LastBeaconRx_us + ((BEACON_INTERVAL_us + BeaconCtx.known_working_BeaconRxTimerError_us) * BeaconCtx.num_missed);
+        BeaconCtx.sendAt += BeaconCtx.tx_slot_offset * PING_SLOT_RESOLUTION_us;
+        _tx_timeout.attach_us(&send_callback, BeaconCtx.sendAt);
+
     } else {
         if (LoRaMacFlags.Bits.MlmeReq && ( MlmeConfirm.MlmeRequest == MLME_JOIN )) {
             /* no join accept received: join retry */
-            tx_timeout.attach_us(&join_send, (JoinRequestTrials*20000) + randr(0, 70000));
+            _tx_timeout.attach_us(&join_send, _tx_timeout.read_us() + (JoinRequestTrials*20000) + randr(0, 70000));
         }
     }
 
-    if (NodeAckRequested)
+    if (LoRaMacFlags.Bits.NodeAckRequested)
     {
         McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_RX_TIMEOUT;
     }
     MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_RX_TIMEOUT;
     LoRaMacFlags.Bits.MacDone = 1;
-    
+
     application_callbacks();
 } // ..OnRadioRxTimeout();
 
-static void OnRxWindowTimerEventBH()
-{
-    Radio.Standby( );
-    if (expecting_beacon) {
-        isr_printf("rxwin-during-beacon\r\n");
-        McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_RX_ERROR;
-        MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_RX_ERROR;
-        LoRaMacFlags.Bits.MacDone = 1;
-        LoRaMacPrimitives->MacMcpsConfirm( &McpsConfirm );
-    } else {
-        //RxWindowSetup( LORAMAC_FIRST_CHANNEL + ( Channel * LORAMAC_STEPWIDTH_CHANNEL), RxWindowsParam.Datarate, RxWindowsParam.Bandwidth, RxWindowsParam.RxWindowTimeout, false );
-        RxWindowSetup( Channels[Channel].Frequency, RxWindowsParam.Datarate, RxWindowsParam.Bandwidth, RxWindowsParam.RxWindowTimeout, false );
-    }
-}
-
-static void OnRxWindowTimerEvent( void )
-{
-    flags.rx_win = 1;
-}
-
 static void OnAckTimeoutTimerEvent( void )
 {
 }
 
-static bool RxWindowSetup( uint32_t freq, int8_t datarate, uint32_t bandwidth, uint16_t timeout, bool rxContinuous )
-{
-    uint8_t downlinkDatarate = Datarates[datarate];
-    RadioModems_t modem;
-
-    if( Radio.GetStatus( ) == RF_IDLE )
-    {
-        Radio.SetChannel( freq );
-
-        // Store downlink datarate
-        McpsIndication.RxDatarate = ( uint8_t ) datarate;
-
-        modem = MODEM_LORA;
-        Radio.SetRxConfig( modem, bandwidth, downlinkDatarate, 1, 0, 8, timeout, false, 0, false, 0, 0, true, rxContinuous );
-        Radio.SetMaxPayloadLength( MODEM_LORA, 255 );
-
-        if( rxContinuous == false )
-        {
-            Radio.Rx( LoRaMacParams.MaxRxWindow );
-        }
-        else
-        {
-            Radio.Rx( 0 ); // Continuous mode
-        }
-        return true;
-    }
-    return false;
-}
-
 static bool ValueInRange( int8_t value, int8_t min, int8_t max )
 {
     if( ( value >= min ) && ( value <= max ) )
@@ -1907,7 +1800,7 @@
 
 static void ResetMacParameters( void )
 {
-    IsLoRaMacNetworkJoined = false;
+    LoRaMacFlags.Bits.IsLoRaMacNetworkJoined = false;
 
     // Counters
     UpLinkCounter = 0;
@@ -1925,8 +1818,8 @@
 
     LoRaMacParams.Rx1DrOffset = LoRaMacParamsDefaults.Rx1DrOffset;
 
-    NodeAckRequested = false;
-    SrvAckRequested = false;
+    LoRaMacFlags.Bits.NodeAckRequested = false;
+    LoRaMacFlags.Bits.SrvAckRequested = false;
     MacCommandsInNextTx = false;
 
     // Reset Multicast downlink counters
@@ -1942,14 +1835,13 @@
 LoRaMacStatus_t PrepareFrame( LoRaMacHeader_t *macHdr, LoRaMacFrameCtrl_t *fCtrl, uint8_t fPort, void *fBuffer, uint16_t fBufferSize )
 {
     uint16_t i;
-    uint8_t pktHeaderLen = 0;
     uint32_t mic = 0;
     const void* payload = fBuffer;
     uint8_t framePort = fPort;
-
-    LoRaMacBufferPktLen = 0;
-
-    NodeAckRequested = false;
+    uint8_t LoRaMacTxPayloadLen = 0;
+
+    LoRaMacFlags.Bits.NodeAckRequested = false;
+    tx_buf_len = 0;
 
     if( fBuffer == NULL )
     {
@@ -1958,57 +1850,55 @@
 
     LoRaMacTxPayloadLen = fBufferSize;
 
-    LoRaMacBuffer[pktHeaderLen++] = macHdr->Value;
+    Radio::radio.tx_buf[tx_buf_len++] = macHdr->Value;
 
     switch( macHdr->Bits.MType )
     {
         case FRAME_TYPE_JOIN_REQ:
-            LoRaMacBufferPktLen = pktHeaderLen;
-
-            memcpyr( LoRaMacBuffer + LoRaMacBufferPktLen, LoRaMacAppEui, 8 );
-            LoRaMacBufferPktLen += 8;
-            memcpyr( LoRaMacBuffer + LoRaMacBufferPktLen, LoRaMacDevEui, 8 );
-            LoRaMacBufferPktLen += 8;
-
-            LoRaMacDevNonce = Radio.Random( );
-
-            LoRaMacBuffer[LoRaMacBufferPktLen++] = LoRaMacDevNonce & 0xFF;
-            LoRaMacBuffer[LoRaMacBufferPktLen++] = ( LoRaMacDevNonce >> 8 ) & 0xFF;
-
-            LoRaMacJoinComputeMic( LoRaMacBuffer, LoRaMacBufferPktLen & 0xFF, LoRaMacAppKey, &mic );
-
-            LoRaMacBuffer[LoRaMacBufferPktLen++] = mic & 0xFF;
-            LoRaMacBuffer[LoRaMacBufferPktLen++] = ( mic >> 8 ) & 0xFF;
-            LoRaMacBuffer[LoRaMacBufferPktLen++] = ( mic >> 16 ) & 0xFF;
-            LoRaMacBuffer[LoRaMacBufferPktLen++] = ( mic >> 24 ) & 0xFF;
+            memcpyr( Radio::radio.tx_buf + tx_buf_len, LoRaMacAppEui, 8 );
+            tx_buf_len += 8;
+            memcpyr( Radio::radio.tx_buf + tx_buf_len, LoRaMacDevEui, 8 );
+            tx_buf_len += 8;
+
+            LoRaMacDevNonce = Radio::Random( );
+
+            Radio::radio.tx_buf[tx_buf_len++] = LoRaMacDevNonce & 0xFF;
+            Radio::radio.tx_buf[tx_buf_len++] = ( LoRaMacDevNonce >> 8 ) & 0xFF;
+
+            LoRaMacJoinComputeMic( Radio::radio.tx_buf, tx_buf_len & 0xFF, LoRaMacAppKey, &mic );
+
+            Radio::radio.tx_buf[tx_buf_len++] = mic & 0xFF;
+            Radio::radio.tx_buf[tx_buf_len++] = ( mic >> 8 ) & 0xFF;
+            Radio::radio.tx_buf[tx_buf_len++] = ( mic >> 16 ) & 0xFF;
+            Radio::radio.tx_buf[tx_buf_len++] = ( mic >> 24 ) & 0xFF;
 
             break;
         case FRAME_TYPE_DATA_CONFIRMED_UP:
-            NodeAckRequested = true;
+            LoRaMacFlags.Bits.NodeAckRequested = true;
             //Intentional fallthrough
         case FRAME_TYPE_DATA_UNCONFIRMED_UP:
-            if (!IsLoRaMacNetworkJoined)
+            if (!LoRaMacFlags.Bits.IsLoRaMacNetworkJoined)
             {
                 return LORAMAC_STATUS_NO_NETWORK_JOINED; // No network has been joined yet
             }
 
             fCtrl->Bits.AdrAckReq = 0;
 
-            if( SrvAckRequested == true )
+            if( LoRaMacFlags.Bits.SrvAckRequested == true )
             {
-                SrvAckRequested = false;
+                LoRaMacFlags.Bits.SrvAckRequested = false;
                 fCtrl->Bits.Ack = 1;
             }
 
-            LoRaMacBuffer[pktHeaderLen++] = ( LoRaMacDevAddr ) & 0xFF;
-            LoRaMacBuffer[pktHeaderLen++] = ( LoRaMacDevAddr >> 8 ) & 0xFF;
-            LoRaMacBuffer[pktHeaderLen++] = ( LoRaMacDevAddr >> 16 ) & 0xFF;
-            LoRaMacBuffer[pktHeaderLen++] = ( LoRaMacDevAddr >> 24 ) & 0xFF;
-
-            LoRaMacBuffer[pktHeaderLen++] = fCtrl->Value;
-
-            LoRaMacBuffer[pktHeaderLen++] = UpLinkCounter & 0xFF;
-            LoRaMacBuffer[pktHeaderLen++] = ( UpLinkCounter >> 8 ) & 0xFF;
+            Radio::radio.tx_buf[tx_buf_len++] = ( LoRaMacDevAddr ) & 0xFF;
+            Radio::radio.tx_buf[tx_buf_len++] = ( LoRaMacDevAddr >> 8 ) & 0xFF;
+            Radio::radio.tx_buf[tx_buf_len++] = ( LoRaMacDevAddr >> 16 ) & 0xFF;
+            Radio::radio.tx_buf[tx_buf_len++] = ( LoRaMacDevAddr >> 24 ) & 0xFF;
+
+            Radio::radio.tx_buf[tx_buf_len++] = fCtrl->Value;
+
+            Radio::radio.tx_buf[tx_buf_len++] = UpLinkCounter & 0xFF;
+            Radio::radio.tx_buf[tx_buf_len++] = ( UpLinkCounter >> 8 ) & 0xFF;
 
             // Copy the MAC commands which must be re-send into the MAC command buffer
             memcpy1( &MacCommandsBuffer[MacCommandsBufferIndex], MacCommandsBufferToRepeat, MacCommandsBufferToRepeatIndex );
@@ -2021,10 +1911,10 @@
                     fCtrl->Bits.FOptsLen += MacCommandsBufferIndex;
 
                     // Update FCtrl field with new value of OptionsLength
-                    LoRaMacBuffer[0x05] = fCtrl->Value;
+                    Radio::radio.tx_buf[0x05] = fCtrl->Value;
                     for( i = 0; i < MacCommandsBufferIndex; i++ )
                     {
-                        LoRaMacBuffer[pktHeaderLen++] = MacCommandsBuffer[i];
+                        Radio::radio.tx_buf[tx_buf_len++] = MacCommandsBuffer[i];
                     }
                 }
             }
@@ -2047,34 +1937,34 @@
 
             if( ( payload != NULL ) && ( LoRaMacTxPayloadLen > 0 ) )
             {
-                LoRaMacBuffer[pktHeaderLen++] = framePort;
+                Radio::radio.tx_buf[tx_buf_len++] = framePort;
 
                 if( framePort == 0 )
                 {
-                    LoRaMacPayloadEncrypt( (uint8_t* ) payload, LoRaMacTxPayloadLen, LoRaMacNwkSKey, LoRaMacDevAddr, UP_LINK, UpLinkCounter, &LoRaMacBuffer[pktHeaderLen] );
+                    LoRaMacPayloadEncrypt( (uint8_t* ) payload, LoRaMacTxPayloadLen, LoRaMacNwkSKey, LoRaMacDevAddr, UP_LINK, UpLinkCounter, &Radio::radio.tx_buf[tx_buf_len] );
                 }
                 else
                 {
-                    LoRaMacPayloadEncrypt( (uint8_t* ) payload, LoRaMacTxPayloadLen, LoRaMacAppSKey, LoRaMacDevAddr, UP_LINK, UpLinkCounter, &LoRaMacBuffer[pktHeaderLen] );
+                    LoRaMacPayloadEncrypt( (uint8_t* ) payload, LoRaMacTxPayloadLen, LoRaMacAppSKey, LoRaMacDevAddr, UP_LINK, UpLinkCounter, &Radio::radio.tx_buf[tx_buf_len] );
                 }
             }
-            LoRaMacBufferPktLen = pktHeaderLen + LoRaMacTxPayloadLen;
-
-            LoRaMacComputeMic( LoRaMacBuffer, LoRaMacBufferPktLen, LoRaMacNwkSKey, LoRaMacDevAddr, UP_LINK, UpLinkCounter, &mic );
-
-            LoRaMacBuffer[LoRaMacBufferPktLen + 0] = mic & 0xFF;
-            LoRaMacBuffer[LoRaMacBufferPktLen + 1] = ( mic >> 8 ) & 0xFF;
-            LoRaMacBuffer[LoRaMacBufferPktLen + 2] = ( mic >> 16 ) & 0xFF;
-            LoRaMacBuffer[LoRaMacBufferPktLen + 3] = ( mic >> 24 ) & 0xFF;
-
-            LoRaMacBufferPktLen += LORAMAC_MFR_LEN;
+            tx_buf_len = tx_buf_len + LoRaMacTxPayloadLen;
+
+            LoRaMacComputeMic( Radio::radio.tx_buf, tx_buf_len, LoRaMacNwkSKey, LoRaMacDevAddr, UP_LINK, UpLinkCounter, &mic );
+
+            Radio::radio.tx_buf[tx_buf_len + 0] = mic & 0xFF;
+            Radio::radio.tx_buf[tx_buf_len + 1] = ( mic >> 8 ) & 0xFF;
+            Radio::radio.tx_buf[tx_buf_len + 2] = ( mic >> 16 ) & 0xFF;
+            Radio::radio.tx_buf[tx_buf_len + 3] = ( mic >> 24 ) & 0xFF;
+
+            tx_buf_len += LORAMAC_MFR_LEN;
 
             break;
         case FRAME_TYPE_PROPRIETARY:
             if( ( fBuffer != NULL ) && ( LoRaMacTxPayloadLen > 0 ) )
             {
-                memcpy1( LoRaMacBuffer + pktHeaderLen, ( uint8_t* ) fBuffer, LoRaMacTxPayloadLen );
-                LoRaMacBufferPktLen = pktHeaderLen + LoRaMacTxPayloadLen;
+                memcpy1( Radio::radio.tx_buf + tx_buf_len, ( uint8_t* ) fBuffer, LoRaMacTxPayloadLen );
+                tx_buf_len = tx_buf_len + LoRaMacTxPayloadLen;
             }
             break;
         default:
@@ -2092,23 +1982,47 @@
 
     txPowerIndex = LoRaMacParams.ChannelsTxPower;
     txPower = TxPowers[txPowerIndex];
-    Radio.SetTxContinuousWave( Channels[Channel].Frequency, txPower, timeout );
+    Radio::SetTxContinuousWave( Channels[Channel].Frequency, txPower, timeout );
 
     return LORAMAC_STATUS_OK;
 }
 
 LoRaMacStatus_t SetTxContinuousWave1( uint16_t timeout, uint32_t frequency, uint8_t power )
 {
-    Radio.SetTxContinuousWave( frequency, power, timeout );
+    Radio::SetTxContinuousWave( frequency, power, timeout );
 
     return LORAMAC_STATUS_OK;
 }
 
 void seconds()
 {
-    isr_printf("second\r\n");
+    mac_printf("second\r\n");
+}
+
+void on_dio0_top_half(us_timestamp_t dio0_at)
+{
+    mac_printf("dio0th\r\n");
 }
 
+unsigned get_symbol_period_us(uint8_t sf)
+{
+    float bwMHz = LORA_BANDWIDTH_KHZ / 1000.0;
+    // return symbol period in microseconds 
+    return (1 << sf) / bwMHz;
+}
+
+const RadioEvents_t rev = {
+    /* Dio0_top_half */     on_dio0_top_half,
+    /* TxDone_topHalf */    NULL,
+    /* TxDone_botHalf */    OnRadioTxDone_bh,
+    /* TxTimeout  */        OnRadioTxTimeout,
+    /* RxDone  */           OnRadioRxDone,
+    /* RxTimeout  */        OnRadioRxTimeout,
+    /* RxError  */          OnRadioRxError,
+    /* FhssChangeChannel  */NULL,
+    /* CadDone  */          NULL
+};
+
 LoRaMacStatus_t LoRaMacInitialization( LoRaMacPrimitives_t *primitives, LoRaMacCallback_t *callbacks )
 {
     if( primitives == NULL )
@@ -2128,18 +2042,20 @@
 
     LoRaMacFlags.Value = 0;
 
-    LoRaMacDeviceClass = CLASS_A;
-
     JoinRequestTrials = 0;
     MaxJoinRequestTrials = 255;
 
+    BeaconCtx.symbol_period_us = get_symbol_period_us(Datarates[LORAMAC_DEFAULT_DATARATE]);
 
     // Reset to defaults
     LoRaMacParamsDefaults.ChannelsTxPower = LORAMAC_DEFAULT_TX_POWER;
     LoRaMacParamsDefaults.ChannelsDatarate_fixed = LORAMAC_DEFAULT_DATARATE;
 
-    LoRaMacParamsDefaults.SystemMaxRxError = 10;
-    LoRaMacParamsDefaults.MinRxSymbols = 6; // TODO XXX increase
+    LoRaMacParamsDefaults.SystemMaxRxError_ms = 20;
+    LoRaMacParamsDefaults.MinRxSymbols = (LoRaMacParamsDefaults.SystemMaxRxError_ms * 1000) / BeaconCtx.symbol_period_us; 
+    if (LoRaMacParamsDefaults.MinRxSymbols < MIN_SYMBOL_TIMEOUT)
+        LoRaMacParamsDefaults.MinRxSymbols = MIN_SYMBOL_TIMEOUT;
+
     LoRaMacParamsDefaults.MaxRxWindow = MAX_RX_WINDOW;
 
     LoRaMacParamsDefaults.ReceiveDelay_us = RECEIVE_DELAY_us;
@@ -2156,7 +2072,7 @@
     }
 
     // Init parameters which are not set in function ResetMacParameters
-    LoRaMacParams.SystemMaxRxError = LoRaMacParamsDefaults.SystemMaxRxError;
+    LoRaMacParams.SystemMaxRxError_ms = LoRaMacParamsDefaults.SystemMaxRxError_ms;
     LoRaMacParams.MinRxSymbols = LoRaMacParamsDefaults.MinRxSymbols;
     LoRaMacParams.MaxRxWindow = LoRaMacParamsDefaults.MaxRxWindow;
     LoRaMacParams.ReceiveDelay_us = LoRaMacParamsDefaults.ReceiveDelay_us;
@@ -2166,30 +2082,22 @@
     ResetMacParameters( );
 
     // Initialize Radio driver
-    RadioEvents.TxDone = OnRadioTxDone;
-    RadioEvents.RxDone = OnRadioRxDone;
-    RadioEvents.RxError = OnRadioRxError;
-    RadioEvents.TxTimeout = OnRadioTxTimeout;
-    RadioEvents.RxTimeout = OnRadioRxTimeout;
-    Radio.Init( &RadioEvents );
+    Radio::Init(&rev);
+    // lpt started later.  lpt will have smaller number.
+    lpt_offset = _rx_timeout.read_us() - Radio::lpt.read_us();
+    mac_printf("lpt_offset:%d\r\n", lpt_offset);
 
     // Random seed initialization
-    srand1( Radio.Random( ) );
+    srand1( Radio::Random( ) );
 
     PublicNetwork = true;
-    Radio.SetPublicNetwork( PublicNetwork );
-    Radio.Sleep( );
-
-    lp_timer.start();
-
-    RxWindowsParam = ComputeRxWindowParameters(LORAMAC_DEFAULT_DATARATE, LoRaMacParams.SystemMaxRxError);
-    
-#ifdef DEBUG_GWTX_JUMPER
-    gwtx_pin.rise(&gwtx_pin_callback);
-#endif
+    Radio::SetPublicNetwork( PublicNetwork );
+    Radio::Sleep( );
+
+    RxWindowsParam = ComputeRxWindowParameters(LORAMAC_DEFAULT_DATARATE, LoRaMacParams.SystemMaxRxError_ms);
 
     return LORAMAC_STATUS_OK;
-}
+} // ..LoRaMacInitialization()
 
 LoRaMacStatus_t LoRaMacQueryTxPossible( uint8_t size, LoRaMacTxInfo_t* txInfo )
 {
@@ -2200,7 +2108,7 @@
         return LORAMAC_STATUS_PARAMETER_INVALID;
     }
 
-    txInfo->CurrentPayloadSize = 255;
+    txInfo->CurrentPayloadSize = LORAMAC_PHY_MAXPAYLOAD;
 
     if( txInfo->CurrentPayloadSize >= fOptLen )
     {
@@ -2225,14 +2133,9 @@
 
     switch( mibGet->Type )
     {
-        case MIB_DEVICE_CLASS:
-        {
-            mibGet->Param.Class = LoRaMacDeviceClass;
-            break;
-        }
         case MIB_NETWORK_JOINED:
         {
-            mibGet->Param.IsNetworkJoined = IsLoRaMacNetworkJoined;
+            mibGet->Param.IsNetworkJoined = LoRaMacFlags.Bits.IsLoRaMacNetworkJoined;
             break;
         }
         case MIB_NET_ID:
@@ -2297,7 +2200,7 @@
         }
         case MIB_SYSTEM_MAX_RX_ERROR:
         {
-            mibGet->Param.SystemMaxRxError = LoRaMacParams.SystemMaxRxError;
+            mibGet->Param.SystemMaxRxError_ms = LoRaMacParams.SystemMaxRxError_ms;
             break;
         }
         case MIB_MIN_RX_SYMBOLS:
@@ -2324,33 +2227,13 @@
 
     switch( mibSet->Type )
     {
-        case MIB_DEVICE_CLASS:
-        {
-            LoRaMacDeviceClass = mibSet->Param.Class;
-            switch( LoRaMacDeviceClass )
-            {
-                case CLASS_A:
-                {
-                    // Set the radio into sleep to setup a defined state
-                    Radio.Sleep( );
-                    break;
-                }
-                case CLASS_B:
-                {
-                    break;
-                }
-                case CLASS_D:
-                    isr_printf("TODO MIB_DEVICE_CLASS:D\r\n");
-                    break;
-            }
-            break;
-        }
         case MIB_NETWORK_JOINED:
         {
-            IsLoRaMacNetworkJoined = mibSet->Param.IsNetworkJoined;
-            if (!IsLoRaMacNetworkJoined) {
-                BeaconCtx.timeout_rx.detach();
-                BeaconCtx.timeout_guard.detach();
+            LoRaMacFlags.Bits.IsLoRaMacNetworkJoined = mibSet->Param.IsNetworkJoined;
+            if (!LoRaMacFlags.Bits.IsLoRaMacNetworkJoined) {
+                mac_printf("beaconDetach\r\n");
+                BeaconCtx._timeout_rx.detach();
+                BeaconCtx._timeout_guard.detach();
                 BeaconCtx.state = BEACON_STATE_NONE;
             }
             break;
@@ -2394,7 +2277,7 @@
         case MIB_PUBLIC_NETWORK:
         {
             PublicNetwork = mibSet->Param.EnablePublicNetwork;
-            Radio.SetPublicNetwork( PublicNetwork );
+            Radio::SetPublicNetwork( PublicNetwork );
             break;
         }
         case MIB_CHANNELS_NB_REP:
@@ -2453,7 +2336,7 @@
         }
         case MIB_SYSTEM_MAX_RX_ERROR:
         {
-            LoRaMacParams.SystemMaxRxError = LoRaMacParamsDefaults.SystemMaxRxError = mibSet->Param.SystemMaxRxError;
+            LoRaMacParams.SystemMaxRxError_ms = LoRaMacParamsDefaults.SystemMaxRxError_ms = mibSet->Param.SystemMaxRxError_ms;
             break;
         }
         case MIB_MIN_RX_SYMBOLS:
@@ -2583,12 +2466,12 @@
             macHdr.Bits.MType  = FRAME_TYPE_JOIN_REQ;
 
             ResetMacParameters( );
-            expecting_beacon = false;
+            LoRaMacFlags.Bits.expecting_beacon = false;
             BeaconCtx.state = BEACON_STATE_NONE;
 
             Channel = 0;    // start with first channel
-            isr_printf("<ch0>");
-            isr_printf("mlme-join-send ch%u\r\n", Channel);
+            mac_printf("<ch0>");
+            mac_printf("mlme-join-send ch%u\r\n", Channel);
             status = Send( &macHdr, 0, NULL, 0 );
             break;
         }
@@ -2621,7 +2504,7 @@
 
     if( status != LORAMAC_STATUS_OK )
     {
-        NodeAckRequested = false;
+        LoRaMacFlags.Bits.NodeAckRequested = false;
         LoRaMacFlags.Bits.MlmeReq = 0;
     }
 
@@ -2681,17 +2564,17 @@
             break;
     }
 
-    if( readyToSend == true )
+    if (readyToSend)
     {
         status = Send( &macHdr, fPort, fBuffer, fBufferSize );
-        if( status == LORAMAC_STATUS_OK )
+        if (status == LORAMAC_STATUS_OK)
         {
             McpsConfirm.McpsRequest = mcpsRequest->Type;
             LoRaMacFlags.Bits.McpsReq = 1;
         }
         else
         {
-            NodeAckRequested = false;
+            LoRaMacFlags.Bits.NodeAckRequested = false;
         }
     }
 
@@ -2711,30 +2594,17 @@
 
 void LoRaMacTestSetChannel( uint8_t channel )
 {
-    isr_printf("set-testch%u\r\n", channel);
+    mac_printf("set-testch%u\r\n", channel);
     Channel = channel;
 }
 
 
 static RxConfigParams_t ComputeRxWindowParameters( int8_t datarate, uint32_t rxError )
 {
-    RxConfigParams_t rxConfigParams = { 0, 0, 0, 0 };
+    RxConfigParams_t rxConfigParams = { 0, 0 };
     double tSymbol = 0.0;
  
     rxConfigParams.Datarate = datarate;
-    switch( Bandwidths[datarate] )
-    {
-        default:
-        case 125000:
-            rxConfigParams.Bandwidth = 0;
-            break;
-        case 250000:
-            rxConfigParams.Bandwidth = 1;
-            break;
-        case 500000:
-            rxConfigParams.Bandwidth = 2;
-            break;
-    }
  
 #if defined( USE_BAND_433 ) || defined( USE_BAND_780 ) || defined( USE_BAND_868 )
     if( datarate == DR_7 )
@@ -2744,33 +2614,38 @@
     else
 #endif
     { // LoRa
-        tSymbol = ( ( double )( 1 << Datarates[datarate] ) / ( double )Bandwidths[datarate] ) * 1e3;
+        tSymbol = ( ( double )( 1 << Datarates[datarate] ) / LORA_BANDWIDTH_KHZ ) * 1e3;
     }
  
     rxConfigParams.RxWindowTimeout = MAX( ( uint32_t )ceil( ( ( 2 * LoRaMacParams.MinRxSymbols - 8 ) * tSymbol + 2 * rxError ) / tSymbol ), LoRaMacParams.MinRxSymbols ); // Computed number of symbols
- 
-    rxConfigParams.RxOffset = ( int32_t )ceil( ( 4.0 * tSymbol ) - ( ( rxConfigParams.RxWindowTimeout * tSymbol ) / 2.0 ) - RADIO_WAKEUP_TIME );
+    mac_printf("RxWindowTimeout:%u\r\n", rxConfigParams.RxWindowTimeout);
  
     return rxConfigParams;
 }
 
 void LoRaMacBottomHalf()
 {
-    if (flags.rx_win) {
-        OnRxWindowTimerEventBH();
-        flags.rx_win = 0;
+    if (LoRaMacFlags.Bits.join_send) {
+        join_send_bh();
+        LoRaMacFlags.Bits.join_send = 0;
     }
-    if (flags.join_send) {
-        join_send_bh();
-        flags.join_send = 0;
+    if (LoRaMacFlags.Bits.send) {
+        send_bh();
+        LoRaMacFlags.Bits.send = 0;
     }
-    if (flags.beacon_setup) {
-        OnRxBeaconSetupBH();
-        flags.beacon_setup = 0;
+
+    if (LoRaMacFlags.Bits.reJoin) {
+        MlmeReq_t mlmeReq;
+        mlmeReq.Type = MLME_JOIN;
+
+        mlmeReq.Req.Join.DevEui = LoRaMacDevEui;
+        mlmeReq.Req.Join.AppEui = LoRaMacAppEui;
+        mlmeReq.Req.Join.AppKey = LoRaMacAppKey;
+        mlmeReq.Req.Join.NbTrials = 255;
+
+        if (LoRaMacMlmeRequest(&mlmeReq) == LORAMAC_STATUS_OK)
+            LoRaMacFlags.Bits.reJoin = 0;
     }
-    if (flags.send) {
-        send_bh();
-        flags.send = 0;
-    }
-    Radio.BottomHalf();
+
+    Radio::service();
 }