Alcatel-Lucent IoT Development / LMiC

Dependents:   LoRaWAN-lmic-app

Fork of LMiC by Pascal Nysten

Files at this revision

API Documentation at this revision

Comitter:
mluis
Date:
Tue Mar 31 13:36:56 2015 +0000
Parent:
0:62d1edcc13d1
Child:
2:bebd6b2e3d18
Commit message:
Updated LMiC to release v1.4 (http://www.zurich.ibm.com/pdf/lrsc/lmic-release-v1.4.zip)

Changed in this revision

aes.cpp Show annotated file Show diff for this revision Revisions of this file
hal.h Show annotated file Show diff for this revision Revisions of this file
hal/debug.h Show diff for this revision Revisions of this file
hal/hal.cpp Show diff for this revision Revisions of this file
lmic.cpp Show annotated file Show diff for this revision Revisions of this file
lmic.h Show annotated file Show diff for this revision Revisions of this file
lorabase.h Show annotated file Show diff for this revision Revisions of this file
oslmic.cpp Show annotated file Show diff for this revision Revisions of this file
oslmic.h Show annotated file Show diff for this revision Revisions of this file
radio.cpp Show annotated file Show diff for this revision Revisions of this file
--- a/aes.cpp	Thu Jan 22 12:50:49 2015 +0000
+++ b/aes.cpp	Tue Mar 31 13:36:56 2015 +0000
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2014 IBM Corporation.
+ * Copyright (c) 2014-2015 IBM Corporation.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -181,23 +181,23 @@
 #define msbf4_write(p,v) (p)[0]=(v)>>24,(p)[1]=(v)>>16,(p)[2]=(v)>>8,(p)[3]=(v)
 #define swapmsbf(x)      ( (x&0xFF)<<24 | (x&0xFF00)<<8 | (x&0xFF0000)>>8 | (x>>24) )
 
-#define u1(v)		            ((u1_t)(v))
+#define u1(v)                       ((u1_t)(v))
 
 #define AES_key4(r1,r2,r3,r0,i)    r1 = ki[i+1]; \
                                    r2 = ki[i+2]; \
                                    r3 = ki[i+3]; \
                                    r0 = ki[i]
 
-#define AES_expr4(r1,r2,r3,r0,i)   r1 ^= AES_E4[u1(i)];	    \
-			           r2 ^= AES_E3[u1(i>>8)];  \
-			           r3 ^= AES_E2[u1(i>>16)]; \
-			           r0 ^= AES_E1[  (i>>24)]
+#define AES_expr4(r1,r2,r3,r0,i)   r1 ^= AES_E4[u1(i)];     \
+                                   r2 ^= AES_E3[u1(i>>8)];  \
+                                   r3 ^= AES_E2[u1(i>>16)]; \
+                                   r0 ^= AES_E1[  (i>>24)]
 
 #define AES_expr(a,r0,r1,r2,r3,i)  a = ki[i];                    \
-				   a ^= (AES_S[   r0>>24 ]<<24); \
-				   a ^= (AES_S[u1(r1>>16)]<<16); \
-				   a ^= (AES_S[u1(r2>> 8)]<< 8); \
-				   a ^=  AES_S[u1(r3)    ]
+                                   a ^= (AES_S[   r0>>24 ]<<24); \
+                                   a ^= (AES_S[u1(r1>>16)]<<16); \
+                                   a ^= (AES_S[u1(r2>> 8)]<< 8); \
+                                   a ^=  AES_S[u1(r3)    ]
 
 // global area for passing parameters (aux, key) and for storing round keys
 u4_t AESAUX[16/sizeof(u4_t)];
@@ -205,59 +205,59 @@
 
 // generate 1+10 roundkeys for encryption with 128-bit key
 // read 128-bit key from AESKEY in MSBF, generate roundkey words in place
-static void aesroundkeys (void) {
+static void aesroundkeys () {
     int i;
     u4_t b;
 
     for( i=0; i<4; i++) {
-	AESKEY[i] = swapmsbf(AESKEY[i]);
+        AESKEY[i] = swapmsbf(AESKEY[i]);
     }
     
     b = AESKEY[3];
     for( ; i<44; i++ ) {
-	if( i%4==0 ) {
+        if( i%4==0 ) {
             // b = SubWord(RotWord(b)) xor Rcon[i/4]
-	    b = (AES_S[u1(b >> 16)] << 24) ^
-		(AES_S[u1(b >>  8)] << 16) ^
-		(AES_S[u1(b)      ] <<  8) ^
-		(AES_S[   b >> 24 ]      ) ^
+            b = (AES_S[u1(b >> 16)] << 24) ^
+                (AES_S[u1(b >>  8)] << 16) ^
+                (AES_S[u1(b)      ] <<  8) ^
+                (AES_S[   b >> 24 ]      ) ^
                  AES_RCON[(i-4)/4];
-	}
-	AESKEY[i] = b ^= AESKEY[i-4];
+        }
+        AESKEY[i] = b ^= AESKEY[i-4];
     }
 }
 
 u4_t os_aes (u1_t mode, xref2u1_t buf, u2_t len) {
         
-	aesroundkeys();
+        aesroundkeys();
 
-	if( mode & AES_MICNOAUX ) {
-	    AESAUX[0] = AESAUX[1] = AESAUX[2] = AESAUX[3] = 0;
-	} else {
-	    AESAUX[0] = swapmsbf(AESAUX[0]);
-	    AESAUX[1] = swapmsbf(AESAUX[1]);
-	    AESAUX[2] = swapmsbf(AESAUX[2]);
-	    AESAUX[3] = swapmsbf(AESAUX[3]);
-	}
+        if( mode & AES_MICNOAUX ) {
+            AESAUX[0] = AESAUX[1] = AESAUX[2] = AESAUX[3] = 0;
+        } else {
+            AESAUX[0] = swapmsbf(AESAUX[0]);
+            AESAUX[1] = swapmsbf(AESAUX[1]);
+            AESAUX[2] = swapmsbf(AESAUX[2]);
+            AESAUX[3] = swapmsbf(AESAUX[3]);
+        }
 
-	while( (signed char)len > 0 ) {
-	    u4_t a0, a1, a2, a3;
-	    u4_t t0, t1, t2, t3;
-	    u4_t *ki, *ke;
+        while( (signed char)len > 0 ) {
+            u4_t a0, a1, a2, a3;
+            u4_t t0, t1, t2, t3;
+            u4_t *ki, *ke;
 
-	    // load input block
-	    if( (mode & AES_CTR) || ((mode & AES_MIC) && (mode & AES_MICNOAUX)==0) ) { // load CTR block or first MIC block
-		a0 = AESAUX[0];
-		a1 = AESAUX[1];
-		a2 = AESAUX[2];
-		a3 = AESAUX[3];
+            // load input block
+            if( (mode & AES_CTR) || ((mode & AES_MIC) && (mode & AES_MICNOAUX)==0) ) { // load CTR block or first MIC block
+                a0 = AESAUX[0];
+                a1 = AESAUX[1];
+                a2 = AESAUX[2];
+                a3 = AESAUX[3];
             }
             else if( (mode & AES_MIC) && len <= 16 ) { // last MIC block
                 a0 = a1 = a2 = a3 = 0; // load null block
                 mode |= ((len == 16) ? 1 : 2) << 4; // set MICSUB: CMAC subkey K1 or K2
             } else
         LOADDATA: { // load data block (partially)
-		for(t0=0; t0<16; t0++) {
+                for(t0=0; t0<16; t0++) {
                     t1 = (t1<<8) | ((t0<len) ? buf[t0] : (t0==len) ? 0x80 : 0x00);
                     if((t0&3)==3) {
                         a0 = a1;
@@ -266,74 +266,74 @@
                         a3 = t1;
                     }
                 } 
-		if( mode & AES_MIC ) {
-		    a0 ^= AESAUX[0];
-		    a1 ^= AESAUX[1];
-		    a2 ^= AESAUX[2];
-		    a3 ^= AESAUX[3];
-		}
+                if( mode & AES_MIC ) {
+                    a0 ^= AESAUX[0];
+                    a1 ^= AESAUX[1];
+                    a2 ^= AESAUX[2];
+                    a3 ^= AESAUX[3];
+                }
             }
 
-	    // perform AES encryption on block in a0-a3
-	    ki = AESKEY;
-	    ke = ki + 8*4;
-	    a0 ^= ki[0];
-	    a1 ^= ki[1];
-	    a2 ^= ki[2];
-	    a3 ^= ki[3];
-	    do {
-		AES_key4 (t1,t2,t3,t0,4);
-		AES_expr4(t1,t2,t3,t0,a0);
-		AES_expr4(t2,t3,t0,t1,a1);
-		AES_expr4(t3,t0,t1,t2,a2);
-		AES_expr4(t0,t1,t2,t3,a3);
+            // perform AES encryption on block in a0-a3
+            ki = AESKEY;
+            ke = ki + 8*4;
+            a0 ^= ki[0];
+            a1 ^= ki[1];
+            a2 ^= ki[2];
+            a3 ^= ki[3];
+            do {
+                AES_key4 (t1,t2,t3,t0,4);
+                AES_expr4(t1,t2,t3,t0,a0);
+                AES_expr4(t2,t3,t0,t1,a1);
+                AES_expr4(t3,t0,t1,t2,a2);
+                AES_expr4(t0,t1,t2,t3,a3);
 
-		AES_key4 (a1,a2,a3,a0,8);
-		AES_expr4(a1,a2,a3,a0,t0);
-		AES_expr4(a2,a3,a0,a1,t1);
-		AES_expr4(a3,a0,a1,a2,t2);
-		AES_expr4(a0,a1,a2,a3,t3);
-	    } while( (ki+=8) < ke );
+                AES_key4 (a1,a2,a3,a0,8);
+                AES_expr4(a1,a2,a3,a0,t0);
+                AES_expr4(a2,a3,a0,a1,t1);
+                AES_expr4(a3,a0,a1,a2,t2);
+                AES_expr4(a0,a1,a2,a3,t3);
+            } while( (ki+=8) < ke );
 
-	    AES_key4 (t1,t2,t3,t0,4);
-	    AES_expr4(t1,t2,t3,t0,a0);
-	    AES_expr4(t2,t3,t0,t1,a1);
-	    AES_expr4(t3,t0,t1,t2,a2);
-	    AES_expr4(t0,t1,t2,t3,a3);
+            AES_key4 (t1,t2,t3,t0,4);
+            AES_expr4(t1,t2,t3,t0,a0);
+            AES_expr4(t2,t3,t0,t1,a1);
+            AES_expr4(t3,t0,t1,t2,a2);
+            AES_expr4(t0,t1,t2,t3,a3);
 
-	    AES_expr(a0,t0,t1,t2,t3,8);
-	    AES_expr(a1,t1,t2,t3,t0,9);
-	    AES_expr(a2,t2,t3,t0,t1,10);
-	    AES_expr(a3,t3,t0,t1,t2,11);
-	    // result of AES encryption in a0-a3
+            AES_expr(a0,t0,t1,t2,t3,8);
+            AES_expr(a1,t1,t2,t3,t0,9);
+            AES_expr(a2,t2,t3,t0,t1,10);
+            AES_expr(a3,t3,t0,t1,t2,11);
+            // result of AES encryption in a0-a3
 
-	    if( mode & AES_MIC ) {
-		if( (t1 = ((mode & AES_MICSUB) >> 4)) != 0 ) { // last block
-		    do {
-			// compute CMAC subkey K1 and K2
-			t0 = a0 >> 31; // save MSB
-			a0 = (a0 << 1) | (a1 >> 31);
-			a1 = (a1 << 1) | (a2 >> 31);
-			a2 = (a2 << 1) | (a3 >> 31);
-			a3 = (a3 << 1);
-			if( t0 ) a3 ^= 0x87;
-		    } while( --t1 );
+            if( mode & AES_MIC ) {
+                if( (t1 = (mode & AES_MICSUB) >> 4) != 0 ) { // last block
+                    do {
+                        // compute CMAC subkey K1 and K2
+                        t0 = a0 >> 31; // save MSB
+                        a0 = (a0 << 1) | (a1 >> 31);
+                        a1 = (a1 << 1) | (a2 >> 31);
+                        a2 = (a2 << 1) | (a3 >> 31);
+                        a3 = (a3 << 1);
+                        if( t0 ) a3 ^= 0x87;
+                    } while( --t1 );
 
-		    AESAUX[0] ^= a0;
-		    AESAUX[1] ^= a1;
-		    AESAUX[2] ^= a2;
-		    AESAUX[3] ^= a3;
+                    AESAUX[0] ^= a0;
+                    AESAUX[1] ^= a1;
+                    AESAUX[2] ^= a2;
+                    AESAUX[3] ^= a3;
                     mode &= ~AES_MICSUB;
-		    goto LOADDATA;
-		} else {
+                    goto LOADDATA;
+                } else {
                     // save cipher block as new iv
                     AESAUX[0] = a0;
                     AESAUX[1] = a1;
                     AESAUX[2] = a2;
                     AESAUX[3] = a3;
                 }
-	    } else { // CIPHER
-		if( mode & AES_CTR ) { // xor block (partially)
+            } else { // CIPHER
+                if( mode & AES_CTR ) { // xor block (partially)
                     t0 = (len > 16) ? 16: len;
                     for(t1=0; t1<t0; t1++) {
                         buf[t1] ^= (a0>>24);
@@ -344,16 +344,16 @@
                             a2 = a3;
                         }
                     }
-		    // update counter
-		    AESAUX[3]++;
-		} else { // ECB
+                    // update counter
+                    AESAUX[3]++;
+                } else { // ECB
                     // store block
                     msbf4_write(buf+0,  a0);
-		    msbf4_write(buf+4,  a1);
-		    msbf4_write(buf+8,  a2);
-		    msbf4_write(buf+12, a3);
-		}
-	    }
+                    msbf4_write(buf+4,  a1);
+                    msbf4_write(buf+8,  a2);
+                    msbf4_write(buf+12, a3);
+                }
+            }
 
             // update block state
             if( (mode & AES_MIC)==0 || (mode & AES_MICNOAUX) ) {
@@ -361,7 +361,7 @@
                 len -= 16;
             }
             mode |= AES_MICNOAUX;
-	}
-	return AESAUX[0];
+        }
+        return AESAUX[0];
 }
 
--- a/hal.h	Thu Jan 22 12:50:49 2015 +0000
+++ b/hal.h	Tue Mar 31 13:36:56 2015 +0000
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2014 IBM Corporation.
+ * Copyright (c) 2014-2015 IBM Corporation.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -80,36 +80,4 @@
  */
 void hal_failed (void);
 
-//////////////////////////////////////////////////////////////////////
-#ifdef CFG_DEBUG
-void debug_init (void);
-void debug_led (u1_t val);
-void debug_char (u1_t c);
-void debug_hex (u1_t b);
-void debug_buf (const u1_t* buf, u2_t len);
-void debug_uint (u4_t v);
-void debug_str (const u1_t* str);
-void debug_event (int ev);
-void debug_val (const u1_t* label, u4_t val);
-#define DEBUG_INIT()   debug_init()
-#define DEBUG_LED(v)   debug_led(v)
-#define DEBUG_CHAR(c)  debug_char(c)
-#define DEBUG_HEX(b)   debug_hex(b)
-#define DEBUG_BUF(b,l) debug_buf(b,l)
-#define DEBUG_UINT(v)  debug_uint(v)
-#define DEBUG_STR(s)   debug_str(s)
-#define DEBUG_EVENT(e) debug_event(e)
-#define DEBUG_VAL(l,v) debug_val(l, v)
-#else // CFG_DEBUG
-#define DEBUG_INIT()
-#define DEBUG_LED(v)
-#define DEBUG_CHAR(c)
-#define DEBUG_HEX(b)
-#define DEBUG_BUF(b,l)
-#define DEBUG_UINT(v)
-#define DEBUG_STR(s)
-#define DEBUG_EVENT(e)
-#define DEBUG_VAL(l,v)
-#endif // CFG_DEBUG
-
 #endif // _hal_hpp_
--- a/hal/debug.h	Thu Jan 22 12:50:49 2015 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,61 +0,0 @@
-/* Copyright (c) 2012 mbed.org, MIT License
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
- * and associated documentation files (the "Software"), to deal in the Software without restriction,
- * including without limitation the rights to use, copy, modify, merge, publish, distribute,
- * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all copies or
- * substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
- * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
- * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
- 
-#ifndef DEBUG_H
-#define DEBUG_H
-
-/** @file debug.h */
-
-#ifndef NDEBUG
-
-#include <stdarg.h>
-#include <stdio.h>
-
-/** Output a debug message
- * 
- * @param format printf-style format string, followed by variables
- */
-static inline void debug(const char *format, ...) {
-    va_list args;
-    va_start(args, format);
-    vfprintf(stderr, format, args);
-    va_end(args);
-}
-
-/** Conditionally output a debug message
- * 
- * @param condition output only if condition is true
- * @param format printf-style format string, followed by variables
- */
-static inline void debug_if(bool condition, const char *format, ...) {
-    if(condition) {
-        va_list args;
-        va_start(args, format);
-        vfprintf(stderr, format, args);
-        va_end(args);
-    }
-}
-
-#else
-
-static inline void debug(const char *format, ...) {}
-static inline void debug(bool condition, const char *format, ...) {}
-
-#endif
-
-#endif
--- a/hal/hal.cpp	Thu Jan 22 12:50:49 2015 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,270 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 IBM Corporation.
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors:
- *    IBM Zurich Research Lab - initial API, implementation and documentation
- *    Semtech Apps Team       - Modified to support the MBED sx1276 driver
- *                              library.
- *                              Possibility to use original or Semtech's MBED
- *                              radio driver. The selection is done by setting
- *                              USE_SMTC_RADIO_DRIVER preprocessing directive
- *                              in lmic.h
- *******************************************************************************/
-#include "mbed.h"
-#include "lmic.h"
-#include "mbed_debug.h"
-
-#if USE_SMTC_RADIO_DRIVER
-
-#else
-
-extern void radio_irq_handler( u1_t dio );
-
-static DigitalOut nss( D10 );
-static SPI spi( D11, D12, D13 ); // ( mosi, miso, sclk )
- 
-static DigitalInOut rst( A0 );
-static DigitalOut rxtx( A4 );
- 
-static InterruptIn dio0( D2 );
-static InterruptIn dio1( D3 );
-static InterruptIn dio2( D4 ); 
-
-static void dio0Irq( void )
-{
-    radio_irq_handler( 0 );
-}
-
-static void dio1Irq( void )
-{
-    radio_irq_handler( 1 );
-}
-static void dio2Irq( void )
-{
-    radio_irq_handler( 2 );
-}
-
-#endif
-
-static u1_t irqlevel = 0;
-static u4_t ticks = 0;
-
-static Timer timer;
-static Ticker ticker;
-
-static void reset_timer( void )
-{
-    ticks += timer.read_us( ) >> 6;
-    timer.reset( );
-}
-
-void hal_init( void )
-{
-     __disable_irq( );
-     irqlevel = 0;
-
-#if USE_SMTC_RADIO_DRIVER
-
-#else
-    // configure input lines
-    dio0.mode( PullDown );
-    dio0.rise( dio0Irq );
-    dio0.enable_irq( );
-    dio1.mode( PullDown );   
-    dio1.rise( dio1Irq );
-    dio1.enable_irq( );
-    dio2.mode( PullDown );
-    dio2.rise( dio2Irq );
-    dio2.enable_irq( );
-    // configure reset line
-    rst.input( );
-    // configure spi
-    spi.frequency( 8000000 );
-    spi.format( 8, 0 );
-    nss = 1;
-#endif
-    // configure timer
-    timer.start( );
-    ticker.attach_us( reset_timer, 10000000 ); // reset timer every 10sec
-     __enable_irq( );
-}
-
-#if USE_SMTC_RADIO_DRIVER
-
-#else
-
-void hal_pin_rxtx( u1_t val )
-{
-    rxtx = !val;
-}
-
-void hal_pin_nss( u1_t val )
-{
-    nss = val;
-}
-
-void hal_pin_rst( u1_t val )
-{
-    if( val == 0 || val == 1 )
-    { // drive pin
-        rst.output( );
-        rst = val;
-    } 
-    else
-    { // keep pin floating
-        rst.input( );
-    }
-}
-
-u1_t hal_spi( u1_t out )
-{
-    return spi.write( out );
-}
-
-#endif
-
-void hal_disableIRQs( void )
-{
-    __disable_irq( );
-    irqlevel++;
-}
-
-void hal_enableIRQs( void )
-{
-    if( --irqlevel == 0 )
-    {
-        __enable_irq( );
-    }
-}
-
-void hal_sleep( void )
-{
-    // NOP
-}
-
-u4_t hal_ticks( void )
-{
-    hal_disableIRQs( );
-    int t = ticks + ( timer.read_us( ) >> 6 );
-    hal_enableIRQs( );
-    return t;
-}
-
-static u2_t deltaticks( u4_t time )
-{
-    u4_t t = hal_ticks( );
-    s4_t d = time - t;
-    if( d <= 0 )
-    {
-        return 0;    // in the past
-    }
-    if( ( d >> 16 ) != 0 )
-    {
-        return 0xFFFF; // far ahead
-    }
-    return ( u2_t )d;
-}
-
-void hal_waitUntil( u4_t time )
-{
-    while( deltaticks( time ) != 0 ); // busy wait until timestamp is reached
-}
-
-u1_t hal_checkTimer( u4_t time )
-{
-    return ( deltaticks( time ) < 2 );
-}
-
-void hal_failed( void )
-{
-    while( 1 );
-}
-
-//////////////////////////////////////////////////////////////////////
-// DEBUG CODE BELOW (use CFG_DEBUG)
-//////////////////////////////////////////////////////////////////////
-#ifdef CFG_DEBUG
-
-void debug_init( void )
-{
-    // print banner
-    debug( "\r\n============== DEBUG STARTED ==============\r\n" );
-}
-
-void debug_char( u1_t c )
-{
-    debug( "%c", c );
-}
-
-void debug_hex( u1_t b )
-{
-    debug( "%02X", b );
-}
-
-void debug_buf( const u1_t* buf, u2_t len )
-{
-    while( len-- )
-    {
-        debug_hex( *buf++ );
-        debug_char( ' ' );
-    }
-    debug_char( '\r' );
-    debug_char( '\n' );
-}
-
-void debug_uint( u4_t v )
-{
-    for( s1_t n = 24; n >= 0; n -= 8 )
-    {
-        debug_hex( v >> n );
-    }
-}
-
-void debug_str( const u1_t* str )
-{
-    while( *str )
-    {
-        debug_char( *str++ );
-    }
-}
-
-void debug_val( const u1_t* label, u4_t val )
-{
-    debug_str( label );
-    debug_uint( val );
-    debug_char( '\r' );
-    debug_char( '\n' );
-}
-
-void debug_led( u1_t val )
-{
-    debug_val( "LED = ", val );
-}
-
-void debug_event( int ev ) 
-{
-    static const u1_t* evnames[] = 
-    {
-        [EV_SCAN_TIMEOUT]   = "SCAN_TIMEOUT",
-        [EV_BEACON_FOUND]   = "BEACON_FOUND",
-        [EV_BEACON_MISSED]  = "BEACON_MISSED",
-        [EV_BEACON_TRACKED] = "BEACON_TRACKED",
-        [EV_JOINING]        = "JOINING",
-        [EV_JOINED]         = "JOINED",
-        [EV_RFU1]           = "RFU1",
-        [EV_JOIN_FAILED]    = "JOIN_FAILED",
-        [EV_REJOIN_FAILED]  = "REJOIN_FAILED",
-        [EV_TXCOMPLETE]     = "TXCOMPLETE",
-        [EV_LOST_TSYNC]     = "LOST_TSYNC",
-        [EV_RESET]          = "RESET",
-        [EV_RXCOMPLETE]     = "RXCOMPLETE",
-        [EV_LINK_DEAD]      = "LINK_DEAD",
-        [EV_LINK_ALIVE]     = "LINK_ALIVE",
-    };
-    debug( "%s\r\n", evnames[ev] );
-}
-#endif // CFG_DEBUG
--- a/lmic.cpp	Thu Jan 22 12:50:49 2015 +0000
+++ b/lmic.cpp	Tue Mar 31 13:36:56 2015 +0000
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2014 IBM Corporation.
+ * Copyright (c) 2014-2015 IBM Corporation.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -9,10 +9,13 @@
  *    IBM Zurich Research Lab - initial API, implementation and documentation
  *******************************************************************************/
 
+//! \file
 #include "lmic.h"
 
+#if !defined(MINRX_SYMS)
+#define MINRX_SYMS 5
+#endif // !defined(MINRX_SYMS)
 #define PAMBL_SYMS 8
-#define MINRX_SYMS 5
 #define PAMBL_FSK  5
 #define PRERX_FSK  1
 #define RXLEN_FSK  (1+5+2)
@@ -29,6 +32,15 @@
 #define BCN_GUARD_osticks      ms2osticks(BCN_GUARD_ms)
 #define BCN_WINDOW_osticks     ms2osticks(BCN_WINDOW_ms)
 #define AIRTIME_BCN_osticks    us2osticks(AIRTIME_BCN)
+#if defined(CFG_eu868)
+#define DNW2_SAFETY_ZONE       ms2osticks(3000)
+#endif
+#if defined(CFG_us915)
+#define DNW2_SAFETY_ZONE       ms2osticks(750)
+#endif
+
+// Special APIs - for development or testing
+#define isTESTMODE() 0
 
 DEFINE_LMIC;
 DECL_ON_LMIC_EVENT;
@@ -42,36 +54,36 @@
 // ================================================================================
 // BEG OS - default implementations for certain OS suport functions
 
-#if !HAS_os_calls
+#if !defined(HAS_os_calls)
 
-#ifndef os_rlsbf2
+#if !defined(os_rlsbf2)
 u2_t os_rlsbf2 (xref2cu1_t buf) {
     return (u2_t)(buf[0] | (buf[1]<<8));
 }
 #endif
 
-#ifndef os_rlsbf4
+#if !defined(os_rlsbf4)
 u4_t os_rlsbf4 (xref2cu1_t buf) {
     return (u4_t)(buf[0] | (buf[1]<<8) | ((u4_t)buf[2]<<16) | ((u4_t)buf[3]<<24));
 }
 #endif
 
 
-#ifndef os_rmsbf4
+#if !defined(os_rmsbf4)
 u4_t os_rmsbf4 (xref2cu1_t buf) {
     return (u4_t)(buf[3] | (buf[2]<<8) | ((u4_t)buf[1]<<16) | ((u4_t)buf[0]<<24));
 }
 #endif
 
 
-#ifndef os_wlsbf2
+#if !defined(os_wlsbf2)
 void os_wlsbf2 (xref2u1_t buf, u2_t v) {
     buf[0] = v;
     buf[1] = v>>8;
 }
 #endif
 
-#ifndef os_wlsbf4
+#if !defined(os_wlsbf4)
 void os_wlsbf4 (xref2u1_t buf, u4_t v) {
     buf[0] = v;
     buf[1] = v>>8;
@@ -80,7 +92,7 @@
 }
 #endif
 
-#ifndef os_wmsbf4
+#if !defined(os_wmsbf4)
 void os_wmsbf4 (xref2u1_t buf, u4_t v) {
     buf[3] = v;
     buf[2] = v>>8;
@@ -89,54 +101,27 @@
 }
 #endif
 
-#ifndef os_getBattLevel
+#if !defined(os_getBattLevel)
 u1_t os_getBattLevel (void) {
     return MCMD_DEVS_BATT_NOINFO;
 }
 #endif
 
-#ifndef os_crc16
-static const u2_t CRC_TABLE[] = {
-    0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
-    0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
-    0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
-    0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
-    0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
-    0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
-    0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
-    0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
-    0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
-    0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
-    0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
-    0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
-    0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
-    0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
-    0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
-    0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
-    0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
-    0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
-    0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
-    0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
-    0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
-    0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
-    0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
-    0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
-    0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
-    0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
-    0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
-    0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
-    0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
-    0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
-    0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
-    0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
-};
+#if !defined(os_crc16)
+// New CRC-16 CCITT(XMODEM) checksum for beacons:
 u2_t os_crc16 (xref2u1_t data, uint len) {
-    u2_t fcs = 0;
-    for( uint u=0; u<len; u++ ) {
-    u1_t b = data[u];
-    fcs = (CRC_TABLE[(fcs ^ b) & 0xFF] ^ ((fcs >> 8) & 0xFF));
+    u2_t remainder = 0;
+    u2_t polynomial = 0x1021;
+    for( uint i = 0; i < len; i++ ) {
+        remainder ^= data[i] << 8;
+        for( u1_t bit = 8; bit > 0; bit--) {
+            if( (remainder & 0x8000) )
+                remainder = (remainder << 1) ^ polynomial;
+            else 
+                remainder <<= 1;
+        }
     }
-    return fcs;
+    return remainder;
 }
 #endif
 
@@ -193,7 +178,7 @@
 
 static void aes_cipher (xref2cu1_t key, u4_t devaddr, u4_t seqno, int dndir, xref2u1_t payload, int len) {
     if( len <= 0 )
-    return;
+        return;
     os_clearMem(AESaux, 16);
     AESaux[0] = AESaux[15] = 1; // mode=cipher / dir=down / block counter=1
     AESaux[5] = dndir?1:0;
@@ -225,7 +210,7 @@
 // ================================================================================
 // BEG LORA
 
-#if CFG_eu868 // ========================================
+#if defined(CFG_eu868) // ========================================
 
 #define maxFrameLen(dr) ((dr)<=DR_SF9 ? maxFrameLens[(dr)] : 0xFF)
 const u1_t maxFrameLens [] = { 64,64,64,123 };
@@ -248,7 +233,7 @@
 };
 #define pow2dBm(mcmd_ladr_p1) (TXPOWLEVELS[(mcmd_ladr_p1&MCMD_LADR_POW_MASK)>>MCMD_LADR_POW_SHIFT])
 
-#elif CFG_us915 // ========================================
+#elif defined(CFG_us915) // ========================================
 
 #define maxFrameLen(dr) ((dr)<=DR_SF11CR ? maxFrameLens[(dr)] : 0xFF)
 const u1_t maxFrameLens [] = { 24,66,142,255,255,255,255,255,  66,142 };
@@ -296,21 +281,18 @@
     u1_t bw = getBw(rps);  // 0,1,2 = 125,250,500kHz
     u1_t sf = getSf(rps);  // 0=FSK, 1..6 = SF7..12
     if( sf == FSK ) {
-    // MLu - 12012015
-    return (plen+/*preamble*/5+/*syncword*/3+/*len*/1+/*crc*/2) * /*bits/byte*/8
-        * (s4_t)OSTICKS_PER_SEC / /*kbit/s*/50000;
-    //return (plen+/*preamble*/5+/*syncword*/2+/*len*/1+/*crc*/2) * /*bits/byte*/8
-    //    * (s4_t)OSTICKS_PER_SEC / /*kbit/s*/50000;
+        return (plen+/*preamble*/5+/*syncword*/3+/*len*/1+/*crc*/2) * /*bits/byte*/8
+            * (s4_t)OSTICKS_PER_SEC / /*kbit/s*/50000;
     }
     u1_t sfx = 4*(sf+(7-SF7));
     u1_t q = sfx - (sf >= SF11 ? 8 : 0);
     int tmp = 8*plen - sfx + 28 + (getNocrc(rps)?0:16) - (getIh(rps)?20:0);
     if( tmp > 0 ) {
-    tmp = (tmp + q - 1) / q;
-    tmp *= getCr(rps)+5;
-    tmp += 8;
+        tmp = (tmp + q - 1) / q;
+        tmp *= getCr(rps)+5;
+        tmp += 8;
     } else {
-    tmp = 8;
+        tmp = 8;
     }
     tmp = (tmp<<2) + /*preamble*/49 /* 4 * (8 + 4.25) */;
     // bw = 125000 = 15625 * 2^3
@@ -325,9 +307,9 @@
     sfx = sf+(7-SF7) - (3+2) - bw;
     int div = 15625;
     if( sfx > 4 ) {
-    // prevent 32bit signed int overflow in last step
-    div >>= sfx-4;
-    sfx = 4;
+        // prevent 32bit signed int overflow in last step
+        div >>= sfx-4;
+        sfx = 4;
     }
     // Need 32bit arithmetic for this last step
     return (((ostime_t)tmp << sfx) * OSTICKS_PER_SEC + div/2) / div;
@@ -337,8 +319,6 @@
 extern inline int   s12rssi (s1_t v);
 extern inline float  s12snr (s1_t v);
 extern inline s1_t   snr2s1 (double v);
-extern inline int   getRssi (rxqu_t*rxq);
-extern inline void  setRssi (rxqu_t*rxq, int v);
 
 extern inline rps_t updr2rps (dr_t dr);
 extern inline rps_t dndr2rps (dr_t dr);
@@ -374,23 +354,23 @@
     // normal frames - 1st try / no retry
     0,
     // confirmed frames
-    0,0,0,0,0,1,1,1,2
+    0,0,1,0,1,0,1,0,0
 };
 
 
 // Table below defines the size of one symbol as
 //   symtime = 256us * 2^T(sf,bw)
 // 256us is called one symunit. 
-//                 SF:                      
-//      BW:     |__7___8___9__10__11__12
-//      125kHz     |  2   3   4   5   6   7
-//      250kHz     |  1   2   3   4   5   6
-//      500kHz     |  0   1   2   3   4   5
+//                 SF:                                  
+//      BW:      |__7___8___9__10__11__12
+//      125kHz   |  2   3   4   5   6   7
+//      250kHz   |  1   2   3   4   5   6
+//      500kHz   |  0   1   2   3   4   5
 //  
 // Times for half symbol per DR
 // Per DR table to minimize rounding errors
 static const ostime_t DR2HSYM_osticks[] = {
-#if CFG_eu868
+#if defined(CFG_eu868)
 #define dr2hsym(dr) (DR2HSYM_osticks[(dr)])
     us2osticksRound(128<<7),  // DR_SF12
     us2osticksRound(128<<6),  // DR_SF11
@@ -400,7 +380,7 @@
     us2osticksRound(128<<2),  // DR_SF7
     us2osticksRound(128<<1),  // DR_SF7B
     us2osticksRound(80)       // FSK -- not used (time for 1/2 byte)
-#elif CFG_us915
+#elif defined(CFG_us915)
 #define dr2hsym(dr) (DR2HSYM_osticks[(dr)&7])  // map DR_SFnCR -> 0-6
     us2osticksRound(128<<5),  // DR_SF10   DR_SF12CR
     us2osticksRound(128<<4),  // DR_SF9    DR_SF11CR
@@ -415,13 +395,13 @@
 static ostime_t calcRxWindow (u1_t secs, dr_t dr) {
     ostime_t rxoff, err;
     if( secs==0 ) {
-    // aka 128 secs (next becaon)
-    rxoff = LMIC.drift;
-    err = LMIC.lastDriftDiff;
+        // aka 128 secs (next becaon)
+        rxoff = LMIC.drift;
+        err = LMIC.lastDriftDiff;
     } else {
-    // scheduled RX window within secs into current beacon period
-    rxoff = (LMIC.drift * (ostime_t)secs) >> BCN_INTV_exp;
-    err = (LMIC.lastDriftDiff * (ostime_t)secs) >> BCN_INTV_exp;
+        // scheduled RX window within secs into current beacon period
+        rxoff = (LMIC.drift * (ostime_t)secs) >> BCN_INTV_exp;
+        err = (LMIC.lastDriftDiff * (ostime_t)secs) >> BCN_INTV_exp;
     }
     u1_t rxsyms = MINRX_SYMS;
     err += (ostime_t)LMIC.maxDriftDiff * LMIC.missedBcns;
@@ -434,10 +414,10 @@
 // Setup beacon RX parameters assuming we have an error of ms (aka +/-(ms/2))
 static void calcBcnRxWindowFromMillis (u1_t ms, bit_t ini) {
     if( ini ) {
-    LMIC.drift = 0;
-    LMIC.maxDriftDiff = 0;
-    LMIC.missedBcns = 0;
-    LMIC.bcninfo.flags |= BCN_NODRIFT|BCN_NODDIFF;
+        LMIC.drift = 0;
+        LMIC.maxDriftDiff = 0;
+        LMIC.missedBcns = 0;
+        LMIC.bcninfo.flags |= BCN_NODRIFT|BCN_NODDIFF;
     }
     ostime_t hsym = dr2hsym(DR_BCN);
     LMIC.bcnRxsyms = MINRX_SYMS + ms2osticksCeil(ms) / hsym;
@@ -455,8 +435,8 @@
     u1_t intvExp = rxsched->intvExp;
     ostime_t off = os_rlsbf2(LMIC.frame) & (0x0FFF >> (7 - intvExp)); // random offset (slot units)
     rxsched->rxbase = (LMIC.bcninfo.txtime +
-               BCN_RESERVE_osticks +
-               ms2osticks(BCN_SLOT_SPAN_ms * off)); // random offset osticks
+                       BCN_RESERVE_osticks +
+                       ms2osticks(BCN_SLOT_SPAN_ms * off)); // random offset osticks
     rxsched->slot   = 0;
     rxsched->rxtime = rxsched->rxbase - calcRxWindow(/*secs BCN_RESERVE*/2+(1<<intvExp),rxsched->dr);
     rxsched->rxsyms = LMIC.rxsyms;
@@ -466,16 +446,16 @@
 static bit_t rxschedNext (xref2rxsched_t rxsched, ostime_t cando) {
   again:
     if( rxsched->rxtime - cando >= 0 )
-    return 1;
+        return 1;
     u1_t slot;
     if( (slot=rxsched->slot) >= 128 )
-    return 0;
+        return 0;
     u1_t intv = 1<<rxsched->intvExp;
     if( (rxsched->slot = (slot += (intv))) >= 128 )
-    return 0;
+        return 0;
     rxsched->rxtime = rxsched->rxbase
-    + ((BCN_WINDOW_osticks * (ostime_t)slot) >> BCN_INTV_exp)
-    - calcRxWindow(/*secs BCN_RESERVE*/2+slot+intv,rxsched->dr);
+        + ((BCN_WINDOW_osticks * (ostime_t)slot) >> BCN_INTV_exp)
+        - calcRxWindow(/*secs BCN_RESERVE*/2+slot+intv,rxsched->dr);
     rxsched->rxsyms = LMIC.rxsyms;
     goto again;
 }
@@ -485,9 +465,9 @@
     u2_t r = os_getRndU2();
     ostime_t delay = r;
     if( delay > OSTICKS_PER_SEC )
-    delay = r % (u2_t)OSTICKS_PER_SEC;
+        delay = r % (u2_t)OSTICKS_PER_SEC;
     if( secSpan > 0 )
-    delay += ((u1_t)r % secSpan) * OSTICKS_PER_SEC;
+        delay += ((u1_t)r % secSpan) * OSTICKS_PER_SEC;
     return delay;
 }
 
@@ -495,36 +475,39 @@
 static void txDelay (ostime_t reftime, u1_t secSpan) {
     reftime += rndDelay(secSpan);
     if( LMIC.globalDutyRate == 0  ||  (reftime - LMIC.globalDutyAvail) > 0 ) {
-    LMIC.globalDutyAvail = reftime;
-    LMIC.opmode |= OP_RNDTX;
+        LMIC.globalDutyAvail = reftime;
+        LMIC.opmode |= OP_RNDTX;
     }
 }
 
 
 static void setDrJoin (u1_t reason, u1_t dr) {
     EV(drChange, INFO, (e_.reason    = reason,
-            e_.deveui    = MAIN::CDEV->getEui(),
-            e_.dr        = dr|DR_PAGE,
-            e_.txpow     = LMIC.adrTxPow,
-            e_.prevdr    = LMIC.datarate|DR_PAGE,
-            e_.prevtxpow = LMIC.adrTxPow));
+                        e_.deveui    = MAIN::CDEV->getEui(),
+                        e_.dr        = dr|DR_PAGE,
+                        e_.txpow     = LMIC.adrTxPow,
+                        e_.prevdr    = LMIC.datarate|DR_PAGE,
+                        e_.prevtxpow = LMIC.adrTxPow));
     LMIC.datarate = dr;
-    DO_DEVDB(updateDatarate, dr);
+    DO_DEVDB(LMIC.datarate,datarate);
 }
 
 
 static void setDrTxpow (u1_t reason, u1_t dr, s1_t pow) {
     EV(drChange, INFO, (e_.reason    = reason,
-            e_.deveui    = MAIN::CDEV->getEui(),
-            e_.dr        = dr|DR_PAGE,
-            e_.txpow     = pow,
-            e_.prevdr    = LMIC.datarate|DR_PAGE,
-            e_.prevtxpow = LMIC.adrTxPow));
+                        e_.deveui    = MAIN::CDEV->getEui(),
+                        e_.dr        = dr|DR_PAGE,
+                        e_.txpow     = pow,
+                        e_.prevdr    = LMIC.datarate|DR_PAGE,
+                        e_.prevtxpow = LMIC.adrTxPow));
+    
     if( pow != KEEP_TXPOW )
-    LMIC.adrTxPow = pow;
-    LMIC.datarate = dr;
-    DO_DEVDB(updateDatarate, dr);
-    LMIC.opmode |= OP_NEXTCHNL;
+        LMIC.adrTxPow = pow;
+    if( LMIC.datarate != dr ) {
+        LMIC.datarate = dr;
+        DO_DEVDB(LMIC.datarate,datarate);
+        LMIC.opmode |= OP_NEXTCHNL;
+    }
 }
 
 
@@ -540,74 +523,107 @@
     // App may call LMIC_enableTracking() explicitely before
     // Otherwise tracking is implicitly enabled here
     if( (LMIC.opmode & (OP_TRACK|OP_SCAN)) == 0  &&  LMIC.bcninfoTries == 0 )
-    LMIC_enableTracking(0);
+        LMIC_enableTracking(0);
 }
 
 
