// Package sx127x provides a driver for SX127x LoRa transceivers.
// References:
// https://electronics.stackexchange.com/questions/394296/can-t-get-simple-lora-receiver-to-work
// https://www.st.com/resource/en/user_manual/dm00300436-stm32-lora-expansion-package-for-stm32cube-stmicroelectronics.pdf
package sx127x

import (
	"errors"
	"machine"
	"time"

	"tinygo.org/x/drivers"
	"tinygo.org/x/drivers/lora"
)

// So we can keep track of the origin of interruption
const (
	RADIOEVENTCHAN_SIZE = 1
	SPI_BUFFER_SIZE     = 5
)

// Device wraps an SPI connection to a SX127x device.
type Device struct {
	spi            drivers.SPI          // SPI bus for module communication
	rstPin         machine.Pin          // GPIO for reset
	radioEventChan chan lora.RadioEvent // Channel for Receiving events
	loraConf       lora.Config          // Current Lora configuration
	controller     RadioController      // to manage interrupts with the radio
	deepSleep      bool                 // Internal Sleep state
	deviceType     int                  // sx1272, sx1273, sx1276, sx1279 (defaults sx1276)
	spiTxBuf       []byte               // global Tx buffer to avoid heap allocations in interrupt
	spiRxBuf       []byte               // global Rx buffer to avoid heap allocations in interrupt
}

// --------------------------------------------------
//
//	Channel and events
//
// --------------------------------------------------
// Get the RadioEvent channel of the device
func (d *Device) GetRadioEventChan() chan lora.RadioEvent {
	return d.radioEventChan
}

// New creates a new SX127x connection. The SPI bus must already be configured.
func New(spi drivers.SPI, rstPin machine.Pin) *Device {
	k := Device{
		spi:            spi,
		rstPin:         rstPin,
		radioEventChan: make(chan lora.RadioEvent, RADIOEVENTCHAN_SIZE),
		spiTxBuf:       make([]byte, SPI_BUFFER_SIZE),
		spiRxBuf:       make([]byte, SPI_BUFFER_SIZE),
	}
	return &k
}

// SetRadioControl let you define the RadioController
func (d *Device) SetRadioController(rc RadioController) error {
	d.controller = rc
	if err := d.controller.Init(); err != nil {
		return err
	}
	d.controller.SetupInterrupts(d.HandleInterrupt)

	return nil
}

// Specify device type (sx1272, sx1273, sx1276, sx1279)
func (d *Device) SetDeviceType(devType int) {
	d.deviceType = devType
}

// Reset re-initialize the sx127x device
func (d *Device) Reset() {
	d.rstPin.Low()
	time.Sleep(100 * time.Millisecond)
	d.rstPin.High()
	time.Sleep(100 * time.Millisecond)
}

// DetectDevice checks if device responds on the SPI bus
func (d *Device) DetectDevice() bool {
	id := d.GetVersion()
	return (id == 0x12)
}

// ReadRegister reads register value
func (d *Device) ReadRegister(reg uint8) uint8 {
	if d.controller != nil {
		d.controller.SetNss(false)
	}

	// Send register
	d.spiTxBuf = d.spiTxBuf[:0]
	d.spiTxBuf = append(d.spiTxBuf, byte(reg&0x7f))
	d.spi.Tx(d.spiTxBuf, nil)
	// Read value
	d.spiRxBuf = d.spiRxBuf[:0]
	d.spiRxBuf = append(d.spiRxBuf, 0)
	d.spi.Tx(nil, d.spiRxBuf)
	if d.controller != nil {
		d.controller.SetNss(true)
	}

	return d.spiRxBuf[0]
}

