Text menu driven ANSI/VT100 console test utility for LoRa transceivers

radio chip selection

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

This is VT100 text-based menu driven test program for SX12xx transceiver devices.
Serial console is divided into horizontally into top half and bottom half.
The bottom half serves as scrolling area to log activity.
The top half serves as menu, to configure the radio.
For all devices, the serial console operates at 115200 8N1, and requires terminal with ANSI-VT100 capability, such as putty/teraterm/minicom etc.
Use program only with keyboard up/down/left/right keys. Enter to change an item, or number for value item. Some items are single bit, requiring only enter key to toggle. Others with fixed choices give a drop-down menu.

Revision:
10:db4e11a55bda
Parent:
9:295e37c38fb3
Child:
13:8ce61a1897ab
--- a/radio_lr1110.cpp	Mon May 04 17:54:55 2020 -0700
+++ b/radio_lr1110.cpp	Tue May 19 15:35:50 2020 -0700
@@ -29,15 +29,19 @@
 uint8_t Radio::tx_param_buf[2];
 uint8_t Radio::pa_config_buf[4];
 unsigned Radio::recalCnt;
-uint8_t Radio::gfsk_crc_params[8];
+uint32_t gfsk_crc_initValue;
+uint32_t gfsk_crc_Poly;
 
 uint8_t wifiScan_buf[9];
 uint8_t gnssAutonomous_buf[9];
+uint8_t gnssAssisted_buf[7];
+bool wifiResultFormatBasic;
+uint8_t tcxo_buf[5];
 
 void Radio::hw_reset()
 {
     radio.hw_reset();
-    radio.enable_default_irqs();
+    //radio.enable_default_irqs();
     initRfSwDIO();
 }
 
@@ -61,13 +65,6 @@
     radio.xfer(OPCODE_SET_DIO_AS_RFSWITCH, 8, 0, dioBuf);
 }
 
-void Radio::to_big_endian16(uint16_t in, uint8_t *out)
-{
-    out[1] = in & 0xff;
-    in >>= 8;
-    out[0] = in & 0xff;
-}
-
 unsigned Radio::my_round(float x)
 {
     if (x >= 0)
@@ -77,71 +74,49 @@
 
 void Radio::readChip()
 {
-    unsigned rb;
-    uint8_t buf[4];
+    uint32_t u32;
     {
         txParamsB_t txpb;   // txpb.bits.PaSel
         txParamsC_t txpc;
-        radio.memRegRead(REG_ADDR_TX_PARAMS_C, 1, buf);
-        txpc.dword = radio.from_big_endian32(buf);
+        radio.memRegRead(REG_ADDR_TX_PARAMS_C, 1, &txpc.dword);
         tx_param_buf[1] = txpc.bits.pa_ramp_time;
 
-        radio.memRegRead(REG_ADDR_TX_PARAMS_B, 1, buf);
-        txpb.dword = radio.from_big_endian32(buf);
+        radio.memRegRead(REG_ADDR_TX_PARAMS_B, 1, &txpb.dword);
         if (txpb.bits.PaSel)
             tx_param_buf[0] = txpc.bits.tx_dbm - 9;
         else
             tx_param_buf[0] = txpc.bits.tx_dbm - 17;
     }
 
-    {
-        txParamsA_t tpa;
-        txParamsB_t tpb;
-        //txParamsD_t tpd;
-        radio.memRegRead(REG_ADDR_TX_PARAMS_A, 1, buf);
-        tpa.dword = radio.from_big_endian32(buf);
-        radio.memRegRead(REG_ADDR_TX_PARAMS_B, 1, buf);
-        tpb.dword = radio.from_big_endian32(buf);
-        /*radio.memRegRead(REG_ADDR_TX_PARAMS_D, 1, buf);
-        tpd.dword = radio.from_big_endian32(buf);*/
-        pa_config_buf[0] = tpb.bits.PaSel;
-        pa_config_buf[1] = tpa.bits.RegPASupply;
-        pa_config_buf[2] = tpb.bits.PaDutyCycle;
-        pa_config_buf[3] = tpb.bits.PaHPSel;
-    }
+    radio.GetPaConfig(pa_config_buf);
 
     {
         dioEnable_t dio;
-        radio.memRegRead(REG_ADDR_DIO10, 1, buf);
-        dio.dword = radio.from_big_endian32(buf);
+        radio.memRegRead(REG_ADDR_DIO10, 1, &dio.dword);
         if (dio.bits.enable)
             dioBuf[DIO_en_IDX] |= DIO10_BIT;
         else
             dioBuf[DIO_en_IDX] &= ~DIO10_BIT;
 
-        radio.memRegRead(REG_ADDR_DIO8, 1, buf);
-        dio.dword = radio.from_big_endian32(buf);
+        radio.memRegRead(REG_ADDR_DIO8, 1, &dio.dword);
         if (dio.bits.enable)
             dioBuf[DIO_en_IDX] |= DIO8_BIT;
         else
             dioBuf[DIO_en_IDX] &= ~DIO8_BIT;
 
-        radio.memRegRead(REG_ADDR_DIO7, 1, buf);
-        dio.dword = radio.from_big_endian32(buf);
+        radio.memRegRead(REG_ADDR_DIO7, 1, &dio.dword);
         if (dio.bits.enable)
             dioBuf[DIO_en_IDX] |= DIO7_BIT;
         else
             dioBuf[DIO_en_IDX] &= ~DIO7_BIT;
 
-        radio.memRegRead(REG_ADDR_DIO6, 1, buf);
-        dio.dword = radio.from_big_endian32(buf);
+        radio.memRegRead(REG_ADDR_DIO6, 1, &dio.dword);
         if (dio.bits.enable)
             dioBuf[DIO_en_IDX] |= DIO6_BIT;
         else
             dioBuf[DIO_en_IDX] &= ~DIO6_BIT;
 
-        radio.memRegRead(REG_ADDR_DIO5, 1, buf);
-        dio.dword = radio.from_big_endian32(buf);
+        radio.memRegRead(REG_ADDR_DIO5, 1, &dio.dword);
         if (dio.bits.enable)
             dioBuf[DIO_en_IDX] |= DIO5_BIT;
         else
@@ -151,12 +126,11 @@
     {
         gfskConfig4_t cfg4;
         gfskConfig2_t cfg2;
-        gfskConfig1_t gcfg1;
+        gfskConfig1_t cfg1;
         gfskConfig3_t cfg3;
         gfskConfig5_t cfg5;
 
-        radio.memRegRead(REG_ADDR_GFSK_CFG5, 1, buf);
-        cfg5.dword = radio.from_big_endian32(buf);
+        radio.memRegRead(REG_ADDR_GFSK_CFG5, 1, &cfg5.dword);
         gfsk_pp_buf[8] = cfg5.bits.whitening_enable;
 
         if (cfg5.bits.crc_off)
@@ -174,46 +148,45 @@
         else
             gfsk_pp_buf[7] &= ~4;
 
-        radio.memRegRead(REG_ADDR_GFSK_PAYLOAD_LENGTH_A, 1, buf);
-        rb = radio.from_big_endian32(buf);
-        gfsk_pp_buf[6] = rb & 0xff;
-
-        radio.memRegRead(REG_ADDR_GFSK_PAYLOAD_LENGTH_B, 1, buf);
-        cfg4.dword = radio.from_big_endian32(buf);
+        radio.memRegRead(REG_ADDR_GFSK_PAYLOAD_LENGTH_A, 1, &u32);
+        gfsk_pp_buf[6] = u32 & 0xff;
+
+        radio.memRegRead(REG_ADDR_GFSK_PAYLOAD_LENGTH_B, 1, &cfg4.dword);
         gfsk_pp_buf[4] = cfg4.bits.addr_comp;
         if (cfg4.bits.payload_length != gfsk_pp_buf[6])
             log_printf("length_mismatch_%02x_%02x\r\n", cfg4.bits.payload_length, gfsk_pp_buf[6]);
 
-        radio.memRegRead(REG_ADDR_GFSK_CFG3, 1, buf);
-        cfg3.dword = radio.from_big_endian32(buf);
+        radio.memRegRead(REG_ADDR_GFSK_CFG3, 1, &cfg3.dword);
         gfsk_pp_buf[5] = cfg3.bits.variable_length;
 
-        radio.memRegRead(REG_ADDR_GFSK_CFG2, 1, buf);
-        cfg2.dword = radio.from_big_endian32(buf);
+        radio.memRegRead(REG_ADDR_GFSK_CFG2, 1, &cfg2.dword);
         gfsk_pp_buf[3] = cfg2.bits.sync_word_length;
 
-        radio.memRegRead(REG_ADDR_GFSK_CFG1, 1, buf);
-        gcfg1.dword = radio.from_big_endian32(buf);
-        to_big_endian16(gcfg1.bits.preamble_length, gfsk_pp_buf);
-        if (gcfg1.bits.preamble_det_enable)
-            gfsk_pp_buf[2] = gcfg1.bits.preamble_det_len;
+        radio.memRegRead(REG_ADDR_GFSK_CFG1, 1, &cfg1.dword);
+        radio.to_big_endian16(cfg1.bits.preamble_length, gfsk_pp_buf);
+        if (cfg1.bits.preamble_det_enable)
+            gfsk_pp_buf[2] = cfg1.bits.preamble_det_len;
         else
             gfsk_pp_buf[2] = 0;
 
-        radio.memRegRead(REG_ADDR_GFSK_BITRATE, 1, gfsk_mp_buf);
+        {
+            unsigned hz;
+            radio.memRegRead(REG_ADDR_GFSK_BITRATE, 1, &u32);
+            hz = GFSK_BITRATE_NUMERATOR / u32;
+            radio.to_big_endian32(hz, gfsk_mp_buf);
+        }
         //gfsk_mp_buf[4] = bt;
         //gfsk_mp_buf[5] = bwf;
         //gfsk_mp_buf[6,7,8,9] = fdevHz;
         {
             unsigned hz;
-            radio.memRegRead(REG_ADDR_GFSK_FDEV, 1, buf);
-            rb = radio.from_big_endian32(buf);
-            hz = my_round(rb*FREQ_STEP);
+            radio.memRegRead(REG_ADDR_GFSK_FDEV, 1, &u32);
+            hz = my_round(u32 * FREQ_STEP);
             radio.to_big_endian32(hz, gfsk_mp_buf+6);
         }
 
-        radio.memRegRead(REG_ADDR_GFSK_CRC_INIT, 1, gfsk_crc_params);
-        radio.memRegRead(REG_ADDR_GFSK_CRC_POLY, 1, gfsk_crc_params+4);
+        radio.memRegRead(REG_ADDR_GFSK_CRC_INIT, 1, &gfsk_crc_initValue);
+        radio.memRegRead(REG_ADDR_GFSK_CRC_POLY, 1, &gfsk_crc_Poly);
     }
 
     /**********************************************/
@@ -222,27 +195,21 @@
         loraConfig0_t cfg0;
         loraConfigB_t cfgb;
         loraConfigC_t cfgc;
-        radio.memRegRead(REG_ADDR_LORA_CONFIG0, 1, buf);
-        cfg0.dword = radio.from_big_endian32(buf);
-
-        lora_mp_buf[0] = cfg0.bits.modem_sf;
-        lora_mp_buf[1] = cfg0.bits.modem_bw;
-        lora_mp_buf[2] = cfg0.bits.coding_rate;
-        lora_mp_buf[3] = cfg0.bits.ppm_offset;
-
-        radio.memRegRead(REG_ADDR_LORA_CONFIGC, 1, buf);
-        cfgc.dword = radio.from_big_endian32(buf);
+
+        radio.GetLoRaModulationParameters(lora_mp_buf);
+
+        radio.memRegRead(REG_ADDR_LORA_CONFIGC, 1, &cfgc.dword);
         u16 = cfgc.bits.preamble_length;
         lora_pp_buf[1] = u16 & 0xff;
         u16 >>= 8;
         lora_pp_buf[0] = u16;
 
+        radio.memRegRead(REG_ADDR_LORA_CONFIG0, 1, &cfg0.dword);
         lora_pp_buf[2] = cfg0.bits.implicit_header;
         lora_pp_buf[3] = cfg0.bits.payload_length;
         lora_pp_buf[4] = cfg0.bits.crc_on;
 
-        radio.memRegRead(REG_ADDR_LORA_CONFIGB, 1, buf);
-        cfgb.dword = radio.from_big_endian32(buf);
+        radio.memRegRead(REG_ADDR_LORA_CONFIGB, 1, &cfgb.dword);
         lora_pp_buf[5] = cfgb.bits.invertIQ;
 
     }
@@ -263,6 +230,7 @@
         wifiScan_buf[6] = timeout; // Timeout-hi
         wifiScan_buf[8] = 0x00; // AbortOnTimeout
     }
+
 }
 
 void Radio::clearIrqFlags()