-#if CFG_eu868
+#if defined(CFG_eu868)
 // ================================================================================
 //
 // BEG: EU868 related stuff
 //
-enum { BAND_MILLI=0, BAND_CENTI=1, BAND_DECI=2 };
+enum { NUM_DEFAULT_CHANNELS=6 };
 static const u4_t iniChannelFreq[12] = {
     // Join frequencies and duty cycle limit (0.1%)
-    EU868_F1|BAND_MILLI, EU868_F2|BAND_MILLI, EU868_F3|BAND_MILLI,
-    EU868_J4|BAND_MILLI, EU868_J5|BAND_MILLI, EU868_J6|BAND_MILLI,
+    EU868_F1|BAND_MILLI, EU868_J4|BAND_MILLI,
+    EU868_F2|BAND_MILLI, EU868_J5|BAND_MILLI,
+    EU868_F3|BAND_MILLI, EU868_J6|BAND_MILLI,
     // Default operational frequencies
     EU868_F1|BAND_CENTI, EU868_F2|BAND_CENTI, EU868_F3|BAND_CENTI,
     EU868_F4|BAND_MILLI, EU868_F5|BAND_MILLI, EU868_F6|BAND_DECI
 };
 
 static void initDefaultChannels (bit_t join) {
-    LMIC.channelMap = 0x3F;
+    os_clearMem(&LMIC.channelFreq, sizeof(LMIC.channelFreq));
+    os_clearMem(&LMIC.channelDrMap, sizeof(LMIC.channelDrMap));
+    os_clearMem(&LMIC.bands, sizeof(LMIC.bands));
+
+    LMIC.channelMap = 0x1F;
     u1_t su = join ? 0 : 6;
     for( u1_t fu=0; fu<6; fu++,su++ ) {
-    LMIC.channelFreq[fu] = iniChannelFreq[su];
-    LMIC.channelDrs[fu]  = DR_SF12 | (DR_SF7<<4);
+        LMIC.channelFreq[fu]  = iniChannelFreq[su];
+        LMIC.channelDrMap[fu] = DR_RANGE_MAP(DR_SF12,DR_SF7);
     }
-    if( !join )
-    LMIC.channelDrs[1] = DR_SF12 | (DR_FSK<<4);
+    if( !join ) {
+        LMIC.channelDrMap[5] = DR_RANGE_MAP(DR_SF12,DR_SF7);
+        LMIC.channelDrMap[1] = DR_RANGE_MAP(DR_SF12,DR_FSK);
+    }
 
-    LMIC.bands[BAND_MILLI].txcap = 1000;  // 0.1%
-    LMIC.bands[BAND_MILLI].txpow = 14;
-    LMIC.bands[BAND_CENTI].txcap = 100;   // 1%
-    LMIC.bands[BAND_CENTI].txpow = 14;
-    LMIC.bands[BAND_DECI ].txcap = 10;    // 10%
-    LMIC.bands[BAND_DECI ].txpow = 27;
+    LMIC.bands[BAND_MILLI].txcap    = 1000;  // 0.1%
+    LMIC.bands[BAND_MILLI].txpow    = 14;
+    LMIC.bands[BAND_MILLI].lastchnl = os_getRndU1() % MAX_CHANNELS;
+    LMIC.bands[BAND_CENTI].txcap    = 100;   // 1%
+    LMIC.bands[BAND_CENTI].txpow    = 14;
+    LMIC.bands[BAND_CENTI].lastchnl = os_getRndU1() % MAX_CHANNELS;
+    LMIC.bands[BAND_DECI ].txcap    = 10;    // 10%
+    LMIC.bands[BAND_DECI ].txpow    = 27;
+    LMIC.bands[BAND_CENTI].lastchnl = os_getRndU1() % MAX_CHANNELS;
     LMIC.bands[BAND_MILLI].avail = 
     LMIC.bands[BAND_CENTI].avail =
     LMIC.bands[BAND_DECI ].avail = os_getTime();
 }
 
+bit_t LMIC_setupBand (u1_t bandidx, s1_t txpow, u2_t txcap) {
+    if( bandidx > BAND_AUX ) return 0;
+    band_t* b = &LMIC.bands[bandidx];
+    b->txpow = txpow;
+    b->txcap = txcap;
+    b->avail = os_getTime();
+    b->lastchnl = os_getRndU1() % MAX_CHANNELS;
+    return 1;
+}
+
+bit_t LMIC_setupChannel (u1_t chidx, u4_t freq, u2_t drmap, s1_t band) {
+    if( chidx >= MAX_CHANNELS )
+        return 0;
+    if( band == -1 ) {
+        if( freq >= 869400000 && freq <= 869650000 )
+            freq |= BAND_DECI;   // 10% 27dBm
+        else if( (freq >= 868000000 && freq <= 868600000) ||
+                 (freq >= 869700000 && freq <= 870000000)  )
+            freq |= BAND_CENTI;  // 1% 14dBm 
+        else 
+            freq |= BAND_MILLI;  // 0.1% 14dBm
+    } else {
+        if( band > BAND_AUX ) return 0;
+        freq = (freq&~3) | band;
+    }
+    LMIC.channelFreq [chidx] = freq;
+    LMIC.channelDrMap[chidx] = drmap==0 ? DR_RANGE_MAP(DR_SF12,DR_SF7) : drmap;
+    LMIC.channelMap |= 1<<chidx;  // enabled right away
+    return 1;
+}
+
+void LMIC_disableChannel (u1_t channel) {
+    LMIC.channelFreq[channel] = 0;
+    LMIC.channelDrMap[channel] = 0;
+    LMIC.channelMap &= ~(1<<channel);
+}
+
 static u4_t convFreq (xref2u1_t ptr) {
     u4_t freq = (os_rlsbf4(ptr-1) >> 8) * 100;
-    if( freq >= EU868_FREQ_MIN && freq <= EU868_FREQ_MAX )
-    freq = 0;
+    if( freq < EU868_FREQ_MIN || freq > EU868_FREQ_MAX )
+        freq = 0;
     return freq;
 }
 