// WriteRegister writes value to register
func (d *Device) WriteRegister(reg uint8, value uint8) uint8 {
	if d.controller != nil {
		d.controller.SetNss(false)
	}

	// Send register
	d.spiTxBuf = d.spiTxBuf[:0]
	d.spiTxBuf = append(d.spiTxBuf, byte(reg|0x80))
	d.spi.Tx(d.spiTxBuf, nil)
	// Send value
	d.spiTxBuf = d.spiTxBuf[:0]
	d.spiTxBuf = append(d.spiTxBuf, byte(value))
	d.spiRxBuf = d.spiRxBuf[:0]
	d.spiRxBuf = append(d.spiRxBuf, 0)
	d.spi.Tx(d.spiTxBuf, d.spiRxBuf)
	if d.controller != nil {
		d.controller.SetNss(true)
	}

	return d.spiRxBuf[0]
}

// SetOpMode changes the sx1276 mode
func (d *Device) SetOpMode(mode uint8) {
	cur := d.ReadRegister(SX127X_REG_OP_MODE)
	new := (cur & (^SX127X_OPMODE_MASK)) | mode
	d.WriteRegister(SX127X_REG_OP_MODE, new)
}

// SetOpModeLora changes the sx1276 mode to lora.
func (d *Device) SetOpModeLora() {
	d.WriteRegister(SX127X_REG_OP_MODE, d.ReadRegister(SX127X_REG_OP_MODE)|SX127X_OPMODE_LORA)
}

// SetOpModeFsk changes the sx1276 mode to fsk/ook.
func (d *Device) SetOpModeFsk() {
	d.WriteRegister(SX127X_REG_OP_MODE, d.ReadRegister(SX127X_REG_OP_MODE)&^SX127X_OPMODE_LORA)
}

// SetModulationType changes the modulation type (SX127X_OPMODE_MODULATION_FSK, SX127X_OPMODE_MODULATION_OOK)
func (d *Device) SetModulationType(typ uint8) {
	cleared := d.ReadRegister(SX127X_REG_OP_MODE) &^ SX127X_OPMODE_MODULATION_MASK
	d.WriteRegister(SX127X_REG_OP_MODE, cleared|typ)
}

// GetVersion returns hardware version of sx1276 chipset
func (d *Device) GetVersion() uint8 {
	return (d.ReadRegister(SX127X_REG_VERSION))
}

// IsTransmitting tests if a packet transmission is in progress
func (d *Device) IsTransmitting() bool {
	return (d.ReadRegister(SX127X_REG_OP_MODE) & SX127X_OPMODE_TX) == SX127X_OPMODE_TX
}

// LastPacketRSSI gives the RSSI of the last packet received
func (d *Device) LastPacketRSSI() uint8 {
	// section 5.5.5
	var adjustValue uint8 = 157
	if d.loraConf.Freq < 868000000 {
		adjustValue = 164
	}
	return d.ReadRegister(SX127X_REG_PKT_RSSI_VALUE) - adjustValue
}

// LastPacketSNR gives the SNR of the last packet received
func (d *Device) LastPacketSNR() uint8 {
	return uint8(d.ReadRegister(SX127X_REG_PKT_SNR_VALUE) / 4)
}

// GetRSSI returns current RSSI
func (d *Device) GetRSSI() uint8 {
	return d.ReadRegister(SX127X_REG_RSSI_VALUE)
}

/*
// GetBandwidth returns the bandwidth the LoRa module is using
func (d *Device) GetBandwidth() int32 {
	return int32(d.loraConf.Bw)
}
*/

// SetTxPowerWithPaBoost sets the transmitter output power and may activate paBoost
func (d *Device) SetTxPowerWithPaBoost(txPower int8, paBoost bool) {
	if !paBoost {
		// RFO
		if txPower < 0 {
			txPower = 0
		} else if txPower > 14 {
			txPower = 14
		}
		d.WriteRegister(SX127X_REG_PA_CONFIG, uint8(0x70)|uint8(txPower))

	} else {
		//PA_BOOST
		if txPower > 17 {
			if txPower > 20 {
				txPower = 20
			}

			txPower -= 3

			// High Power +20 dBm Operation (Semtech SX1276/77/78/79 5.4.3.)
			d.WriteRegister(SX127X_REG_PA_DAC, 0x87)
			d.SetOCP(140)
		} else {
			if txPower < 2 {
				txPower = 2
			}

			d.WriteRegister(SX127X_REG_PA_DAC, 0x84)
			d.SetOCP(100)

		}

		d.WriteRegister(SX127X_REG_PA_CONFIG, uint8(SX127X_PA_BOOST)|uint8(txPower-2))

	}
}