@@ -274,30 +242,33 @@
 
 uint8_t Radio::get_payload_length()
 {
-    uint8_t buf[4];
     uint8_t pktType = radio.getPacketType();
 
     if (pktType == PACKET_TYPE_LORA) {
+        unsigned foo;
         loraConfig0_t cfg0;
-        radio.memRegRead(REG_ADDR_LORA_CONFIG0, 1, buf);
-        cfg0.dword = radio.from_big_endian32(buf);
+        radio.memRegRead(REG_ADDR_LORA_CONFIG0, 1, &cfg0.dword);
+        foo = cfg0.dword;
+        foo >>= 24;
         lora_pp_buf[3] = cfg0.bits.payload_length;
         return cfg0.bits.payload_length;
     } else if (pktType == PACKET_TYPE_GFSK) {
-        gfskConfig4_t cfg4;
-        unsigned rb;
-        radio.memRegRead(REG_ADDR_GFSK_PAYLOAD_LENGTH_A, 1, buf);
-        rb = radio.from_big_endian32(buf);
-        gfsk_pp_buf[6] = rb & 0xff;
-        radio.memRegRead(REG_ADDR_GFSK_PAYLOAD_LENGTH_B, 1, buf);
-        cfg4.dword = radio.from_big_endian32(buf);
-
+        uint32_t u32;
+        /* TODO: which is rx payloadLength vs tx payloadLength */
+        //gfskConfig4_t cfg4;
+        radio.memRegRead(REG_ADDR_GFSK_PAYLOAD_LENGTH_A, 1, &u32);
+        gfsk_pp_buf[6] = u32 & 0xff;
+        /*radio.memRegRead(REG_ADDR_GFSK_PAYLOAD_LENGTH_B, 1, buf);
+        cfg4.dword = radio.from_big_endian32(buf);*/
+
+        /*
         if ((rb & 0xff) != cfg4.bits.payload_length) {
             log_printf("gfsk payload length A:%u B:%u\r\n", rb & 0xff, cfg4.bits.payload_length);
             // todo: which is tx-length vs rx-length
         }
-
-        return rb & 0xff;
+        */
+
+        return u32 & 0xff;
     }
     return 0;
 }
@@ -312,9 +283,9 @@
 
 unsigned Radio::read_register(unsigned addr)
 {
-    uint8_t buf[4];
-    radio.memRegRead(addr, 1, buf);
-    return radio.from_big_endian32(buf);
+    uint32_t u32;
+    radio.memRegRead(addr, 1, &u32);
+    return u32;
 }
 
 void Radio::write_register(unsigned addr, unsigned val)
@@ -345,6 +316,95 @@
 
 const value_item_t Radio::txTimeout_item = { _ITEM_VALUE, 6, txTimeout_print, txTimeout_write};
 
+bool regulator_read()
+{
+    regulatorMode_t rm;
+    Radio::radio.memRegRead(REG_ADDR_REGULATOR_MODE, 1, &rm.dword);
+    return rm.bits.dcdc_en;
+}
+
+bool regulator_push()
+{
+    uint8_t buf = regulator_read() ? 0 : 1;
+    Radio::radio.xfer(OPCODE_SET_REGULATOR_MODE, 1, 0, &buf);
+    return buf == 1;
+}
+
+const toggle_item_t regulator_item = { _ITEM_TOGGLE,
+    " LDO ",
+    "DC-DC",
+    regulator_read,
+    regulator_push
+};
+
+const char* const tcxovolts_strs[] 
+{
+    "1.6", // 0
+    "1.8", // 1
+    "1.8", // 2
+    "2.2", // 3
+    "2.4", // 4
+    "2.7", // 5
+    "3.0", // 6
+    "3.3", // 7
+    NULL
+};
+
+unsigned tcxovolts_read(bool for_writing)
+{
+    tcxo_t tcxo;
+    Radio::radio.memRegRead(REG_ADDR_TCXO, 1, &tcxo.dword);
+    return tcxo.bits.volts;
+}
+
+menuMode_e tcxovolts_write(unsigned sidx)
+{
+    tcxo_buf[0] = sidx;
+    Radio::radio.xfer(OPCODE_SET_TCXO_MODE, 5, 0, tcxo_buf);
+    return MENUMODE_REDRAW;
+}
+
+const dropdown_item_t tcxo_volts_item = { _ITEM_DROPDOWN, tcxovolts_strs, tcxovolts_strs, tcxovolts_read, tcxovolts_write};
+
+void tcxo_delay_print()
+{
+    unsigned ticks = tcxo_buf[1];
+    ticks <<= 8;
+    ticks |= tcxo_buf[2];
+    ticks <<= 8;
+    ticks |= tcxo_buf[3];
+    pc.printf("%.1f", ticks / 32.768);
+}
+
+bool tcxo_delay_write(const char *txt)
+{
+    float ms;
+    if (sscanf(txt, "%f", &ms) == 1) {
+        unsigned ticks = ms * 32.768;
+        Radio::radio.to_big_endian24(ticks, tcxo_buf+1);
+        Radio::radio.xfer(OPCODE_SET_TCXO_MODE, 5, 0, tcxo_buf);
+    }
+    return false;
+}
+
+const value_item_t tcxo_delay_item = { _ITEM_VALUE, 5, tcxo_delay_print, tcxo_delay_write};
+
+void recalibrate_print() { }
+
+bool recalibrate_write(const char *txt)
+{
+    unsigned bits;
+    uint8_t buf = 0;
+    if (sscanf(txt, "%x", &bits) == 1) {
+        buf = bits;
+    }
+    Radio::radio.xfer(OPCODE_CALIBRATE, 1, 0, &buf);
+    return false;
+}
+
+const value_item_t recalibrate_item = { _ITEM_VALUE, 5, recalibrate_print, recalibrate_write};
+
+
 void Radio::rxBuffer_push()
 {
     uint8_t buf[4];
@@ -388,9 +448,9 @@
         data2 <<= 8;
         data2 |= buf[7];
         if (pktType == PACKET_TYPE_LORA) {
-            printf("nbPktRx:%u nbPktCrcErr:%u hdrErr:%u falseSync:%u\r\n", nbPktRx, nbPktCrcErr, data1, data2);
+            log_printf("nbPktRx:%u nbPktCrcErr:%u hdrErr:%u falseSync:%u\r\n", nbPktRx, nbPktCrcErr, data1, data2);
         } else if (pktType == PACKET_TYPE_GFSK) {
-            printf("nbPktRx:%u nbPktCrcErr:%u nbLenErr:%u\r\n", nbPktRx, nbPktCrcErr, data1);
+            log_printf("nbPktRx:%u nbPktCrcErr:%u nbLenErr:%u\r\n", nbPktRx, nbPktCrcErr, data1);
         }
     }
 }
@@ -401,17 +461,15 @@
 void Radio::txPkt()
 {
     uint8_t txlen = get_payload_length();
-    //log_printf("txPkt():%u\r\n", txlen);
     radio.start_tx(txlen);
-    radio.log_cmds = 1;
 }
 
 void Radio::Rx()
 {
     stat_t stat;
     uint8_t buf[3];
-    unsigned tx_timeout = 0xffffff; // receive until instructed not to
-    radio.to_big_endian24(tx_timeout, buf);
+    unsigned rx_timeout = 0xffffff; // receive until instructed not to
+    radio.to_big_endian24(rx_timeout, buf);
     stat.word = radio.xfer(OPCODE_SET_RX, 3, 0, buf);
     print_stat(stat);
 }
@@ -504,10 +562,8 @@
 
 bool Radio::PaSel_read()
 {
-    uint8_t buf[4];
     txParamsB_t txpb;
-    radio.memRegRead(REG_ADDR_TX_PARAMS_B, 1, buf);
-    txpb.dword = radio.from_big_endian32(buf);
+    radio.memRegRead(REG_ADDR_TX_PARAMS_B, 1, &txpb.dword);
     pa_config_buf[0] = txpb.bits.PaSel;
     return txpb.bits.PaSel;
 }
