1

Revision:
0:9c052ff8dd6a
Child:
1:e79b0a55135f
diff -r 000000000000 -r 9c052ff8dd6a radio_sx128x.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/radio_sx128x.cpp	Thu Jul 05 17:31:54 2018 -0700
@@ -0,0 +1,616 @@
+#include "radio.h"
+#ifdef SX128x_H 
+#include "SPIu.h"
+#include <float.h>
+
+#ifdef TARGET_FF_ARDUINO    /* pins of SX126xDVK1xAS board */
+    SPIu spi(D11, D12, D13); // mosi, miso, sclk
+    //           spi, nss, busy, dio1
+    SX128x Radio::radio(spi,  D7,   D3,   D5 );
+
+    #define LED_ON      1
+    #define LED_OFF     0
+    DigitalOut tx_led(A4);
+    DigitalOut rx_led(A5);
+
+    #define NRST_PIN        A0
+
+    DigitalOut ant_sw(A3);
+    DigitalOut cps(D6); // SE2436L
+
+    bool fe_enable; // SE2436L
+
+    void Radio::chipModeChange()
+    {
+        if (radio.chipMode == CHIPMODE_NONE) {
+            cps = 0;
+            tx_led = LED_OFF;
+            rx_led = LED_OFF;
+        } else if (radio.chipMode == CHIPMODE_TX) {
+            cps = fe_enable;
+            tx_led = LED_ON;
+            rx_led = LED_OFF;
+        } else if (radio.chipMode == CHIPMODE_RX) {
+            cps = fe_enable;
+            tx_led = LED_OFF;
+            rx_led = LED_ON;
+        }
+    }
+#endif /* TARGET_FF_ARDUINO */
+
+#ifdef TARGET_FF_MORPHO
+    DigitalOut pc3(PC_3);   // debug RX indication, for nucleo boards
+#endif /* TARGET_FF_MORPHO */
+
+
+LowPowerTimer Radio::lpt;
+RadioModems_t Radio::_m_;
+
+PacketParams_t Radio::ppGFSK;
+PacketParams_t Radio::ppLORA;
+PacketParams_t Radio::ppFLRC;
+
+ModulationParams_t Radio::mpBLE_GFSK;
+ModulationParams_t Radio::mpFLRC;
+ModulationParams_t Radio::mpLORA;
+
+const RadioEvents_t* RadioEvents;
+volatile us_timestamp_t Radio::irqAt;
+
+void Radio::readChip()
+{
+    uint8_t reg8;
+
+    reg8 = radio.readReg(REG_ADDR_PKTCTRL0, 1);
+    ppGFSK.gfskFLRC.HeaderType = reg8 & 0x20;
+    ppFLRC.gfskFLRC.HeaderType = reg8 & 0x20;
+
+    reg8 = radio.readReg(REG_ADDR_PKTCTRL1, 1);
+    ppGFSK.gfskFLRC.PreambleLength = reg8 & 0x70;
+    ppFLRC.gfskFLRC.PreambleLength = reg8 & 0x70;
+    ppGFSK.gfskFLRC.SyncWordLength = reg8 & 0x0e;
+    ppFLRC.gfskFLRC.SyncWordLength = reg8 & 0x06;
+    if (ppFLRC.gfskFLRC.SyncWordLength == 0x06)
+        ppFLRC.gfskFLRC.SyncWordLength = FLRC_SYNC_WORD_LEN_P32S;
+
+    reg8 = radio.readReg(REG_ADDR_PKT_SYNC_ADRS_CTRL, 1);
+    ppGFSK.gfskFLRC.SyncWordMatch = reg8 & 0x70;
+    ppFLRC.gfskFLRC.SyncWordMatch = reg8 & 0x70;
+
+    reg8 = radio.readReg(REG_ADDR_PAYLOAD_LEN, 1);
+    ppGFSK.gfskFLRC.PayloadLength = reg8;
+    ppFLRC.gfskFLRC.PayloadLength = reg8;
+
+    reg8 = radio.readReg(REG_ADDR_PKT_TX_HEADER, 1);    // TODO hi bit of payload length
+    //ppBLE.ble.ConnectionState = reg8 & 0xe0;
+    //ppBLE.ble.BleTestPayload = reg8 & 0x1c;
+
+    reg8 = radio.readReg(REG_ADDR_PKT_BITSTREAM_CTRL, 1);
+    //ppBLE.ble.CrcLength = reg8 & 0x30;
+    //ppBLE.ble.Whitening = reg8 & 0x08;
+    ppGFSK.gfskFLRC.CRCLength = reg8 & 0x30;
+    ppFLRC.gfskFLRC.CRCLength = reg8 & 0x30;
+    ppGFSK.gfskFLRC.Whitening = reg8 & 0x08;
+    ppFLRC.gfskFLRC.Whitening = reg8 & 0x08;
+
+    {
+        LoRaPktPar0_t LoRaPktPar0;
+        LoRaPktPar0.octet = radio.readReg(REG_ADDR_LORA_PKTPAR0, 1);
+        switch (LoRaPktPar0.bits.modem_bw) {
+            case 2: mpLORA.lora.bandwidth = LORA_BW_200; break;
+            case 3: mpLORA.lora.bandwidth = LORA_BW_400; break;
+            case 4: mpLORA.lora.bandwidth = LORA_BW_800; break;
+            case 5: mpLORA.lora.bandwidth = LORA_BW_1600; break;
+        }
+        mpLORA.lora.spreadingFactor = LoRaPktPar0.bits.modem_sf << 4;
+    }
+
+    {
+        LoRaPktPar1_t LoRaPktPar1;
+        LoRaPktPar1.octet = radio.readReg(REG_ADDR_LORA_PKTPAR1, 1);
+        mpLORA.lora.codingRate = LoRaPktPar1.bits.coding_rate;
+        ppLORA.lora.InvertIQ = LoRaPktPar1.bits.rxinvert_iq;
+        ppLORA.lora.HeaderType = LoRaPktPar1.bits.implicit_header;
+        // LoRaPktPar1.bits.ppm_offset
+    }
+
+    {
+        LoRaPreambleReg_t LoRaPreambleReg;
+        LoRaPreambleReg.octet = radio.readReg(REG_ADDR_LORA_PREAMBLE, 1);
+        ppLORA.lora.PreambleLength = LoRaPreambleReg.bits.preamble_symb1_nb * (1 << LoRaPreambleReg.bits.preamble_symb_nb_exp);
+    }
+    ppLORA.lora.PayloadLength = radio.readReg(REG_ADDR_LORA_TX_PAYLOAD_LENGTH, 1);
+
+    {
+        LoRaLrCtl_t LoRaLrCtl;
+        LoRaLrCtl.octet = radio.readReg(REG_ADDR_LORA_LRCTL, 1);
+        ppLORA.lora.crc = LoRaLrCtl.octet & 0x20; // LoRaLrCtl.bits.crc_en
+    }
+
+    {
+        RegRxBw_t RegRxBw;
+        unsigned bps;
+        FloraPreambleHi_t FloraPreambleHi;
+        float mi, fdev_hz;
+        unsigned freqDev;
+        FskModDfH_t FskModDfH;
+        FskModDfH.octet = radio.readReg(REG_ADDR_FSK_MODDFH, 1);
+        freqDev = FskModDfH.bits.freqDev;
+        freqDev <<= 8;
+        freqDev |= radio.readReg(REG_ADDR_FSK_MODDFL, 1);
+        fdev_hz = freqDev * PLL_STEP_HZ;
+
+        FloraPreambleHi.octet = radio.readReg(REG_ADDR_FLORA_PREAMBLE_HI, 1);
+        switch (FloraPreambleHi.bits.data_rate) {
+            case 0:
+                bps = 2.0e6;
+                //mpFLRC.flrc.bitrateBandwidth = ??; // 2.6
+                break;
+            case 1:
+                bps = 1.6e6;
+                //mpFLRC.flrc.bitrateBandwidth = ??; // 2.08
+                break;
+            case 2:
+                bps = 1.0e6;
+                mpFLRC.flrc.bitrateBandwidth = FLRC_BR_1_300_BW_1_2; // 1.3
+                break;
+            case 3:
+                bps = 0.8e6;
+                mpFLRC.flrc.bitrateBandwidth = FLRC_BR_1_000_BW_1_2; // 1.04
+                break;
+            case 4:
+                bps = 0.5e6;
+                mpFLRC.flrc.bitrateBandwidth = FLRC_BR_0_650_BW_0_6; // 0.65
+                break;
+            case 5:
+                bps = 0.4e6;
+                mpFLRC.flrc.bitrateBandwidth = FLRC_BR_0_520_BW_0_6; // 0.52
+                break;
+            case 6:
+                bps = 0.25e6;
+                mpFLRC.flrc.bitrateBandwidth = FLRC_BR_0_325_BW_0_3; // 0.325
+                break;
+            case 7:
+                bps = 0.125e6;
+                mpFLRC.flrc.bitrateBandwidth = FLRC_BR_0_260_BW_0_3; // 0.26
+                break;
+        }
+
+        mi = (fdev_hz * 2.0) / bps;
+        if (mi > 0.35) {
+            mi -= 0.5;
+            mi /= 0.25;
+            mpBLE_GFSK.gfskBle.ModulationIndex = ((uint8_t)mi) + 1;
+        } else
+            mpBLE_GFSK.gfskBle.ModulationIndex = 0;
+
+        RegRxBw.octet = radio.readReg(REG_ADDR_RXBW, 1);
+
+        switch (RegRxBw.bits.bw) {
+            case 0:
+                if (FloraPreambleHi.bits.data_rate == 0)
+                    mpBLE_GFSK.gfskBle.bitrateBandwidth = GFSK_BLE_BR_2_000_BW_2_4;
+                if (FloraPreambleHi.bits.data_rate == 1)
+                    mpBLE_GFSK.gfskBle.bitrateBandwidth = GFSK_BLE_BR_1_600_BW_2_4;
+                if (FloraPreambleHi.bits.data_rate == 2)
+                    mpBLE_GFSK.gfskBle.bitrateBandwidth = GFSK_BLE_BR_1_000_BW_2_4;
+                if (FloraPreambleHi.bits.data_rate == 3)
+                    mpBLE_GFSK.gfskBle.bitrateBandwidth = GFSK_BLE_BR_0_800_BW_2_4;
+                break;
+            case 1:
+                if (FloraPreambleHi.bits.data_rate == 2)
+                    mpBLE_GFSK.gfskBle.bitrateBandwidth = GFSK_BLE_BR_1_000_BW_1_2;
+                if (FloraPreambleHi.bits.data_rate == 3)
+                    mpBLE_GFSK.gfskBle.bitrateBandwidth = GFSK_BLE_BR_0_800_BW_1_2;
+                if (FloraPreambleHi.bits.data_rate == 4)
+                    mpBLE_GFSK.gfskBle.bitrateBandwidth = GFSK_BLE_BR_0_500_BW_1_2;
+                if (FloraPreambleHi.bits.data_rate == 5)
+                    mpBLE_GFSK.gfskBle.bitrateBandwidth = GFSK_BLE_BR_0_400_BW_1_2;
+                break;
+            case 2:
+                if (FloraPreambleHi.bits.data_rate == 4)
+                    mpBLE_GFSK.gfskBle.bitrateBandwidth = GFSK_BLE_BR_0_500_BW_0_6;
+                if (FloraPreambleHi.bits.data_rate == 5)
+                    mpBLE_GFSK.gfskBle.bitrateBandwidth = GFSK_BLE_BR_0_400_BW_0_6;
+                if (FloraPreambleHi.bits.data_rate == 6)
+                    mpBLE_GFSK.gfskBle.bitrateBandwidth = GFSK_BLE_BR_0_250_BW_0_6;
+                break;
+            case 3:
+                if (FloraPreambleHi.bits.data_rate == 6)
+                    mpBLE_GFSK.gfskBle.bitrateBandwidth = GFSK_BLE_BR_0_250_BW_0_3;
+                if (FloraPreambleHi.bits.data_rate == 7)
+                    mpBLE_GFSK.gfskBle.bitrateBandwidth = GFSK_BLE_BR_0_125_BW_0_3;
+                break;
+        }
+        mpBLE_GFSK.gfskBle.bitrateBandwidth = reg8;
+    }
+
+    {
+        FskCfg_t FskCfg;
+        FskCfg.octet = radio.readReg(REG_ADDR_FSK_CFG, 1);
+        mpBLE_GFSK.gfskBle.ModulationShaping = FskCfg.bits.gf_bt << 4;
+        mpFLRC.flrc.ModulationShaping = mpBLE_GFSK.gfskBle.ModulationShaping;
+    }
+
+    {
+        PktBitStreamCtrl_t PktBitStreamCtrl;
+        PktBitStreamCtrl.octet = radio.readReg(REG_ADDR_PKT_BITSTREAM_CTRL, 1);
+        mpFLRC.flrc.CodingRate = PktBitStreamCtrl.octet & 0x06; // PktBitStreamCtrl.bits.flora_coding_rate 
+    }
+
+}
+
+void Radio:: diox_top_half()
+{
+    irqAt = lpt.read_us();
+
+    if (radio.chipMode == CHIPMODE_TX) {
+        /* TxDone handling requires low latency */
+        if (RadioEvents->TxDone_topHalf) {
+            RadioEvents->TxDone_topHalf();
+        }
+    }
+#ifdef TARGET_FF_MORPHO
+    else
+        pc3 = 0;
+#endif /* TARGET_FF_MORPHO */
+}
+
+void Radio::rxDone(uint8_t size, const pktStatus_t* pktStatus)
+{
+    float rssi, snr;
+
+    if (pktStatus->ble_gfsk_flrc.sync.syncAddrsCode == 0) {
+        int8_t s = pktStatus->lora.snr;
+        rssi = -pktStatus->lora.rssiSync / 2.0;
+        snr = s / 4.0;
+    } else {
+        rssi = -pktStatus->ble_gfsk_flrc.rssiSync / 2.0;
+        snr = FLT_MIN;
+    }
+
+    RadioEvents->RxDone(size, rssi, snr);
+}
+
+void Radio::timeout_callback(bool tx)
+{
+    if (!tx) {
+        if (RadioEvents->RxTimeout)
+            RadioEvents->RxTimeout();
+#ifdef TARGET_FF_MORPHO
+        pc3 = 0;
+#endif /* TARGET_FF_MORPHO */
+    } // else TODO tx timeout
+}
+
+void Radio::txDoneBottom()
+{
+    if (RadioEvents->TxDone_botHalf)
+        RadioEvents->TxDone_botHalf();
+}
+
+void Radio::Init(const RadioEvents_t* e)
+{
+    uint64_t sa;
+
+    radio.txDone = txDoneBottom;
+    radio.rxDone = rxDone;
+    radio.timeout = timeout_callback;
+    radio.chipModeChange = chipModeChange;
+    radio.diox_topHalf = diox_top_half;
+
+    readChip();
+
+    radio.setRegulator(0);  // default to LDO
+
+    sa = 0xc194c1;
+    radio.setSyncAddr(1, sa);
+
+    RadioEvents = e;
+    lpt.start();
+
+    fe_enable = true;
+
+    radio.periodBase = 2;   // 1ms resolution
+}
+
+int Radio::Send(uint8_t size, timestamp_t maxListenTime, timestamp_t channelFreeTime, int rssiThresh)
+{
+    uint8_t buf[8];
+
+    if (_m_ == MODEM_FSK) {
+        ppGFSK.gfskFLRC.PayloadLength = size;
+        radio.xfer(OPCODE_SET_PACKET_PARAMS, 7, 0, ppGFSK.buf);
+    } else if (_m_ == MODEM_LORA) {
+        ppLORA.lora.PayloadLength = size;
+        radio.xfer(OPCODE_SET_PACKET_PARAMS, 5, 0, ppLORA.buf);
+    }
+
+    if (maxListenTime > 0) {
+        int rssi;
+        us_timestamp_t startAt, chFreeAt, now;
+        radio.start_rx(-1);
+        startAt = lpt.read_us();
+Lstart:
+        do {
+            now = lpt.read_us();
+            if ((now - startAt) > maxListenTime) {
+                return -1;
+            }
+            radio.xfer(OPCODE_GET_RSSIINST, 0, 2, buf);
+            rssi = buf[1] / -2;
+        } while (rssi > rssiThresh);
+        chFreeAt = lpt.read_us();
+        do {
+            now = lpt.read_us();
+            radio.xfer(OPCODE_GET_RSSIINST, 0, 2, buf);
+            rssi = buf[1] / -2;
+            if (rssi > rssiThresh) {
+                goto Lstart;
+            }
+        } while ((now - chFreeAt) < channelFreeTime);
+    }
+
+    radio.start_tx(size, 4000);
+
+    return 0;
+}
+
+void Radio::service()
+{
+    radio.service();
+}
+
+bool Radio::CheckRfFrequency(unsigned hz)
+{
+    return true;
+}
+
+void Radio::Sleep()
+{
+    radio.setSleep(true);
+}
+
+void Radio::SetPublicNetwork(bool en)
+{
+    uint16_t ppg;
+
+    if (en)
+        ppg = 0x3444;
+    else
+        ppg = 0x1424;
+
+    radio.writeReg(REG_ADDR_LORA_SYNC, ppg, 2);
+}
+
+uint32_t Radio::lora_toa_us( uint8_t pktLen )
+{
+    double bwKHz;
+    LoRaPktPar0_t LoRaPktPar0;
+    LoRaLrCtl_t LoRaLrCtl;
+    LoRaPktPar1_t LoRaPktPar1;
+    uint8_t LowDatarateOptimize;
+
+    {
+        LoRaPktPar1.octet = radio.readReg(REG_ADDR_LORA_PKTPAR1, 1);
+        LowDatarateOptimize = LoRaPktPar1.bits.ppm_offset ? 1 : 0;
+        ppLORA.lora.HeaderType = LoRaPktPar1.bits.implicit_header;
+        ppLORA.lora.InvertIQ = LoRaPktPar1.bits.rxinvert_iq;
+        mpLORA.lora.codingRate = LoRaPktPar1.bits.coding_rate;
+    }
+
+    {
+        LoRaLrCtl.octet = radio.readReg(REG_ADDR_LORA_LRCTL, 1);
+        ppLORA.lora.crc = LoRaLrCtl.octet & 0x20; // LoRaLrCtl.bits.crc_en
+    }
+
+    {
+        LoRaPreambleReg_t LoRaPreambleReg;
+        LoRaPreambleReg.octet = radio.readReg(REG_ADDR_LORA_PREAMBLE, 1);
+        ppLORA.lora.PreambleLength = LoRaPreambleReg.bits.preamble_symb1_nb * (1 << LoRaPreambleReg.bits.preamble_symb_nb_exp);
+    }
+
+    {
+        LoRaPktPar0.octet = radio.readReg(REG_ADDR_LORA_PKTPAR0, 1);
+        switch (LoRaPktPar0.bits.modem_bw) {
+            case 0: bwKHz = 50; break;
+            case 1: bwKHz = 100; break;
+            case 2: mpLORA.lora.bandwidth = LORA_BW_200; bwKHz = 200; break;
+            case 3: mpLORA.lora.bandwidth = LORA_BW_400; bwKHz = 400; break;
+            case 4: mpLORA.lora.bandwidth = LORA_BW_800; bwKHz = 800; break;
+            case 5: mpLORA.lora.bandwidth = LORA_BW_1600; bwKHz = 1600; break;
+            default: bwKHz = 0; break;
+        }
+        mpLORA.lora.spreadingFactor = LoRaPktPar0.bits.modem_sf << 4;
+    }
+
+    // Symbol rate : time for one symbol (secs)
+    double rs = bwKHz / (1 << LoRaPktPar0.bits.modem_sf);
+    double ts = 1 / rs;
+    // time of preamble
+    //
+    double tPreamble = ( ppLORA.lora.PreambleLength + 4.25 ) * ts;
+    // Symbol length of payload and time
+    
+    double tmp = ceil( ( 8 * pktLen - 4 * LoRaPktPar0.bits.modem_sf +
+                         28 + 16 * LoRaLrCtl.bits.crc_en -
+                         ( LoRaPktPar1.bits.implicit_header ? 20 : 0 ) ) /
+                         ( double )( 4 * ( LoRaPktPar0.bits.modem_sf -
+                         ( ( LowDatarateOptimize > 0 ) ? 2 : 0 ) ) ) ) *
+                         ( LoRaPktPar1.bits.coding_rate + 4 );
+
+    double nPayload = 8 + ( ( tmp > 0 ) ? tmp : 0 );
+    double tPayload = nPayload * ts;
+    // Time on air
+    double tOnAir = tPreamble + tPayload;
+    // return microseconds
+    return floor( tOnAir * 1000 + 0.999 );
+}
+
+void Radio::GFSKModemConfig(unsigned bps, unsigned bw_hz, unsigned fdev_hz)
+{
+    uint8_t u8;
+    float mi, Mbps = bps / 1000000.0;
+
+    if (Mbps > 1.6) {
+        /* 2.0Mbps */
+        u8 = GFSK_BLE_BR_2_000_BW_2_4;
+    } else if (Mbps > 1.0) {
+        /* 1.6Mbps */
+        u8 = GFSK_BLE_BR_1_600_BW_2_4;
+    } else if (Mbps > 0.8) {
+        /* 1.0Mbps */
+        /*if (bwMHz > 1.2)
+            u8 = GFSK_BLE_BR_1_000_BW_2_4;
+        else*/
+            u8 = GFSK_BLE_BR_1_000_BW_1_2;
+    } else if (Mbps > 0.5) {
+        /* 0.8Mbps */
+        /*if (bwMHz > 1.2)
+            u8 = GFSK_BLE_BR_0_800_BW_2_4;
+        else*/
+            u8 = GFSK_BLE_BR_0_800_BW_1_2;
+    } else if (Mbps > 0.4) {
+        /* 0.5Mbps */
+        /*if (bwMHz > 0.6)
+            u8 = GFSK_BLE_BR_0_500_BW_1_2;
+        else*/
+            u8 = GFSK_BLE_BR_0_500_BW_0_6;
+    } else if (Mbps > 0.25) {
+        /* 0.4Mbps */
+        /*if (bwMHz > 0.6)
+            u8 = GFSK_BLE_BR_0_400_BW_1_2;
+        else*/
+            u8 = GFSK_BLE_BR_0_400_BW_0_6;
+    } else if (Mbps > 0.125) {
+        /* 0.25Mbps */
+        /*if (bwMHz > 0.3)
+            u8 = GFSK_BLE_BR_0_250_BW_0_6;
+        else*/
+            u8 = GFSK_BLE_BR_0_250_BW_0_3;
+    } else {
+        /* 0.125Mbps */
+        u8 = GFSK_BLE_BR_0_125_BW_0_3;
+    }
+
+    mpBLE_GFSK.gfskBle.bitrateBandwidth = u8;
+
+    mpBLE_GFSK.gfskBle.ModulationShaping = BT_OFF;
+
+    mi = (fdev_hz * 2.0) / bps;
+    if (mi > 0.35) {
+        mi -= 0.5;
+        mi /= 0.25;
+        mpBLE_GFSK.gfskBle.ModulationIndex = ((uint8_t)mi) + 1;
+    } else
+        mpBLE_GFSK.gfskBle.ModulationIndex = 0;
+
+    radio.xfer(OPCODE_SET_MODULATION_PARAMS, 3, 0, mpBLE_GFSK.buf);
+}
+
+void Radio::GFSKPacketConfig(unsigned preambleLen, bool fixLen, bool crcOn)
+{
+    ppGFSK.gfskFLRC.PreambleLength = (preambleLen - 4) / 4;
+    ppGFSK.gfskFLRC.PreambleLength <<= 4;
+    ppGFSK.gfskFLRC.SyncWordLength = (3 - 1) << 1;  // 3 byte 0xc194c1
+    ppGFSK.gfskFLRC.HeaderType = fixLen ? RADIO_PACKET_FIXED_LENGTH : RADIO_PACKET_VARIABLE_LENGTH;
+    ppGFSK.gfskFLRC.CRCLength = crcOn ? RADIO_CRC_2_BYTES : RADIO_CRC_OFF;
+
+    // TODO ppGFSK.gfskFLRC.PayloadLength = ;
+
+    radio.xfer(OPCODE_SET_PACKET_PARAMS, 7, 0, ppGFSK.buf);
+}
+
+void Radio::SetLoRaSymbolTimeout(uint8_t symbs)
+{
+    //symbolTimeout = symbs;
+}
+
+void Radio::LoRaModemConfig(unsigned bwKHz, uint8_t sf, uint8_t cr)
+{
+    if (bwKHz > 800)
+        mpLORA.lora.bandwidth = LORA_BW_1600;
+    if (bwKHz > 400)
+        mpLORA.lora.bandwidth = LORA_BW_800;
+    if (bwKHz > 200)
+        mpLORA.lora.bandwidth = LORA_BW_400;
+    else
+        mpLORA.lora.bandwidth = LORA_BW_200;
+
+    mpLORA.lora.codingRate = cr;
+
+    mpLORA.lora.spreadingFactor = sf << 4;
+
+    radio.xfer(OPCODE_SET_MODULATION_PARAMS, 3, 0, mpLORA.buf);
+}
+
+void Radio::LoRaPacketConfig(unsigned preambleLen, bool fixLen, bool crcOn, bool invIQ)
+{
+    ppLORA.lora.PreambleLength = preambleLen;
+    ppLORA.lora.HeaderType = fixLen ? IMPLICIT_HEADER : EXPLICIT_HEADER;
+    ppLORA.lora.crc = crcOn ? LORA_CRC_ENABLE : LORA_CRC_DISABLE;
+    ppLORA.lora.InvertIQ = invIQ ? LORA_IQ_INVERTED : LORA_IQ_STD;
+
+    radio.xfer(OPCODE_SET_PACKET_PARAMS, 5, 0, ppLORA.buf);
+}
+
+void Radio::SetChannel(unsigned hz)
+{
+    radio.setMHz(hz / 1000000.0);
+}
+
+uint32_t Radio::Random(void)
+{
+    uint8_t buf[2];
+    uint32_t ret = 0;
+    unsigned n;
+
+    radio.start_rx(-1);
+
+    for (n = 0; n < 8; n++) {
+        uint32_t r, s;
+        wait_us(5000);
+        radio.xfer(OPCODE_GET_RSSIINST, 0, 2, buf);
+        r = buf[1];
+        s = n * 4;
+        r <<= s;
+        ret ^= r;
+    }
+
+    radio.setStandby(STDBY_RC);
+
+    return ret;
+}
+
+void Radio::Rx(unsigned timeout)
+{
+    radio.start_rx(timeout / 1000);
+}
+
+void Radio::Standby()
+{
+    radio.setStandby(STDBY_RC);
+}
+
+void Radio::set_tx_dbm(int8_t dbm)
+{
+    radio.set_tx_dbm(dbm);
+}
+
+void Radio::SetTxContinuousWave(unsigned hz, int8_t dbm, unsigned timeout_us)
+{
+    SetChannel(hz);
+    radio.set_tx_dbm(dbm);
+    radio.xfer(OPCODE_SET_TX_CARRIER, 0, 0, NULL);
+}
+
+void Radio::SetRxMaxPayloadLength(RadioModems_t modem, uint8_t max)
+{
+    if (_m_ == MODEM_FSK) {
+        ppGFSK.gfskFLRC.PayloadLength = max;
+    } else if (_m_ == MODEM_LORA) {
+        ppLORA.lora.PayloadLength = max;
+    }
+}
+
+#endif /* ..SX126x_H */