// ---------------
// Internal functions
// ---------------

// SetRxTimeout defines RX Timeout expressed as number of symbols
// Default timeout is 64 * Ts
func (d *Device) SetRxTimeout(tmoutSymb uint8) {
	d.WriteRegister(SX127X_REG_SYMB_TIMEOUT_LSB, tmoutSymb)
}

// SetOCP defines Overload Current Protection configuration
func (d *Device) SetOCP(mA uint8) {
	ocpTrim := uint8(27)
	if mA < 45 {
		mA = 45
	}
	if mA <= 120 {
		ocpTrim = (mA - 45) / 5
	} else if mA <= 240 {
		ocpTrim = (mA + 30) / 10
	}
	d.WriteRegister(SX127X_REG_OCP, 0x20|(0x1F&ocpTrim))
}

// SetAgcAutoOn enables Automatic Gain Control
func (d *Device) SetAgcAuto(val uint8) {
	if val == SX127X_AGC_AUTO_ON {
		d.WriteRegister(SX127X_REG_MODEM_CONFIG_3, d.ReadRegister(SX127X_REG_MODEM_CONFIG_3)|0x04)
	} else {
		d.WriteRegister(SX127X_REG_MODEM_CONFIG_3, d.ReadRegister(SX127X_REG_MODEM_CONFIG_3)&0xfb)
	}
}

// SetLowDataRateOptimize enables Low Data Rate Optimization
func (d *Device) SetLowDataRateOptim(val uint8) {
	if val == lora.LowDataRateOptimizeOn {
		d.WriteRegister(SX127X_REG_MODEM_CONFIG_3, d.ReadRegister(SX127X_REG_MODEM_CONFIG_3)|0x08)
	} else {
		d.WriteRegister(SX127X_REG_MODEM_CONFIG_3, d.ReadRegister(SX127X_REG_MODEM_CONFIG_3)&0xf7)
	}
}

// SetLowFrequencyModeOn enables Low Data Rate Optimization
func (d *Device) SetLowFrequencyModeOn(val bool) {
	if val {
		d.WriteRegister(SX127X_REG_OP_MODE, d.ReadRegister(SX127X_REG_OP_MODE)|SX127X_OPMODE_LOW_FREQUENCY)
	} else {
		d.WriteRegister(SX127X_REG_OP_MODE, d.ReadRegister(SX127X_REG_OP_MODE)&^SX127X_OPMODE_LOW_FREQUENCY)
	}
}

// SetHopPeriod sets number of symbol periods between frequency hops. (0 = disabled).
func (d *Device) SetHopPeriod(val uint8) {
	d.WriteRegister(SX127X_REG_HOP_PERIOD, val)
}

//
// LORA FUNCTIONS
//

// LoraConfig() defines Lora configuration for next Lora operations
func (d *Device) LoraConfig(cnf lora.Config) {
	// Save given configuration
	d.loraConf = cnf
	d.loraConf.SyncWord = syncword(int(cnf.SyncWord))
}

// SetFrequency updates the frequency the LoRa module is using
func (d *Device) SetFrequency(frequency uint32) {
	d.loraConf.Freq = frequency
	var frf = (uint64(frequency) << 19) / 32000000
	d.WriteRegister(SX127X_REG_FRF_MSB, uint8(frf>>16))
	d.WriteRegister(SX127X_REG_FRF_MID, uint8(frf>>8))
	d.WriteRegister(SX127X_REG_FRF_LSB, uint8(frf>>0))
}