@@ -516,35 +572,24 @@
 {
     pa_config_buf[0] ^= 1;
     radio.xfer(OPCODE_SET_PA_CONFIG, 4, 0, pa_config_buf);
-    return pa_config_buf[0];
+    return pa_config_buf[0] == 1;
 }
 
 const toggle_item_t Radio::PaSel_item = { _ITEM_TOGGLE, "PaSel:LP", "PaSel:HP", PaSel_read, PaSel_push};
 
 bool Radio::RegPASupply_read()
 {
-    uint8_t buf[4];
     txParamsA_t tpa;
-    radio.memRegRead(REG_ADDR_TX_PARAMS_A, 1, buf);
-    tpa.dword = radio.from_big_endian32(buf);
+    radio.memRegRead(REG_ADDR_TX_PARAMS_A, 1, &tpa.dword);
     pa_config_buf[1] = tpa.bits.RegPASupply;
     return tpa.bits.RegPASupply;
 }
 
 bool Radio::RegPASupply_push(void)
 {
-    bool ret;
-    //pa_config_buf[1] ^= 1;
-    if (pa_config_buf[1]) {
-        pa_config_buf[1] = 0;
-        ret = false;
-    } else {
-        pa_config_buf[1] = 1;
-        ret = true;
-    }
-
+    pa_config_buf[1] ^= 1;
     radio.xfer(OPCODE_SET_PA_CONFIG, 4, 0, pa_config_buf);
-    return ret;
+    return pa_config_buf[1] == 1;
 }
 
 const toggle_item_t Radio::RegPASupply_item = { _ITEM_TOGGLE,
@@ -556,10 +601,8 @@
 
 void Radio::PaDutyCycle_print()
 {
-    uint8_t buf[4];
     txParamsB_t tpb;
-    radio.memRegRead(REG_ADDR_TX_PARAMS_B, 1, buf);
-    tpb.dword = radio.from_big_endian32(buf);
+    radio.memRegRead(REG_ADDR_TX_PARAMS_B, 1, &tpb.dword);
     pa_config_buf[2] = tpb.bits.PaDutyCycle;
     pc.printf("%u", tpb.bits.PaDutyCycle);
 }
@@ -578,10 +621,8 @@
 
 void Radio::PaHPSel_print()
 {
-    uint8_t buf[4];
     txParamsB_t tpb;
-    radio.memRegRead(REG_ADDR_TX_PARAMS_B, 1, buf);
-    tpb.dword = radio.from_big_endian32(buf);
+    radio.memRegRead(REG_ADDR_TX_PARAMS_B, 1, &tpb.dword);
     pa_config_buf[3] = tpb.bits.PaHPSel;
     pc.printf("%u", tpb.bits.PaHPSel);
 }
@@ -602,6 +643,7 @@
 void Radio::wifiScan_push()
 {
     radio.xfer(OPCODE_WIFI_SCAN, 9, 0, wifiScan_buf);
+    log_printf("wifiScan...\r\n");
 }
 
 const char* const wifitype_strs[] = 
@@ -718,38 +760,277 @@
 bool wifi_ch5_push() { wifiScan_buf[2] ^= 0x10; return wifi_ch5_read(); }
 const toggle_item_t wifi_ch5_item = { _ITEM_TOGGLE, "ch5", NULL, wifi_ch5_read, wifi_ch5_push};
 
+bool wifi_ch4_read() { return (wifiScan_buf[2] & 0x08) == 0x08; }
+bool wifi_ch4_push() { wifiScan_buf[2] ^= 0x08; return wifi_ch4_read(); }
+const toggle_item_t wifi_ch4_item = { _ITEM_TOGGLE, "ch4", NULL, wifi_ch4_read, wifi_ch4_push};
+
+bool wifi_ch3_read() { return (wifiScan_buf[2] & 0x04) == 0x04; }
+bool wifi_ch3_push() { wifiScan_buf[2] ^= 0x04; return wifi_ch3_read(); }
+const toggle_item_t wifi_ch3_item = { _ITEM_TOGGLE, "ch3", NULL, wifi_ch3_read, wifi_ch3_push};
+
+bool wifi_ch2_read() { return (wifiScan_buf[2] & 0x02) == 0x02; }
+bool wifi_ch2_push() { wifiScan_buf[2] ^= 0x02; return wifi_ch2_read(); }
+const toggle_item_t wifi_ch2_item = { _ITEM_TOGGLE, "ch2", NULL, wifi_ch2_read, wifi_ch2_push};
+
+bool wifi_ch1_read() { return (wifiScan_buf[2] & 0x01) == 0x01; }
+bool wifi_ch1_push() { wifiScan_buf[2] ^= 0x01; return wifi_ch1_read(); }
+const toggle_item_t wifi_ch1_item = { _ITEM_TOGGLE, "ch1", NULL, wifi_ch1_read, wifi_ch1_push};
+
+void wifiTimeout_print(void)
+{
+    unsigned t;
+    t = wifiScan_buf[6];
+    t <<= 8;
+    t |= wifiScan_buf[7];
+    pc.printf("%u", t);
+}
+
+bool wifiTimeout_write(const char *txt)
+{
+    unsigned t;
+    sscanf(txt, "%u", &t);
+    wifiScan_buf[7] = t;
+    t >>= 8;
+    wifiScan_buf[6] = t;
+    return false;
+}
+
+const value_item_t wifiTimeout_item = { _ITEM_VALUE, 6, wifiTimeout_print, wifiTimeout_write};
+
+bool AbortOnTimeout_read()
+{
+    return wifiScan_buf[8] == 1;
+}
+
+bool AbortOnTimeout_push()
+{
+    wifiScan_buf[8] ^= 1;
+    return wifiScan_buf[8] == 1;
+}
+
+const toggle_item_t wifiAbort_item = { _ITEM_TOGGLE, "AbortOnTimeout", NULL, AbortOnTimeout_read, AbortOnTimeout_push};
+
+bool wifiResultFormat_read()
+{
+    // false=full, true=basic
+    return wifiResultFormatBasic;
+}
+
+bool wifiResultFormat_push()
+{
+    // false=full, true=basic
+    wifiResultFormatBasic ^= true;
+    return wifiResultFormatBasic;
+}
+
+const toggle_item_t wifiResultFormat_item = { _ITEM_TOGGLE,
+    "full ",
+    "basic",
+    wifiResultFormat_read,
+    wifiResultFormat_push
+};
+
 void Radio::gnssAutonomous_push()
 {
     gnssAutonomous_buf[4] = 0;  // EffortMode
-    gnssAutonomous_buf[5] = 0;  // ResultMask
-    gnssAutonomous_buf[6] = 0;  // NbSvMax (0=all satellites)
     radio.xfer(OPCODE_GNSS_AUTONOMOUS, 7, 0, gnssAutonomous_buf);
+    log_printf("gnssAutonomous...\r\n");
 }
 
 const button_item_t Radio::gnssAutonomous_item = { _ITEM_BUTTON, "gnssAutonomous", gnssAutonomous_push };
 
-void Radio::gnssAutonomous_time_print()
+void gnssAutonomous_time_print()
 {
-    unsigned t = radio.from_big_endian32(gnssAutonomous_buf);
+    unsigned t = Radio::radio.from_big_endian32(gnssAutonomous_buf);
     pc.printf("%u", t);
 }
 
-bool Radio::gnssAutonomous_time_write(const char *txt)
+bool gnssAutonomous_time_write(const char *txt)
+{
+    unsigned t;
+    if (sscanf(txt, "%u", &t) == 1) {
+        Radio::radio.to_big_endian32(t, gnssAutonomous_buf);
+    }
+    return false;
+}
+
+
+bool gnssAutoResultTimeStamp_read()
+{
+    return (gnssAutonomous_buf[5] & 0x01) == 0x01;
+}
+bool gnssAutoResultTimeStamp_push()
+{
+    gnssAutonomous_buf[5] ^= 0x01;
+    return (gnssAutonomous_buf[5] & 0x01) == 0x01;
+}
+const toggle_item_t gnssAutoResultTimeStamp_item = { _ITEM_TOGGLE, "timestamp", NULL, gnssAutoResultTimeStamp_read, gnssAutoResultTimeStamp_push};
+
+bool gnssAutoResultDoppler_read()
+{
+    return (gnssAutonomous_buf[5] & 0x02) == 0x02;
+}
+bool gnssAutoResultDoppler_push()
+{
+    gnssAutonomous_buf[5] ^= 0x02;
+    return (gnssAutonomous_buf[5] & 0x02) == 0x02;
+}
+const toggle_item_t gnssAutoResultDoppler_item = { _ITEM_TOGGLE, "doppler", NULL, gnssAutoResultDoppler_read, gnssAutoResultDoppler_push};
+
+bool gnssAutoResultBitChange_read()
+{
+    return (gnssAutonomous_buf[5] & 0x04) == 0x04;
+}
+bool gnssAutoResultBitChange_push()
+{
+    gnssAutonomous_buf[5] ^= 0x04;
+    return (gnssAutonomous_buf[5] & 0x04) == 0x04;
+}
+const toggle_item_t gnssAutoResultBitChange_item = { _ITEM_TOGGLE, "bit change", NULL, gnssAutoResultBitChange_read, gnssAutoResultBitChange_push};
+
+void gnssAutoNbSvMax_print()
+{
+    pc.printf("%u", gnssAutonomous_buf[6]);
+}
+
+bool gnssAutoNbSvMax_write(const char *txt)
+{
+    unsigned n;
+    sscanf(txt, "%u", &n);
+    gnssAutonomous_buf[6] = n;
+    return false;
+}
+
+const value_item_t gnssAutoNbSvMax_item = { _ITEM_VALUE, 3, gnssAutoNbSvMax_print, gnssAutoNbSvMax_write};
+
+const value_item_t gnssAutonomous_time_item = { _ITEM_VALUE, 9, gnssAutonomous_time_print, gnssAutonomous_time_write};
+
+void Radio::gnssAssisted_push()
+{
+    gnssAssisted_buf[5] = 3; // ResultMask
+    radio.xfer(OPCODE_GNSS_ASSISTED, 7, 0, gnssAssisted_buf);
+    log_printf("gnssAssisted...\r\n");
+}
+
+const button_item_t Radio::gnssAssisted_item = { _ITEM_BUTTON, "gnssAssisted", gnssAssisted_push};
+
+void gnssAssisted_time_print()
+{
+    unsigned t = Radio::radio.from_big_endian32(gnssAssisted_buf);
+    pc.printf("%u", t);
+}
+
+bool gnssAssisted_time_write(const char *txt)
 {
     unsigned t;
     if (sscanf(txt, "%u", &t) == 1) {
-        radio.to_big_endian32(t, gnssAutonomous_buf);
+        Radio::radio.to_big_endian32(t, gnssAssisted_buf);
     }
     return false;
 }
-
-const value_item_t Radio::gnssAutonomous_time_item = { _ITEM_VALUE, 3, gnssAutonomous_time_print, gnssAutonomous_time_write};
-
-void Radio::gnssAssisted_push()
+const value_item_t gnssAssisted_time_item = { _ITEM_VALUE, 9, gnssAssisted_time_print, gnssAssisted_time_write};
+
+bool gnssAssisted_effort_read()
+{
+    return gnssAssisted_buf[4] == 0x01;
+}
+
+bool gnssAssisted_effort_push()
+{
+    gnssAssisted_buf[4] ^= 0x01;
+    return gnssAssisted_buf[4] == 0x01;
+}
+
+const toggle_item_t gnssAssisted_effort_item = { _ITEM_TOGGLE,
+    "lowPower",
+    "best    ",
+    gnssAssisted_effort_read,
+    gnssAssisted_effort_push
+};
+
+void gnssAssisted_nbsvmax_print()
+{
+    pc.printf("%u", gnssAssisted_buf[6]);
+}
+
+bool gnssAssisted_nbsvmax_write(const char *txt)
+{
+    unsigned n;
+    sscanf(txt, "%u", &n);
+    gnssAssisted_buf[6] = n;
+    return false;
+}
+
+const value_item_t gnssAssisted_nbsvmax_item = { _ITEM_VALUE, 3, gnssAssisted_nbsvmax_print, gnssAssisted_nbsvmax_write};
+
+bool gnss_const_gps_enable_read()
+{
+    gnssConstellation_t gc;
+    Radio::radio.memRegRead(REG_ADDR_GNSS_CONST, 1, &gc.dword);
+    return gc.bits.gps;
+}
+
+bool gnss_const_gps_enable_push()
 {
+    uint8_t ConstellationBitMask = 0;
+    gnssConstellation_t gc;
+    Radio::radio.memRegRead(REG_ADDR_GNSS_CONST, 1, &gc.dword);
+    if (gc.bits.gps)
+        ConstellationBitMask &= ~1;
+    else
+        ConstellationBitMask |= 1;
+    if (gc.bits.beidou)
+        ConstellationBitMask |= 2;
+    Radio::radio.xfer(OPCODE_GNSS_SET_CONSTELLATION, 1, 0, &ConstellationBitMask);
+    return ConstellationBitMask & 1;
 }
 
-const button_item_t Radio::gnssAssisted_item = { _ITEM_BUTTON, "gnssAssisted", gnssAssisted_push };
+const toggle_item_t gnss_const_gps_enable_item = { _ITEM_TOGGLE, "GPS", NULL, gnss_const_gps_enable_read, gnss_const_gps_enable_push};
+
+bool gnss_const_beidou_enable_read()
+{
+    gnssConstellation_t gc;
+    Radio::radio.memRegRead(REG_ADDR_GNSS_CONST, 1, &gc.dword);
+    return gc.bits.beidou;
+}
+
+bool gnss_const_beidou_enable_push()
+{
+    uint8_t ConstellationBitMask = 0;
+    gnssConstellation_t gc;
+    Radio::radio.memRegRead(REG_ADDR_GNSS_CONST, 1, &gc.dword);
+    if (gc.bits.beidou)
+        ConstellationBitMask &= ~2;
+    else
+        ConstellationBitMask |= 2;
+    if (gc.bits.gps)
+        ConstellationBitMask |= 1;
+    Radio::radio.xfer(OPCODE_GNSS_SET_CONSTELLATION, 1, 0, &ConstellationBitMask);
+    return ConstellationBitMask & 1;
+}
+
+const toggle_item_t gnss_const_beidou_enable_item = { _ITEM_TOGGLE, "BEIDOU", NULL, gnss_const_beidou_enable_read, gnss_const_beidou_enable_push};
+
+bool gnssmode_read()
+{
+    gnssMode_t gm;
+    Radio::radio.memRegRead(REG_ADDR_GNSS_MODE, 1, &gm.dword);
+    return gm.bits.gnss_scan_single;
+}
+
+bool gnssmode_push()
+{
+    uint8_t buf = gnssmode_read();  // GnssSetMode takes 1 for dual-scan
+    Radio::radio.xfer(OPCODE_GNSS_SET_MODE, 1, 0, &buf);
+    return buf == 0;
+}
+
+const toggle_item_t gnss_mode_item = { _ITEM_TOGGLE,
+    "dual  ",
+    "single",
+    gnssmode_read,
+    gnssmode_push
+};
 
 const menu_t Radio::common_menu[] = {
     { {FIRST_CHIP_MENU_ROW,  1},          NULL, &PaSel_item, FLAG_MSGTYPE_ALL, &tx_dbm_item},
@@ -757,42 +1038,71 @@
     { {FIRST_CHIP_MENU_ROW, 30}, "PaDutyCycle:", &PaDutyCycle_item, FLAG_MSGTYPE_ALL},
     { {FIRST_CHIP_MENU_ROW, 47},     "PaHPSel:", &PaHPSel_item, FLAG_MSGTYPE_ALL},
 
-
-
-    { {FIRST_CHIP_MENU_ROW+1,  1}, "txTimeout(sec):", &txTimeout_item, FLAG_MSGTYPE_ALL},
-    { {FIRST_CHIP_MENU_ROW+1, 24},              NULL, &rxBufferStatus_item, FLAG_MSGTYPE_ALL},
-    { {FIRST_CHIP_MENU_ROW+1, 35},              NULL, &getStats_item, FLAG_MSGTYPE_ALL},
-
-    { {LAST_CHIP_MENU_ROW-5,  1},          NULL, &wifiScan_item, FLAG_MSGTYPE_ALL},
-    { {LAST_CHIP_MENU_ROW-5, 10},          NULL, &wifiType_item, FLAG_MSGTYPE_ALL},
-    { {LAST_CHIP_MENU_ROW-5, 19},          NULL, &wifiAcqMode_item, FLAG_MSGTYPE_ALL},
-    { {LAST_CHIP_MENU_ROW-5, 35},   "NbMaxRes:", &wifiNbMaxRes_item, FLAG_MSGTYPE_ALL},
-    { {LAST_CHIP_MENU_ROW-5, 48}, "NbScanPerChan:", &wifiNbScanPerChan_item, FLAG_MSGTYPE_ALL},
-
-    { {LAST_CHIP_MENU_ROW-4,  1},        NULL, &wifi_ch14_item, FLAG_MSGTYPE_ALL},
-    { {LAST_CHIP_MENU_ROW-4,  6},        NULL, &wifi_ch13_item, FLAG_MSGTYPE_ALL},
-    { {LAST_CHIP_MENU_ROW-4, 11},        NULL, &wifi_ch12_item, FLAG_MSGTYPE_ALL},
-    { {LAST_CHIP_MENU_ROW-4, 16},        NULL, &wifi_ch11_item, FLAG_MSGTYPE_ALL},
-    { {LAST_CHIP_MENU_ROW-4, 21},        NULL, &wifi_ch10_item, FLAG_MSGTYPE_ALL},
-    { {LAST_CHIP_MENU_ROW-4, 26},        NULL, &wifi_ch9_item, FLAG_MSGTYPE_ALL},
-    { {LAST_CHIP_MENU_ROW-4, 31},        NULL, &wifi_ch8_item, FLAG_MSGTYPE_ALL},
-    { {LAST_CHIP_MENU_ROW-4, 36},        NULL, &wifi_ch7_item, FLAG_MSGTYPE_ALL},
-    { {LAST_CHIP_MENU_ROW-4, 41},        NULL, &wifi_ch6_item, FLAG_MSGTYPE_ALL},
-    { {LAST_CHIP_MENU_ROW-4, 46},        NULL, &wifi_ch5_item, FLAG_MSGTYPE_ALL},
-
-    { {LAST_CHIP_MENU_ROW-3,  1},          NULL, &gnssAutonomous_item, FLAG_MSGTYPE_ALL},
-    { {LAST_CHIP_MENU_ROW-3, 16},          NULL, &gnssAutonomous_time_item, FLAG_MSGTYPE_ALL},
-
-    { {LAST_CHIP_MENU_ROW-1,  1},          NULL, &gnssAssisted_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+1,  1}, "regulator:", &regulator_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+1,  18}, "tcxoVolts:", &tcxo_volts_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+1,  32}, "tcxoDelay(ms):", &tcxo_delay_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+1,  52}, "re-cal(hex):", &recalibrate_item, FLAG_MSGTYPE_ALL},
+
+   //012345678901234567890
+
+    { {FIRST_CHIP_MENU_ROW+2,  1}, "txTimeout(sec):", &txTimeout_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+2, 24},              NULL, &rxBufferStatus_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+2, 35},              NULL, &getStats_item, FLAG_MSGTYPE_ALL},
+
+    { {FIRST_CHIP_MENU_ROW+10,  1},          NULL, &wifiScan_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+10, 10},          NULL, &wifiType_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+10, 19},          NULL, &wifiAcqMode_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+10, 35},   "NbMaxRes:", &wifiNbMaxRes_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+10, 48}, "NbScanPerChan:", &wifiNbScanPerChan_item, FLAG_MSGTYPE_ALL},
+
+    { {FIRST_CHIP_MENU_ROW+11,  1}, "timeout(ms):", &wifiTimeout_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+11, 20},           NULL, &wifiAbort_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+11, 45}, "resultFormat:", &wifiResultFormat_item, FLAG_MSGTYPE_ALL},
+
+
+    { {FIRST_CHIP_MENU_ROW+12,  1},        NULL, &wifi_ch14_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+12,  6},        NULL, &wifi_ch13_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+12, 11},        NULL, &wifi_ch12_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+12, 16},        NULL, &wifi_ch11_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+12, 21},        NULL, &wifi_ch10_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+12, 26},        NULL, &wifi_ch9_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+12, 31},        NULL, &wifi_ch8_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+12, 36},        NULL, &wifi_ch7_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+12, 41},        NULL, &wifi_ch6_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+12, 46},        NULL, &wifi_ch5_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+12, 51},        NULL, &wifi_ch4_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+12, 56},        NULL, &wifi_ch3_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+12, 61},        NULL, &wifi_ch2_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+12, 66},        NULL, &wifi_ch1_item, FLAG_MSGTYPE_ALL},
+
+    { {FIRST_CHIP_MENU_ROW+13,  1},        NULL, &gnss_const_gps_enable_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+13,  5},        NULL, &gnss_const_beidou_enable_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+13, 12},     "scanning:", &gnss_mode_item, FLAG_MSGTYPE_ALL},
+
+    { {FIRST_CHIP_MENU_ROW+14,  1},          NULL, &gnssAutonomous_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+14, 16},       "time:", &gnssAutonomous_time_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+14, 31},          NULL, &gnssAutoResultTimeStamp_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+14, 41},          NULL, &gnssAutoResultDoppler_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+14, 51},          NULL, &gnssAutoResultBitChange_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+14, 63},    "NbSvMax:", &gnssAutoNbSvMax_item, FLAG_MSGTYPE_ALL},
+
+    { {FIRST_CHIP_MENU_ROW+15,  1},          NULL, &gnssAssisted_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+15, 16},       "time:", &gnssAssisted_time_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+15, 30},     "effort:", &gnssAssisted_effort_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+15, 50},   "NbSvMax::", &gnssAssisted_nbsvmax_item, FLAG_MSGTYPE_ALL},
+
+   //0123456789012345
     { {0, 0}, NULL, NULL }
 };
 
 void Radio::gfsk_bitrate_print()
 {
-    unsigned rb;
-    radio.memRegRead(REG_ADDR_GFSK_BITRATE, 1, gfsk_mp_buf);
-    rb = radio.from_big_endian32(gfsk_mp_buf);
-    pc.printf("%u", GFSK_BITRATE_NUMERATOR / rb);
+    uint32_t u32;
+    unsigned hz;
+    radio.memRegRead(REG_ADDR_GFSK_BITRATE, 1, &u32);
+    hz = GFSK_BITRATE_NUMERATOR / u32;
+    pc.printf("%u", hz);
+    radio.to_big_endian32(hz, gfsk_mp_buf);
 }
 
 bool Radio::gfsk_bitrate_write(const char* txt)