-static bit_t setupChannel (u1_t chidx, u4_t freq, int drs) {
-    if( chidx >= MAX_CHANNELS )
-    return 0;
-    if( freq >= 869400000 && freq <= 869650000 )
-    freq |= BAND_DECI;  // 10% 27dBm
-    else if( (freq >= 868000000 && freq <= 868600000) ||
-         (freq >= 869700000 && freq <= 870000000)  )
-    freq |= BAND_CENTI;  // 1% 14dBm
-    //else 
-    //  freq |= BAND_MILLI;  // 0.1% 14dBm
-    LMIC.channelFreq[chidx] = freq;
-    LMIC.channelDrs [chidx] = drs < 0 ? (DR_SF12|(DR_SF7<<4)) : drs;
-    return 1;
-}
-
 static u1_t mapChannels (u1_t chpage, u2_t chmap) {
-    if( chpage != 0 || (chmap & ~((u2_t)(1<<MAX_CHANNELS)-1)) != 0 )
-    return 0;  // illegal input
+    // Bad page, disable all channel, enable non-existent
+    if( chpage != 0 || chmap==0 || (chmap & ~LMIC.channelMap) != 0 )
+        return 0;  // illegal input
     for( u1_t chnl=0; chnl<MAX_CHANNELS; chnl++ ) {
-    if( (chmap & (1<<chnl)) != 0 && LMIC.channelFreq[chnl] == 0 )
-        chmap &= ~(1<<chnl); // ignore - channel is not defined
+        if( (chmap & (1<<chnl)) != 0 && LMIC.channelFreq[chnl] == 0 )
+            chmap &= ~(1<<chnl); // ignore - channel is not defined
     }
     LMIC.channelMap = chmap;
     return 1;
@@ -623,68 +639,39 @@
     LMIC.freq  = freq & ~(u4_t)3;
     LMIC.txpow = band->txpow;
     band->avail = txbeg + airtime * band->txcap;
-    // Update obsolete avail's to prevent rollover
-    // (needed for devices that send rarely - e.g. once/twice a day)
-    for( u1_t b=0; b<MAX_BANDS; b++ ) {
-    if( LMIC.bands[b].avail - txbeg < 0 )
-        LMIC.bands[b].avail = txbeg;
-    }
     if( LMIC.globalDutyRate != 0 )
-    LMIC.globalDutyAvail = txbeg + (airtime<<LMIC.globalDutyRate);
+        LMIC.globalDutyAvail = txbeg + (airtime<<LMIC.globalDutyRate);
 }
 
 static ostime_t nextTx (ostime_t now) {
-    // If we do not find a suitable channel stop sending (misconfigured device)
-    ostime_t mintime = now + /*10h*/36000*OSTICKS_PER_SEC;
-    s1_t     bmask   = 0;
-    for( u1_t b=0; b<MAX_BANDS; b++ ) {
-    xref2band_t band = &LMIC.bands[b];
-    if( band->txcap == 0 ) // band not setup
-        continue;
-    if( now - band->avail >= 0 ) {
-        bmask = (bmask < 0 ? 0 : bmask) | (1<<b);
-    }
-    else if( mintime - band->avail > 0 ) {
-        mintime = band->avail;
-    }
-    }
-    if( bmask == 0 )
-    return mintime;
-    u1_t chnl, ccnt;
-    u2_t cset, mask;
-    chnl = cset = ccnt = 0;
-    mask = 1;
-    while( mask && mask <= LMIC.channelMap ) {
-    // Channel is enabled AND and in a ready band and can handle the datarate
-    if( (mask & LMIC.channelMap) != 0  &&  (bmask & (1 << (LMIC.channelFreq[chnl] & 0x3))) != 0) {
-        u1_t drs = LMIC.channelDrs[chnl];
-        if( !(isSlowerDR((dr_t)LMIC.datarate, (dr_t)(drs & 0xF)) ||   // lowest datarate
-          isFasterDR((dr_t)LMIC.datarate, (dr_t)(drs >>  4))) ) { // fastest datarate
-        cset |= mask;
-        ccnt++;
+    u1_t bmap=0xF;
+    do {
+        ostime_t mintime = now + /*10h*/36000*OSTICKS_PER_SEC;
+        u1_t band=0;
+        for( u1_t bi=0; bi<4; bi++ ) {
+            if( (bmap & (1<<bi)) && mintime - LMIC.bands[bi].avail > 0 )
+                mintime = LMIC.bands[band = bi].avail;
         }
-    }
-    mask <<= 1;
-    chnl++;
-    }
-    if( ccnt == 0 ) // No eligible channel - misconfigured device?
-    return mintime;
-    ccnt = os_getRndU2() % ccnt;
-    mask = 1;
-    chnl = 0;
-    do {
-    if( (cset & mask) != 0 ) {
-        if( ccnt==0 ) {
-        LMIC.txChnl = chnl;
-        return now;
+        // Find next channel in given band
+        u1_t chnl = LMIC.bands[band].lastchnl;
+        for( u1_t ci=0; ci<MAX_CHANNELS; ci++ ) {
+            if( (chnl = (chnl+1)) >= MAX_CHANNELS )
+                chnl -=  MAX_CHANNELS;
+            if( (LMIC.channelMap & (1<<chnl)) != 0  &&  // channel enabled
+                (LMIC.channelDrMap[chnl] & (1<<(LMIC.datarate&0xF))) != 0  &&
+                band == (LMIC.channelFreq[chnl] & 0x3) ) { // in selected band
+                LMIC.txChnl = LMIC.bands[band].lastchnl = chnl;
+                return mintime;
+            }
         }
-        ccnt--;
-    }
-    mask <<= 1;
-    chnl++;
+        if( (bmap &= ~(1<<band)) == 0 ) {
+            // No feasible channel  found!
+            return mintime;
+        }
     } while(1);
 }
 
+
 static void setBcnRxParams (void) {
     LMIC.dataLen = 0;
     LMIC.freq = LMIC.channelFreq[LMIC.bcnChnl] & ~(u4_t)3;
@@ -694,84 +681,96 @@
 #define setRx1Params() /*LMIC.freq/rps remain unchanged*/
 
 static void initJoinLoop (void) {
-    LMIC.txChnl = os_getRndU1() % 3;
+    LMIC.txChnl = os_getRndU1() % 6;
     LMIC.adrTxPow = 14;
     setDrJoin(DRCHG_SET, DR_SF7);
     initDefaultChannels(1);
     ASSERT((LMIC.opmode & OP_NEXTCHNL)==0);
-    LMIC.txend = LMIC.bands[BAND_MILLI].avail;
+    LMIC.txend = LMIC.bands[BAND_MILLI].avail + rndDelay(8);
 }
 
 
 static ostime_t nextJoinState (void) {
+    u1_t failed = 0;
+
     // Try 869.x and then 864.x with same DR
     // If both fail try next lower datarate
+    if( ++LMIC.txChnl == 6 )
+        LMIC.txChnl = 0;
+    if( (++LMIC.txCnt & 1) == 0 ) {
+        // Lower DR every 2nd try (having tried 868.x and 864.x with the same DR)
+        if( LMIC.datarate == DR_SF12 )
+            failed = 1; // we have tried all DR - signal EV_JOIN_FAILED
+        else
+            setDrJoin(DRCHG_NOJACC, decDR((dr_t)LMIC.datarate));
+    }
+    // Clear NEXTCHNL because join state engine controls channel hopping
     LMIC.opmode &= ~OP_NEXTCHNL;
-    LMIC.txend = LMIC.bands[BAND_MILLI].avail;
-    if( LMIC.txChnl < 3 ) {
-    LMIC.txChnl += 3;
-    } else {
-    if( LMIC.datarate == DR_SF12 ) {
-        setDrJoin(DRCHG_NOJACC, DR_SF7);
-        // We tried one round of FSK..SF12
-        // Now pick new random channel and insert a random delay and try another round.
-        LMIC.txChnl = os_getRndU1() % 3;
-        if( LMIC.txCnt < 5 )
-        LMIC.txCnt += 1;
-        // Delay by 7,15,31,63,127,255 secs
-        return rndDelay((4<<LMIC.txCnt)-1) | 1;   // |1 - trigger EV_JOIN_FAILED event
-    }
-    LMIC.txChnl = LMIC.txChnl==5 ? 0 : LMIC.txChnl-2;
-    setDrJoin(DRCHG_NOJACC, decDR((dr_t)LMIC.datarate));
-    }
-    // Avoid collision with JOIN ACCEPT being sent by GW (but we missed it)
-    return 3*OSTICKS_PER_SEC;
+    // Move txend to randomize synchronized concurrent joins.
+    // Duty cycle is based on txend.
+    ostime_t time = os_getTime();
+    if( time - LMIC.bands[BAND_MILLI].avail < 0 )
+        time = LMIC.bands[BAND_MILLI].avail;
+    LMIC.txend = time +
+        (isTESTMODE()
+         // Avoid collision with JOIN ACCEPT @ SF12 being sent by GW (but we missed it)
+         ? DNW2_SAFETY_ZONE
+         // Otherwise: randomize join (street lamp case):
+         // SF12:255, SF11:127, .., SF7:8secs
+         : DNW2_SAFETY_ZONE+rndDelay(255>>LMIC.datarate));
+    // 1 - triggers EV_JOIN_FAILED event
+    return failed;
 }
 
 //
 // END: EU868 related stuff
 //
 // ================================================================================
-#elif CFG_us915
+#elif defined(CFG_us915)
 // ================================================================================
 //
 // BEG: US915 related stuff
 //
 
+
 static void initDefaultChannels (void) {
     for( u1_t i=0; i<4; i++ )
-    LMIC.channelMap[i] = 0xFFFF;
+        LMIC.channelMap[i] = 0xFFFF;
     LMIC.channelMap[4] = 0x00FF;
 }
 
 static u4_t convFreq (xref2u1_t ptr) {
     u4_t freq = (os_rlsbf4(ptr-1) >> 8) * 100;
     if( freq >= US915_FREQ_MIN && freq <= US915_FREQ_MAX )
-    freq = 0;
+        freq = 0;
     return freq;
 }
 
-static bit_t setupChannel (u1_t chidx, u4_t freq, int drs) {
+bit_t LMIC_setupChannel (u1_t chidx, u4_t freq, u2_t drmap, s1_t band) {
     if( chidx < 72 || chidx >= 72+MAX_XCHANNELS )
-    return 0; // channels 0..71 are hardwired
+        return 0; // channels 0..71 are hardwired
     chidx -= 72;
     LMIC.xchFreq[chidx] = freq;
-    LMIC.xchDrs[chidx] = drs < 0 ? (DR_SF10|(DR_SF8C<<4)) : drs;
+    LMIC.xchDrMap[chidx] = drmap==0 ? DR_RANGE_MAP(DR_SF10,DR_SF8C) : drmap;
     LMIC.channelMap[chidx>>4] |= (1<<(chidx&0xF));
     return 1;
 }
 
+void LMIC_disableChannel (u1_t channel) {
+    if( channel < 72+MAX_XCHANNELS )
+        LMIC.channelMap[channel/4] &= ~(1<<(channel&0xF));
+}
 
 static u1_t mapChannels (u1_t chpage, u2_t chmap) {
     if( chpage == MCMD_LADR_CHP_125ON || chpage == MCMD_LADR_CHP_125OFF ) {
-    u2_t en125 = chpage == MCMD_LADR_CHP_125ON ? 0xFFFF : 0x0000;
-    for( u1_t u=0; u<4; u++ )
-        LMIC.channelMap[u] = en125;
-    LMIC.channelMap[64/16] = chmap;
+        u2_t en125 = chpage == MCMD_LADR_CHP_125ON ? 0xFFFF : 0x0000;
+        for( u1_t u=0; u<4; u++ )
+            LMIC.channelMap[u] = en125;
+        LMIC.channelMap[64/16] = chmap;
     } else {
-    if( chpage >= (72+MAX_XCHANNELS+15)/16 )
-        return 0;
-    LMIC.channelMap[chpage] = chmap;
+        if( chpage >= (72+MAX_XCHANNELS+15)/16 )
+            return 0;
+        LMIC.channelMap[chpage] = chmap;
     }
     return 1;
 }
@@ -779,77 +778,68 @@
 static void updateTx (ostime_t txbeg) {
     u1_t chnl = LMIC.txChnl;
     if( chnl < 64 ) {
-    LMIC.freq = US915_125kHz_UPFBASE + chnl*US915_125kHz_UPFSTEP;
-    LMIC.txpow = 30;
-    return;
+        LMIC.freq = US915_125kHz_UPFBASE + chnl*US915_125kHz_UPFSTEP;
+        LMIC.txpow = 30;
+        return;
     }
     LMIC.txpow = 26;
     if( chnl < 64+8 ) {
-    LMIC.freq = US915_500kHz_UPFBASE + (chnl-64)*US915_500kHz_UPFSTEP;
+        LMIC.freq = US915_500kHz_UPFBASE + (chnl-64)*US915_500kHz_UPFSTEP;
     } else {
-    ASSERT(chnl < 64+8+MAX_XCHANNELS);
-    LMIC.freq = LMIC.xchFreq[chnl-72];
+        ASSERT(chnl < 64+8+MAX_XCHANNELS);
+        LMIC.freq = LMIC.xchFreq[chnl-72];
     }
 
     // Update global duty cycle stats
     if( LMIC.globalDutyRate != 0 ) {
-    ostime_t airtime = calcAirTime(LMIC.rps, LMIC.dataLen);
-    LMIC.globalDutyAvail = txbeg + (airtime<<LMIC.globalDutyRate);
+        ostime_t airtime = calcAirTime(LMIC.rps, LMIC.dataLen);
+        LMIC.globalDutyAvail = txbeg + (airtime<<LMIC.globalDutyRate);
     }
 }
 
 // US does not have duty cycling - return now as earliest TX time
 #define nextTx(now) (_nextTx(),(now))
 static void _nextTx (void) {
-    u1_t  chnl  = LMIC.txChnl;
-    u1_t  cnt   = 0;
-    bit_t bw500 = (LMIC.datarate >= DR_SF8C);
-
-    if( (LMIC.chRnd & 0xF) == 0 ) {
-    chnl += LMIC.chRnd>>4;
-    LMIC.chRnd = os_getRndU1();
+    if( LMIC.chRnd==0 )
+        LMIC.chRnd = os_getRndU1() & 0x3F;
+    if( LMIC.datarate >= DR_SF8C ) { // 500kHz
+        u1_t map = LMIC.channelMap[64/16]&0xFF;
+        for( u1_t i=0; i<8; i++ ) {
+            if( (map & (1<<(++LMIC.chRnd & 7))) != 0 ) {
+                LMIC.txChnl = 64 + (LMIC.chRnd & 7);
+                return;
+            }
+        }
+    } else { // 125kHz
+        for( u1_t i=0; i<64; i++ ) {
+            u1_t chnl = ++LMIC.chRnd & 0x3F;
+            if( (LMIC.channelMap[(chnl >> 4)] & (1<<(chnl & 0xF))) != 0 ) {
+                LMIC.txChnl = chnl;
+                return;
+            }
+        }
     }
-    LMIC.chRnd--;
-  again:
-    chnl += 1;
-    if( bw500 ) {
-    // Only channels 64..71
-    chnl = 64+(chnl&0x7);
-    // At least one 500kHz channel must be enabled
-    // - otherwise MAC should not have used such a datarate
-    ASSERT((LMIC.channelMap[64/16]&0xFF)!=0x00);
-    if( ++cnt == 8 )
-        return; // no appropriate channel enabled stay where we are
-    } else {
-    // At least one 125kHz channel must be enabled
-    // - otherwise MAC should not have used DR_SF10-7
-    ASSERT((LMIC.channelMap[0]|LMIC.channelMap[1]|LMIC.channelMap[2]|LMIC.channelMap[3])!=0x0000);
-    chnl = chnl&0x3F;
-    if( ++cnt == 64 )
-        return; // no appropriate channel enabled stay where we are
-    }
-    if( (LMIC.channelMap[(chnl >> 4)] & (1<<(chnl&0xF))) == 0 )
-    goto again;  // not enabled
-    LMIC.txChnl = chnl;
+    // No feasible channel  found! Keep old one.
 }
 
 static void setBcnRxParams (void) {
     LMIC.dataLen = 0;
-    LMIC.freq = US915_500kHz_DNFBASE + LMIC.bcnChnl*US915_500kHz_DNFSTEP;
+    LMIC.freq = US915_500kHz_DNFBASE + LMIC.bcnChnl * US915_500kHz_DNFSTEP;
     LMIC.rps  = setIh(setNocrc(dndr2rps((dr_t)DR_BCN),1),LEN_BCN);
 }
 
-#define setRx1Params(void) {                         \
+#define setRx1Params() {                                                \
     LMIC.freq = US915_500kHz_DNFBASE + (LMIC.txChnl & 0x7) * US915_500kHz_DNFSTEP; \
-    if( /* TX datarate */LMIC.rxsyms < DR_SF8C )                \
-    LMIC.rxsyms += DR_SF10CR - DR_SF10;                \
-    else if( LMIC.rxsyms == DR_SF8C )                    \
-    LMIC.rxsyms = DR_SF7CR;                        \
-    LMIC.rps = dndr2rps(LMIC.rxsyms);                    \
+    if( /* TX datarate */LMIC.dndr < DR_SF8C )                          \
+        LMIC.dndr += DR_SF10CR - DR_SF10;                               \
+    else if( LMIC.dndr == DR_SF8C )                                     \
+        LMIC.dndr = DR_SF7CR;                                           \
+    LMIC.rps = dndr2rps(LMIC.dndr);                                     \
 }
 
 static void initJoinLoop (void) {
-    LMIC.txChnl = os_getRndU1() & 0x3F;
+    LMIC.chRnd = 0;
+    LMIC.txChnl = 0;
     LMIC.adrTxPow = 20;
     ASSERT((LMIC.opmode & OP_NEXTCHNL)==0);
     LMIC.txend = os_getTime();
@@ -861,23 +851,29 @@
     //   SF7/8/9/10  on a random channel 0..63
     //   SF8C        on a random channel 64..71
     //
-    LMIC.opmode &= ~OP_NEXTCHNL;
-    LMIC.txend = os_getTime();
+    u1_t failed = 0;
     if( LMIC.datarate != DR_SF8C ) {
-    LMIC.txChnl = 64+(LMIC.txChnl&7);
-    LMIC.datarate = DR_SF8C;
+        LMIC.txChnl = 64+(LMIC.txChnl&7);
+        setDrJoin(DRCHG_SET, DR_SF8C);
     } else {
-    LMIC.txChnl = os_getRndU1() & 0x3F;
-    LMIC.datarate = DR_SF7 - (LMIC.txCnt++ & 3);
-    if( (LMIC.txCnt & 3) == 0 ) {
-        // Tried SF10/SF8C
-        // Delay by 7,15,31,63,127,255 secs
-        return rndDelay((4<<(LMIC.txCnt>>2))-1) | 1;   // |1 - trigger EV_JOIN_FAILED event
+        LMIC.txChnl = os_getRndU1() & 0x3F;
+        s1_t dr = DR_SF7 - ++LMIC.txCnt;
+        if( dr < DR_SF10 ) {
+            dr = DR_SF10;
+            failed = 1; // All DR exhausted - signal failed
+        }
+        setDrJoin(DRCHG_SET, dr);
     }
-    }
-    // Always wait 500ms in case there was a TX and we just missed it.
-    // Sending immediately would not be picked up because GW is still busy txing missed frame.
-    return (OSTICKS_PER_SEC/2) & ~1;
+    LMIC.opmode &= ~OP_NEXTCHNL;
+    LMIC.txend = os_getTime() +
+        (isTESTMODE()
+         // Avoid collision with JOIN ACCEPT being sent by GW (but we missed it - GW is still busy)
+         ? DNW2_SAFETY_ZONE
+         // Otherwise: randomize join (street lamp case):
+         // SF10:16, SF9=8,..SF8C:1secs
+         : rndDelay(16>>LMIC.datarate));
+    // 1 - triggers EV_JOIN_FAILED event
+    return failed;
 }
 
 //
@@ -896,13 +892,20 @@
 
 static void reportEvent (ev_t ev) {
     EV(devCond, INFO, (e_.reason = EV::devCond_t::LMIC_EV,
-               e_.eui    = MAIN::CDEV->getEui(),
-               e_.info   = ev));
+                       e_.eui    = MAIN::CDEV->getEui(),
+                       e_.info   = ev));
     ON_LMIC_EVENT(ev);
     engineUpdate();
 }
 
 
+static void runReset (xref2osjob_t osjob) {
+    // Disable session
+    LMIC_reset();
+    LMIC_startJoining();
+    reportEvent(EV_RESET);
+}
+
 static void stateJustJoined (void) {
     LMIC.seqnoDn     = LMIC.seqnoUp = 0;
     LMIC.rejoinCnt   = 0;
@@ -927,23 +930,30 @@
 static int decodeBeacon (void) {
     ASSERT(LMIC.dataLen == LEN_BCN); // implicit header RX guarantees this
     xref2u1_t d = LMIC.frame;
-    if( os_rlsbf2(&d[OFF_BCN_CRC1]) != os_crc16(d, OFF_BCN_CRC1) )
-    return 0;   // first (common) part fails CRC check
+    if(
+#if CFG_eu868
+        d[OFF_BCN_CRC1] != (u1_t)os_crc16(d,OFF_BCN_CRC1)
+#elif CFG_us915
+        os_rlsbf2(&d[OFF_BCN_CRC1]) != os_crc16(d,OFF_BCN_CRC1)
+#endif
+        )
+        return 0;   // first (common) part fails CRC check
     // First set of fields is ok
     u4_t bcnnetid = os_rlsbf4(&d[OFF_BCN_NETID]) & 0xFFFFFF;
     if( bcnnetid != LMIC.netid )
-    return -1;  // not the beacon we're looking for
+        return -1;  // not the beacon we're looking for
 
     LMIC.bcninfo.flags &= ~(BCN_PARTIAL|BCN_FULL);
     // Match - update bcninfo structure
-    os_copyMem((xref2u1_t)&LMIC.bcninfo.rxq, (xref2u1_t)&LMIC.rxq, SIZEOFEXPR(LMIC.rxq));
+    LMIC.bcninfo.snr    = LMIC.snr;
+    LMIC.bcninfo.rssi   = LMIC.rssi;
     LMIC.bcninfo.txtime = LMIC.rxtime - AIRTIME_BCN_osticks;
     LMIC.bcninfo.time   = os_rlsbf4(&d[OFF_BCN_TIME]);
     LMIC.bcninfo.flags |= BCN_PARTIAL;
 
     // Check 2nd set
     if( os_rlsbf2(&d[OFF_BCN_CRC2]) != os_crc16(d,OFF_BCN_CRC2) )
-    return 1;
+        return 1;
     // Second set of fields is ok
     LMIC.bcninfo.lat    = (s4_t)os_rlsbf4(&d[OFF_BCN_LAT-1]) >> 8; // read as signed 24-bit
     LMIC.bcninfo.lon    = (s4_t)os_rlsbf4(&d[OFF_BCN_LON-1]) >> 8; // ditto
@@ -959,16 +969,16 @@
     u1_t ftype  = hdr & HDR_FTYPE;
     int  dlen   = LMIC.dataLen;
     if( dlen < OFF_DAT_OPTS+4 ||
-    (hdr & HDR_MAJOR) != HDR_MAJOR_V1 ||
-    (ftype != HDR_FTYPE_DADN  &&  ftype != HDR_FTYPE_DCDN) ) {
-    // Basic sanity checks failed
-    EV(specCond, WARN, (e_.reason = EV::specCond_t::UNEXPECTED_FRAME,
-                e_.eui    = MAIN::CDEV->getEui(),
-                e_.info   = dlen < 4 ? 0 : os_rlsbf4(&d[dlen-4]),
-                e_.info2  = hdr + (dlen<<8)));
+        (hdr & HDR_MAJOR) != HDR_MAJOR_V1 ||
+        (ftype != HDR_FTYPE_DADN  &&  ftype != HDR_FTYPE_DCDN) ) {
+        // Basic sanity checks failed
+        EV(specCond, WARN, (e_.reason = EV::specCond_t::UNEXPECTED_FRAME,
+                            e_.eui    = MAIN::CDEV->getEui(),
+                            e_.info   = dlen < 4 ? 0 : os_rlsbf4(&d[dlen-4]),
+                            e_.info2  = hdr + (dlen<<8)));
       norx:
-    LMIC.dataLen = 0;
-    return 0;
+        LMIC.dataLen = 0;
+        return 0;
     }
     // Validate exact frame length
     // Note: device address was already read+evaluated in order to arrive here.
@@ -981,252 +991,261 @@
     int  pend  = dlen-4;  // MIC
 
     if( addr != LMIC.devaddr ) {
-    EV(specCond, WARN, (e_.reason = EV::specCond_t::ALIEN_ADDRESS,
-                e_.eui    = MAIN::CDEV->getEui(),
-                e_.info   = addr,
-                e_.info2  = LMIC.devaddr));
-    goto norx;
+        EV(specCond, WARN, (e_.reason = EV::specCond_t::ALIEN_ADDRESS,
+                            e_.eui    = MAIN::CDEV->getEui(),
+                            e_.info   = addr,
+                            e_.info2  = LMIC.devaddr));
+        goto norx;
     }
     if( poff > pend ) {
-    EV(specCond, ERR, (e_.reason = EV::specCond_t::CORRUPTED_FRAME,
-               e_.eui    = MAIN::CDEV->getEui(),
-               e_.info   = 0x1000000 + (poff-pend) + (fct<<8) + (dlen<<16)));
-    goto norx;
+        EV(specCond, ERR, (e_.reason = EV::specCond_t::CORRUPTED_FRAME,
+                           e_.eui    = MAIN::CDEV->getEui(),
+                           e_.info   = 0x1000000 + (poff-pend) + (fct<<8) + (dlen<<16)));
+        goto norx;
     }
 
     int port = -1;
     int replayConf = 0;
 
     if( pend > poff )
-    port = d[poff++];
+        port = d[poff++];
 
     seqno = LMIC.seqnoDn + (u2_t)(seqno - LMIC.seqnoDn);
 
     if( !aes_verifyMic(LMIC.nwkKey, LMIC.devaddr, seqno, /*dn*/1, d, pend) ) {
-    EV(specCond, ERR, (e_.reason = EV::specCond_t::CORRUPTED_MIC,
-               e_.eui    = MAIN::CDEV->getEui(),
-               e_.info   = Base::lsbf4(&d[pend]),
-               e_.info2  = seqno));
-    goto norx;
-    }
-    if( seqno < LMIC.seqnoDn ) {
-    if( (s4_t)seqno > (s4_t)LMIC.seqnoDn ) {
-        EV(specCond, INFO, (e_.reason = EV::specCond_t::DNSEQNO_ROLL_OVER,
-                e_.eui    = MAIN::CDEV->getEui(),
-                e_.info   = LMIC.seqnoDn, 
-                e_.info2  = seqno));
+        EV(spe3Cond, ERR, (e_.reason = EV::spe3Cond_t::CORRUPTED_MIC,
+                           e_.eui1   = MAIN::CDEV->getEui(),
+                           e_.info1  = Base::lsbf4(&d[pend]),
+                           e_.info2  = seqno,
+                           e_.info3  = LMIC.devaddr));
         goto norx;
     }
-    if( seqno != LMIC.seqnoDn-1 || !LMIC.dnConf || ftype != HDR_FTYPE_DCDN ) {
-        EV(specCond, INFO, (e_.reason = EV::specCond_t::DNSEQNO_OBSOLETE,
-                e_.eui    = MAIN::CDEV->getEui(),
-                e_.info   = LMIC.seqnoDn, 
-                e_.info2  = seqno));
-        goto norx;
-    }
-    // Replay of previous sequence number allowed only if
-    // previous frame and repeated both requested confirmation
-    replayConf = 1;
+    if( seqno < LMIC.seqnoDn ) {
+        if( (s4_t)seqno > (s4_t)LMIC.seqnoDn ) {
+            EV(specCond, INFO, (e_.reason = EV::specCond_t::DNSEQNO_ROLL_OVER,
+                                e_.eui    = MAIN::CDEV->getEui(),
+                                e_.info   = LMIC.seqnoDn, 
+                                e_.info2  = seqno));
+            goto norx;
+        }
+        if( seqno != LMIC.seqnoDn-1 || !LMIC.dnConf || ftype != HDR_FTYPE_DCDN ) {
+            EV(specCond, INFO, (e_.reason = EV::specCond_t::DNSEQNO_OBSOLETE,
+                                e_.eui    = MAIN::CDEV->getEui(),
+                                e_.info   = LMIC.seqnoDn, 
+                                e_.info2  = seqno));
+            goto norx;
+        }
+        // Replay of previous sequence number allowed only if
+        // previous frame and repeated both requested confirmation
+        replayConf = 1;
     }
     else {
-    if( seqno > LMIC.seqnoDn ) {
-        EV(specCond, INFO, (e_.reason = EV::specCond_t::DNSEQNO_SKIP,
-                e_.eui    = MAIN::CDEV->getEui(),
-                e_.info   = LMIC.seqnoDn, 
-                e_.info2  = seqno));
-    }
-    LMIC.seqnoDn = seqno+1;  // next number to be expected
-    DO_DEVDB(updateSeqnoDn, LMIC.seqnoDn);
-    // DN frame requested confirmation - provide ACK once with next UP frame
-    LMIC.dnConf = (ftype == HDR_FTYPE_DCDN ? FCT_ACK : 0);
+        if( seqno > LMIC.seqnoDn ) {
+            EV(specCond, INFO, (e_.reason = EV::specCond_t::DNSEQNO_SKIP,
+                                e_.eui    = MAIN::CDEV->getEui(),
+                                e_.info   = LMIC.seqnoDn, 
+                                e_.info2  = seqno));
+        }
+        LMIC.seqnoDn = seqno+1;  // next number to be expected
+        DO_DEVDB(LMIC.seqnoDn,seqnoDn);
+        // DN frame requested confirmation - provide ACK once with next UP frame
+        LMIC.dnConf = (ftype == HDR_FTYPE_DCDN ? FCT_ACK : 0);
     }
 
     if( LMIC.dnConf || (fct & FCT_MORE) )
-    LMIC.opmode |= OP_POLL;
+        LMIC.opmode |= OP_POLL;
 
     // We heard from network
-    LMIC.adrChanged = 0;
-    LMIC.adrAckReq = LINK_CHECK_INIT;
+    LMIC.adrChanged = LMIC.rejoinCnt = 0;
+    if( LMIC.adrAckReq != LINK_CHECK_OFF )
+        LMIC.adrAckReq = LINK_CHECK_INIT;
 
     // Process OPTS
-    int m = LMIC.rxq.rssi - RSSI_OFF - getSensitivity(LMIC.rps);
+    int m = LMIC.rssi - RSSI_OFF - getSensitivity(LMIC.rps);
     LMIC.margin = m < 0 ? 0 : m > 254 ? 254 : m;
 
     xref2u1_t opts = &d[OFF_DAT_OPTS];
     int oidx = 0;
     while( oidx < olen ) {
-    switch( opts[oidx] ) {
-    case MCMD_LCHK_ANS: {
-        //int gwmargin = opts[oidx+1];
-        //int ngws = opts[oidx+2];
-        oidx += 3;
-        continue;
-    }
-    case MCMD_LADR_REQ: {
-        u1_t p1     = opts[oidx+1];            // txpow + DR
-        u2_t chmap  = os_rlsbf2(&opts[oidx+2]);// list of enabled channel
-        u1_t chpage = opts[oidx+4] & MCMD_LADR_CHPAGE_MASK;     // channel page
-        u1_t uprpt  = opts[oidx+4] & MCMD_LADR_REPEAT_MASK;     // up repeat count
-        oidx += 5;
+        switch( opts[oidx] ) {
+        case MCMD_LCHK_ANS: {
+            //int gwmargin = opts[oidx+1];
+            //int ngws = opts[oidx+2];
+            oidx += 3;
+            continue;
+        }
+        case MCMD_LADR_REQ: {
+            u1_t p1     = opts[oidx+1];            // txpow + DR
+            u2_t chmap  = os_rlsbf2(&opts[oidx+2]);// list of enabled channels
+            u1_t chpage = opts[oidx+4] & MCMD_LADR_CHPAGE_MASK;     // channel page
+            u1_t uprpt  = opts[oidx+4] & MCMD_LADR_REPEAT_MASK;     // up repeat count
+            oidx += 5;
 
-        LMIC.ladrAns = 0x80 |     // Include an answer into next frame up
-        MCMD_LADR_ANS_POWACK | MCMD_LADR_ANS_CHACK | MCMD_LADR_ANS_DRACK;
-        if( !mapChannels(chpage, chmap) )
-        LMIC.ladrAns &= ~MCMD_LADR_ANS_CHACK;
-        dr_t dr = (dr_t)(p1>>MCMD_LADR_DR_SHIFT);
-        if( !validDR(dr) ) {
-        LMIC.ladrAns &= ~MCMD_LADR_ANS_DRACK;
-        dr = (dr_t)LMIC.datarate;
-        EV(specCond, ERR, (e_.reason = EV::specCond_t::BAD_MAC_CMD,
-                   e_.eui    = MAIN::CDEV->getEui(),
-                   e_.info   = Base::lsbf4(&d[pend]),
-                   e_.info2  = Base::msbf4(&opts[oidx-4])));
+            LMIC.ladrAns = 0x80 |     // Include an answer into next frame up
+                MCMD_LADR_ANS_POWACK | MCMD_LADR_ANS_CHACK | MCMD_LADR_ANS_DRACK;
+            if( !mapChannels(chpage, chmap) )
+                LMIC.ladrAns &= ~MCMD_LADR_ANS_CHACK;
+            dr_t dr = (dr_t)(p1>>MCMD_LADR_DR_SHIFT);
+            if( !validDR(dr) ) {
+                LMIC.ladrAns &= ~MCMD_LADR_ANS_DRACK;
+                EV(specCond, ERR, (e_.reason = EV::specCond_t::BAD_MAC_CMD,
+                                   e_.eui    = MAIN::CDEV->getEui(),
+                                   e_.info   = Base::lsbf4(&d[pend]),
+                                   e_.info2  = Base::msbf4(&opts[oidx-4])));
+            }
+            if( (LMIC.ladrAns & 0x7F) == (MCMD_LADR_ANS_POWACK | MCMD_LADR_ANS_CHACK | MCMD_LADR_ANS_DRACK) ) {
+                // Nothing went wrong - use settings
+                LMIC.upRepeat = uprpt;
+                setDrTxpow(DRCHG_NWKCMD, dr, pow2dBm(p1));
+            }
+            LMIC.adrChanged = 1;  // Trigger an ACK to NWK
+            continue;
         }
-        LMIC.upRepeat = uprpt;
-        setDrTxpow(DRCHG_NWKCMD, dr, pow2dBm(p1));
-        LMIC.adrChanged = 1;  // Trigger an ACK to NWK
-        continue;
-    }
-    case MCMD_DEVS_REQ: {
-        LMIC.devsAns = 1;
-        oidx += 1;
-        continue;
-    }
-    case MCMD_DN2P_SET: {
-        dr_t dr = (dr_t)(opts[oidx+1] & 0x0F);
-        u4_t freq = convFreq(&opts[oidx+2]);
-        oidx += 5;
-        LMIC.dn2Ans = 0x80;   // answer pending
-        if( validDR(dr) )
-        LMIC.dn2Ans |= MCMD_DN2P_ANS_DRACK;
-        if( freq != 0 )
-        LMIC.dn2Ans |= MCMD_DN2P_ANS_CHACK;
-        if( LMIC.dn2Ans == (0x80|MCMD_DN2P_ANS_DRACK|MCMD_DN2P_ANS_CHACK) ) {
-        LMIC.dn2Dr = dr;
-        LMIC.dn2Freq = freq;
-        DO_DEVDB(updateDn2,LMIC.dn2Dr,LMIC.dn2Freq);
+        case MCMD_DEVS_REQ: {
+            LMIC.devsAns = 1;
+            oidx += 1;
+            continue;
+        }
+        case MCMD_DN2P_SET: {
+            dr_t dr = (dr_t)(opts[oidx+1] & 0x0F);
+            u4_t freq = convFreq(&opts[oidx+2]);
+            oidx += 5;
+            LMIC.dn2Ans = 0x80;   // answer pending
+            if( validDR(dr) )
+                LMIC.dn2Ans |= MCMD_DN2P_ANS_DRACK;
+            if( freq != 0 )
+                LMIC.dn2Ans |= MCMD_DN2P_ANS_CHACK;
+            if( LMIC.dn2Ans == (0x80|MCMD_DN2P_ANS_DRACK|MCMD_DN2P_ANS_CHACK) ) {
+                LMIC.dn2Dr = dr;
+                LMIC.dn2Freq = freq;
+                DO_DEVDB(LMIC.dn2Dr,dn2Dr);
+                DO_DEVDB(LMIC.dn2Freq,dn2Freq);
+            }
+            continue;
         }
-        continue;
-    }
-    case MCMD_DCAP_REQ: {
-        u1_t cap = opts[oidx+1];
-        oidx += 2;
-        // A value cap=0xFF means device is OFF unless enabled again manually
-        // We just set duty cap to 0xF which is 0.003% -- pretty much off.
-        // We don't check 0xF0 bits if cap!=0xFF
-        LMIC.globalDutyRate  = cap & 0xF;
-        LMIC.globalDutyAvail = os_getTime();
-        DO_DEVDB(updateDutyCap,cap);
-        LMIC.dutyCapAns = 1;
-        continue;
-    }
-    case MCMD_SNCH_REQ: {
-        u1_t chidx = opts[oidx+1];  // channel
-        u4_t freq  = convFreq(&opts[oidx+2]); // freq
-        u1_t drs   = opts[oidx+5];  // datarate span
-        LMIC.snchAns = 0x80;
-        if( freq != 0 && setupChannel(chidx,freq,drs) )
-        LMIC.snchAns |= MCMD_SNCH_ANS_DRACK|MCMD_SNCH_ANS_FQACK;
-        oidx += 6;
-    }
-    case MCMD_PING_SET: {
-        u4_t freq = convFreq(&opts[oidx+1]);
-        oidx += 4;
-        u1_t flags = 0x80;
-        if( freq != 0 ) {
-        flags |= MCMD_PING_ANS_FQACK;
-        LMIC.ping.freq = freq;
-        DO_DEVDB(updateClassB,LMIC.ping.intvExp,freq,LMIC.ping.dr);
+        case MCMD_DCAP_REQ: {
+            u1_t cap = opts[oidx+1];
+            oidx += 2;
+            // A value cap=0xFF means device is OFF unless enabled again manually.
+            if( cap==0xFF )
+                LMIC.opmode |= OP_SHUTDOWN;  // stop any sending
+            LMIC.globalDutyRate  = cap & 0xF;
+            LMIC.globalDutyAvail = os_getTime();
+            DO_DEVDB(cap,dutyCap);
+            LMIC.dutyCapAns = 1;
+            continue;
+        }
+        case MCMD_SNCH_REQ: {
+            u1_t chidx = opts[oidx+1];  // channel
+            u4_t freq  = convFreq(&opts[oidx+2]); // freq
+            u1_t drs   = opts[oidx+5];  // datarate span
+            LMIC.snchAns = 0x80;
+            if( freq != 0 && LMIC_setupChannel(chidx, freq, DR_RANGE_MAP(drs&0xF,drs>>4), -1) )
+                LMIC.snchAns |= MCMD_SNCH_ANS_DRACK|MCMD_SNCH_ANS_FQACK;
+            oidx += 6;
+            continue;
         }
-        LMIC.pingSetAns = flags;
-        continue;
-    }
-    case MCMD_BCNI_ANS: {
-        // Ignore if tracking already enabled
-        if( (LMIC.opmode & OP_TRACK) == 0 ) {
-        LMIC.bcnChnl = opts[oidx+3];
-        // Enable tracking - bcninfoTries
-        LMIC.opmode |= OP_TRACK;
-        // Cleared later in txComplete handling - triggers EV_BEACON_FOUND
-        ASSERT(LMIC.bcninfoTries!=0);
-        // Setup RX parameters
-        LMIC.bcninfo.txtime =  (LMIC.rxtime
-                       - calcAirTime(LMIC.rps, LMIC.dataLen)
-                       + ms2osticks(os_rlsbf2(&opts[oidx+1]) * 10)
-                       + ms2osticksCeil(5)
-                       - BCN_INTV_osticks);
-        LMIC.bcninfo.flags = 0;  // txtime above cannot be used as reference (BCN_PARTIAL|BCN_FULL cleared)
-        calcBcnRxWindowFromMillis(10,1);  // error of +/-5 ms 
+        case MCMD_PING_SET: {
+            u4_t freq = convFreq(&opts[oidx+1]);
+            oidx += 4;
+            u1_t flags = 0x80;
+            if( freq != 0 ) {
+                flags |= MCMD_PING_ANS_FQACK;
+                LMIC.ping.freq = freq;
+                DO_DEVDB(LMIC.ping.intvExp, pingIntvExp);
+                DO_DEVDB(LMIC.ping.freq, pingFreq);
+                DO_DEVDB(LMIC.ping.dr, pingDr);
+            }
+            LMIC.pingSetAns = flags;
+            continue;
+        }
+        case MCMD_BCNI_ANS: {
+            // Ignore if tracking already enabled
+            if( (LMIC.opmode & OP_TRACK) == 0 ) {
+                LMIC.bcnChnl = opts[oidx+3];
+                // Enable tracking - bcninfoTries
+                LMIC.opmode |= OP_TRACK;
+                // Cleared later in txComplete handling - triggers EV_BEACON_FOUND
+                ASSERT(LMIC.bcninfoTries!=0);
+                // Setup RX parameters
+                LMIC.bcninfo.txtime = (LMIC.rxtime
+                                       + ms2osticks(os_rlsbf2(&opts[oidx+1]) * MCMD_BCNI_TUNIT)
+                                       + ms2osticksCeil(MCMD_BCNI_TUNIT/2)
+                                       - BCN_INTV_osticks);
+                LMIC.bcninfo.flags = 0;  // txtime above cannot be used as reference (BCN_PARTIAL|BCN_FULL cleared)
+                calcBcnRxWindowFromMillis(MCMD_BCNI_TUNIT,1);  // error of +/-N ms 
 
-        EV(lostFrame, INFO, (e_.reason  = EV::lostFrame_t::MCMD_BCNI_ANS,
-                     e_.eui     = MAIN::CDEV->getEui(),
-                     e_.lostmic = Base::lsbf4(&d[pend]),
-                     e_.info    = (LMIC.missedBcns |
-                           (osticks2us(LMIC.bcninfo.txtime + BCN_INTV_osticks
-                                   - LMIC.bcnRxtime) << 8)),
-                     e_.time    = MAIN::CDEV->ostime2ustime(LMIC.bcninfo.txtime + BCN_INTV_osticks)));
+                EV(lostFrame, INFO, (e_.reason  = EV::lostFrame_t::MCMD_BCNI_ANS,
+                                     e_.eui     = MAIN::CDEV->getEui(),
+                                     e_.lostmic = Base::lsbf4(&d[pend]),
+                                     e_.info    = (LMIC.missedBcns |
+                                                   (osticks2us(LMIC.bcninfo.txtime + BCN_INTV_osticks
+                                                               - LMIC.bcnRxtime) << 8)),
+                                     e_.time    = MAIN::CDEV->ostime2ustime(LMIC.bcninfo.txtime + BCN_INTV_osticks)));
+            }
+            oidx += 4;
+            continue;
         }
-        oidx += 4;
-        continue;
-    }
-    }
-    EV(specCond, ERR, (e_.reason = EV::specCond_t::BAD_MAC_CMD,
-               e_.eui    = MAIN::CDEV->getEui(),
-               e_.info   = Base::lsbf4(&d[pend]),
-               e_.info2  = Base::msbf4(&opts[oidx])));
-    break;
+        }
+        EV(specCond, ERR, (e_.reason = EV::specCond_t::BAD_MAC_CMD,
+                           e_.eui    = MAIN::CDEV->getEui(),
+                           e_.info   = Base::lsbf4(&d[pend]),
+                           e_.info2  = Base::msbf4(&opts[oidx])));
+        break;
     }
     if( oidx != olen ) {
-    EV(specCond, ERR, (e_.reason = EV::specCond_t::CORRUPTED_FRAME,
-               e_.eui    = MAIN::CDEV->getEui(),
-               e_.info   = 0x1000000 + (oidx) + (olen<<8)));
+        EV(specCond, ERR, (e_.reason = EV::specCond_t::CORRUPTED_FRAME,
+                           e_.eui    = MAIN::CDEV->getEui(),
+                           e_.info   = 0x1000000 + (oidx) + (olen<<8)));
     }
 
     if( !replayConf ) {
-    // Handle payload only if not a replay
-    // Decrypt payload - if any
-    if( port >= 0  &&  pend-poff > 0 )
-        aes_cipher(port <= 0 ? LMIC.nwkKey : LMIC.artKey, LMIC.devaddr, seqno, /*dn*/1, d+poff, pend-poff);
+        // Handle payload only if not a replay
+        // Decrypt payload - if any
+        if( port >= 0  &&  pend-poff > 0 )
+            aes_cipher(port <= 0 ? LMIC.nwkKey : LMIC.artKey, LMIC.devaddr, seqno, /*dn*/1, d+poff, pend-poff);
 
-    EV(dfinfo, DEBUG, (e_.deveui  = MAIN::CDEV->getEui(),
-               e_.devaddr = LMIC.devaddr,
-               e_.seqno   = seqno,
-               e_.flags   = (port < 0 ? EV::dfinfo_t::NOPORT : 0) | EV::dfinfo_t::DN,
-               e_.mic     = Base::lsbf4(&d[pend]),
-               e_.hdr     = d[LORA::OFF_DAT_HDR],
-               e_.fct     = d[LORA::OFF_DAT_FCT],
-               e_.port    = port,
-               e_.plen    = dlen,
-               e_.olen    = olen,
-               memcpy(e_.opts, opts, olen)));
+        EV(dfinfo, DEBUG, (e_.deveui  = MAIN::CDEV->getEui(),
+                           e_.devaddr = LMIC.devaddr,
+                           e_.seqno   = seqno,
+                           e_.flags   = (port < 0 ? EV::dfinfo_t::NOPORT : 0) | EV::dfinfo_t::DN,
+                           e_.mic     = Base::lsbf4(&d[pend]),
+                           e_.hdr     = d[LORA::OFF_DAT_HDR],
+                           e_.fct     = d[LORA::OFF_DAT_FCT],
+                           e_.port    = port,
+                           e_.plen    = dlen,
+                           e_.opts.length = olen,
+                           memcpy(&e_.opts[0], opts, olen)));
     } else {
-    EV(specCond, INFO, (e_.reason = EV::specCond_t::DNSEQNO_REPLAY,
-                e_.eui    = MAIN::CDEV->getEui(),
-                e_.info   = Base::lsbf4(&d[pend]),
-                e_.info2  = seqno));
+        EV(specCond, INFO, (e_.reason = EV::specCond_t::DNSEQNO_REPLAY,
+                            e_.eui    = MAIN::CDEV->getEui(),
+                            e_.info   = Base::lsbf4(&d[pend]),
+                            e_.info2  = seqno));
     }
 
     if( // NWK acks but we don't have a frame pending
-    (ackup && LMIC.txCnt == 0) ||
-    // We sent up confirmed and we got a response in DNW1/DNW2
-    // BUT it did not carry an ACK - this should never happen
-    // Do not resend and assume frame was not ACKed.
-    (!ackup && LMIC.txCnt != 0) ) {
-    EV(specCond, ERR, (e_.reason = EV::specCond_t::SPURIOUS_ACK,
-               e_.eui    = MAIN::CDEV->getEui(),
-               e_.info   = seqno,
-               e_.info2  = ackup));
+        (ackup && LMIC.txCnt == 0) ||
+        // We sent up confirmed and we got a response in DNW1/DNW2
+        // BUT it did not carry an ACK - this should never happen
+        // Do not resend and assume frame was not ACKed.
+        (!ackup && LMIC.txCnt != 0) ) {
+        EV(specCond, ERR, (e_.reason = EV::specCond_t::SPURIOUS_ACK,
+                           e_.eui    = MAIN::CDEV->getEui(),
+                           e_.info   = seqno,
+                           e_.info2  = ackup));
     }
 
     if( LMIC.txCnt != 0 ) // we requested an ACK
-    LMIC.txrxFlags |= ackup ? TXRX_ACK : TXRX_NACK;
+        LMIC.txrxFlags |= ackup ? TXRX_ACK : TXRX_NACK;
 
     if( port < 0 ) {
-    LMIC.txrxFlags |= TXRX_NOPORT;
-    LMIC.dataBeg = LMIC.dataLen = 0;
+        LMIC.txrxFlags |= TXRX_NOPORT;
+        LMIC.dataBeg = poff;
+        LMIC.dataLen = 0;
     } else {
-    LMIC.dataBeg = poff;
-    LMIC.dataLen = pend-poff;
+        LMIC.txrxFlags |= TXRX_PORT;
+        LMIC.dataBeg = poff;
+        LMIC.dataLen = pend-poff;
     }
     return 1;
 }
@@ -1263,27 +1282,25 @@
 
 // Called by HAL once TX complete and delivers exact end of TX time stamp in LMIC.rxtime
 static void txDone (ostime_t delay, osjobcb_t func) {
-    // NOTE: LMIC.rxsyms carries the modified DR for TX (LMIC.datarate is the base DR - [CONFIRM mode])
-    u1_t dr = LMIC.rxsyms;    // rxschedInit - would overwrite rxsyms
     if( (LMIC.opmode & (OP_TRACK|OP_PINGABLE|OP_PINGINI)) == (OP_TRACK|OP_PINGABLE) ) {
-    rxschedInit(&LMIC.ping);    // note: reuses LMIC.frame buffer!
-    LMIC.opmode |= OP_PINGINI;
+        rxschedInit(&LMIC.ping);    // note: reuses LMIC.frame buffer!
+        LMIC.opmode |= OP_PINGINI;
     }
     // Change RX frequency / rps (US only) before we increment txChnl
     setRx1Params();
     // LMIC.rxsyms carries the TX datarate (can be != LMIC.datarate [confirm retries etc.])
     // Setup receive - LMIC.rxtime is preloaded with 1.5 symbols offset to tune
     // into the middle of the 8 symbols preamble.
-#if CFG_eu868
+#if defined(CFG_eu868)
     if( /* TX datarate */LMIC.rxsyms == DR_FSK ) {
-    LMIC.rxtime = LMIC.txend + delay - PRERX_FSK*us2osticksRound(160);
-    LMIC.rxsyms = RXLEN_FSK;
+        LMIC.rxtime = LMIC.txend + delay - PRERX_FSK*us2osticksRound(160);
+        LMIC.rxsyms = RXLEN_FSK;
     }
     else
 #endif
     {
-    LMIC.rxtime = LMIC.txend + delay + (PAMBL_SYMS-MINRX_SYMS)*dr2hsym(dr);
-    LMIC.rxsyms = MINRX_SYMS;
+        LMIC.rxtime = LMIC.txend + delay + (PAMBL_SYMS-MINRX_SYMS)*dr2hsym(LMIC.dndr);
+        LMIC.rxsyms = MINRX_SYMS;
     }
     os_setTimedCallback(&LMIC.osjob, LMIC.rxtime - RX_RAMPUP, func);
 }
@@ -1305,86 +1322,90 @@
 
     if( LMIC.dataLen == 0 ) {
       nojoinframe:
-    if( (LMIC.opmode & OP_OTA) != 0 ) {
-    if( (LMIC.opmode & OP_JOINING) == 0 ) {
-        ASSERT((LMIC.opmode & OP_REJOIN) != 0);
-        // REJOIN attempt for roaming
-        LMIC.opmode &= ~(OP_REJOIN|OP_TXRXPEND);
-        if( LMIC.rejoinCnt < 10 )
-        LMIC.rejoinCnt++;
-        reportEvent(EV_REJOIN_FAILED);
+        if( (LMIC.opmode & OP_JOINING) == 0 ) {
+            ASSERT((LMIC.opmode & OP_REJOIN) != 0);
+            // REJOIN attempt for roaming
+            LMIC.opmode &= ~(OP_REJOIN|OP_TXRXPEND);
+            if( LMIC.rejoinCnt < 10 )
+                LMIC.rejoinCnt++;
+            reportEvent(EV_REJOIN_FAILED);
+            return 1;
+        }
+        LMIC.opmode &= ~OP_TXRXPEND;
+        ostime_t delay = nextJoinState();
+        EV(devCond, DEBUG, (e_.reason = EV::devCond_t::NO_JACC,
+                            e_.eui    = MAIN::CDEV->getEui(),
+                            e_.info   = LMIC.datarate|DR_PAGE,
+                            e_.info2  = osticks2ms(delay)));
+        // Build next JOIN REQUEST with next engineUpdate call
+        // Optionally, report join failed.
+        // Both after a random/chosen amount of ticks.
+        os_setTimedCallback(&LMIC.osjob, os_getTime()+delay,
+                            (delay&1) != 0
+                            ? FUNC_ADDR(onJoinFailed)      // one JOIN iteration done and failed
+                            : FUNC_ADDR(runEngineUpdate)); // next step to be delayed
         return 1;
     }
-    }
-    LMIC.opmode &= ~OP_TXRXPEND;
-    ostime_t delay = nextJoinState();
-    EV(devCond, DEBUG, (e_.reason = EV::devCond_t::NO_JACC,
-                e_.eui    = MAIN::CDEV->getEui(),
-                e_.info   = LMIC.datarate|DR_PAGE,
-                e_.info2  = osticks2ms(delay)));
-    // Build next JOIN REQUEST with next engineUpdate call
-    // Optionally, report join failed.
-    // Both after a random/chosen amount of ticks.
-    os_setTimedCallback(&LMIC.osjob, os_getTime()+delay,
-                (delay&1) != 0
-                ? FUNC_ADDR(onJoinFailed)      // one JOIN iteration done and failed
-                : FUNC_ADDR(runEngineUpdate)); // next step to be delayed
-    return 1;
-    }
     u1_t hdr  = LMIC.frame[0];
     u1_t dlen = LMIC.dataLen;
     u4_t mic  = os_rlsbf4(&LMIC.frame[dlen-4]); // safe before modified by encrypt!
     if( (dlen != LEN_JA && dlen != LEN_JAEXT)
-    || (hdr & (HDR_FTYPE|HDR_MAJOR)) != (HDR_FTYPE_JACC|HDR_MAJOR_V1) ) {
-    EV(specCond, ERR, (e_.reason = EV::specCond_t::UNEXPECTED_FRAME,
-               e_.eui    = MAIN::CDEV->getEui(),
-               e_.info   = dlen < 4 ? 0 : mic,
-               e_.info2  = hdr + (dlen<<8)));
+        || (hdr & (HDR_FTYPE|HDR_MAJOR)) != (HDR_FTYPE_JACC|HDR_MAJOR_V1) ) {
+        EV(specCond, ERR, (e_.reason = EV::specCond_t::UNEXPECTED_FRAME,
+                           e_.eui    = MAIN::CDEV->getEui(),
+                           e_.info   = dlen < 4 ? 0 : mic,
+                           e_.info2  = hdr + (dlen<<8)));
       badframe:
-    goto nojoinframe;
+        if( (LMIC.txrxFlags & TXRX_DNW1) != 0 )
+            return 0;
+        goto nojoinframe;
     }
     aes_encrypt(LMIC.frame+1, dlen-1);
     if( !aes_verifyMic0(LMIC.frame, dlen-4) ) {
-    EV(specCond, ERR, (e_.reason = EV::specCond_t::JOIN_BAD_MIC,
-               e_.info   = mic));
-    goto badframe;
+        EV(specCond, ERR, (e_.reason = EV::specCond_t::JOIN_BAD_MIC,
+                           e_.info   = mic));
+        goto badframe;
     }
 
     u4_t addr = os_rlsbf4(LMIC.frame+OFF_JA_DEVADDR);
     LMIC.devaddr = addr;
-    LMIC.netid = os_rlsbf4(&LMIC.frame[OFF_BCN_NETID-1]) >> 8;
+    LMIC.netid = os_rlsbf4(&LMIC.frame[OFF_JA_NETID]) & 0xFFFFFF;
 
-#if CFG_eu868
+#if defined(CFG_eu868)
     initDefaultChannels(0);
 #endif
     if( dlen > LEN_JA ) {
-    dlen = OFF_CFLIST;
-#if CFG_eu868
-    u1_t chidx=3;
-#elif CFG_us915
-    u1_t chidx=72;
+        dlen = OFF_CFLIST;
+#if defined(CFG_eu868)
+        u1_t chidx=3;
+#elif defined(CFG_us915)
+        u1_t chidx=72;
 #endif
-    for( ; chidx<8; chidx++, dlen+=3 )
-        setupChannel(chidx, os_rlsbf4(&LMIC.frame[dlen-1]) >> 8, -1);
+        for( ; chidx<8; chidx++, dlen+=3 )
+            LMIC_setupChannel(chidx, os_rlsbf4(&LMIC.frame[dlen-1]) >> 8, 0, -1);
     }
 
     // already incremented when JOIN REQ got sent off
     aes_sessKeys(LMIC.devNonce-1, &LMIC.frame[OFF_JA_ARTNONCE], LMIC.nwkKey, LMIC.artKey);
-    DO_DEVDB(updateJoinAcc, LMIC.devaddr, LMIC.nwkKey, LMIC.artKey);
+    DO_DEVDB(LMIC.netid,   netid);
+    DO_DEVDB(LMIC.devaddr, devaddr);
+    DO_DEVDB(LMIC.nwkKey,  nwkkey);
+    DO_DEVDB(LMIC.artKey,  artkey);
 
     EV(joininfo, INFO, (e_.arteui  = MAIN::CDEV->getArtEui(),
-            e_.deveui  = MAIN::CDEV->getEui(),
-            e_.devaddr = LMIC.devaddr,
-            e_.oldaddr = oldaddr,
-            e_.nonce   = LMIC.devNonce-1,
-            e_.mic     = mic,
-            e_.flags   = ((LMIC.opmode & OP_REJOIN) != 0
-                      ? EV::joininfo_t::REJOIN_ACCEPT
-                      : EV::joininfo_t::ACCEPT)));
+                        e_.deveui  = MAIN::CDEV->getEui(),
+                        e_.devaddr = LMIC.devaddr,
+                        e_.oldaddr = oldaddr,
+                        e_.nonce   = LMIC.devNonce-1,
+                        e_.mic     = mic,
+                        e_.reason  = ((LMIC.opmode & OP_REJOIN) != 0
+                                      ? EV::joininfo_t::REJOIN_ACCEPT
+                                      : EV::joininfo_t::ACCEPT)));
     
     ASSERT((LMIC.opmode & (OP_JOINING|OP_REJOIN))!=0);
     if( (LMIC.opmode & OP_REJOIN) != 0 ) {
-    LMIC.datarate = lowerDR(LMIC.datarate, LMIC.rejoinCnt);
+        // Lower DR every try below current UP DR
+        LMIC.datarate = lowerDR(LMIC.datarate, LMIC.rejoinCnt);
     }
     LMIC.opmode &= ~(OP_JOINING|OP_TRACK|OP_REJOIN|OP_TXRXPEND|OP_PINGINI) | OP_NEXTCHNL;
     stateJustJoined();
@@ -1392,12 +1413,26 @@
     return 1;
 }
 
-static void processRx1Jacc (xref2osjob_t osjob) {
+
+static void processRx2Jacc (xref2osjob_t osjob) {
     if( LMIC.dataLen == 0 )
-    LMIC.txrxFlags = 0;  // nothing in 1st/2nd DN slot
+        LMIC.txrxFlags = 0;  // nothing in 1st/2nd DN slot
     processJoinAccept();
 }
 
+
+static void setupRx2Jacc (xref2osjob_t osjob) {
+    LMIC.osjob.func = FUNC_ADDR(processRx2Jacc);
+    setupRx2();
+}
+
+
+static void processRx1Jacc (xref2osjob_t osjob) {
+    if( LMIC.dataLen == 0 || !processJoinAccept() )
+        schedRx2(DELAY_JACC2_osticks, FUNC_ADDR(setupRx2Jacc));
+}
+
+
 static void setupRx1Jacc (xref2osjob_t osjob) {
     setupRx1(FUNC_ADDR(processRx1Jacc));
 }
@@ -1412,9 +1447,20 @@
 // Fwd decl.
 static bit_t processDnData(void);
 
+static void processRx2DnDataDelay (xref2osjob_t osjob) {
+    processDnData();
+}
+
 static void processRx2DnData (xref2osjob_t osjob) {
-    if( LMIC.dataLen == 0 )
-    LMIC.txrxFlags = 0;  // nothing in 1st/2nd DN slot
+    if( LMIC.dataLen == 0 ) {
+        LMIC.txrxFlags = 0;  // nothing in 1st/2nd DN slot
+        // Delay callback processing to avoid up TX while gateway is txing our missed frame! 
+        // Since DNW2 uses SF12 by default we wait 3 secs.
+        os_setTimedCallback(&LMIC.osjob,
+                            (os_getTime() + DNW2_SAFETY_ZONE + rndDelay(2)),
+                            processRx2DnDataDelay);
+        return;
+    }
     processDnData();
 }
 
@@ -1427,7 +1473,7 @@
 
 static void processRx1DnData (xref2osjob_t osjob) {
     if( LMIC.dataLen == 0 || !processDnData() )
-    schedRx2(DELAY_DNW2_osticks, FUNC_ADDR(setupRx2DnData));
+        schedRx2(DELAY_DNW2_osticks, FUNC_ADDR(setupRx2DnData));
 }
 
 
@@ -1451,77 +1497,80 @@
     // Prioritize by importance
     int  end = OFF_DAT_OPTS;
     if( (LMIC.opmode & (OP_TRACK|OP_PINGABLE)) == (OP_TRACK|OP_PINGABLE) ) {
-    // Indicate pingability in every UP frame
-    LMIC.frame[end] = MCMD_PING_IND;
-    LMIC.frame[end+1] = LMIC.ping.dr | (LMIC.ping.intvExp<<4);
-    end += 2;
+        // Indicate pingability in every UP frame
+        LMIC.frame[end] = MCMD_PING_IND;
+        LMIC.frame[end+1] = LMIC.ping.dr | (LMIC.ping.intvExp<<4);
+        end += 2;
     }
     if( LMIC.dutyCapAns ) {
-    LMIC.frame[end] = MCMD_DCAP_ANS;
-    end += 1;
-    LMIC.dutyCapAns = 0;
+        LMIC.frame[end] = MCMD_DCAP_ANS;
+        end += 1;
+        LMIC.dutyCapAns = 0;
     }
     if( LMIC.dn2Ans ) {
-    LMIC.frame[end+0] = MCMD_DN2P_ANS;
-    LMIC.frame[end+1] = LMIC.dn2Ans & ~MCMD_DN2P_ANS_RFU;
-    end += 2;
-    LMIC.dn2Ans = 0;
+        LMIC.frame[end+0] = MCMD_DN2P_ANS;
+        LMIC.frame[end+1] = LMIC.dn2Ans & ~MCMD_DN2P_ANS_RFU;
+        end += 2;
+        LMIC.dn2Ans = 0;
     }
     if( LMIC.devsAns ) {  // answer to device status
-    LMIC.frame[end+0] = MCMD_DEVS_ANS;
-    LMIC.frame[end+1] = LMIC.margin;
-    LMIC.frame[end+2] = os_getBattLevel();
-    end += 3;
-    LMIC.devsAns = 0;
+        LMIC.frame[end+0] = MCMD_DEVS_ANS;
+        LMIC.frame[end+1] = LMIC.margin;
+        LMIC.frame[end+2] = os_getBattLevel();
+        end += 3;
+        LMIC.devsAns = 0;
     }
     if( LMIC.ladrAns ) {  // answer to ADR change
-    LMIC.frame[end+0] = MCMD_LADR_ANS;
-    LMIC.frame[end+1] = LMIC.ladrAns & ~MCMD_LADR_ANS_RFU;
-    end += 2;
-    LMIC.ladrAns = 0;
+        LMIC.frame[end+0] = MCMD_LADR_ANS;
+        LMIC.frame[end+1] = LMIC.ladrAns & ~MCMD_LADR_ANS_RFU;
+        end += 2;
+        LMIC.ladrAns = 0;
     }
     if( LMIC.bcninfoTries > 0 ) {
-    LMIC.frame[end] = MCMD_BCNI_REQ;
-    end += 1;
+        LMIC.frame[end] = MCMD_BCNI_REQ;
+        end += 1;
     }
     if( LMIC.adrChanged ) {
-    LMIC.adrAckReq = LMIC.adrAckReq < 0 ? 0 : LMIC.adrAckReq;
-    LMIC.adrChanged = 0;
+        if( LMIC.adrAckReq < 0 )
+            LMIC.adrAckReq = 0;
+        LMIC.adrChanged = 0;
     }
     if( LMIC.pingSetAns != 0 ) {
-    LMIC.frame[end+0] = MCMD_PING_ANS;
-    LMIC.frame[end+1] = LMIC.pingSetAns & ~MCMD_PING_ANS_RFU;
-    end += 2;
-    LMIC.pingSetAns = 0;
+        LMIC.frame[end+0] = MCMD_PING_ANS;
+        LMIC.frame[end+1] = LMIC.pingSetAns & ~MCMD_PING_ANS_RFU;
+        end += 2;
+        LMIC.pingSetAns = 0;
     }
     if( LMIC.snchAns ) {
-    LMIC.frame[end+0] = MCMD_SNCH_ANS;
-    LMIC.frame[end+1] = LMIC.snchAns;
-    end += 2;
-    LMIC.snchAns = 0;
+        LMIC.frame[end+0] = MCMD_SNCH_ANS;
+        LMIC.frame[end+1] = LMIC.snchAns & ~MCMD_SNCH_ANS_RFU;
+        end += 2;
+        LMIC.snchAns = 0;
     }
     ASSERT(end <= OFF_DAT_OPTS+16);
 
     u1_t flen = end + (txdata ? 5+dlen : 4);
     if( flen > MAX_LEN_FRAME ) {
-    // Options and payload too big - delay payload
-    txdata = 0;
-    flen = end+4;
+        // Options and payload too big - delay payload
+        txdata = 0;
+        flen = end+4;
     }
     LMIC.frame[OFF_DAT_HDR] = HDR_FTYPE_DAUP | HDR_MAJOR_V1;
     LMIC.frame[OFF_DAT_FCT] = (LMIC.dnConf | LMIC.adrEnabled
-                  | (LMIC.adrAckReq >= 0 ? FCT_ADRARQ : 0)
-                  | (end-OFF_DAT_OPTS));
+                              | (LMIC.adrAckReq >= 0 ? FCT_ADRARQ : 0)
+                              | (end-OFF_DAT_OPTS));
     os_wlsbf4(LMIC.frame+OFF_DAT_ADDR,  LMIC.devaddr);
 
     if( LMIC.txCnt == 0 ) {
-    LMIC.seqnoUp += 1;
-    DO_DEVDB(updateSeqnoUp, LMIC.seqnoUp);
+        LMIC.seqnoUp += 1;
+        DO_DEVDB(LMIC.seqnoUp,seqnoUp);
     } else {
-    EV(devCond, INFO, (e_.reason = EV::devCond_t::RE_TX,
-               e_.eui    = MAIN::CDEV->getEui(),
-               e_.info   = LMIC.seqnoUp-1,
-               e_.info2  = (LMIC.txCnt+1) | (DRADJUST[LMIC.txCnt+1] << 8) | ((LMIC.datarate|DR_PAGE)<<16)));
+        EV(devCond, INFO, (e_.reason = EV::devCond_t::RE_TX,
+                           e_.eui    = MAIN::CDEV->getEui(),
+                           e_.info   = LMIC.seqnoUp-1,
+                           e_.info2  = ((LMIC.txCnt+1) |
+                                        (DRADJUST[LMIC.txCnt+1] << 8) |
+                                        ((LMIC.datarate|DR_PAGE)<<16))));
     }
     os_wlsbf2(LMIC.frame+OFF_DAT_SEQNO, LMIC.seqnoUp-1);
 
@@ -1529,30 +1578,30 @@
     LMIC.dnConf = 0;
 
     if( txdata ) {
-    if( LMIC.pendTxConf ) {
-        // Confirmed only makes sense if we have a payload (or at least a port)
-        LMIC.frame[OFF_DAT_HDR] = HDR_FTYPE_DCUP | HDR_MAJOR_V1;
-        LMIC.txCnt += 1;
-    }
-    LMIC.frame[end] = LMIC.pendTxPort;
-    os_copyMem(LMIC.frame+end+1, LMIC.pendTxData, dlen);
-    aes_cipher(LMIC.pendTxPort==0 ? LMIC.nwkKey : LMIC.artKey,
-           LMIC.devaddr, LMIC.seqnoUp-1,
-           /*up*/0, LMIC.frame+end+1, dlen);
+        if( LMIC.pendTxConf ) {
+            // Confirmed only makes sense if we have a payload (or at least a port)
+            LMIC.frame[OFF_DAT_HDR] = HDR_FTYPE_DCUP | HDR_MAJOR_V1;
+            if( LMIC.txCnt == 0 ) LMIC.txCnt = 1;
+        }
+        LMIC.frame[end] = LMIC.pendTxPort;
+        os_copyMem(LMIC.frame+end+1, LMIC.pendTxData, dlen);
+        aes_cipher(LMIC.pendTxPort==0 ? LMIC.nwkKey : LMIC.artKey,
+                   LMIC.devaddr, LMIC.seqnoUp-1,
+                   /*up*/0, LMIC.frame+end+1, dlen);
     }
     aes_appendMic(LMIC.nwkKey, LMIC.devaddr, LMIC.seqnoUp-1, /*up*/0, LMIC.frame, flen-4);
 
     EV(dfinfo, DEBUG, (e_.deveui  = MAIN::CDEV->getEui(),
-               e_.devaddr = LMIC.devaddr,
-               e_.seqno   = LMIC.seqnoUp-1,
-               e_.flags   = (LMIC.pendTxPort < 0 ? EV::dfinfo_t::NOPORT : EV::dfinfo_t::NOP),
-               e_.mic     = Base::lsbf4(&LMIC.frame[flen-4]),
-               e_.hdr     = LMIC.frame[LORA::OFF_DAT_HDR],
-               e_.fct     = LMIC.frame[LORA::OFF_DAT_FCT],
-               e_.port    = LMIC.pendTxPort,
-               e_.plen    = txdata ? dlen : 0,
-               e_.olen    = end-LORA::OFF_DAT_OPTS,
-               memcpy(e_.opts, LMIC.frame+LORA::OFF_DAT_OPTS, end-LORA::OFF_DAT_OPTS)));
+                       e_.devaddr = LMIC.devaddr,
+                       e_.seqno   = LMIC.seqnoUp-1,
+                       e_.flags   = (LMIC.pendTxPort < 0 ? EV::dfinfo_t::NOPORT : EV::dfinfo_t::NOP),
+                       e_.mic     = Base::lsbf4(&LMIC.frame[flen-4]),
+                       e_.hdr     = LMIC.frame[LORA::OFF_DAT_HDR],
+                       e_.fct     = LMIC.frame[LORA::OFF_DAT_FCT],
+                       e_.port    = LMIC.pendTxPort,
+                       e_.plen    = txdata ? dlen : 0,
+                       e_.opts.length = end-LORA::OFF_DAT_OPTS,
+                       memcpy(&e_.opts[0], LMIC.frame+LORA::OFF_DAT_OPTS, end-LORA::OFF_DAT_OPTS)));
     LMIC.dataLen = flen;
 }
 
@@ -1563,17 +1612,17 @@
     os_radio(RADIO_RST);
     os_clearCallback(&LMIC.osjob);
     if( LMIC.dataLen == 0 ) {
-    // Nothing received - timeout
-    LMIC.opmode &= ~(OP_SCAN | OP_TRACK);
-    reportEvent(EV_SCAN_TIMEOUT);
-    return;
+        // Nothing received - timeout
+        LMIC.opmode &= ~(OP_SCAN | OP_TRACK);
+        reportEvent(EV_SCAN_TIMEOUT);
+        return;
     }
     if( decodeBeacon() <= 0 ) {
-    // Something is wrong with the beacon - continue scan
-    LMIC.dataLen = 0;
-    os_radio(RADIO_RXON);
-    os_setTimedCallback(&LMIC.osjob, LMIC.bcninfo.txtime, FUNC_ADDR(onBcnRx));
-    return;
+        // Something is wrong with the beacon - continue scan
+        LMIC.dataLen = 0;
+        os_radio(RADIO_RXON);
+        os_setTimedCallback(&LMIC.osjob, LMIC.bcninfo.txtime, FUNC_ADDR(onBcnRx));
+        return;
     }
     // Found our 1st beacon
     // We don't have a previous beacon to calc some drift - assume
@@ -1593,7 +1642,7 @@
 static void startScan (void) {
     ASSERT(LMIC.devaddr!=0 && (LMIC.opmode & OP_JOINING)==0);
     if( (LMIC.opmode & OP_SHUTDOWN) != 0 )
-    return;
+        return;
     // Cancel onging TX/RX transaction
     LMIC.txCnt = LMIC.dnConf = LMIC.bcninfo.flags = 0;
     LMIC.opmode = (LMIC.opmode | OP_SCAN) & ~(OP_TXRXPEND);
@@ -1606,11 +1655,11 @@
 
 bit_t LMIC_enableTracking (u1_t tryBcnInfo) {
     if( (LMIC.opmode & (OP_SCAN|OP_TRACK|OP_SHUTDOWN)) != 0 )
-    return 0;  // already in progress or failed to enable
+        return 0;  // already in progress or failed to enable
     // If BCN info requested from NWK then app has to take are
     // of sending data up so that MCMD_BCNI_REQ can be attached.
     if( (LMIC.bcninfoTries = tryBcnInfo) == 0 )
-    startScan();
+        startScan();
     return 1;  // enabled
 }
 
@@ -1639,16 +1688,16 @@
     aes_appendMic0(d, OFF_JR_MIC);
 
     EV(joininfo,INFO,(e_.deveui  = MAIN::CDEV->getEui(),
-              e_.arteui  = MAIN::CDEV->getArtEui(),
-              e_.nonce   = LMIC.devNonce,
-              e_.oldaddr = LMIC.devaddr,
-              e_.mic     = Base::lsbf4(&d[LORA::OFF_JR_MIC]),
-              e_.flags   = ((LMIC.opmode & OP_REJOIN) != 0
-                    ? EV::joininfo_t::REJOIN_REQUEST
-                    : EV::joininfo_t::REQUEST)));
+                      e_.arteui  = MAIN::CDEV->getArtEui(),
+                      e_.nonce   = LMIC.devNonce,
+                      e_.oldaddr = LMIC.devaddr,
+                      e_.mic     = Base::lsbf4(&d[LORA::OFF_JR_MIC]),
+                      e_.reason  = ((LMIC.opmode & OP_REJOIN) != 0
+                                    ? EV::joininfo_t::REJOIN_REQUEST
+                                    : EV::joininfo_t::REQUEST)));
     LMIC.dataLen = LEN_JR;
     LMIC.devNonce++;
-    DO_DEVDB(updateDevNonce, LMIC.devNonce);
+    DO_DEVDB(LMIC.devNonce,devNonce);
 }
 
 static void startJoining (xref2osjob_t osjob) {
@@ -1658,19 +1707,21 @@
 // Start join procedure if not already joined.
 bit_t LMIC_startJoining (void) {
     if( LMIC.devaddr == 0 ) {
-    // There should be no TX/RX going on
-    ASSERT((LMIC.opmode & (OP_POLL|OP_TXRXPEND)) == 0);
-    // Cancel scanning
-    LMIC.opmode &= ~(OP_SCAN|OP_REJOIN|OP_LINKDEAD|OP_NEXTCHNL);
-    // Setup state
-    LMIC.rejoinCnt = LMIC.txCnt = 0;
-    initJoinLoop();
-    LMIC.opmode |= OP_OTA|OP_JOINING;
-    // reportEvent will call engineUpdate which then starts sending JOIN REQUESTS
-    os_setCallback(&LMIC.osjob, FUNC_ADDR(startJoining));
-    return 1;
+        // There should be no TX/RX going on
+        ASSERT((LMIC.opmode & (OP_POLL|OP_TXRXPEND)) == 0);
+        // Lift any previous duty limitation
+        LMIC.globalDutyRate = 0;
+        // Cancel scanning
+        LMIC.opmode &= ~(OP_SCAN|OP_REJOIN|OP_LINKDEAD|OP_NEXTCHNL);
+        // Setup state
+        LMIC.rejoinCnt = LMIC.txCnt = LMIC.pendTxConf = 0;
+        initJoinLoop();
+        LMIC.opmode |= OP_JOINING;
+        // reportEvent will call engineUpdate which then starts sending JOIN REQUESTS
+        os_setCallback(&LMIC.osjob, FUNC_ADDR(startJoining));
+        return 1;
     }
-    return 0;
+    return 0; // already joined
 }
 
 
@@ -1682,11 +1733,11 @@
 
 static void processPingRx (xref2osjob_t osjob) {
     if( LMIC.dataLen != 0 ) {
-    LMIC.txrxFlags = TXRX_PING;
-    if( decodeFrame() ) {
-        reportEvent(EV_RXCOMPLETE);
-        return;
-    }
+        LMIC.txrxFlags = TXRX_PING;
+        if( decodeFrame() ) {
+            reportEvent(EV_RXCOMPLETE);
+            return;
+        }
     }
     // Pick next ping slot
     engineUpdate();
@@ -1698,66 +1749,61 @@
 
     if( LMIC.dataLen == 0 ) {
       norx:
-    if( LMIC.txCnt != 0 ) {
-        if( LMIC.txCnt <= TXCONF_ATTEMPTS ) {
-        // Schedule another retransmission
-        txDelay(LMIC.rxtime, RETRY_PERIOD_secs);
-        LMIC.opmode &= ~OP_TXRXPEND;
-        engineUpdate();
-        return 1;
+        if( LMIC.txCnt != 0 ) {
+            if( LMIC.txCnt < TXCONF_ATTEMPTS ) {
+                LMIC.txCnt += 1;
+                setDrTxpow(DRCHG_NOACK, lowerDR(LMIC.datarate, DRADJUST[LMIC.txCnt]), KEEP_TXPOW);
+                // Schedule another retransmission
+                txDelay(LMIC.rxtime, RETRY_PERIOD_secs);
+                LMIC.opmode &= ~OP_TXRXPEND;
+                engineUpdate();
+                return 1;
+            }
+            LMIC.txrxFlags = TXRX_NACK | TXRX_NOPORT;
+        } else {
+            // Nothing received - implies no port
+            LMIC.txrxFlags = TXRX_NOPORT;
         }
-        LMIC.txrxFlags = TXRX_NACK | TXRX_NOPORT;
-    } else {
-        // Nothing received - implies no port
-        LMIC.txrxFlags = TXRX_NOPORT;
-    }
-    LMIC.adrAckReq += 1;
-    LMIC.dataBeg = LMIC.dataLen = 0;
+        if( LMIC.adrAckReq != LINK_CHECK_OFF )
+            LMIC.adrAckReq += 1;
+        LMIC.dataBeg = LMIC.dataLen = 0;
       txcomplete:
-    LMIC.opmode &= ~(OP_TXDATA|OP_TXRXPEND);
-    if( (LMIC.txrxFlags & (TXRX_DNW1|TXRX_DNW2|TXRX_PING)) != 0  &&  (LMIC.opmode & OP_LINKDEAD) != 0 ) {
-        LMIC.opmode &= ~OP_LINKDEAD;
-        reportEvent(EV_LINK_ALIVE);
-    }
-    reportEvent(EV_TXCOMPLETE);
-    // If we haven't heard from NWK in a while although we asked for a sign
-    // assume link is dead - notify application and keep going
-    if( LMIC.adrAckReq > LINK_CHECK_DEAD ) {
-        // We haven't heard from NWK for some time although
-        // We asked for a response for some time - assume we're disconnected. Lower DR one notch.
-        EV(devCond, ERR, (e_.reason = EV::devCond_t::LINK_DEAD,
-                  e_.eui    = MAIN::CDEV->getEui(),
-                  e_.info   = LMIC.adrAckReq));
-        setDrTxpow(DRCHG_NOADRACK, decDR((dr_t)LMIC.datarate), KEEP_TXPOW);
-        LMIC.adrAckReq = LINK_CHECK_CONT;
-        if( (LMIC.opmode & OP_OTA) != 0 ) {
+        LMIC.opmode &= ~(OP_TXDATA|OP_TXRXPEND);
+        if( (LMIC.txrxFlags & (TXRX_DNW1|TXRX_DNW2|TXRX_PING)) != 0  &&  (LMIC.opmode & OP_LINKDEAD) != 0 ) {
+            LMIC.opmode &= ~OP_LINKDEAD;
+            reportEvent(EV_LINK_ALIVE);
+        }
+        reportEvent(EV_TXCOMPLETE);
+        // If we haven't heard from NWK in a while although we asked for a sign
+        // assume link is dead - notify application and keep going
+        if( LMIC.adrAckReq > LINK_CHECK_DEAD ) {
+            // We haven't heard from NWK for some time although we
+            // asked for a response for some time - assume we're disconnected. Lower DR one notch.
+            EV(devCond, ERR, (e_.reason = EV::devCond_t::LINK_DEAD,
+                              e_.eui    = MAIN::CDEV->getEui(),
+                              e_.info   = LMIC.adrAckReq));
+            setDrTxpow(DRCHG_NOADRACK, decDR((dr_t)LMIC.datarate), KEEP_TXPOW);
+            LMIC.adrAckReq = LINK_CHECK_CONT;
             LMIC.opmode |= OP_REJOIN|OP_LINKDEAD;
-        }
-        else {
-            LMIC.opmode |= OP_LINKDEAD;
+            reportEvent(EV_LINK_DEAD);
         }
-        reportEvent(EV_LINK_DEAD);
-    }
-    // If this falls to zero the NWK did not answer our MCMD_BCNI_REQ commands - try full scan
-    if( LMIC.bcninfoTries > 0 ) {
-        if( (LMIC.opmode & OP_TRACK) != 0 ) {
-        reportEvent(EV_BEACON_FOUND);
-        LMIC.bcninfoTries = 0;
+        // If this falls to zero the NWK did not answer our MCMD_BCNI_REQ commands - try full scan
+        if( LMIC.bcninfoTries > 0 ) {
+            if( (LMIC.opmode & OP_TRACK) != 0 ) {
+                reportEvent(EV_BEACON_FOUND);
+                LMIC.bcninfoTries = 0;
+            }
+            else if( --LMIC.bcninfoTries == 0 ) {
+                startScan();   // NWK did not answer - try scan
+            }
         }
-        else if( --LMIC.bcninfoTries == 0 ) {
-        startScan();   // NWK did not answer - try scan
-        }
-    }
-    return 1;
+        return 1;
     }
     if( !decodeFrame() ) {
-    if( (LMIC.txrxFlags & TXRX_DNW1) != 0 )
-        return 0;
-    goto norx;
+        if( (LMIC.txrxFlags & TXRX_DNW1) != 0 )
+            return 0;
+        goto norx;
     }
-    // If we received a frame reset counter
-    LMIC.adrAckReq = LINK_CHECK_INIT;
-    LMIC.rejoinCnt = 0;
     goto txcomplete;
 }
 
@@ -1768,55 +1814,57 @@
     ev_t ev;
 
     if( LMIC.dataLen != 0 && decodeBeacon() >= 1 ) {
-    ev = EV_BEACON_TRACKED;
-    if( (flags & (BCN_PARTIAL|BCN_FULL)) == 0 ) {
-        // We don't have a previous beacon to calc some drift - assume
-        // an max error of 13ms = 128sec*100ppm which is roughly +/-100ppm
-        calcBcnRxWindowFromMillis(13,0);
-        goto rev;
-    }
-    // We have a previous BEACON to calculate some drift
-    s2_t drift = BCN_INTV_osticks - (LMIC.bcninfo.txtime - lasttx);
-    if( LMIC.missedBcns > 0 ) {
-        drift = LMIC.drift + (drift - LMIC.drift) / (LMIC.missedBcns+1);
-    }
-    if( (LMIC.bcninfo.flags & BCN_NODRIFT) == 0 ) {
-        s2_t diff = LMIC.drift - drift;
-        if( diff < 0 ) diff = -diff;
-        LMIC.lastDriftDiff = diff;
-        if( LMIC.maxDriftDiff < diff )
-        LMIC.maxDriftDiff = diff;
-        LMIC.bcninfo.flags &= ~BCN_NODDIFF;
-    }
-    LMIC.drift = drift;
-    LMIC.missedBcns = LMIC.rejoinCnt = 0;
-    LMIC.bcninfo.flags &= ~BCN_NODRIFT;
-    EV(devCond,INFO,(e_.reason = EV::devCond_t::CLOCK_DRIFT,
-             e_.info   = drift,
-             e_.info2  = /*occasion BEACON*/0));
-    ASSERT((LMIC.bcninfo.flags & (BCN_PARTIAL|BCN_FULL)) != 0);
+        ev = EV_BEACON_TRACKED;
+        if( (flags & (BCN_PARTIAL|BCN_FULL)) == 0 ) {
+            // We don't have a previous beacon to calc some drift - assume
+            // an max error of 13ms = 128sec*100ppm which is roughly +/-100ppm
+            calcBcnRxWindowFromMillis(13,0);
+            goto rev;
+        }
+        // We have a previous BEACON to calculate some drift
+        s2_t drift = BCN_INTV_osticks - (LMIC.bcninfo.txtime - lasttx);
+        if( LMIC.missedBcns > 0 ) {
+            drift = LMIC.drift + (drift - LMIC.drift) / (LMIC.missedBcns+1);
+        }
+        if( (LMIC.bcninfo.flags & BCN_NODRIFT) == 0 ) {
+            s2_t diff = LMIC.drift - drift;
+            if( diff < 0 ) diff = -diff;
+            LMIC.lastDriftDiff = diff;
+            if( LMIC.maxDriftDiff < diff )
+                LMIC.maxDriftDiff = diff;
+            LMIC.bcninfo.flags &= ~BCN_NODDIFF;
+        }
+        LMIC.drift = drift;
+        LMIC.missedBcns = LMIC.rejoinCnt = 0;
+        LMIC.bcninfo.flags &= ~BCN_NODRIFT;
+        EV(devCond,INFO,(e_.reason = EV::devCond_t::CLOCK_DRIFT,
+                         e_.eui    = MAIN::CDEV->getEui(),
+                         e_.info   = drift,
+                         e_.info2  = /*occasion BEACON*/0));
+        ASSERT((LMIC.bcninfo.flags & (BCN_PARTIAL|BCN_FULL)) != 0);
     } else {
-    ev = EV_BEACON_MISSED;
-    LMIC.bcninfo.txtime += BCN_INTV_osticks - LMIC.drift;
-    LMIC.bcninfo.time   += BCN_INTV_sec;
-    LMIC.missedBcns++;
-    // Delay any possible TX after surmised beacon - it's there although we missed it
-    txDelay(LMIC.bcninfo.txtime + BCN_RESERVE_osticks, 4);
-    if( (LMIC.opmode & OP_OTA) != 0 ) {
-    if( LMIC.missedBcns > MAX_MISSED_BCNS )
-        LMIC.opmode |= OP_REJOIN;  // try if we can roam to another network
-    }
-    if( LMIC.bcnRxsyms > MAX_RXSYMS ) {
-        LMIC.opmode &= ~(OP_TRACK|OP_PINGABLE|OP_PINGINI|OP_REJOIN);
-        reportEvent(EV_LOST_TSYNC);
-        return;
-    }
+        ev = EV_BEACON_MISSED;
+        LMIC.bcninfo.txtime += BCN_INTV_osticks - LMIC.drift;
+        LMIC.bcninfo.time   += BCN_INTV_sec;
+        LMIC.missedBcns++;
+        // Delay any possible TX after surmised beacon - it's there although we missed it
+        txDelay(LMIC.bcninfo.txtime + BCN_RESERVE_osticks, 4);
+        if( LMIC.missedBcns > MAX_MISSED_BCNS )
+            LMIC.opmode |= OP_REJOIN;  // try if we can roam to another network
+        if( LMIC.bcnRxsyms > MAX_RXSYMS ) {
+            LMIC.opmode &= ~(OP_TRACK|OP_PINGABLE|OP_PINGINI|OP_REJOIN);
+            reportEvent(EV_LOST_TSYNC);
+            return;
+        }
     }
     LMIC.bcnRxtime = LMIC.bcninfo.txtime + BCN_INTV_osticks - calcRxWindow(0,DR_BCN);
     LMIC.bcnRxsyms = LMIC.rxsyms;    
   rev:
+#if CFG_us915
+    LMIC.bcnChnl = (LMIC.bcnChnl+1) & 7;
+#endif
     if( (LMIC.opmode & OP_PINGINI) != 0 )
-    rxschedInit(&LMIC.ping);  // note: reuses LMIC.frame buffer!
+        rxschedInit(&LMIC.ping);  // note: reuses LMIC.frame buffer!
     reportEvent(ev);
 }
 
@@ -1837,11 +1885,11 @@
 static void engineUpdate (void) {
     // Check for ongoing state: scan or TX/RX transaction
     if( (LMIC.opmode & (OP_SCAN|OP_TXRXPEND|OP_SHUTDOWN)) != 0 ) 
-    return;
+        return;
 
     if( LMIC.devaddr == 0 && (LMIC.opmode & OP_JOINING) == 0 ) {
-    LMIC_startJoining();
-    return;
+        LMIC_startJoining();
+        return;
     }
 
     ostime_t now    = os_getTime();
@@ -1849,123 +1897,131 @@
     ostime_t txbeg  = 0;
 
     if( (LMIC.opmode & OP_TRACK) != 0 ) {
-    // We are tracking a beacon
-    ASSERT( now + RX_RAMPUP - LMIC.bcnRxtime <= 0 );
-    rxtime = LMIC.bcnRxtime - RX_RAMPUP;
+        // We are tracking a beacon
+        ASSERT( now + RX_RAMPUP - LMIC.bcnRxtime <= 0 );
+        rxtime = LMIC.bcnRxtime - RX_RAMPUP;
     }
 
     if( (LMIC.opmode & (OP_JOINING|OP_REJOIN|OP_TXDATA|OP_POLL)) != 0 ) {
-    // Need to TX some data...
-    // Assuming txChnl points to channel which first becomes available again.
-    bit_t jacc = ((LMIC.opmode & (OP_JOINING|OP_REJOIN)) != 0 ? 1 : 0);
-    // Find next suitable channel and return availability time
-    if( (LMIC.opmode & OP_NEXTCHNL) != 0 ) {
-        txbeg = LMIC.txend = nextTx(now);
-        LMIC.opmode &= ~OP_NEXTCHNL;
-    } else {
-        txbeg = LMIC.txend;
-    }
-    // Delayed TX or waiting for duty cycle?
-    if( (LMIC.globalDutyRate != 0 || (LMIC.opmode & OP_RNDTX) != 0)  &&  (txbeg - LMIC.globalDutyAvail) < 0 )
-        txbeg = LMIC.globalDutyAvail;
-    // If we're tracking a beacon...
-    // then make sure TX-RX transaction is complete before beacon
-    if( (LMIC.opmode & OP_TRACK) != 0 &&
-        txbeg + (jacc ? JOIN_GUARD_osticks : TXRX_GUARD_osticks) - rxtime > 0 ) {
-        // Not enough time to complete TX-RX before beacon - postpone after beacon.
-        // In order to avoid clustering of postponed TX right after beacon randomize start!
-        txDelay(rxtime + BCN_RESERVE_osticks, 16);
-        txbeg = 0;
-        goto checkrx;
-    }
-    // Earliest possible time vs overhead to setup radio
-    if( txbeg - (now + TX_RAMPUP) < 0 ) {
-        // We could send right now!
-        dr_t txdr = (dr_t)LMIC.datarate;
-        if( jacc ) {
-        u1_t ftype;
-        if( (LMIC.opmode & OP_REJOIN) != 0 ) {
-            txdr = lowerDR(txdr, LMIC.rejoinCnt);
-            ftype = HDR_FTYPE_REJOIN;
+        // Need to TX some data...
+        // Assuming txChnl points to channel which first becomes available again.
+        bit_t jacc = ((LMIC.opmode & (OP_JOINING|OP_REJOIN)) != 0 ? 1 : 0);
+        // Find next suitable channel and return availability time
+        if( (LMIC.opmode & OP_NEXTCHNL) != 0 ) {
+            txbeg = LMIC.txend = nextTx(now);
+            LMIC.opmode &= ~OP_NEXTCHNL;
         } else {
-            ftype = HDR_FTYPE_JREQ;
+            txbeg = LMIC.txend;
+        }
+        // Delayed TX or waiting for duty cycle?
+        if( (LMIC.globalDutyRate != 0 || (LMIC.opmode & OP_RNDTX) != 0)  &&  (txbeg - LMIC.globalDutyAvail) < 0 )
+            txbeg = LMIC.globalDutyAvail;
+        // If we're tracking a beacon...
+        // then make sure TX-RX transaction is complete before beacon
+        if( (LMIC.opmode & OP_TRACK) != 0 &&
+            txbeg + (jacc ? JOIN_GUARD_osticks : TXRX_GUARD_osticks) - rxtime > 0 ) {
+            // Not enough time to complete TX-RX before beacon - postpone after beacon.
+            // In order to avoid clustering of postponed TX right after beacon randomize start!
+            txDelay(rxtime + BCN_RESERVE_osticks, 16);
+            txbeg = 0;
+            goto checkrx;
         }
-        buildJoinRequest(ftype);
-        LMIC.osjob.func = FUNC_ADDR(jreqDone);
-        } else {
-        if( LMIC.seqnoUp == 0xFFFFFFFF ) {
-            // Roll over of up seq counter
-            EV(specCond, ERR, (e_.reason = EV::specCond_t::UPSEQNO_ROLL_OVER,
-                       e_.eui    = MAIN::CDEV->getEui()));
-            // Rerun join procedure - start with current datarate
-            LMIC.devaddr = 0;
-            LMIC_startJoining();
-            reportEvent(EV_RESET);
+        // Earliest possible time vs overhead to setup radio
+        if( txbeg - (now + TX_RAMPUP) < 0 ) {
+            // We could send right now!
+            dr_t txdr = (dr_t)LMIC.datarate;
+            if( jacc ) {
+                u1_t ftype;
+                if( (LMIC.opmode & OP_REJOIN) != 0 ) {
+                    txdr = lowerDR(txdr, LMIC.rejoinCnt);
+                    ftype = HDR_FTYPE_REJOIN;
+                } else {
+                    ftype = HDR_FTYPE_JREQ;
+                }
+                buildJoinRequest(ftype);
+                LMIC.osjob.func = FUNC_ADDR(jreqDone);
+            } else {
+                if( LMIC.seqnoDn >= 0xFFFFFF80 ) {
+                    // Imminent roll over - proactively reset MAC
+                    EV(specCond, INFO, (e_.reason = EV::specCond_t::DNSEQNO_ROLL_OVER,
+                                        e_.eui    = MAIN::CDEV->getEui(),
+                                        e_.info   = LMIC.seqnoDn, 
+                                        e_.info2  = 0));
+                    // Device has to react! NWK will not roll over and just stop sending.
+                    // Thus, we have N frames to detect a possible lock up.
+                  reset:
+                    os_setCallback(&LMIC.osjob, FUNC_ADDR(runReset));
+                    return;
+                }
+                if( (LMIC.txCnt==0 && LMIC.seqnoUp == 0xFFFFFFFF) ) {
+                    // Roll over of up seq counter
+                    EV(specCond, ERR, (e_.reason = EV::specCond_t::UPSEQNO_ROLL_OVER,
+                                       e_.eui    = MAIN::CDEV->getEui(),
+                                       e_.info2  = LMIC.seqnoUp));
+                    // Do not run RESET event callback from here!
+                    // App code might do some stuff after send unaware of RESET.
+                    goto reset;
+                }
+                buildDataFrame();
+                LMIC.osjob.func = FUNC_ADDR(updataDone);
+            }
+            LMIC.rps    = setCr(updr2rps(txdr), (cr_t)LMIC.errcr);
+            LMIC.dndr   = txdr;  // carry TX datarate (can be != LMIC.datarate) over to txDone/setupRx1
+            LMIC.opmode = (LMIC.opmode & ~(OP_POLL|OP_RNDTX)) | OP_TXRXPEND | OP_NEXTCHNL;
+            updateTx(txbeg);
+            os_radio(RADIO_TX);
             return;
         }
-        buildDataFrame();
-        // NOTE: a channel decision for LMIC.txChnl above will never be rendered invalid
-        // by chosing a slower datarate (it could be invalided ONLY by a faster datarate).
-        txdr = lowerDR(txdr, DRADJUST[LMIC.txCnt]);
-        LMIC.osjob.func = FUNC_ADDR(updataDone);
-        }
-        LMIC.rps    = setCr(updr2rps(txdr), (cr_t)LMIC.errcr);
-        LMIC.rxsyms = txdr;  // carry TX datarate (can be != LMIC.datarate) over to txDone/setupRx1
-        LMIC.opmode = (LMIC.opmode & ~(OP_POLL|OP_RNDTX)) | OP_TXRXPEND | OP_NEXTCHNL;
-        updateTx(txbeg);
-        os_radio(RADIO_TX);
-        return;
-    }
-    // Cannot yet TX
-    if( (LMIC.opmode & OP_TRACK) == 0 )
-        goto txdelay; // We don't track the beacon - nothing else to do - so wait for the time to TX
-    // Consider RX tasks
-    if( txbeg == 0 ) // zero indicates no TX pending
-        txbeg += 1;  // TX delayed by one tick (insignificant amount of time)
+        // Cannot yet TX
+        if( (LMIC.opmode & OP_TRACK) == 0 )
+            goto txdelay; // We don't track the beacon - nothing else to do - so wait for the time to TX
+        // Consider RX tasks
+        if( txbeg == 0 ) // zero indicates no TX pending
+            txbeg += 1;  // TX delayed by one tick (insignificant amount of time)
     } else {
-    // No TX pending - no scheduled RX
-    if( (LMIC.opmode & OP_TRACK) == 0 )
-        return;
+        // No TX pending - no scheduled RX
+        if( (LMIC.opmode & OP_TRACK) == 0 )
+            return;
     }
 
     // Are we pingable?
   checkrx:
     if( (LMIC.opmode & OP_PINGINI) != 0 ) {
-    // One more RX slot in this beacon period?
-    if( rxschedNext(&LMIC.ping, now+RX_RAMPUP) ) {
-        if( txbeg != 0  &&  (txbeg - LMIC.ping.rxtime) < 0 )
-        goto txdelay;
-        LMIC.rxsyms  = LMIC.ping.rxsyms;
-        LMIC.rxtime  = LMIC.ping.rxtime;
-        LMIC.freq    = LMIC.ping.freq;
-        LMIC.rps     = dndr2rps(LMIC.ping.dr);
-        LMIC.dataLen = 0;
-        ASSERT(LMIC.rxtime - now+RX_RAMPUP >= 0 );
-        os_setTimedCallback(&LMIC.osjob, LMIC.rxtime - RX_RAMPUP, FUNC_ADDR(startRxPing));
-        return;
-    }
-    // no - just wait for the beacon
+        // One more RX slot in this beacon period?
+        if( rxschedNext(&LMIC.ping, now+RX_RAMPUP) ) {
+            if( txbeg != 0  &&  (txbeg - LMIC.ping.rxtime) < 0 )
+                goto txdelay;
+            LMIC.rxsyms  = LMIC.ping.rxsyms;
+            LMIC.rxtime  = LMIC.ping.rxtime;
+            LMIC.freq    = LMIC.ping.freq;
+            LMIC.rps     = dndr2rps(LMIC.ping.dr);
+            LMIC.dataLen = 0;
+            ASSERT(LMIC.rxtime - now+RX_RAMPUP >= 0 );
+            os_setTimedCallback(&LMIC.osjob, LMIC.rxtime - RX_RAMPUP, FUNC_ADDR(startRxPing));
+            return;
+        }
+        // no - just wait for the beacon
     }
 
     if( txbeg != 0  &&  (txbeg - rxtime) < 0 )
-    goto txdelay;
+        goto txdelay;
 
     setBcnRxParams();
     LMIC.rxsyms = LMIC.bcnRxsyms;
     LMIC.rxtime = LMIC.bcnRxtime;
     if( now - rxtime >= 0 ) {
-    LMIC.osjob.func = FUNC_ADDR(processBeacon);
-    os_radio(RADIO_RX);
-    return;
+        LMIC.osjob.func = FUNC_ADDR(processBeacon);
+        os_radio(RADIO_RX);
+        return;
     }
     os_setTimedCallback(&LMIC.osjob, rxtime, FUNC_ADDR(startRxBcn));
     return;
 
   txdelay:
     EV(devCond, INFO, (e_.reason = EV::devCond_t::TX_DELAY,
-               e_.eui    = MAIN::CDEV->getEui(),
-               e_.info   = osticks2ms(txbeg-now),
-               e_.info2  = LMIC.seqnoUp-1));
+                       e_.eui    = MAIN::CDEV->getEui(),
+                       e_.info   = osticks2ms(txbeg-now),
+                       e_.info2  = LMIC.seqnoUp-1));
     os_setTimedCallback(&LMIC.osjob, txbeg-TX_RAMPUP, FUNC_ADDR(runEngineUpdate));
 }
 
@@ -1990,22 +2046,32 @@
 
 void LMIC_reset (void) {
     EV(devCond, INFO, (e_.reason = EV::devCond_t::LMIC_EV,
-               e_.eui    = MAIN::CDEV->getEui(),
-               e_.info   = EV_RESET));
+                       e_.eui    = MAIN::CDEV->getEui(),
+                       e_.info   = EV_RESET));
     os_radio(RADIO_RST);
     os_clearCallback(&LMIC.osjob);
 
     os_clearMem((xref2u1_t)&LMIC,SIZEOFEXPR(LMIC));
-    LMIC.devaddr     = 0;
-    LMIC.devNonce    = os_getRndU2();
-    LMIC.opmode      = OP_NONE;
-    LMIC.errcr       = CR_4_5;
-    LMIC.adrEnabled  = FCT_ADREN;
-    LMIC.dn2Dr       = DR_DNW2;   // we need this for 2ns DN window of join accept
-    LMIC.dn2Freq     = FREQ_DNW2; // ditto
-#if CFG_us915
+    LMIC.devaddr      =  0;
+    LMIC.devNonce     =  os_getRndU2();
+    LMIC.opmode       =  OP_NONE;
+    LMIC.errcr        =  CR_4_5;
+    LMIC.adrEnabled   =  FCT_ADREN;
+    LMIC.dn2Dr        =  DR_DNW2;   // we need this for 2nd DN window of join accept
+    LMIC.dn2Freq      =  FREQ_DNW2; // ditto
+    LMIC.ping.freq    =  FREQ_PING; // defaults for ping
+    LMIC.ping.dr      =  DR_PING;   // ditto
+    LMIC.ping.intvExp =  0xFF;
+#if defined(CFG_us915)
     initDefaultChannels();
 #endif
+    DO_DEVDB(LMIC.devaddr,      devaddr);
+    DO_DEVDB(LMIC.devNonce,     devNonce);
+    DO_DEVDB(LMIC.dn2Dr,        dn2Dr);
+    DO_DEVDB(LMIC.dn2Freq,      dn2Freq);
+    DO_DEVDB(LMIC.ping.freq,    pingFreq);
+    DO_DEVDB(LMIC.ping.dr,      pingDr);
+    DO_DEVDB(LMIC.ping.intvExp, pingIntvExp);
 }
 
 
@@ -2018,7 +2084,7 @@
     LMIC.opmode &= ~(OP_TXDATA|OP_TXRXPEND|OP_POLL);
     LMIC.pendTxLen = 0;
     if( (LMIC.opmode & (OP_JOINING|OP_SCAN)) != 0 ) // do not interfere with JOINING
-    return;
+        return;
     os_clearCallback(&LMIC.osjob);
     os_radio(RADIO_RST);
     engineUpdate();
@@ -2028,7 +2094,7 @@
 void LMIC_setTxData (void) {
     LMIC.opmode |= OP_TXDATA;
     if( (LMIC.opmode & OP_JOINING) == 0 )
-    LMIC.txCnt = 0;             // cancel any ongoing TX/RX retries
+        LMIC.txCnt = 0;             // cancel any ongoing TX/RX retries
     engineUpdate();
 }
 
@@ -2036,9 +2102,9 @@
 //
 int LMIC_setTxData2 (u1_t port, xref2u1_t data, u1_t dlen, u1_t confirmed) {
     if( dlen > SIZEOFEXPR(LMIC.pendTxData) )
-    return -2;
+        return -2;
     if( data != (xref2u1_t)0 )
-    os_copyMem(LMIC.pendTxData, data, dlen);
+        os_copyMem(LMIC.pendTxData, data, dlen);
     LMIC.pendTxConf = confirmed;
     LMIC.pendTxPort = port;
     LMIC.pendTxLen  = dlen;
@@ -2060,19 +2126,48 @@
     engineUpdate();
 }
 
-void LMIC_startABP(u4_t netid, devaddr_t devaddr, u1_t* nwkKey, u1_t* artKey)
-{
+//! \brief Setup given session keys
+//! and put the MAC in a state as if 
+//! a join request/accept would have negotiated just these keys.
+//! It is crucial that the combinations `devaddr/nwkkey` and `devaddr/artkey`
+//! are unique within the network identified by `netid`.
+//! NOTE: on Harvard architectures when session keys are in flash:
+//!  Caller has to fill in LMIC.{nwk,art}Key  before and pass {nwk,art}Key are NULL
+//! \param netid a 24 bit number describing the network id this device is using
+//! \param devaddr the 32 bit session address of the device. It is strongly recommended
+//!    to ensure that different devices use different numbers with high probability.
+//! \param nwkKey  the 16 byte network session key used for message integrity.
+//!     If NULL the caller has copied the key into `LMIC.nwkKey` before.
+//! \param artKey  the 16 byte application router session key used for message confidentiality.
+//!     If NULL the caller has copied the key into `LMIC.artKey` before.
+void LMIC_setSession (u4_t netid, devaddr_t devaddr, xref2u1_t nwkKey, xref2u1_t artKey) {
     LMIC.netid = netid;
     LMIC.devaddr = devaddr;
-    memcpy(LMIC.nwkKey, nwkKey, 16);
-    memcpy(LMIC.artKey, artKey, 16);
+    if( nwkKey != (xref2u1_t)0 )
+        os_copyMem(LMIC.nwkKey, nwkKey, 16);
+    if( artKey != (xref2u1_t)0 )
+        os_copyMem(LMIC.artKey, artKey, 16);
     
-#if CFG_eu868
+#if defined(CFG_eu868)
     initDefaultChannels(0);
 #endif
-
-    LMIC.opmode &= ~(OP_JOINING|OP_TRACK|OP_REJOIN|OP_TXRXPEND|OP_PINGINI) | OP_NEXTCHNL;
+ 
+    LMIC.opmode &= ~(OP_JOINING|OP_TRACK|OP_REJOIN|OP_TXRXPEND|OP_PINGINI);
+    LMIC.opmode |= OP_NEXTCHNL;
     stateJustJoined();
-    //engineUpdate();
-    reportEvent(EV_JOINED);
 }
+
+// Enable/disable link check validation.
+// LMIC sets the ADRACKREQ bit in UP frames if there were no DN frames
+// for a while. It expects the network to provide a DN message to prove
+// connectivity with a span of UP frames. If this no such prove is coming
+// then the datarate is lowered and a LINK_DEAD event is generated.
+// This mode can be disabled and no connectivity prove (ADRACKREQ) is requested
+// nor is the datarate changed.
+// This must be called only if a session is established (e.g. after EV_JOINED)
+void LMIC_setLinkCheckMode (bit_t enabled) {
+    LMIC.adrChanged = 0;
+    LMIC.adrAckReq = enabled ? LINK_CHECK_INIT : LINK_CHECK_OFF;
+}
+
+ 
--- a/lmic.h	Thu Jan 22 12:50:49 2015 +0000
+++ b/lmic.h	Tue Mar 31 13:36:56 2015 +0000
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2014 IBM Corporation.
+ * Copyright (c) 2014-2015 IBM Corporation.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -9,32 +9,39 @@
  *    IBM Zurich Research Lab - initial API, implementation and documentation
  *******************************************************************************/
 
-#define NEW_CHNL 1
+//! @file
+//! @brief LMIC API
 
 #ifndef _lmic_h_
 #define _lmic_h_
 
-#define CFG_DEBUG    1
-#define CFG_eu868    1
-//#define CFG_us915    0
+// MBED compiler options
+#define CFG_eu868                                   1
+//#define CFG_us915                                   0
 
-#define USE_SMTC_RADIO_DRIVER 1
+#define USE_SMTC_RADIO_DRIVER                       1
 
-//#define CFG_sx1272_radio    0
-#define CFG_sx1276_radio    1
+//#define CFG_sx1272_radio                            0
+#define CFG_sx1276_radio                            1
+// End MBED compiler options
 
-#include "mbed.h"
 #include "oslmic.h"
 #include "lorabase.h"
 
-enum { MAX_FRAME_LEN      =  64 };   // Library cap on max frame length
-enum { TXCONF_ATTEMPTS    =   8 };   // transmit attempts for confirmed frames
+// LMIC version
+#define LMIC_VERSION_MAJOR 1
+#define LMIC_VERSION_MINOR 4
+#define LMIC_VERSION_BUILD 1426605786
+
+enum { MAX_FRAME_LEN      =  64 };   //!< Library cap on max frame length
+enum { TXCONF_ATTEMPTS    =   8 };   //!< Transmit attempts for confirmed frames
 enum { MAX_MISSED_BCNS    =  20 };   // threshold for triggering rejoin requests
 enum { MAX_RXSYMS         = 100 };   // stop tracking beacon beyond this
 
-enum { LINK_CHECK_CONT    =   6 };   // continue with this after reported dead link
-enum { LINK_CHECK_DEAD    =  12 };   // after this UP frames and no response from NWK assume link is dead
-enum { LINK_CHECK_INIT    = -12 };   // UP frame count until we inc datarate
+enum { LINK_CHECK_CONT    =   6 ,    // continue with this after reported dead link
+       LINK_CHECK_DEAD    =  12 ,    // after this UP frames and no response from NWK assume link is dead
+       LINK_CHECK_INIT    = -12 ,    // UP frame count until we inc datarate
+       LINK_CHECK_OFF     =-128 };   // link check disabled
 
 enum { TIME_RESYNC        = 6*128 }; // secs
 enum { TXRX_GUARD_ms      =  6000 };  // msecs - don't start TX-RX transaction before beacon
@@ -42,20 +49,22 @@
 enum { TXRX_BCNEXT_secs   =     2 };  // secs - earliest start after beacon time
 enum { RETRY_PERIOD_secs  =     3 };  // secs - random period for retrying a confirmed send
 
-#if CFG_eu868 // EU868 spectrum ====================================================
+#if defined(CFG_eu868) // EU868 spectrum ====================================================
 
-enum { MAX_CHANNELS = 8 };      // library may not support all 16 channels
-enum { MAX_BANDS    = 4 };
+enum { MAX_CHANNELS = 16 };      //!< Max supported channels
+enum { MAX_BANDS    =  4 };
 
 enum { LIMIT_CHANNELS = (1<<4) };   // EU868 will never have more channels
+//! \internal
 struct band_t {
-    u2_t     txcap;  // duty cycle limitation: 1/txcap
-    s1_t     txpow;  // maximum TX power
-    ostime_t avail;  // channel is blocked until this time
+    u2_t     txcap;     // duty cycle limitation: 1/txcap
+    s1_t     txpow;     // maximum TX power
+    u1_t     lastchnl;  // last used channel
+    ostime_t avail;     // channel is blocked until this time
 };
-TYPEDEF_xref2band_t;
+TYPEDEF_xref2band_t; //!< \internal
 
-#elif CFG_us915  // US915 spectrum =================================================
+#elif defined(CFG_us915)  // US915 spectrum =================================================
 
 enum { MAX_XCHANNELS = 2 };      // extra channels in RAM, channels 0-71 are immutable 
 enum { MAX_TXPOW_125kHz = 30 };
@@ -67,6 +76,7 @@
 enum { KEEP_TXPOW = -128 };
 
 
+//! \internal
 struct rxsched_t {
     u1_t     dr;
     u1_t     intvExp;   // 0..7
@@ -76,30 +86,32 @@
     ostime_t rxtime;    // start of next spot
     u4_t     freq;
 };
-TYPEDEF_xref2rxsched_t;
+TYPEDEF_xref2rxsched_t;  //!< \internal
 
 
-// Information extracted from (last) beacon
-enum { BCN_NONE    = 0x00,
-       BCN_PARTIAL = 0x01,
-       BCN_FULL    = 0x02,
-       BCN_NODRIFT = 0x04,    // no drift value measured
-       BCN_NODDIFF = 0x08 };  // no differential drift measured yet
+//! Parsing and tracking states of beacons.
+enum { BCN_NONE    = 0x00,   //!< No beacon received
+       BCN_PARTIAL = 0x01,   //!< Only first (common) part could be decoded (info,lat,lon invalid/previous)
+       BCN_FULL    = 0x02,   //!< Full beacon decoded
+       BCN_NODRIFT = 0x04,   //!< No drift value measured yet
+       BCN_NODDIFF = 0x08 }; //!< No differential drift measured yet
+//! Information about the last and previous beacons.
 struct bcninfo_t {
-    rxqu_t   rxq;
-    ostime_t txtime;  // time when beacon was sent
-    u1_t     flags;
-    u4_t     time;    // GPS time in seconds
-    // This part is valid only if flags==BCN_FULL
-    u1_t     info;    // info field
-    s4_t     lat;
-    s4_t     lon;
+    ostime_t txtime;  //!< Time when the beacon was sent
+    s1_t     rssi;    //!< Adjusted RSSI value of last received beacon
+    s1_t     snr;     //!< Scaled SNR value of last received beacon
+    u1_t     flags;   //!< Last beacon reception and tracking states. See BCN_* values.
+    u4_t     time;    //!< GPS time in seconds of last beacon (received or surrogate)
+    //
+    u1_t     info;    //!< Info field of last beacon (valid only if BCN_FULL set)
+    s4_t     lat;     //!< Lat field of last beacon (valid only if BCN_FULL set)
+    s4_t     lon;     //!< Lon field of last beacon (valid only if BCN_FULL set)
 };
 
 // purpose of receive window - lmic_t.rxState
 enum { RADIO_RST=0, RADIO_TX=1, RADIO_RX=2, RADIO_RXON=3 };
 // Netid values /  lmic_t.netid
-enum { NETID_NONE=(int)~0U, NETID_MASK=0xFFFFFF };
+enum { NETID_NONE=(int)~0U, NETID_MASK=(int)0xFFFFFF };
 // MAC operation modes (lmic_t.opmode).
 enum { OP_NONE     = 0x0000,
        OP_SCAN     = 0x0001, // radio scan to find a beacon
@@ -115,21 +127,22 @@
        OP_PINGABLE = 0x0400, // we're pingable
        OP_NEXTCHNL = 0x0800, // find a new channel
        OP_LINKDEAD = 0x1000, // link was reported as dead
-       OP_OTA      = 0x2000, // Over the Air Activation in use
+       OP_TESTMODE = 0x2000, // developer test mode
 };
 // TX-RX transaction flags - report back to user
 enum { TXRX_ACK    = 0x80,   // confirmed UP frame was acked
        TXRX_NACK   = 0x40,   // confirmed UP frame was not acked
        TXRX_NOPORT = 0x20,   // set if a frame with a port was RXed, clr if no frame/no port
+       TXRX_PORT   = 0x10,   // set if a frame with a port was RXed, LMIC.frame[LMIC.dataBeg-1] => port
        TXRX_DNW1   = 0x01,   // received in 1st DN slot
        TXRX_DNW2   = 0x02,   // received in 2dn DN slot
        TXRX_PING   = 0x04 }; // received in a scheduled RX slot
 // Event types for event callback
 enum _ev_t { EV_SCAN_TIMEOUT=1, EV_BEACON_FOUND,
-         EV_BEACON_MISSED, EV_BEACON_TRACKED, EV_JOINING,
-         EV_JOINED, EV_RFU1, EV_JOIN_FAILED, EV_REJOIN_FAILED,
-         EV_TXCOMPLETE, EV_LOST_TSYNC, EV_RESET,
-         EV_RXCOMPLETE, EV_LINK_DEAD, EV_LINK_ALIVE };
+             EV_BEACON_MISSED, EV_BEACON_TRACKED, EV_JOINING,
+             EV_JOINED, EV_RFU1, EV_JOIN_FAILED, EV_REJOIN_FAILED,
+             EV_TXCOMPLETE, EV_LOST_TSYNC, EV_RESET,
+             EV_RXCOMPLETE, EV_LINK_DEAD, EV_LINK_ALIVE };
 typedef enum _ev_t ev_t;
 
 
@@ -137,25 +150,27 @@
     // Radio settings TX/RX (also accessed by HAL)
     ostime_t    txend;
     ostime_t    rxtime;
-    rxqu_t      rxq;
     u4_t        freq;
+    s1_t        rssi;
+    s1_t        snr;
     rps_t       rps;
     u1_t        rxsyms;
+    u1_t        dndr;
     s1_t        txpow;     // dBm
 
     osjob_t     osjob;
 
     // Channel scheduling
-#if CFG_eu868
+#if defined(CFG_eu868)
     band_t      bands[MAX_BANDS];
     u4_t        channelFreq[MAX_CHANNELS];
-    u1_t        channelDrs[MAX_CHANNELS];
+    u2_t        channelDrMap[MAX_CHANNELS];
     u2_t        channelMap;
-#elif CFG_us915
-    u4_t        xchFreq[MAX_XCHANNELS];  // extra channel frequencies (if device is behind a repeater)
-    u1_t        xchDrs[MAX_XCHANNELS];   // extra channel datarate ranges  ---XXX: ditto
+#elif defined(CFG_us915)
+    u4_t        xchFreq[MAX_XCHANNELS];    // extra channel frequencies (if device is behind a repeater)
+    u2_t        xchDrMap[MAX_XCHANNELS];   // extra channel datarate ranges  ---XXX: ditto
     u2_t        channelMap[(72+MAX_XCHANNELS+15)/16];  // enabled bits
-    u1_t        chRnd;        // channel randomizer
+    u2_t        chRnd;        // channel randomizer
 #endif
     u1_t        txChnl;          // channel for next TX
     u1_t        globalDutyRate;  // max rate: 1/2^k
@@ -218,8 +233,18 @@
     ostime_t    bcnRxtime;
     bcninfo_t   bcninfo;      // Last received beacon info
 };
-DECLARE_LMIC;
+//! \var struct lmic_t LMIC
+//! The state of LMIC MAC layer is encapsulated in this variable.
+DECLARE_LMIC; //!< \internal
 
+//! Construct a bit map of allowed datarates from drlo to drhi (both included). 
+#define DR_RANGE_MAP(drlo,drhi) (((u2_t)0xFFFF<<(drlo)) & ((u2_t)0xFFFF>>(15-(drhi))))
+#if defined(CFG_eu868)
+enum { BAND_MILLI=0, BAND_CENTI=1, BAND_DECI=2, BAND_AUX=3 };
+bit_t LMIC_setupBand (u1_t bandidx, s1_t txpow, u2_t txcap);
+#endif
+bit_t LMIC_setupChannel (u1_t channel, u4_t freq, u2_t drmap, s1_t band);
+void  LMIC_disableChannel (u1_t channel);
 
 void  LMIC_setDrTxpow   (dr_t dr, s1_t txpow);  // set default/start DR/txpow
 void  LMIC_setAdrMode   (bit_t enabled);        // set ADR mode (if mobile turn off)
@@ -239,6 +264,11 @@
 void  LMIC_stopPingable  (void);
 void  LMIC_setPingable   (u1_t intvExp);
 void  LMIC_tryRejoin     (void);
-void  LMIC_startABP      (u4_t netid, devaddr_t devaddr, u1_t* nwkKey, u1_t* artKey);
+
+void LMIC_setSession (u4_t netid, devaddr_t devaddr, xref2u1_t nwkKey, xref2u1_t artKey);
+void LMIC_setLinkCheckMode (bit_t enabled);
+
+// Special APIs - for development or testing
+// !!!See implementation for caveats!!!
 
 #endif // _lmic_h_
--- a/lorabase.h	Thu Jan 22 12:50:49 2015 +0000
+++ b/lorabase.h	Tue Mar 31 13:36:56 2015 +0000
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2014 IBM Corporation.
+ * Copyright (c) 2014-2015 IBM Corporation.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -54,7 +54,7 @@
 enum { BCN_GUARD_us      = 3000000 };
 enum { BCN_SLOT_SPAN_us  =   30000 };
 
-#if CFG_eu868 // ==============================================
+#if defined(CFG_eu868) // ==============================================
 
 enum _dr_eu868_t { DR_SF12=0, DR_SF11, DR_SF10, DR_SF9, DR_SF8, DR_SF7, DR_SF7B, DR_FSK, DR_NONE };
 enum { DR_DFLTMIN = DR_SF7 };
@@ -84,17 +84,30 @@
 enum { DR_PING           = SF9 };       // default ping DR
 enum { CHNL_DNW2         = 5 };
 enum { FREQ_DNW2         = EU868_F6 };
-enum { DR_DNW2           = DR_SF9 };
+enum { DR_DNW2           = DR_SF9 };    // changed from LoRaWAN specification.
+                                        // Default value is DR_SF12
 enum { CHNL_BCN          = 5 };
 enum { FREQ_BCN          = EU868_F6 };
 enum { DR_BCN            = DR_SF9 };
-enum { AIRTIME_BCN       = 185344 };  // micros
+enum { AIRTIME_BCN       = 144384 };  // micros
 
-#elif CFG_us915  // =========================================
+enum {
+    // Beacon frame format EU SF9
+    OFF_BCN_NETID    = 0,         
+    OFF_BCN_TIME     = 3,
+    OFF_BCN_CRC1     = 7,
+    OFF_BCN_INFO     = 8,
+    OFF_BCN_LAT      = 9,
+    OFF_BCN_LON      = 12,
+    OFF_BCN_CRC2     = 15,
+    LEN_BCN          = 17
+};
+
+#elif defined(CFG_us915)  // =========================================
 
 enum _dr_us915_t { DR_SF10=0, DR_SF9, DR_SF8, DR_SF7, DR_SF8C, DR_NONE,
-           // Devices behind a router:
-           DR_SF12CR=8, DR_SF11CR, DR_SF10CR, DR_SF9CR, DR_SF8CR, DR_SF7CR };
+                   // Devices behind a router:
+                   DR_SF12CR=8, DR_SF11CR, DR_SF10CR, DR_SF9CR, DR_SF8CR, DR_SF7CR };
 enum { DR_DFLTMIN = DR_SF8C };
 enum { DR_PAGE = DR_PAGE_US915 };
 