// SetBandwidth updates the bandwidth the LoRa module is using
func (d *Device) SetBandwidth(bw uint8) {
	d.loraConf.Bw = bandwidth(bw)
	d.WriteRegister(SX127X_REG_MODEM_CONFIG_1, (d.ReadRegister(SX127X_REG_MODEM_CONFIG_1)&0x0f)|(bw<<4))
}

// SetCodingRate updates the coding rate the LoRa module is using
func (d *Device) SetCodingRate(cr uint8) {
	d.loraConf.Cr = cr
	d.WriteRegister(SX127X_REG_MODEM_CONFIG_1, (d.ReadRegister(SX127X_REG_MODEM_CONFIG_1)&0xf1)|(cr<<1))
}

// SetHeaderType set implicit or explicit mode
func (d *Device) SetHeaderType(headerType uint8) {
	d.loraConf.HeaderType = headerType
	if headerType == lora.HeaderImplicit {
		d.WriteRegister(SX127X_REG_MODEM_CONFIG_1, d.ReadRegister(SX127X_REG_MODEM_CONFIG_1)|0x01)
	} else {
		d.WriteRegister(SX127X_REG_MODEM_CONFIG_1, d.ReadRegister(SX127X_REG_MODEM_CONFIG_1)&0xfe)
	}
}

// SetSpreadingFactor changes spreading factor
func (d *Device) SetSpreadingFactor(sf uint8) {
	d.loraConf.Sf = sf
	if sf == lora.SpreadingFactor6 {
		d.WriteRegister(SX127X_REG_DETECTION_OPTIMIZE, 0xc5)
		d.WriteRegister(SX127X_REG_DETECTION_THRESHOLD, 0x0c)
	} else {
		d.WriteRegister(SX127X_REG_DETECTION_OPTIMIZE, 0xc3)
		d.WriteRegister(SX127X_REG_DETECTION_THRESHOLD, 0x0a)
	}
	var newValue = (d.ReadRegister(SX127X_REG_MODEM_CONFIG_2) & 0x0f) | ((sf << 4) & 0xf0)
	d.WriteRegister(SX127X_REG_MODEM_CONFIG_2, newValue)
}

// SetTxPower sets the transmitter output (with paBoost ON)
func (d *Device) SetTxPower(txPower int8) {
	d.loraConf.LoraTxPowerDBm = txPower
	d.SetTxPowerWithPaBoost(txPower, true)
}

// SetCrc Enable CRC generation and check on payload
func (d *Device) SetCrc(enable bool) {
	if enable {
		d.loraConf.Crc = lora.CRCOn
		d.WriteRegister(SX127X_REG_MODEM_CONFIG_2, d.ReadRegister(SX127X_REG_MODEM_CONFIG_2)|0x04)
	} else {
		d.loraConf.Crc = lora.CRCOff
		d.WriteRegister(SX127X_REG_MODEM_CONFIG_2, d.ReadRegister(SX127X_REG_MODEM_CONFIG_2)&0xfb)
	}
}

// SetPreambleLength defines number of preamble
func (d *Device) SetPreambleLength(pLen uint16) {
	d.loraConf.Preamble = pLen
	d.WriteRegister(SX127X_REG_PREAMBLE_MSB, uint8((pLen>>8)&0xFF))
	d.WriteRegister(SX127X_REG_PREAMBLE_LSB, uint8(pLen&0xFF))
}

// SetSyncWord defines sync word
func (d *Device) SetSyncWord(syncWord uint16) {
	d.loraConf.SyncWord = syncWord
	sw := uint8(syncWord & 0xFF)
	d.WriteRegister(SX127X_REG_SYNC_WORD, sw)
}