@@ -819,11 +1129,16 @@
 
 unsigned Radio::gfsk_bt_read(bool forWriting)
 {
-    uint8_t buf[4];
     gfskConfig0_t cfg0;
-    radio.memRegRead(REG_ADDR_GFSK_CFG0, 1, buf);
-    cfg0.dword = radio.from_big_endian32(buf);
+    radio.memRegRead(REG_ADDR_GFSK_CFG0, 1, &cfg0.dword);
     if (cfg0.bits.shaping_en) {
+        switch (cfg0.bits.bt) {
+            case 0: /* off */ gfsk_mp_buf[4] = GFSK_BT_OFF; break;
+            case 1: /* 0.3 */ gfsk_mp_buf[4] = GFSK_BT_0_3; break;
+            case 2: /* 0.5 */ gfsk_mp_buf[4] = GFSK_BT_0_5; break;
+            case 3: /* 0.7 */ gfsk_mp_buf[4] = GFSK_BT_0_7; break;
+            case 4: /* 1.0 */ gfsk_mp_buf[4] = GFSK_BT_1_0; break;
+        }
         return cfg0.bits.bt + 1;
     } else
         return 0;
@@ -847,10 +1162,9 @@
 void Radio::gfsk_rxbw_print()
 {
     unsigned n;
-    uint8_t bwf, buf[4];
+    uint8_t bwf;
     gfskBW_t bw_reg;
-    radio.memRegRead(REG_ADDR_GFSK_BWF, 1, buf);
-    bw_reg.dword = radio.from_big_endian32(buf);
+    radio.memRegRead(REG_ADDR_GFSK_BWF, 1, &bw_reg.dword);
     bwf = bw_reg.bits.bwf_hi;
     bwf <<= 3;
     bwf |= bw_reg.bits.bwf_lo;
@@ -909,11 +1223,12 @@
 
 void Radio::gfsk_fdev_print()
 {
-    unsigned rb;
-    uint8_t *fdev_buf = gfsk_mp_buf+6;
-    radio.memRegRead(REG_ADDR_GFSK_FDEV, 1, fdev_buf);
-    rb = radio.from_big_endian32(fdev_buf);
-    pc.printf("%u", my_round(rb*FREQ_STEP));
+    unsigned hz;
+    uint32_t u32;
+    radio.memRegRead(REG_ADDR_GFSK_FDEV, 1, &u32);
+    hz = my_round(u32 * FREQ_STEP);
+    pc.printf("%u", hz);
+    radio.to_big_endian32(hz, gfsk_mp_buf+6);
 }
 
 bool Radio::gfsk_fdev_write(const char* txt)
@@ -930,13 +1245,11 @@
 
 void Radio::gfsk_pblLen_print()
 {
-    uint8_t buf[4];
     unsigned n;
-    gfskConfig1_t gcfg1;
-    radio.memRegRead(REG_ADDR_GFSK_CFG1, 1, buf);
-    gcfg1.dword = radio.from_big_endian32(buf);
-    pc.printf("%u", gcfg1.bits.preamble_length);
-    n = gcfg1.bits.preamble_length;
+    gfskConfig1_t cfg1;
+    radio.memRegRead(REG_ADDR_GFSK_CFG1, 1, &cfg1.dword);
+    pc.printf("%u", cfg1.bits.preamble_length);
+    n = cfg1.bits.preamble_length;
     gfsk_pp_buf[1] = n;
     n >>= 8;
     gfsk_pp_buf[0] = n;
@@ -946,7 +1259,7 @@
 {
     unsigned n;
     if (sscanf(txt, "%u", &n) == 1) {
-        to_big_endian16(n, gfsk_pp_buf);
+        radio.to_big_endian16(n, gfsk_pp_buf);
         radio.xfer(OPCODE_SET_PACKET_PARAM, 9, 0, gfsk_pp_buf);
     }
     return false;
@@ -965,14 +1278,11 @@
 
 unsigned Radio::gfsk_pblDetLen_read(bool forWriting)
 {
-    uint8_t buf[4];
-    gfskConfig1_t gcfg1;
-    radio.memRegRead(REG_ADDR_GFSK_CFG1, 1, buf);
-    gcfg1.dword = radio.from_big_endian32(buf);
-    if (gcfg1.bits.preamble_det_enable) {
-        //pc.printf("%u", (gcfg1.bits.preamble_det_len * 8) + 8);
-        gfsk_pp_buf[2] = gcfg1.bits.preamble_det_len + 4;
-        return gcfg1.bits.preamble_det_len + 1;
+    gfskConfig1_t cfg1;
+    radio.memRegRead(REG_ADDR_GFSK_CFG1, 1, &cfg1.dword);
+    if (cfg1.bits.preamble_det_enable) {
+        gfsk_pp_buf[2] = cfg1.bits.preamble_det_len + 4;
+        return cfg1.bits.preamble_det_len + 1;
     } else {
         gfsk_pp_buf[2] = 0;
         return 0;
@@ -994,26 +1304,10 @@
 
 void Radio::gfsk_syncWord_print(void)
 {
-    uint8_t buf[8];
-    uint32_t dword;
-#if 0
-    unsigned n, stop;
-    gfskConfig2_t cfg2;
-
-    radio.memRegRead(REG_ADDR_GFSK_CFG2, 1, buf);
-    cfg2.dword = radio.from_big_endian32(buf);
-    gfsk_pp_buf[3] = cfg2.bits.sync_word_length;
-
-    stop = cfg2.bits.sync_word_length >> 3; // bit-length to byte-length
-    for (n = 0; n < stop; n++) {
-        pc.printf("%02x", 0xaa);
-    }
-#endif /* if 0 */
-    radio.memRegRead(REG_ADDR_GFSK_SYNC_LO, 2, buf);
-    dword = radio.from_big_endian32(buf+4);
-    pc.printf("%08lx", dword);
-    dword = radio.from_big_endian32(buf);
-    pc.printf("%08lx", dword);
+    uint32_t dword[2];
+    radio.memRegRead(REG_ADDR_GFSK_SYNC_LO, 2, dword);
+    pc.printf("%08lx", dword[1]);
+    pc.printf("%08lx", dword[0]);
 }
 
 bool Radio::gfsk_syncWord_write(const char* txt)
@@ -1046,10 +1340,8 @@
 
 unsigned Radio::gfsk_addrcomp_read(bool forWriting)
 {
-    uint8_t buf[4];
     gfskConfig4_t cfg4;
-    radio.memRegRead(REG_ADDR_GFSK_PAYLOAD_LENGTH_B, 1, buf);
-    cfg4.dword = radio.from_big_endian32(buf);
+    radio.memRegRead(REG_ADDR_GFSK_PAYLOAD_LENGTH_B, 1, &cfg4.dword);
     gfsk_pp_buf[4] = cfg4.bits.addr_comp;
     return cfg4.bits.addr_comp;
 }
@@ -1065,10 +1357,8 @@
 
 bool Radio::gfsk_varlen_read()
 {
-    uint8_t buf[4];
     gfskConfig3_t cfg3;
-    radio.memRegRead(REG_ADDR_GFSK_CFG3, 1, buf);
-    cfg3.dword = radio.from_big_endian32(buf);
+    radio.memRegRead(REG_ADDR_GFSK_CFG3, 1, &cfg3.dword);
     gfsk_pp_buf[5] = cfg3.bits.variable_length;
     return cfg3.bits.variable_length;
 }
@@ -1084,10 +1374,8 @@
 
 void Radio::gfsk_syncLen_print()
 {
-    uint8_t buf[4];
     gfskConfig2_t cfg2;
-    radio.memRegRead(REG_ADDR_GFSK_CFG2, 1, buf);
-    cfg2.dword = radio.from_big_endian32(buf);
+    radio.memRegRead(REG_ADDR_GFSK_CFG2, 1, &cfg2.dword);
     gfsk_pp_buf[3] = cfg2.bits.sync_word_length;
     pc.printf("%u", cfg2.bits.sync_word_length);
 }
@@ -1116,11 +1404,9 @@
 
 unsigned Radio::gfsk_crcType_read(bool for_writing)
 {
-    uint8_t buf[4];
     unsigned ret;
     gfskConfig5_t cfg5;
-    radio.memRegRead(REG_ADDR_GFSK_CFG5, 1, buf);
-    cfg5.dword = radio.from_big_endian32(buf);
+    radio.memRegRead(REG_ADDR_GFSK_CFG5, 1, &cfg5.dword);
     // gfsk_pp_buf[7]: bit2:inv  bit1:twobyte  bit0:disabled
     if (cfg5.bits.crc_off)
         gfsk_pp_buf[7] |= 1;
@@ -1165,10 +1451,8 @@
 
 bool Radio::gfsk_dcfree_read(void)
 {
-    uint8_t buf[4];
     gfskConfig5_t cfg5;
-    radio.memRegRead(REG_ADDR_GFSK_CFG5, 1, buf);
-    cfg5.dword = radio.from_big_endian32(buf);
+    radio.memRegRead(REG_ADDR_GFSK_CFG5, 1, &cfg5.dword);
     gfsk_pp_buf[8] = cfg5.bits.whitening_enable;
     return cfg5.bits.whitening_enable;
 }
@@ -1184,21 +1468,44 @@
 
 void Radio::gfsk_crcinit_print()
 {
-    radio.memRegRead(REG_ADDR_GFSK_CRC_INIT, 1, gfsk_crc_params);
-    pc.printf("%04x", (unsigned)radio.from_big_endian32(gfsk_crc_params));
+    radio.memRegRead(REG_ADDR_GFSK_CRC_INIT, 1, &gfsk_crc_Poly);
+    pc.printf("%04x", (unsigned)gfsk_crc_Poly);
 }
 
 bool Radio::gfsk_crcinit_write(const char *txt)
 {
+    uint8_t buf[8];
     unsigned crcInit;
     sscanf(txt, "%x", &crcInit);
-    to_big_endian16(crcInit, gfsk_crc_params); 
-    radio.xfer(OPCODE_SET_GFSK_CRC_PARAMS, 8, 0, gfsk_crc_params);
+    gfsk_crc_initValue = crcInit;
+    radio.to_big_endian32(gfsk_crc_initValue, buf);
+    radio.to_big_endian32(gfsk_crc_Poly, buf+4);
+    radio.xfer(OPCODE_SET_GFSK_CRC_PARAMS, 8, 0, buf);
     return false;
 }
 
 const value_item_t Radio::gfsk_crcinit_item = { _ITEM_VALUE, 4, gfsk_crcinit_print, gfsk_crcinit_write}; 
 
+void Radio::gfsk_crcpoly_print()
+{
+    radio.memRegRead(REG_ADDR_GFSK_CRC_POLY, 1, &gfsk_crc_Poly);
+    pc.printf("%04x", (unsigned)gfsk_crc_Poly);
+}
+
+bool Radio::gfsk_crcpoly_write(const char *txt)
+{
+    uint8_t buf[8];
+    unsigned crcPoly;
+    sscanf(txt, "%x", &crcPoly);
+    gfsk_crc_Poly = crcPoly;
+    radio.to_big_endian32(gfsk_crc_initValue, buf);
+    radio.to_big_endian32(gfsk_crc_Poly, buf+4);
+    radio.xfer(OPCODE_SET_GFSK_CRC_PARAMS, 8, 0, buf);
+    return false;
+}
+
+const value_item_t Radio::gfsk_crcpoly_item = { _ITEM_VALUE, 4, gfsk_crcpoly_print, gfsk_crcpoly_write}; 
+
 const menu_t Radio::gfsk_menu[] = {
     { {FIRST_CHIP_MENU_ROW+2,  1},    "bps:", &gfsk_bitrate_item, FLAG_MSGTYPE_ALL },
     { {FIRST_CHIP_MENU_ROW+2, 15},    "bt:",       &gfsk_bt_item, FLAG_MSGTYPE_ALL },
@@ -1215,29 +1522,34 @@
     { {FIRST_CHIP_MENU_ROW+5,  1},        NULL,   &gfsk_varlen_item, FLAG_MSGTYPE_ALL },
     { {FIRST_CHIP_MENU_ROW+5, 10},   "whiten:",   &gfsk_dcfree_item, FLAG_MSGTYPE_ALL },
     { {FIRST_CHIP_MENU_ROW+5, 23},  "crcInit:",   &gfsk_crcinit_item, FLAG_MSGTYPE_ALL },
+    { {FIRST_CHIP_MENU_ROW+5, 36},  "crcPoly:",   &gfsk_crcpoly_item, FLAG_MSGTYPE_ALL },
 
     { {FIRST_CHIP_MENU_ROW+6,  1},          NULL, &dbg_item, FLAG_MSGTYPE_ALL},
     { {0, 0}, NULL, NULL }
 };
 
 static const char* const lora_bwstrs[] = {
-    " 62.5KHz", "  125KHz", "  250KHz",
-    "  500KHz",
-    NULL
+   /* 0 */ "  7.8KHz",
+   /* 1 */ " 15.6KHz",
+   /* 2 */ "31,25KHz",
+   /* 3 */ " 62.5KHz",
+   /* 4 */ "  125KHz",
+   /* 5 */ "  250KHz",
+   /* 6 */ "  500KHz",
+   /* 7 */ " 1000KHz",
+   /* 0 */ NULL
 };
 
 unsigned Radio::lora_bw_read(bool forWriting)
 {
-    uint8_t buf[4];
     loraConfig0_t cfg0;
-    radio.memRegRead(REG_ADDR_LORA_CONFIG0, 1, buf);
-    cfg0.dword = radio.from_big_endian32(buf);
-    return cfg0.bits.modem_bw - 3;
+    radio.memRegRead(REG_ADDR_LORA_CONFIG0, 1, &cfg0.dword);
+    return cfg0.bits.modem_bw;
 }
 
 menuMode_e Radio::lora_bw_write(unsigned sidx)
 {
-    lora_mp_buf[1] = sidx + 3;
+    lora_mp_buf[1] = sidx;
     radio.xfer(OPCODE_SET_MODULATION, 4, 0, lora_mp_buf);
     return MENUMODE_REDRAW;
 }
@@ -1246,10 +1558,8 @@
 
 void Radio::lora_sf_print()
 {
-    uint8_t buf[4];
     loraConfig0_t cfg0;
-    radio.memRegRead(REG_ADDR_LORA_CONFIG0, 1, buf);
-    cfg0.dword = radio.from_big_endian32(buf);
+    radio.memRegRead(REG_ADDR_LORA_CONFIG0, 1, &cfg0.dword);
     lora_mp_buf[0] = cfg0.bits.modem_sf;
     pc.printf("%u", cfg0.bits.modem_sf);
 }
@@ -1268,11 +1578,9 @@
 
 void Radio::lora_pblLen_print()
 {
-    uint8_t buf[4];
     unsigned pl;
     loraConfigC_t cfgc;
-    radio.memRegRead(REG_ADDR_LORA_CONFIGC, 1, buf);
-    cfgc.dword = radio.from_big_endian32(buf);
+    radio.memRegRead(REG_ADDR_LORA_CONFIGC, 1, &cfgc.dword);
     pc.printf("%u", cfgc.bits.preamble_length);
     pl = cfgc.bits.preamble_length;
     lora_pp_buf[1] = pl & 0xff;
@@ -1284,7 +1592,7 @@
 {
     unsigned n;
     if (sscanf(txt, "%u", &n) == 1) {
-        to_big_endian16(n, lora_pp_buf); 
+        radio.to_big_endian16(n, lora_pp_buf); 
         radio.xfer(OPCODE_SET_PACKET_PARAM, 6, 0, lora_pp_buf);
     }
     return false;
@@ -1294,10 +1602,8 @@
 
 bool Radio::lora_headerType_read()
 {
-    uint8_t buf[4];
     loraConfig0_t cfg0;
-    radio.memRegRead(REG_ADDR_LORA_CONFIG0, 1, buf);
-    cfg0.dword = radio.from_big_endian32(buf);
+    radio.memRegRead(REG_ADDR_LORA_CONFIG0, 1, &cfg0.dword);
     lora_pp_buf[2] = cfg0.bits.implicit_header;
     return cfg0.bits.implicit_header;
 }
@@ -1331,10 +1637,8 @@
 
 unsigned Radio::lora_cr_read(bool forWriting)
 {
-    uint8_t buf[4];
     loraConfig0_t cfg0;
-    radio.memRegRead(REG_ADDR_LORA_CONFIG0, 1, buf);
-    cfg0.dword = radio.from_big_endian32(buf);
+    radio.memRegRead(REG_ADDR_LORA_CONFIG0, 1, &cfg0.dword);
     lora_mp_buf[2] = cfg0.bits.coding_rate;
     if (lora_mp_buf[2] > 7)
         return 7;
@@ -1353,10 +1657,8 @@
 
 bool Radio::ppmOffset_read()
 {
-    uint8_t buf[4];
     loraConfig0_t cfg0;
-    radio.memRegRead(REG_ADDR_LORA_CONFIG0, 1, buf);
-    cfg0.dword = radio.from_big_endian32(buf);
+    radio.memRegRead(REG_ADDR_LORA_CONFIG0, 1, &cfg0.dword);
     lora_mp_buf[3] = cfg0.bits.ppm_offset;
     return lora_mp_buf[3];
 }
@@ -1379,10 +1681,8 @@
 
 bool Radio::lora_crcon_read()
 {
-    uint8_t buf[4];
     loraConfig0_t cfg0;
-    radio.memRegRead(REG_ADDR_LORA_CONFIG0, 1, buf);
-    cfg0.dword = radio.from_big_endian32(buf);
+    radio.memRegRead(REG_ADDR_LORA_CONFIG0, 1, &cfg0.dword);
     lora_pp_buf[4] = cfg0.bits.crc_on;
     return cfg0.bits.crc_on;
 }
@@ -1410,11 +1710,9 @@
 
 bool Radio::lora_inviq_read()
 {
-    uint8_t buf[4];
     loraConfigA_t cfgA;
     //loraConfigB_t cfgB;
-    radio.memRegRead(REG_ADDR_LORA_CONFIGA, 1, buf);
-    cfgA.dword = radio.from_big_endian32(buf);
+    radio.memRegRead(REG_ADDR_LORA_CONFIGA, 1, &cfgA.dword);
     /*radio.memRegRead(REG_ADDR_LORA_CONFIGB, 1, buf);
     cfgB.dword = radio.from_big_endian32(buf);*/
     //TODO which one is RX vs TX: cfgA.bits.invertIQ, cfgB.bits.invertIQ
@@ -1426,16 +1724,9 @@
 
 bool Radio::lora_inviq_push()
 {
-    bool ret;
-    if (lora_pp_buf[5]) {
-        lora_pp_buf[5] = 0;
-        ret = false;
-    } else {
-        lora_pp_buf[5] = 1;
-        ret = true;
-    }
+    lora_pp_buf[5] ^= 1;
     radio.xfer(OPCODE_SET_PACKET_PARAM, 6, 0, lora_pp_buf);
-    return ret;
+    return lora_pp_buf[5] == 1;
 }
 
 const toggle_item_t Radio::lora_inviq_item = { _ITEM_TOGGLE,
@@ -1446,11 +1737,8 @@
 
 bool Radio::lora_sync_read()
 {
-    uint8_t buf[4];
     loraSync_t sync;
-    radio.memRegRead(REG_ADDR_LORA_SYNC, 1, buf);
-    sync.dword = radio.from_big_endian32(buf);
-    //printf("[%u %u]\r\n", sync.bits.ppg_a, sync.bits.ppg_b);
+    radio.memRegRead(REG_ADDR_LORA_SYNC, 1, &sync.dword);
     if (sync.bits.ppg_a == 2 && sync.bits.ppg_b == 4)
         return false;   // private
     else if (sync.bits.ppg_a == 6 && sync.bits.ppg_b == 8)
@@ -1496,14 +1784,7 @@
 
 void Radio::dbg_push()
 {
-/*    log_printf("pa_config_buf %02x %02x %02x %02x\r\n",
-            pa_config_buf[0],
-            pa_config_buf[1],
-            pa_config_buf[2],
-            pa_config_buf[3]
-    );*/
-    log_printf("cmdCnt:%u\r\n", radio.cmdCnt);
-    radio.cmdCnt = 0;
+    log_printf("dbg\r\n");
 }
 
 const button_item_t Radio::dbg_item  = { _ITEM_BUTTON, "DBG", dbg_push };
@@ -1520,9 +1801,7 @@
 bool Radio::DIO10en_read()
 {
     dioEnable_t dio;
-    uint8_t buf[4];
-    radio.memRegRead(REG_ADDR_DIO10, 1, buf);
-    dio.dword = radio.from_big_endian32(buf);
+    radio.memRegRead(REG_ADDR_DIO10, 1, &dio.dword);
     if (dio.bits.enable)
         dioBuf[DIO_en_IDX] |= DIO10_BIT;
     else
@@ -1533,9 +1812,7 @@
 bool Radio::DIO8en_read()
 {
     dioEnable_t dio;
-    uint8_t buf[4];
-    radio.memRegRead(REG_ADDR_DIO8, 1, buf);
-    dio.dword = radio.from_big_endian32(buf);
+    radio.memRegRead(REG_ADDR_DIO8, 1, &dio.dword);
     if (dio.bits.enable)
         dioBuf[DIO_en_IDX] |= DIO8_BIT;
     else
@@ -1546,9 +1823,7 @@
 bool Radio::DIO7en_read()
 {
     dioEnable_t dio;
-    uint8_t buf[4];
-    radio.memRegRead(REG_ADDR_DIO7, 1, buf);
-    dio.dword = radio.from_big_endian32(buf);
+    radio.memRegRead(REG_ADDR_DIO7, 1, &dio.dword);
     if (dio.bits.enable)
         dioBuf[DIO_en_IDX] |= DIO7_BIT;
     else
@@ -1559,9 +1834,7 @@
 bool Radio::DIO6en_read()
 {
     dioEnable_t dio;
-    uint8_t buf[4];
-    radio.memRegRead(REG_ADDR_DIO6, 1, buf);
-    dio.dword = radio.from_big_endian32(buf);
+    radio.memRegRead(REG_ADDR_DIO6, 1, &dio.dword);
     if (dio.bits.enable)
         dioBuf[DIO_en_IDX] |= DIO6_BIT;
     else
@@ -1572,9 +1845,7 @@
 bool Radio::DIO5en_read()
 {
     dioEnable_t dio;
-    uint8_t buf[4];
-    radio.memRegRead(REG_ADDR_DIO5, 1, buf);
-    dio.dword = radio.from_big_endian32(buf);
+    radio.memRegRead(REG_ADDR_DIO5, 1, &dio.dword);
     if (dio.bits.enable)
         dioBuf[DIO_en_IDX] |= DIO5_BIT;
     else
@@ -2020,56 +2291,56 @@
 const toggle_item_t Radio::DIO10wifi_item = { _ITEM_TOGGLE, "DIO10", NULL, DIO10wifi_read, DIO10wifi_push};
 
 const menu_t Radio::none_menu[] = {
-    { {FIRST_CHIP_MENU_ROW+1,  1},          NULL, &rfswEn_item, FLAG_MSGTYPE_ALL},
-    { {FIRST_CHIP_MENU_ROW+1, 14},          NULL, &DIO10en_item, FLAG_MSGTYPE_ALL},
-    { {FIRST_CHIP_MENU_ROW+1, 21},          NULL, &DIO8en_item, FLAG_MSGTYPE_ALL},
-    { {FIRST_CHIP_MENU_ROW+1, 26},          NULL, &DIO7en_item, FLAG_MSGTYPE_ALL},
-    { {FIRST_CHIP_MENU_ROW+1, 32},          NULL, &DIO6en_item, FLAG_MSGTYPE_ALL},
-    { {FIRST_CHIP_MENU_ROW+1, 38},          NULL, &DIO5en_item, FLAG_MSGTYPE_ALL},
-
-    { {FIRST_CHIP_MENU_ROW+2,  1},          NULL, &rfswStbyCfg_item, FLAG_MSGTYPE_ALL},
-    { {FIRST_CHIP_MENU_ROW+2, 14},          NULL, &DIO10stby_item, FLAG_MSGTYPE_ALL},
-    { {FIRST_CHIP_MENU_ROW+2, 21},          NULL, &DIO8stby_item, FLAG_MSGTYPE_ALL},
-    { {FIRST_CHIP_MENU_ROW+2, 26},          NULL, &DIO7stby_item, FLAG_MSGTYPE_ALL},
-    { {FIRST_CHIP_MENU_ROW+2, 32},          NULL, &DIO6stby_item, FLAG_MSGTYPE_ALL},
-    { {FIRST_CHIP_MENU_ROW+2, 38},          NULL, &DIO5stby_item, FLAG_MSGTYPE_ALL},
-
-    { {FIRST_CHIP_MENU_ROW+3,  1},          NULL, &rfswRxCfg_item, FLAG_MSGTYPE_ALL},
-    { {FIRST_CHIP_MENU_ROW+3, 14},          NULL, &DIO10rx_item, FLAG_MSGTYPE_ALL},
-    { {FIRST_CHIP_MENU_ROW+3, 21},          NULL, &DIO8rx_item, FLAG_MSGTYPE_ALL},
-    { {FIRST_CHIP_MENU_ROW+3, 26},          NULL, &DIO7rx_item, FLAG_MSGTYPE_ALL},
-    { {FIRST_CHIP_MENU_ROW+3, 32},          NULL, &DIO6rx_item, FLAG_MSGTYPE_ALL},
-    { {FIRST_CHIP_MENU_ROW+3, 38},          NULL, &DIO5rx_item, FLAG_MSGTYPE_ALL},
-
-    { {FIRST_CHIP_MENU_ROW+4,  1},          NULL, &rfswTxCfg_item, FLAG_MSGTYPE_ALL},
-    { {FIRST_CHIP_MENU_ROW+4, 14},          NULL, &DIO10tx_item, FLAG_MSGTYPE_ALL},
-    { {FIRST_CHIP_MENU_ROW+4, 21},          NULL, &DIO8tx_item, FLAG_MSGTYPE_ALL},
-    { {FIRST_CHIP_MENU_ROW+4, 26},          NULL, &DIO7tx_item, FLAG_MSGTYPE_ALL},
-    { {FIRST_CHIP_MENU_ROW+4, 32},          NULL, &DIO6tx_item, FLAG_MSGTYPE_ALL},
-    { {FIRST_CHIP_MENU_ROW+4, 38},          NULL, &DIO5tx_item, FLAG_MSGTYPE_ALL},
-
-    { {FIRST_CHIP_MENU_ROW+5,  1},          NULL, &rfswTxHPCfg_item, FLAG_MSGTYPE_ALL},
-    { {FIRST_CHIP_MENU_ROW+5, 14},          NULL, &DIO10txhp_item, FLAG_MSGTYPE_ALL},
-    { {FIRST_CHIP_MENU_ROW+5, 21},          NULL, &DIO8txhp_item, FLAG_MSGTYPE_ALL},
-    { {FIRST_CHIP_MENU_ROW+5, 26},          NULL, &DIO7txhp_item, FLAG_MSGTYPE_ALL},
-    { {FIRST_CHIP_MENU_ROW+5, 32},          NULL, &DIO6txhp_item, FLAG_MSGTYPE_ALL},
-    { {FIRST_CHIP_MENU_ROW+5, 38},          NULL, &DIO5txhp_item, FLAG_MSGTYPE_ALL},
-
-    { {FIRST_CHIP_MENU_ROW+6,  1},          NULL, &rfswGnssCfg_item, FLAG_MSGTYPE_ALL},
-    { {FIRST_CHIP_MENU_ROW+6, 14},          NULL, &DIO10gnss_item, FLAG_MSGTYPE_ALL},
-    { {FIRST_CHIP_MENU_ROW+6, 21},          NULL, &DIO8gnss_item, FLAG_MSGTYPE_ALL},
-    { {FIRST_CHIP_MENU_ROW+6, 26},          NULL, &DIO7gnss_item, FLAG_MSGTYPE_ALL},
-    { {FIRST_CHIP_MENU_ROW+6, 32},          NULL, &DIO6gnss_item, FLAG_MSGTYPE_ALL},
-    { {FIRST_CHIP_MENU_ROW+6, 38},          NULL, &DIO5gnss_item, FLAG_MSGTYPE_ALL},
-
-    { {FIRST_CHIP_MENU_ROW+7,  1},          NULL, &rfswWifiCfg_item, FLAG_MSGTYPE_ALL},
-    { {FIRST_CHIP_MENU_ROW+7, 14},          NULL, &DIO10wifi_item, FLAG_MSGTYPE_ALL},
-    { {FIRST_CHIP_MENU_ROW+7, 21},          NULL, &DIO8wifi_item, FLAG_MSGTYPE_ALL},
-    { {FIRST_CHIP_MENU_ROW+7, 26},          NULL, &DIO7wifi_item, FLAG_MSGTYPE_ALL},
-    { {FIRST_CHIP_MENU_ROW+7, 32},          NULL, &DIO6wifi_item, FLAG_MSGTYPE_ALL},
-    { {FIRST_CHIP_MENU_ROW+7, 38},          NULL, &DIO5wifi_item, FLAG_MSGTYPE_ALL},
-
-    { {FIRST_CHIP_MENU_ROW+8,  1},          NULL, &dbg_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+3,  1},          NULL, &rfswEn_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+3, 14},          NULL, &DIO10en_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+3, 21},          NULL, &DIO8en_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+3, 26},          NULL, &DIO7en_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+3, 32},          NULL, &DIO6en_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+3, 38},          NULL, &DIO5en_item, FLAG_MSGTYPE_ALL},
+
+    { {FIRST_CHIP_MENU_ROW+4,  1},          NULL, &rfswStbyCfg_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+4, 14},          NULL, &DIO10stby_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+4, 21},          NULL, &DIO8stby_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+4, 26},          NULL, &DIO7stby_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+4, 32},          NULL, &DIO6stby_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+4, 38},          NULL, &DIO5stby_item, FLAG_MSGTYPE_ALL},
+
+    { {FIRST_CHIP_MENU_ROW+5,  1},          NULL, &rfswRxCfg_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+5, 14},          NULL, &DIO10rx_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+5, 21},          NULL, &DIO8rx_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+5, 26},          NULL, &DIO7rx_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+5, 32},          NULL, &DIO6rx_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+5, 38},          NULL, &DIO5rx_item, FLAG_MSGTYPE_ALL},
+
+    { {FIRST_CHIP_MENU_ROW+6,  1},          NULL, &rfswTxCfg_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+6, 14},          NULL, &DIO10tx_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+6, 21},          NULL, &DIO8tx_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+6, 26},          NULL, &DIO7tx_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+6, 32},          NULL, &DIO6tx_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+6, 38},          NULL, &DIO5tx_item, FLAG_MSGTYPE_ALL},
+
+    { {FIRST_CHIP_MENU_ROW+7,  1},          NULL, &rfswTxHPCfg_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+7, 14},          NULL, &DIO10txhp_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+7, 21},          NULL, &DIO8txhp_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+7, 26},          NULL, &DIO7txhp_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+7, 32},          NULL, &DIO6txhp_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+7, 38},          NULL, &DIO5txhp_item, FLAG_MSGTYPE_ALL},
+
+    { {FIRST_CHIP_MENU_ROW+8,  1},          NULL, &rfswGnssCfg_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+8, 14},          NULL, &DIO10gnss_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+8, 21},          NULL, &DIO8gnss_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+8, 26},          NULL, &DIO7gnss_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+8, 32},          NULL, &DIO6gnss_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+8, 38},          NULL, &DIO5gnss_item, FLAG_MSGTYPE_ALL},
+
+    { {FIRST_CHIP_MENU_ROW+9,  1},          NULL, &rfswWifiCfg_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+9, 14},          NULL, &DIO10wifi_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+9, 21},          NULL, &DIO8wifi_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+9, 26},          NULL, &DIO7wifi_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+9, 32},          NULL, &DIO6wifi_item, FLAG_MSGTYPE_ALL},
+    { {FIRST_CHIP_MENU_ROW+9, 38},          NULL, &DIO5wifi_item, FLAG_MSGTYPE_ALL},
+
+    { {LAST_CHIP_MENU_ROW,  1},          NULL, &dbg_item, FLAG_MSGTYPE_ALL},
     { {0, 0}, NULL, NULL }
 };
 