@@ -109,33 +122,31 @@
 enum { US915_FREQ_MIN = 902000000,
        US915_FREQ_MAX = 928000000 };
 
-enum { CHNL_PING         = 2 };
+enum { CHNL_PING         = 0 }; // used only for default init of state (follows beacon - rotating)
 enum { FREQ_PING         = US915_500kHz_DNFBASE + CHNL_PING*US915_500kHz_DNFSTEP };  // default ping freq
 enum { DR_PING           = DR_SF10CR };       // default ping DR
-enum { CHNL_DNW2         = 1 };
+enum { CHNL_DNW2         = 0 };
 enum { FREQ_DNW2         = US915_500kHz_DNFBASE + CHNL_DNW2*US915_500kHz_DNFSTEP };
-enum { DR_DNW2           = DR_SF10CR };
-enum { CHNL_BCN          = 0 };
-enum { FREQ_BCN          = US915_500kHz_DNFBASE + CHNL_BCN*US915_500kHz_DNFSTEP };
+enum { DR_DNW2           = DR_SF12CR };
+enum { CHNL_BCN          = 0 }; // used only for default init of state (rotating beacon scheme)
 enum { DR_BCN            = DR_SF10CR };
-enum { AIRTIME_BCN       = 82432 };  // micros
+enum { AIRTIME_BCN       = 72192 };  // micros
+
+enum {
+    // Beacon frame format US SF10
+    OFF_BCN_NETID    = 0,         
+    OFF_BCN_TIME     = 3,
+    OFF_BCN_CRC1     = 7,
+    OFF_BCN_INFO     = 9,
+    OFF_BCN_LAT      = 10,
+    OFF_BCN_LON      = 13,
+    OFF_BCN_RFU1     = 16,
+    OFF_BCN_CRC2     = 17,
+    LEN_BCN          = 19
+};
 
 #endif // ===================================================
 