// SetIQMode Sets I/Q polarity configuration
func (d *Device) SetIqMode(val uint8) {
	d.loraConf.Iq = val
	if val == lora.IQStandard {
		//Set IQ to normal values
		d.WriteRegister(SX127X_REG_INVERTIQ, 0x27)
		d.WriteRegister(SX127X_REG_INVERTIQ2, 0x1D)
	} else {
		//Invert IQ Back
		d.WriteRegister(SX127X_REG_INVERTIQ, 0x66)
		d.WriteRegister(SX127X_REG_INVERTIQ2, 0x19)
	}
}

// SetPublicNetwork changes Sync Word to match network type
func (d *Device) SetPublicNetwork(enabled bool) {
	if enabled {
		d.SetSyncWord(SX127X_LORA_MAC_PUBLIC_SYNCWORD)
	} else {
		d.SetSyncWord(SX127X_LORA_MAC_PRIVATE_SYNCWORD)
	}
}

// Tx sends a lora packet, (with timeout)
func (d *Device) Tx(pkt []uint8, timeoutMs uint32) error {
	d.SetOpModeLora()
	d.SetOpMode(SX127X_OPMODE_SLEEP)

	d.SetHopPeriod(0x00)
	d.SetLowFrequencyModeOn(false)                                                      // High freq mode
	d.WriteRegister(SX127X_REG_PA_RAMP, (d.ReadRegister(SX127X_REG_PA_RAMP)&0xF0)|0x08) // set PA ramp-up time 50 uSec
	d.WriteRegister(SX127X_REG_LNA, SX127X_LNA_MAX_GAIN)                                // Set Low Noise Amplifier to MAX

	d.SetFrequency(d.loraConf.Freq)
	d.SetPreambleLength(d.loraConf.Preamble)
	d.SetSyncWord(d.loraConf.SyncWord)
	d.SetBandwidth(d.loraConf.Bw)
	d.SetSpreadingFactor(d.loraConf.Sf)
	d.SetIqMode(d.loraConf.Iq)
	d.SetCodingRate(d.loraConf.Cr)
	d.SetCrc(d.loraConf.Crc == lora.CRCOn)
	d.SetTxPower(d.loraConf.LoraTxPowerDBm)
	d.SetHeaderType(d.loraConf.HeaderType)
	d.SetAgcAuto(SX127X_AGC_AUTO_ON)

	// set the IRQ mapping DIO0=TxDone DIO1=NOP DIO2=NOP
	d.WriteRegister(SX127X_REG_DIO_MAPPING_1, SX127X_MAP_DIO0_LORA_TXDONE|SX127X_MAP_DIO1_LORA_NOP|SX127X_MAP_DIO2_LORA_NOP)
	// Clear all radio IRQ Flags
	d.WriteRegister(SX127X_REG_IRQ_FLAGS, 0xFF)
	// Mask all but TxDone
	d.WriteRegister(SX127X_REG_IRQ_FLAGS_MASK, ^SX127X_IRQ_LORA_TXDONE_MASK)

	// initialize the payload size and address pointers
	d.WriteRegister(SX127X_REG_PAYLOAD_LENGTH, uint8(len(pkt)))
	d.WriteRegister(SX127X_REG_FIFO_TX_BASE_ADDR, 0)
	d.WriteRegister(SX127X_REG_FIFO_ADDR_PTR, 0)

	// FIFO OPs cannot take place in Sleep mode !!!
	d.SetOpMode(SX127X_OPMODE_STANDBY)
	time.Sleep(time.Millisecond)
	// Copy payload to FIFO // TODO: Bulk
	for i := 0; i < len(pkt); i++ {
		d.WriteRegister(SX127X_REG_FIFO, pkt[i])
	}

	// Enable TX
	d.SetOpMode(SX127X_OPMODE_TX)

	msg := <-d.GetRadioEventChan()
	if msg.EventType != lora.RadioEventTxDone {
		return errors.New("Unexpected Radio Event while TX " + string(0x30+msg.EventType))
	}
	return nil
}