@@ -2112,6 +2383,13 @@
 void Radio::boardInit(const RadioEvents_t* e)
 {
     uint8_t buf[4];
+
+    {
+        float default_ms = 62.5;
+        unsigned ticks = default_ms * 32.768;
+        radio.to_big_endian24(ticks, tcxo_buf+1);
+    }
+
     radio.txDone = txDoneBottom;
     radio.rxDone = rxDone;
     radio.cadDone = cadDone;
@@ -2134,6 +2412,117 @@
     readChip();
 }
 
+const char* const navToHostStr[] = {
+    /*  0 */ "ok",
+    /*  1 */ "cmd unexpected",
+    /*  2 */ "cmd not implemented",
+    /*  3 */ "cmd paramters invalid",
+    /*  4 */ "message sanity check",
+    /*  5 */ "scanning failed",
+    /*  6 */ "no time",
+    /*  7 */ "no satellite detected",
+    /*  8 */ "almanac too old",
+    /*  9 */ "alamanac update fail: crc",
+    /* 10 */ "alamanac update fail: flash integrity",
+    /* 11 */ "alamanac update fail: date too old",
+    /* 12 */ "alamanac update fail: not allowed",
+    /* 13 */ "global almanac crc error",
+    /* 14 */ "almanac version not supported",
+    /* 15 */ ""
+};
+
+struct wifidr {
+    const char *txt;
+    float Mbps;
+};
+
+const struct wifidr wifiDatarates[] = {
+    /*   0 */ { NULL, 0},
+    /*   1 */ { "DBPSK", 1},
+    /*   2 */ { "DQPSK", 2},
+    /*   3 */ { "BPSK", 6},
+    /*   4 */ { "BPSK", 9},
+    /*   5 */ { "QPSK", 12},
+    /*   6 */ { "QPSK", 18},
+    /*   7 */ { "16-QAM", 24},
+    /*   8 */ { "16-QAM", 36},
+    /*   9 */ { "(9)", 0},
+    /*  10 */ { "(10)", 0},
+    /*  11 */ { "BPSK", 6.5},
+    /*  12 */ { "QPSK", 13},
+    /*  13 */ { "QPSK", 19.5},
+    /*  14 */ { "16-QAM", 26},
+    /*  15 */ { "16-QAM", 39},
+    /*  16 */ { "(16)", 0},
+    /*  17 */ { "(17)", 0},
+    /*  18 */ { "(18)", 0},
+    /*  19 */ { "BPSK", 7.2},
+    /*  20 */ { "QPSK", 14.4},
+    /*  21 */ { "QPSK", 21.7},
+    /*  22 */ { "16-QAM", 28.9},
+    /*  23 */ { "16-QAM", 43.3},
+};
+
+
+void print_wifi_result(const uint8_t *result)
+{
+    char out[96];
+    char str[24];
+    unsigned n, macStart;
+    wifiType_t wt;
+    wifiChanInfo_t ci;
+    wt.octet = result[0];
+    ci.octet = result[1];
+    out[0] = 0;
+    strcat(out, "802.11");
+    switch (wt.bits.signal) {
+        case 1: strcat(out, "b"); break;
+        case 2: strcat(out, "g"); break;
+        case 3: strcat(out, "n"); break;
+    }
+    sprintf(str, " %s %.1fMbps", wifiDatarates[wt.bits.datarate].txt, wifiDatarates[wt.bits.datarate].Mbps);
+    strcat(out, str);
+    strcat(out, " ");
+
+    sprintf(str, "ch%u ", ci.bits.channelID);
+    strcat(out, str);
+    switch (ci.bits.channelID) {
+        // table 10-5
+    }
+    strcat(out, " ");
+    sprintf(str, "mv:%u ", ci.bits.macValidationID);
+    strcat(out, str);
+    switch (ci.bits.macValidationID) {
+        case 1: strcat(out, "gateway"); break;
+        case 2: strcat(out, "phone"); break;
+        case 3: strcat(out, "?"); break;
+        // table 10.8
+    }
+
+    sprintf(str, " rssi:%d ", (int8_t)result[2]);
+    strcat(out, str);
+
+    if (wifiResultFormatBasic) {
+        macStart = 3;
+    } else {
+        macStart = 4;
+    }
+    for (n = 0; n < 6; n++) {
+        sprintf(str, "%02x", result[n+macStart]);
+        strcat(out, str);
+        if (n < 5)
+            strcat(out, ":");
+    }
+    strcat(out, " ");
+
+
+    if (!wifiResultFormatBasic) {
+        sprintf(str, "frameCtrl:%02x ", result[3]);
+        strcat(out, str);
+    }
+    log_printf("%s\r\n", out);
+}
+
 bool Radio::service(int8_t statusRow)
 {
     irq_t irq;
@@ -2180,19 +2569,8 @@
                 strcat(str, "pll_calib ");
             if (radio.errorStat.bits.img_calib)
                 strcat(str, "img_calib ");
-            if (radio.errorStat.bits.hf_xosc_start) {
-                char txt[32];
-                uint8_t buf[5];
-                unsigned delay = 2048;  // 2048=62.5ms
+            if (radio.errorStat.bits.hf_xosc_start_)
                 strcat(str, "hf_xosc_start ");
-                buf[0] = 5; // tune-voltage
-                radio.to_big_endian32(delay, buf+1);
-                sprintf(txt, "%u_tcxo-recalibrate ", recalCnt++);
-                strcat(str, txt);
-                radio.xfer(OPCODE_SET_TCXO_MODE, 5, 0, buf);
-                buf[0] = 0x3f;  // TODO actual bit for HF-xosc
-                radio.xfer(OPCODE_CALIBRATE, 1, 0, buf);
-            }
             if (radio.errorStat.bits.lf_xosc_start)
                 strcat(str, "lf_xosc_start ");
             if (radio.errorStat.bits.pll_lock)
@@ -2209,12 +2587,86 @@
             strcat(str, "GNSSDone ");
         }
         log_printf("%s\r\n", str);