-
-enum {
-    // Beacon frame format
-    OFF_BCN_RFU      = 0,
-    OFF_BCN_NETID    = 4,         
-    OFF_BCN_CMAP     = 7,
-    OFF_BCN_TIME     = 9,
-    OFF_BCN_CRC1     = 13,
-    OFF_BCN_INFO     = 15,
-    OFF_BCN_LAT      = 16,
-    OFF_BCN_LON      = 19,
-    OFF_BCN_CRC2     = 22,
-    LEN_BCN          = 24
-};
 enum {
     // Join Request frame format
     OFF_JR_HDR      = 0,
@@ -152,7 +163,9 @@
     OFF_JA_NETID    = 4,
     OFF_JA_DEVADDR  = 7,
     OFF_JA_RFU      = 11,
-    OFF_CFLIST      = 14,
+    OFF_JA_DLSET    = 11,
+    OFF_JA_RXDLY    = 12,
+    OFF_CFLIST      = 13,
     LEN_JA          = 17,
     LEN_JAEXT       = 17+16
 };
@@ -191,10 +204,14 @@
     FCT_ADREN  = 0x80,
     FCT_ADRARQ = 0x40,
     FCT_ACK    = 0x20,
-    FCT_MORE   = 0x10,
+    FCT_MORE   = 0x10,   // also in DN direction: Class B indicator
     FCT_OPTLEN = 0x0F,
 };
 enum {
+    // In UP direction: signals class B enabled
+    FCT_CLASSB = FCT_MORE
+};
+enum {
     NWKID_MASK = (int)0xFE000000,
     NWKID_BITS = 7
 };