// Rx tries to receive a Lora packet (with timeout in milliseconds)
func (d *Device) Rx(timeoutMs uint32) ([]uint8, error) {
	if d.loraConf.Freq == 0 {
		return nil, lora.ErrUndefinedLoraConf
	}

	d.SetOpModeLora()
	d.SetOpMode(SX127X_OPMODE_SLEEP)

	d.SetHopPeriod(0x00)
	d.SetLowFrequencyModeOn(false)                                                      // High freq mode
	d.WriteRegister(SX127X_REG_PA_RAMP, (d.ReadRegister(SX127X_REG_PA_RAMP)&0xF0)|0x08) // set PA ramp-up time 50 uSec
	d.WriteRegister(SX127X_REG_LNA, SX127X_LNA_MAX_GAIN)                                // Set Low Noise Amplifier to MAX

	d.SetFrequency(d.loraConf.Freq)
	d.SetPreambleLength(d.loraConf.Preamble)
	d.SetSyncWord(d.loraConf.SyncWord)
	d.SetBandwidth(d.loraConf.Bw)
	d.SetSpreadingFactor(d.loraConf.Sf)
	d.SetIqMode(d.loraConf.Iq)
	d.SetCodingRate(d.loraConf.Cr)
	d.SetCrc(d.loraConf.Crc == lora.CRCOn)
	d.SetTxPower(d.loraConf.LoraTxPowerDBm)
	d.SetHeaderType(d.loraConf.HeaderType)
	d.SetAgcAuto(SX127X_AGC_AUTO_ON)

	// set the IRQ mapping DIO0=RxDone DIO1=RxTimeout DIO2=NOP
	d.WriteRegister(SX127X_REG_DIO_MAPPING_1, SX127X_MAP_DIO0_LORA_RXDONE|SX127X_MAP_DIO1_LORA_RXTOUT|SX127X_MAP_DIO2_LORA_NOP)
	// Clear all radio IRQ Flags
	d.WriteRegister(SX127X_REG_IRQ_FLAGS, 0xFF)
	// Mask all but RxDone
	d.WriteRegister(SX127X_REG_IRQ_FLAGS_MASK, ^(SX127X_IRQ_LORA_RXDONE_MASK | SX127X_IRQ_LORA_RXTOUT_MASK))

	// Single RX mode don't properly handle Timeouts on sx127x, so we use Continuous RX
	// Go routine is a workaround to stop the Continuous RX and fire a timeout Event
	d.SetOpMode(SX127X_OPMODE_RX)

	var msg lora.RadioEvent
	select {
	case msg = <-d.radioEventChan:
		if msg.EventType != lora.RadioEventRxDone {
			return nil, errors.New("Unexpected Radio Event while RX " + string(0x30+msg.EventType))
		}
	case <-time.After(time.Millisecond * time.Duration(timeoutMs)):
		d.SetOpMode(SX127X_OPMODE_STANDBY)
		return nil, nil
	}

	// Get the received payload
	d.WriteRegister(SX127X_REG_FIFO_RX_BASE_ADDR, 0)
	d.WriteRegister(SX127X_REG_FIFO_ADDR_PTR, 0)

	pLen := d.ReadRegister(SX127X_REG_RX_NB_BYTES)
	d.WriteRegister(SX127X_REG_FIFO_ADDR_PTR, d.ReadRegister(SX127X_REG_FIFO_RX_CURRENT_ADDR))

	rxData := []uint8{}
	for i := uint8(0); i < pLen; i++ {
		rxData = append(rxData, d.ReadRegister(SX127X_REG_FIFO))
	}
	return rxData, nil
}