-    }
+
+        /****************************/
+        if (irq.bits.WifiDone) {
+            stat_t stat;
+            uint8_t nbResults;
+            stat.word = radio.xfer(OPCODE_GET_WIFI_NB_RESULTS, 0, 0, NULL);
+            stat.word = radio.xfer(0x0000, 0, 1, &nbResults);
+            if (stat.bits.cmdStatus == CMD_DAT) {
+                unsigned n;
+                log_printf("nbResults:%u\r\n", nbResults);
+                for (n = 0; n < nbResults; n++) {
+                    uint8_t buf[3];
+                    uint8_t resultBuf[22];
+                    buf[0] = n;
+                    buf[1] = 1; // number of results in this read
+                    buf[2] = wifiResultFormatBasic ? 4 : 1;
+                    stat.word = radio.xfer(OPCODE_WIFI_READ_RESULTS, 3, 0, buf);
+                    // basic =  9byte length
+                    // full  = 22byte length
+                    stat.word = radio.xfer(0x0000, 0, wifiResultFormatBasic ? 9 : 22, resultBuf);
+                    if (stat.bits.cmdStatus == CMD_DAT)
+                        print_wifi_result(resultBuf);
+                    else
+                        log_printf("readResult:%s\r\n", radio.cmdStatus_toString(stat.bits.cmdStatus));
+                }
+            } else
+                log_printf("nbResults:%s\r\n", radio.cmdStatus_toString(stat.bits.cmdStatus));
+        } // ..if (irq.bits.WifiDone)
+
+        if (irq.bits.GNSSDone) {
+            uint8_t gnssResultBuf[512];
+            uint8_t buf[2];
+            stat_t stat;
+            unsigned resultSize;
+            stat.word = radio.xfer(OPCODE_GNSS_GET_RESULT_SIZE, 0, 0, NULL);
+            stat.word = radio.xfer(0x0000, 0, 2, buf);
+            if (stat.bits.cmdStatus == CMD_DAT) {
+                resultSize = buf[1];
+                resultSize <<= 8;
+                resultSize |= buf[0];
+                log_printf("resultSize:%u\r\n", resultSize);
+                stat.word = radio.xfer(OPCODE_GNSS_READ_RESULTS, 0, 0, NULL);
+                stat.word = radio.xfer(0x0000, 0, resultSize, gnssResultBuf);
+                if (stat.bits.cmdStatus == CMD_DAT) {
+                    unsigned i = 0, n;
+                    switch (gnssResultBuf[0]) {
+                        case 0: log_printf("navToHost:%s\r\n", navToHostStr[gnssResultBuf[0]]); break;
+                        case 1: log_printf("navToSolver\r\n"); break;
+                        case 2: log_printf("navToDMC\r\n"); break;
+                        default: log_printf("nav:%u\r\n", gnssResultBuf[0]); break;
+                    }
+                    for (i = 0; i < resultSize; ) {
+                        printf("%03x:", i);
+                        for (n = 0; n < 16; n++) {
+                            printf("%02x ", gnssResultBuf[i+n]);
+                            if (n == 7)
+                                printf(" ");
+                        }
+                        for (n = 0; n < 16; n++) {
+                            if (n > ' ' && n < 0x7f)
+                                printf("%c", gnssResultBuf[i+n]);
+                            else
+                                printf(".");
+                            if (n == 7)
+                                printf(" ");
+                        }
+                        printf("\r\n");
+                        i += 16;
+                    }
+                } else
+                    log_printf("resultBuf:%s\r\n", radio.cmdStatus_toString(stat.bits.cmdStatus));
+            } else
+                log_printf("resultSize:%s\r\n", radio.cmdStatus_toString(stat.bits.cmdStatus));
+        } // ..if (irq.bits.GNSSDone)
+
+    } // ..if (irq.dword != 0)
     return false;
 }
 
 char Radio::chip_ver[24];