@@ -225,10 +242,13 @@
     MCMD_SNCH_REQ = 0x07, // set new channel    : u1:chidx, u3:freq, u1:DRrange
     // Class B
     MCMD_PING_SET = 0x11, // set ping freq      : u3: freq
-    MCMD_BCNI_ANS = 0x12, // next beacon start  : u2: delay(10ms), u1:channel
+    MCMD_BCNI_ANS = 0x12, // next beacon start  : u2: delay(in TUNIT millis), u1:channel
 };
 
 enum {
+    MCMD_BCNI_TUNIT = 30  // time unit of delay value in millis
+};
+enum {
     MCMD_LADR_ANS_RFU    = 0xF8, // RFU bits
     MCMD_LADR_ANS_POWACK = 0x04, // 0=not supported power level
     MCMD_LADR_ANS_DRACK  = 0x02, // 0=unknown data rate
@@ -272,7 +292,7 @@
     MCMD_LADR_POW_MASK   = 0x0F,
     MCMD_LADR_DR_SHIFT   = 4,
     MCMD_LADR_POW_SHIFT  = 0,
-#if CFG_eu868
+#if defined(CFG_eu868)
     MCMD_LADR_SF12      = DR_SF12<<4,
     MCMD_LADR_SF11      = DR_SF11<<4,
     MCMD_LADR_SF10      = DR_SF10<<4,
@@ -288,7 +308,7 @@
     MCMD_LADR_8dBm      = 3,
     MCMD_LADR_5dBm      = 4,
     MCMD_LADR_2dBm      = 5,
-#elif CFG_us915
+#elif defined(CFG_us915)
     MCMD_LADR_SF10      = DR_SF10<<4,
     MCMD_LADR_SF9       = DR_SF9 <<4,
     MCMD_LADR_SF8       = DR_SF8 <<4,
@@ -318,15 +338,8 @@
 // Device address
 typedef u4_t devaddr_t;
 
-
 // RX quality (device)
 enum { RSSI_OFF=64, SNR_SCALEUP=4 };
-struct rxqu_t {
-    s1_t rssi;    // offset by RSSI_OFF, max physical RSSI range -198..+63
-    s1_t snr;     // scaled by SNR_SCALEUP, max physical SNR range -32..+31.75
-};
-TYPEDEF_xref2rxqu_t;
-
 
 inline sf_t  getSf   (rps_t params)            { return   (sf_t)(params &  0x7); }
 inline rps_t setSf   (rps_t params, sf_t sf)   { return (rps_t)((params & ~0x7) | sf); }
--- a/oslmic.cpp	Thu Jan 22 12:50:49 2015 +0000
+++ b/oslmic.cpp	Tue Mar 31 13:36:56 2015 +0000
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2014 IBM Corporation.
+ * Copyright (c) 2014-2015 IBM Corporation.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -17,14 +17,14 @@
     osjob_t* runnablejobs;
 } OS;
 