// SetTxContinuousMode enable Continuous Tx mode
func (d *Device) SetTxContinuousMode(val bool) {
	if val {
		d.WriteRegister(SX127X_REG_MODEM_CONFIG_2, d.ReadRegister(SX127X_REG_MODEM_CONFIG_2)|0x08)
	} else {
		d.WriteRegister(SX127X_REG_MODEM_CONFIG_2, d.ReadRegister(SX127X_REG_MODEM_CONFIG_2)&0xf7)
	}
}

//
// HELPER FUNCTIONS
//

// PrintRegisters outputs the sx127x transceiver registers
func (d *Device) PrintRegisters(compact bool) {
	for i := uint8(0); i < 128; i++ {
		v := d.ReadRegister(i)
		print(v, " ")
	}
	println()
}

// PrintRegisters outputs the sx127x transceiver registers
func (d *Device) RandomU32() uint32 {
	// Disable ALL irqs
	d.WriteRegister(SX127X_REG_IRQ_FLAGS, 0xFF)
	d.SetOpModeLora()
	d.SetOpMode(SX127X_OPMODE_SLEEP)
	d.SetFrequency(d.loraConf.Freq)
	d.SetOpMode(SX127X_OPMODE_RX)
	rnd := uint32(0)
	for i := 0; i < 32; i++ {
		time.Sleep(time.Millisecond * 10)
		// Unfiltered RSSI value reading. Only takes the LSB value
		rnd |= (uint32(d.ReadRegister(SX127X_REG_RSSI_WIDEBAND)) & 0x01) << i
	}
	return rnd
}

// HandleInterrupt must be called by main code on DIO state change.
func (d *Device) HandleInterrupt() {

	// Get IRQ and clear
	st := d.ReadRegister(SX127X_REG_IRQ_FLAGS)
	d.WriteRegister(SX127X_REG_IRQ_FLAGS, 0xFF)

	if (st & SX127X_IRQ_LORA_RXDONE_MASK) > 0 {
		select {
		case d.radioEventChan <- lora.RadioEvent{lora.RadioEventRxDone, uint16(st), nil}:
		default:
		}
	}

	if (st & SX127X_IRQ_LORA_TXDONE_MASK) > 0 {
		select {
		case d.radioEventChan <- lora.RadioEvent{lora.RadioEventTxDone, uint16(st), nil}:
		default:
		}
	}

	if (st & SX127X_IRQ_LORA_RXTOUT_MASK) > 0 {
		select {
		case d.radioEventChan <- lora.RadioEvent{lora.RadioEventTimeout, uint16(st), nil}:
		default:
		}
	}

	if (st & SX127X_IRQ_LORA_CRCERR_MASK) > 0 {
		select {
		case d.radioEventChan <- lora.RadioEvent{lora.RadioEventCrcError, uint16(st), nil}:
		default:
		}
	}
}

func bandwidth(bw uint8) uint8 {
	switch bw {
	case lora.Bandwidth_7_8:
		return SX127X_LORA_BW_7_8
	case lora.Bandwidth_10_4:
		return SX127X_LORA_BW_10_4
	case lora.Bandwidth_15_6:
		return SX127X_LORA_BW_15_6
	case lora.Bandwidth_20_8:
		return SX127X_LORA_BW_20_8
	case lora.Bandwidth_31_25:
		return SX127X_LORA_BW_31_25
	case lora.Bandwidth_41_7:
		return SX127X_LORA_BW_41_7
	case lora.Bandwidth_62_5:
		return SX127X_LORA_BW_62_5
	case lora.Bandwidth_125_0:
		return SX127X_LORA_BW_125_0
	case lora.Bandwidth_250_0:
		return SX127X_LORA_BW_250_0
	case lora.Bandwidth_500_0:
		return SX127X_LORA_BW_500_0
	default:
		return 0
	}
}

func syncword(sw int) uint16 {
	if sw == lora.SyncPublic {
		return SX127X_LORA_MAC_PUBLIC_SYNCWORD
	}
	return SX127X_LORA_MAC_PRIVATE_SYNCWORD
}