-//const char* const Radio::chipNum_str = "LR1110";
 const char* const Radio::chipNum_str = chip_ver;
 
 const char* const Radio::opmode_select_strs[] = {
@@ -2324,13 +2776,10 @@
 
 void Radio::tx_dbm_print()
 {
-    uint8_t buf[4];
     txParamsB_t txpb;   // txpb.bits.PaSel
     txParamsC_t txpc;
-    radio.memRegRead(REG_ADDR_TX_PARAMS_C, 1, buf);
-    txpc.dword = radio.from_big_endian32(buf);
-    radio.memRegRead(REG_ADDR_TX_PARAMS_B, 1, buf);
-    txpb.dword = radio.from_big_endian32(buf);
+    radio.memRegRead(REG_ADDR_TX_PARAMS_C, 1, &txpc.dword);
+    radio.memRegRead(REG_ADDR_TX_PARAMS_B, 1, &txpb.dword);
     if (txpb.bits.PaSel)
         pc.printf("%d", txpc.bits.tx_dbm - 9);
     else
@@ -2351,10 +2800,8 @@
 
 unsigned Radio::tx_ramp_read(bool fw)
 {
-    uint8_t buf[4];
     txParamsC_t txpc;
-    radio.memRegRead(REG_ADDR_TX_PARAMS_C, 1, buf);
-    txpc.dword = radio.from_big_endian32(buf);
+    radio.memRegRead(REG_ADDR_TX_PARAMS_C, 1, &txpc.dword);
     return txpc.bits.pa_ramp_time;
 }