-void os_init (void) {
+void os_init () {
     memset(&OS, 0x00, sizeof(OS));
     hal_init();
     radio_init();
     LMIC_init();
 }
 
-ostime_t os_getTime (void) {
+ostime_t os_getTime () {
     return hal_ticks();
 }
 
@@ -72,7 +72,7 @@
     job->next = NULL;
     // insert into schedule
     for(pnext=&OS.scheduledjobs; *pnext; pnext=&((*pnext)->next)) {
-        if(time < (*pnext)->deadline) {
+        if((*pnext)->deadline - time > 0) { // (cmp diff, not abs!)
             // enqueue before next element and stop
             job->next = *pnext;
             break;
@@ -83,15 +83,15 @@
 }
 
 // execute jobs from timer and from run queue
-void os_runloop (void) {
+void os_runloop () {
     while(1) {
         osjob_t* j = NULL;
         hal_disableIRQs();
-	// check for runnable jobs
-	if(OS.runnablejobs) {
+        // check for runnable jobs
+        if(OS.runnablejobs) {
             j = OS.runnablejobs;
             OS.runnablejobs = j->next;
-	} else if(OS.scheduledjobs && hal_checkTimer(OS.scheduledjobs->deadline)) { // check for expired timed jobs
+        } else if(OS.scheduledjobs && hal_checkTimer(OS.scheduledjobs->deadline)) { // check for expired timed jobs
             j = OS.scheduledjobs;
             OS.scheduledjobs = j->next;
         } else { // nothing pending
@@ -99,9 +99,7 @@
         }
         hal_enableIRQs();
         if(j) { // run job callback
-            if(j->func) {
-                j->func(j);
-            }
+            j->func(j);
         }
     }
 }
--- a/oslmic.h	Thu Jan 22 12:50:49 2015 +0000
+++ b/oslmic.h	Tue Mar 31 13:36:56 2015 +0000
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2014 IBM Corporation.
+ * Copyright (c) 2014-2015 IBM Corporation.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -9,6 +9,7 @@
  *    IBM Zurich Research Lab - initial API, implementation and documentation
  *******************************************************************************/
 
+//! \file
 #ifndef _oslmic_h_
 #define _oslmic_h_
 
@@ -33,12 +34,11 @@
 typedef unsigned int       uint;
 typedef const char* str_t;
 
-#define CFG_noassert 1
 #include <string.h>
 #include "hal.h"
 #define EV(a,b,c) /**/
-#define DO_DEVDB(meth,...) /**/
-#if !CFG_noassert
+#define DO_DEVDB(field1,field2) /**/
+#if !defined(CFG_noassert)
 #define ASSERT(cond) if(!(cond)) hal_failed()
 #else
 #define ASSERT(cond) /**/
@@ -49,14 +49,12 @@
 
 typedef     struct osjob_t osjob_t;
 typedef      struct band_t band_t;
-typedef      struct rxqu_t rxqu_t;
 typedef   struct chnldef_t chnldef_t;
 typedef   struct rxsched_t rxsched_t;
 typedef   struct bcninfo_t bcninfo_t;
 typedef        const u1_t* xref2cu1_t;
 typedef              u1_t* xref2u1_t;
 #define TYPEDEF_xref2rps_t     typedef         rps_t* xref2rps_t
-#define TYPEDEF_xref2rxqu_t    typedef        rxqu_t* xref2rxqu_t
 #define TYPEDEF_xref2rxsched_t typedef     rxsched_t* xref2rxsched_t
 #define TYPEDEF_xref2chnldef_t typedef     chnldef_t* xref2chnldef_t
 #define TYPEDEF_xref2band_t    typedef        band_t* xref2band_t
@@ -79,7 +77,7 @@
 #define DEFINE_LMIC  struct lmic_t LMIC
 #define DECLARE_LMIC extern struct lmic_t LMIC
 
-void radio_init(void);
+void radio_init (void);
 void radio_irq_handler (u1_t dio);
 void os_init (void);
 void os_runloop (void);
@@ -126,7 +124,7 @@
 TYPEDEF_xref2osjob_t;
 
 
-#if !HAS_os_calls
+#ifndef HAS_os_calls
 
 #ifndef os_getDevKey
 void os_getDevKey (xref2u1_t buf);
--- a/radio.cpp	Thu Jan 22 12:50:49 2015 +0000
+++ b/radio.cpp	Tue Mar 31 13:36:56 2015 +0000
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2014 IBM Corporation.
+ * Copyright (c) 2014-2015 IBM Corporation.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -14,15 +14,16 @@
  *                              USE_SMTC_RADIO_DRIVER preprocessing directive
  *                              in lmic.h
  *******************************************************************************/
+
 #include "lmic.h"
 
 #if USE_SMTC_RADIO_DRIVER
 #include "sx1276-hal.h"
 
 /*!
- * Syncword for lora networks (nibbles swapped)
+ * Syncword for lora networks
  */
-#define LORA_MAC_SYNCWORD           0x34
+#define LORA_MAC_SYNCWORD                           0x34
 
 /*
  * Callback functions prototypes
@@ -81,7 +82,7 @@
 {
     ostime_t now = os_getTime( );
     // save exact tx time
-    LMIC.txend = now - us2osticks( 43 ); // TXDONE FIXUP
+    LMIC.txend = now - us2osticks( RADIO_WAKEUP_TIME ); // TXDONE FIXUP
     
     // go from stanby to sleep
     Radio.Sleep( );
@@ -93,14 +94,18 @@
 {
     ostime_t now = os_getTime( );
     // save exact rx time
-    LMIC.rxtime = now - LORA_RXDONE_FIXUP[getSf( LMIC.rps )];
+    if( getBw( LMIC.rps ) == BW125 )
+    {
+        now -= LORA_RXDONE_FIXUP[getSf( LMIC.rps )];
+    }
+    LMIC.rxtime = now;
     // read the PDU and inform the MAC that we received something
     LMIC.dataLen = size;
     // now read the FIFO
     memcpy( LMIC.frame, payload, size );
     // read rx quality parameters
-    LMIC.rxq.snr  = snr; // SNR [dB] * 4
-    LMIC.rxq.rssi = rssi; // RSSI [dBm] (-196...+63)
+    LMIC.snr  = snr; // SNR [dB] * 4
+    LMIC.rssi = rssi; // RSSI [dBm] (-196...+63)
 
     // go from stanby to sleep
     Radio.Sleep( );
@@ -113,7 +118,8 @@
     ostime_t now = os_getTime( );
 
     // indicate error
-
+    LMIC.dataLen = 0;
+    
     // go from stanby to sleep
     Radio.Sleep( );
     // run os job (use preset func ptr)
@@ -230,7 +236,7 @@
         else
         { // LoRa modem
             
-            Radio.SetTxConfig( MODEM_LORA, LMIC.txpow, 0, getBw( LMIC.rps ), getSf( LMIC.rps ) + 6, getCr( LMIC.rps ) + 1, 8, getIh( LMIC.rps ) ? true : false, ( getNocrc(LMIC.rps) == 0 ) ? true : false, 0, 0, false, 3e6 );
+            Radio.SetTxConfig( MODEM_LORA, LMIC.txpow, 0, getBw( LMIC.rps ), getSf( LMIC.rps ) + 6, getCr( LMIC.rps ) + 1, 8, getIh( LMIC.rps ) ? true : false, ( getNocrc( LMIC.rps ) == 0 ) ? true : false, 0, 0, false, 3e6 );
         }
 
         //starttx( ); // buf=LMIC.frame, len=LMIC.dataLen
@@ -249,8 +255,14 @@
         }
         else
         { // LoRa modem
-            
-            Radio.SetRxConfig( MODEM_LORA, getBw( LMIC.rps ), getSf( LMIC.rps ) + 6, getCr( LMIC.rps ) + 1, 0, 8, LMIC.rxsyms, getIh( LMIC.rps ) ? true : false, getIh(LMIC.rps), ( getNocrc(LMIC.rps) == 0 ) ? true : false, 0, 0, true, false );
+            if( ( getSf( LMIC.rps ) <= SF9 ) && ( LMIC.rxsyms < 8 ) )
+            {
+                Radio.SetRxConfig( MODEM_LORA, getBw( LMIC.rps ), getSf( LMIC.rps ) + 6, getCr( LMIC.rps ) + 1, 0, 8, LMIC.rxsyms + 3, getIh( LMIC.rps ) ? true : false, getIh( LMIC.rps ), ( getNocrc( LMIC.rps ) == 0 ) ? true : false, 0, 0, true, false );
+            }
+            else
+            {
+                Radio.SetRxConfig( MODEM_LORA, getBw( LMIC.rps ), getSf( LMIC.rps ) + 6, getCr( LMIC.rps ) + 1, 0, 8, LMIC.rxsyms, getIh( LMIC.rps ) ? true : false, getIh( LMIC.rps ), ( getNocrc( LMIC.rps ) == 0 ) ? true : false, 0, 0, true, false );
+            }
         }
 
         // now instruct the radio to receive
@@ -279,10 +291,10 @@
         }
         else
         { // LoRa modem
-            Radio.SetRxConfig( MODEM_LORA, getBw( LMIC.rps ), getSf( LMIC.rps ) + 6, getCr( LMIC.rps ) + 1, 0, 8, LMIC.rxsyms, getIh( LMIC.rps ) ? true : false, getIh(LMIC.rps), ( getNocrc(LMIC.rps) == 0 ) ? true : false, 0, 0, true, true );
+            Radio.SetRxConfig( MODEM_LORA, getBw( LMIC.rps ), getSf( LMIC.rps ) + 6, getCr( LMIC.rps ) + 1, 0, 8, LMIC.rxsyms, getIh( LMIC.rps ) ? true : false, getIh( LMIC.rps ), ( getNocrc( LMIC.rps ) == 0 ) ? true : false, 0, 0, true, true );
         }
 
-        //startrx( RXMODE_SCAN) ; // buf = LMIC.frame
+        //startrx( RXMODE_SCAN ); // buf = LMIC.frame
         Radio.Rx( 0 );
         break;
     }
@@ -448,7 +460,7 @@
 #define SX1276_MC3_AGCAUTO                 0x04
 
 // preamble for lora networks (nibbles swapped)
-#define LORA_MAC_PREAMBLE           0x34
+#define LORA_MAC_PREAMBLE                  0x34
 
 #define RXLORA_RXMODE_RSSI_REG_MODEM_CONFIG1 0x0A
 #ifdef CFG_sx1276_radio
@@ -539,6 +551,12 @@
 #error Missing CFG_sx1272_radio/CFG_sx1276_radio
 #endif
 
+#define RADIO_DBG
+#if defined(RADIO_DBG)
+DigitalOut txStateIo( PB_8 );
+DigitalOut rxStateIo( PB_9 );
+#endif
+
 static void writeReg (u1_t addr, u1_t data ) {
     hal_pin_nss(0);
     hal_spi(addr | 0x80);
@@ -576,7 +594,7 @@
     writeReg(RegOpMode, (readReg(RegOpMode) & ~OPMODE_MASK) | mode);
 }
 
-static void opmodeLora(void) {
+static void opmodeLora() {
     u1_t u = OPMODE_LORA;
 #ifdef CFG_sx1276_radio
     u |= 0x8;   // TBD: sx1276 high freq
@@ -584,7 +602,7 @@
     writeReg(RegOpMode, u);
 }
 
-static void opmodeFSK(void) {
+static void opmodeFSK() {
     u1_t u = 0;
 #ifdef CFG_sx1276_radio
     u |= 0x8;   // TBD: sx1276 high freq
@@ -593,79 +611,79 @@
 }
 
 // configure LoRa modem (cfg1, cfg2)
-static void configLoraModem (void) {
+static void configLoraModem () {
     sf_t sf = getSf(LMIC.rps);
 
 #ifdef CFG_sx1276_radio
-    u1_t mc1 = 0, mc2 = 0, mc3 = 0;
+        u1_t mc1 = 0, mc2 = 0, mc3 = 0;
 
-    switch (getBw(LMIC.rps)) {
-    case BW125: mc1 |= SX1276_MC1_BW_125; break;
-    case BW250: mc1 |= SX1276_MC1_BW_250; break;
-    case BW500: mc1 |= SX1276_MC1_BW_500; break;
-    default:
-        ASSERT(0);
-    }
-    switch( getCr(LMIC.rps) ) {
-    case CR_4_5: mc1 |= SX1276_MC1_CR_4_5; break;
-    case CR_4_6: mc1 |= SX1276_MC1_CR_4_6; break;
-    case CR_4_7: mc1 |= SX1276_MC1_CR_4_7; break;
-    case CR_4_8: mc1 |= SX1276_MC1_CR_4_8; break;
-    default:
-        ASSERT(0);
-    }
+        switch (getBw(LMIC.rps)) {
+        case BW125: mc1 |= SX1276_MC1_BW_125; break;
+        case BW250: mc1 |= SX1276_MC1_BW_250; break;
+        case BW500: mc1 |= SX1276_MC1_BW_500; break;
+        default:
+            ASSERT(0);
+        }
+        switch( getCr(LMIC.rps) ) {
+        case CR_4_5: mc1 |= SX1276_MC1_CR_4_5; break;
+        case CR_4_6: mc1 |= SX1276_MC1_CR_4_6; break;
+        case CR_4_7: mc1 |= SX1276_MC1_CR_4_7; break;
+        case CR_4_8: mc1 |= SX1276_MC1_CR_4_8; break;
+        default:
+            ASSERT(0);
+        }
 
-    if (getIh(LMIC.rps)) {
-        mc1 |= SX1276_MC1_IMPLICIT_HEADER_MODE_ON;
-        writeReg(LORARegPayloadLength, getIh(LMIC.rps)); // required length
-    }
-    // set ModemConfig1
-    writeReg(LORARegModemConfig1, mc1);
+        if (getIh(LMIC.rps)) {
+            mc1 |= SX1276_MC1_IMPLICIT_HEADER_MODE_ON;
+            writeReg(LORARegPayloadLength, getIh(LMIC.rps)); // required length
+        }
+        // set ModemConfig1
+        writeReg(LORARegModemConfig1, mc1);
 
-    mc2 = (SX1272_MC2_SF7 + ((sf-1)<<4));
-    if (getNocrc(LMIC.rps) == 0) {
-        mc2 |= SX1276_MC2_RX_PAYLOAD_CRCON;
-    }
-    writeReg(LORARegModemConfig2, mc2);
-    
-    mc3 = SX1276_MC3_AGCAUTO;
-    if (sf == SF11 || sf == SF12) {
-        mc3 |= SX1276_MC3_LOW_DATA_RATE_OPTIMIZE;
-    }
-    writeReg(LORARegModemConfig3, mc3);
+        mc2 = (SX1272_MC2_SF7 + ((sf-1)<<4));
+        if (getNocrc(LMIC.rps) == 0) {
+            mc2 |= SX1276_MC2_RX_PAYLOAD_CRCON;
+        }
+        writeReg(LORARegModemConfig2, mc2);
+        
+        mc3 = SX1276_MC3_AGCAUTO;
+        if ((sf == SF11 || sf == SF12) && getBw(LMIC.rps) == BW125) {
+            mc3 |= SX1276_MC3_LOW_DATA_RATE_OPTIMIZE;
+        }
+        writeReg(LORARegModemConfig3, mc3);
 #elif CFG_sx1272_radio
-    u1_t mc1 = (getBw(LMIC.rps)<<6);
+        u1_t mc1 = (getBw(LMIC.rps)<<6);
 
-    switch( getCr(LMIC.rps) ) {
-    case CR_4_5: mc1 |= SX1272_MC1_CR_4_5; break;
-    case CR_4_6: mc1 |= SX1272_MC1_CR_4_6; break;
-    case CR_4_7: mc1 |= SX1272_MC1_CR_4_7; break;
-    case CR_4_8: mc1 |= SX1272_MC1_CR_4_8; break;
-    }
-    
-    if (sf == SF11 || sf == SF12) {
-        mc1 |= SX1272_MC1_LOW_DATA_RATE_OPTIMIZE;
-    }
-    
-    if (getNocrc(LMIC.rps) == 0) {
-        mc1 |= SX1272_MC1_RX_PAYLOAD_CRCON;
-    }
-    
-    if (getIh(LMIC.rps)) {
-        mc1 |= SX1272_MC1_IMPLICIT_HEADER_MODE_ON;
-        writeReg(LORARegPayloadLength, getIh(LMIC.rps)); // required length
-    }
-    // set ModemConfig1
-    writeReg(LORARegModemConfig1, mc1);
-    
-    // set ModemConfig2 (sf, AgcAutoOn=1 SymbTimeoutHi=00)
-    writeReg(LORARegModemConfig2, (SX1272_MC2_SF7 + ((sf-1)<<4)) | 0x04);
+        switch( getCr(LMIC.rps) ) {
+        case CR_4_5: mc1 |= SX1272_MC1_CR_4_5; break;
+        case CR_4_6: mc1 |= SX1272_MC1_CR_4_6; break;
+        case CR_4_7: mc1 |= SX1272_MC1_CR_4_7; break;
+        case CR_4_8: mc1 |= SX1272_MC1_CR_4_8; break;
+        }
+        
+        if ((sf == SF11 || sf == SF12) && getBw(LMIC.rps) == BW125) {
+            mc1 |= SX1272_MC1_LOW_DATA_RATE_OPTIMIZE;
+        }
+        
+        if (getNocrc(LMIC.rps) == 0) {
+            mc1 |= SX1272_MC1_RX_PAYLOAD_CRCON;
+        }
+        
+        if (getIh(LMIC.rps)) {
+            mc1 |= SX1272_MC1_IMPLICIT_HEADER_MODE_ON;
+            writeReg(LORARegPayloadLength, getIh(LMIC.rps)); // required length
+        }
+        // set ModemConfig1
+        writeReg(LORARegModemConfig1, mc1);
+        
+        // set ModemConfig2 (sf, AgcAutoOn=1 SymbTimeoutHi=00)
+        writeReg(LORARegModemConfig2, (SX1272_MC2_SF7 + ((sf-1)<<4)) | 0x04);
 #else
 #error Missing CFG_sx1272_radio/CFG_sx1276_radio
 #endif /* CFG_sx1272_radio */
 }
 
-static void configChannel (void) {
+static void configChannel () {
     // set frequency: FQ = (FRF * 32 Mhz) / (2 ^ 19)
     u8_t frf = ((u8_t)LMIC.freq << 19) / 32000000;
     writeReg(RegFrfMsb, (u1_t)(frf>>16));
@@ -675,26 +693,26 @@
 
 
 
-static void configPower (void) {
+static void configPower () {
 #ifdef CFG_sx1276_radio
     // no boost used for now
     s1_t pw = (s1_t)LMIC.txpow;
-    if(pw > 14) {
-    pw = 14;
-    } else if(pw < -1) {
-    pw = -1;
+    if(pw >= 17) {
+        pw = 15;
+    } else if(pw < 2) {
+        pw = 2;
     }
     // check board type for BOOST pin
-    writeReg(RegPaConfig, (u1_t)(0x00|((pw+1)&0xf)));
+    writeReg(RegPaConfig, (u1_t)(0x80|(pw&0xf)));
     writeReg(RegPaDac, readReg(RegPaDac)|0x4);
 
 #elif CFG_sx1272_radio
     // set PA config (2-17 dBm using PA_BOOST)
     s1_t pw = (s1_t)LMIC.txpow;
     if(pw > 17) {
-    pw = 17;
+        pw = 17;
     } else if(pw < 2) {
-    pw = 2;
+        pw = 2;
     }
     writeReg(RegPaConfig, (u1_t)(0x80|(pw-2)));
 #else
@@ -702,12 +720,16 @@
 #endif /* CFG_sx1272_radio */
 }
 
-static void txfsk (void) {
+static void txfsk () {
     // select FSK modem (from sleep mode)
     writeReg(RegOpMode, 0x10); // FSK, BT=0.5
     ASSERT(readReg(RegOpMode) == 0x10);
     // enter standby mode (required for FIFO loading))
     opmode(OPMODE_STANDBY);
+#if defined(RADIO_DBG)
+    txStateIo = 0;
+    rxStateIo = 0;
+#endif
     // set bitrate
     writeReg(FSKRegBitrateMsb, 0x02); // 50kbps
     writeReg(FSKRegBitrateLsb, 0x80);
@@ -743,9 +765,12 @@
     
     // now we actually start the transmission
     opmode(OPMODE_TX);
+#if defined(RADIO_DBG)
+    txStateIo = 1;
+#endif
 }
 
-static void txlora (void) {
+static void txlora () {
     // select LoRa modem (from sleep mode)
     //writeReg(RegOpMode, OPMODE_LORA);
     opmodeLora();
@@ -753,6 +778,10 @@
 
     // enter standby mode (required for FIFO loading))
     opmode(OPMODE_STANDBY);
+#if defined(RADIO_DBG)
+    txStateIo = 0;
+    rxStateIo = 0;
+#endif
     // configure LoRa modem (cfg1, cfg2)
     configLoraModem();
     // configure frequency
@@ -786,7 +815,7 @@
 }
 
 // start transmitter (buf=LMIC.frame, len=LMIC.dataLen)
-static void starttx (void) {
+static void starttx () {
     ASSERT( (readReg(RegOpMode) & OPMODE_MASK) == OPMODE_SLEEP );
     if(getSf(LMIC.rps) == FSK) { // FSK modem
         txfsk();
@@ -812,6 +841,10 @@
     ASSERT((readReg(RegOpMode) & OPMODE_LORA) != 0);
     // enter standby mode (warm up))
     opmode(OPMODE_STANDBY);
+#if defined(RADIO_DBG)
+    txStateIo = 0;
+    rxStateIo = 0;
+#endif
     // don't use MAC settings at startup
     if(rxmode == RXMODE_RSSI) { // use fixed settings for rssi scan
         writeReg(LORARegModemConfig1, RXLORA_RXMODE_RSSI_REG_MODEM_CONFIG1);
@@ -846,9 +879,15 @@
     // now instruct the radio to receive
     if (rxmode == RXMODE_SINGLE) { // single rx
         hal_waitUntil(LMIC.rxtime); // busy wait until exact rx time
-    opmode(OPMODE_RX_SINGLE);
+        opmode(OPMODE_RX_SINGLE);
+#if defined(RADIO_DBG)
+        rxStateIo = 1;
+#endif
     } else { // continous rx (scan or rssi)
-    opmode(OPMODE_RX); 
+        opmode(OPMODE_RX); 
+#if defined(RADIO_DBG)
+        rxStateIo = 1;
+#endif
     }
 }
 
@@ -861,6 +900,10 @@
     ASSERT((readReg(RegOpMode) & OPMODE_LORA) == 0);
     // enter standby mode (warm up))
     opmode(OPMODE_STANDBY);
+#if defined(RADIO_DBG)
+    txStateIo = 0;
+    rxStateIo = 0;
+#endif
     // configure frequency
     configChannel();
     // set LNA gain
@@ -901,6 +944,9 @@
     // now instruct the radio to receive
     hal_waitUntil(LMIC.rxtime); // busy wait until exact rx time
     opmode(OPMODE_RX); // no single rx mode available in FSK
+#if defined(RADIO_DBG)
+    rxStateIo = 1;
+#endif
 }
 
 static void startrx (u1_t rxmode) {
@@ -915,7 +961,7 @@
 }
 
 // get random seed from wideband noise rssi
-void radio_init (void) {
+void radio_init () {
     hal_disableIRQs();
 
     // manually reset radio
@@ -929,6 +975,10 @@
     hal_waitUntil(os_getTime()+ms2osticks(5)); // wait 5ms
 
     opmode(OPMODE_SLEEP);
+#if defined(RADIO_DBG)
+    txStateIo = 0;
+    rxStateIo = 0;
+#endif
     // some sanity checks, e.g., read version number
     u1_t v = readReg(RegVersion);
 #ifdef CFG_sx1276_radio
@@ -950,7 +1000,7 @@
     }
     randbuf[0] = 16; // set initial index
   
-#ifdef CFG_sx1276_radio
+#ifdef CFG_sx1276mb1_board
     // chain calibration
     writeReg(RegPaConfig, 0);
     
@@ -967,15 +1017,19 @@
     // Launch Rx chain calibration for HF band 
     writeReg(FSKRegImageCal, (readReg(FSKRegImageCal) & RF_IMAGECAL_IMAGECAL_MASK)|RF_IMAGECAL_IMAGECAL_START);
     while((readReg(FSKRegImageCal) & RF_IMAGECAL_IMAGECAL_RUNNING) == RF_IMAGECAL_IMAGECAL_RUNNING) { ; }
-#endif /* CFG_sx1276_radio */
+#endif /* CFG_sx1276mb1_board */
 
     opmode(OPMODE_SLEEP);
+#if defined(RADIO_DBG)
+    txStateIo = 0;
+    rxStateIo = 0;
+#endif
     hal_enableIRQs();
 }
 
 // return next random byte derived from seed buffer
 // (buf[0] holds index of next byte to be returned)
-u1_t radio_rand1 (void) {
+u1_t radio_rand1 () {
     u1_t i = randbuf[0];
     ASSERT( i != 0 );
     if( i==16 ) {
@@ -987,7 +1041,7 @@
     return v;
 }
 
-u1_t radio_rssi (void) {
+u1_t radio_rssi () {
     hal_disableIRQs();
     u1_t r = readReg(LORARegRssiValue);
     hal_enableIRQs();
@@ -1009,56 +1063,63 @@
 void radio_irq_handler (u1_t dio) {
     ostime_t now = os_getTime();
     if( (readReg(RegOpMode) & OPMODE_LORA) != 0) { // LORA modem
-    u1_t flags = readReg(LORARegIrqFlags);
-    if( flags & IRQ_LORA_TXDONE_MASK ) {
-        // save exact tx time
-        LMIC.txend = now - us2osticks(43); // TXDONE FIXUP
-    } else if( flags & IRQ_LORA_RXDONE_MASK ) {
-        // save exact rx time
-        LMIC.rxtime = now - LORA_RXDONE_FIXUP[getSf(LMIC.rps)];
-        // read the PDU and inform the MAC that we received something
-        LMIC.dataLen = (readReg(LORARegModemConfig1) & SX1272_MC1_IMPLICIT_HEADER_MODE_ON) ?
-        readReg(LORARegPayloadLength) : readReg(LORARegRxNbBytes);
-        // set FIFO read address pointer
-        writeReg(LORARegFifoAddrPtr, readReg(LORARegFifoRxCurrentAddr)); 
-        // now read the FIFO
-        readBuf(RegFifo, LMIC.frame, LMIC.dataLen);
-        // read rx quality parameters
-        LMIC.rxq.snr  = readReg(LORARegPktSnrValue); // SNR [dB] * 4
-        LMIC.rxq.rssi = readReg(LORARegPktRssiValue) - 125 + 64; // RSSI [dBm] (-196...+63)
-    } else if( flags & IRQ_LORA_RXTOUT_MASK ) {
-        // indicate timeout
-        LMIC.dataLen = 0;
-    }
+        u1_t flags = readReg(LORARegIrqFlags);
+        if( flags & IRQ_LORA_TXDONE_MASK ) {
+            // save exact tx time
+            LMIC.txend = now - us2osticks(43); // TXDONE FIXUP
+        } else if( flags & IRQ_LORA_RXDONE_MASK ) {
+            // save exact rx time
+            if(getBw(LMIC.rps) == BW125) {
+                now -= LORA_RXDONE_FIXUP[getSf(LMIC.rps)];
+            }
+            LMIC.rxtime = now;
+            // read the PDU and inform the MAC that we received something
+            LMIC.dataLen = (readReg(LORARegModemConfig1) & SX1272_MC1_IMPLICIT_HEADER_MODE_ON) ?
+                readReg(LORARegPayloadLength) : readReg(LORARegRxNbBytes);
+            // set FIFO read address pointer
+            writeReg(LORARegFifoAddrPtr, readReg(LORARegFifoRxCurrentAddr)); 
+            // now read the FIFO
+            readBuf(RegFifo, LMIC.frame, LMIC.dataLen);
+            // read rx quality parameters
+            LMIC.snr  = readReg(LORARegPktSnrValue); // SNR [dB] * 4
+            LMIC.rssi = readReg(LORARegPktRssiValue) - 125 + 64; // RSSI [dBm] (-196...+63)
+        } else if( flags & IRQ_LORA_RXTOUT_MASK ) {
+            // indicate timeout
+            LMIC.dataLen = 0;
+        }
         // mask all radio IRQs
         writeReg(LORARegIrqFlagsMask, 0xFF);
         // clear radio IRQ flags
         writeReg(LORARegIrqFlags, 0xFF);
     } else { // FSK modem
-    u1_t flags1 = readReg(FSKRegIrqFlags1);
-    u1_t flags2 = readReg(FSKRegIrqFlags2);
-    if( flags2 & IRQ_FSK2_PACKETSENT_MASK ) {
-        // save exact tx time
-        LMIC.txend = now;
+        u1_t flags1 = readReg(FSKRegIrqFlags1);
+        u1_t flags2 = readReg(FSKRegIrqFlags2);
+        if( flags2 & IRQ_FSK2_PACKETSENT_MASK ) {
+            // save exact tx time
+            LMIC.txend = now;
         } else if( flags2 & IRQ_FSK2_PAYLOADREADY_MASK ) {
-        // save exact rx time
-        LMIC.rxtime = now;
-        // read the PDU and inform the MAC that we received something
-        LMIC.dataLen = readReg(FSKRegPayloadLength);
-        // now read the FIFO
-        readBuf(RegFifo, LMIC.frame, LMIC.dataLen);
-        // read rx quality parameters
-        LMIC.rxq.snr  = 0; // determine snr
-        LMIC.rxq.rssi = 0; // determine rssi
-    } else if( flags1 & IRQ_FSK1_TIMEOUT_MASK ) {
-        // indicate timeout
-        LMIC.dataLen = 0;
-    } else {
+            // save exact rx time
+            LMIC.rxtime = now;
+            // read the PDU and inform the MAC that we received something
+            LMIC.dataLen = readReg(FSKRegPayloadLength);
+            // now read the FIFO
+            readBuf(RegFifo, LMIC.frame, LMIC.dataLen);
+            // read rx quality parameters
+            LMIC.snr  = 0; // determine snr
+            LMIC.rssi = 0; // determine rssi
+        } else if( flags1 & IRQ_FSK1_TIMEOUT_MASK ) {
+            // indicate timeout
+            LMIC.dataLen = 0;
+        } else {
             while(1);
         }
     }
     // go from stanby to sleep
     opmode(OPMODE_SLEEP);
+#if defined(RADIO_DBG)
+    txStateIo = 0;
+    rxStateIo = 0;
+#endif
     // run os job (use preset func ptr)
     os_setCallback(&LMIC.osjob, LMIC.osjob.func);
 }
@@ -1069,21 +1130,34 @@
       case RADIO_RST:
         // put radio to sleep
         opmode(OPMODE_SLEEP);
+#if defined(RADIO_DBG)
+        txStateIo = 0;
+        rxStateIo = 0;
+#endif
         break;
 
       case RADIO_TX:
-    // transmit frame now
+        // transmit frame now
         starttx(); // buf=LMIC.frame, len=LMIC.dataLen
+#if defined(RADIO_DBG)
+        txStateIo = 1;
+#endif
         break;
       
       case RADIO_RX:
-    // receive frame now (exactly at rxtime)
+        // receive frame now (exactly at rxtime)
         startrx(RXMODE_SINGLE); // buf=LMIC.frame, time=LMIC.rxtime, timeout=LMIC.rxsyms
+#if defined(RADIO_DBG)
+        rxStateIo = 1;
+#endif
         break;
 
       case RADIO_RXON:
         // start scanning for beacon now
         startrx(RXMODE_SCAN); // buf=LMIC.frame
+#if defined(RADIO_DBG)
+        rxStateIo = 1;
+#endif
         break;
     }
     hal_enableIRQs();