mirror of
https://github.com/soypat/lora.git
synced 2026-07-26 07:28:38 +00:00
195 lines
6.5 KiB
Go
195 lines
6.5 KiB
Go
package lora
|
|
|
|
import "time"
|
|
|
|
// Config is the generic configuration struct for a LoRa modem/radio that provides
|
|
// the most common configuration parameters needed to set up a channel with another
|
|
// radio using same parameters.
|
|
type Config struct {
|
|
// Bandwidth relates to data rate of communication. Double the bandwidth means
|
|
// double the data rate, which implies faster communications and less energy usage.
|
|
Bandwidth Frequency
|
|
// Frequency is the carrier frequency of the radio, a.k.a center frequency.
|
|
// It must match the modem's working frequency to ensure proper functioning.
|
|
Frequency Frequency
|
|
// Length of the preamble preceding the header. Can be arbitrarily long.
|
|
// Longer preambles ensure more robust communications but can lead to congestion.
|
|
//
|
|
// Note: The preamble length on a receiver should be configured equal-to or greater
|
|
// than the expected packet preamble length on the SX1278.
|
|
PreambleLength uint16
|
|
HeaderType HeaderType
|
|
// Must be set when working with implicit headers.
|
|
MaxImplicitPayloadLength uint8
|
|
CodingRate CodingRate
|
|
SpreadingFactor SpreadingFactor
|
|
SyncWord uint16 // for new chips sync word is full 16 bits
|
|
TxPower int8 // Tx Power in dBm.
|
|
CRC bool
|
|
// Low data rate optimisation flag. The use of this flag is mandated when
|
|
// the symbol duration exceeds 16ms. Increases reliability at high spreading factors.
|
|
LDRO bool
|
|
// IQInversion configures I and Q signal inversion.
|
|
IQInversion bool
|
|
}
|
|
|
|
// SymbolPeriod returns the time it takes to transmit a single symbol given the
|
|
// current configuration parameters. It depends on Spreading factor and Bandwidth.
|
|
func (cfg *Config) SymbolPeriod() time.Duration {
|
|
T_s := time.Second * time.Duration(cfg.SpreadingFactor.ChipsPerSymbol()) /
|
|
time.Duration(cfg.Bandwidth.Hertz())
|
|
return T_s
|
|
}
|
|
|
|
// TimeOnAir returns the time it takes to transmit a packet of the given payload
|
|
// length. It depends on the following config parameters:
|
|
// - Bandwidth (proportional)
|
|
// - CRC presence (presence == longer)
|
|
// - Header type (explicit == longer)
|
|
// - Coding rate (proportional)
|
|
// - Spreading factor (inversely proportional)
|
|
// - Preamble length (proportional)
|
|
// - Low data rate optimisation (presence == longer)
|
|
func (cfg *Config) TimeOnAir(payloadLength int) time.Duration {
|
|
if cfg.Bandwidth == 0 {
|
|
return 0
|
|
}
|
|
crc := int64(b2u8(cfg.CRC))
|
|
ih := int64(cfg.HeaderType)
|
|
ldr := int64(b2u8(cfg.LDRO))
|
|
cr := int64(cfg.CodingRate)
|
|
sf := int64(cfg.SpreadingFactor)
|
|
// Page 31 SX1276IMLTRT SEMTECH | Alldatasheet.
|
|
Npayload := 8*int64(payloadLength) - 4*sf + 28 + 16*crc - 20*ih
|
|
div := 4 * (sf - 2*ldr)
|
|
// Apply Ceil and max with minimal branching.
|
|
if Npayload < 0 || div <= 0 {
|
|
Npayload = 0
|
|
} else if Npayload%div == 0 {
|
|
Npayload /= div
|
|
Npayload *= (cr + 4)
|
|
} else {
|
|
Npayload /= div
|
|
Npayload++
|
|
Npayload *= (cr + 4)
|
|
}
|
|
// Says 4.25 in manual but we round up to 5. This means we'll overestimate the
|
|
// time calculated.
|
|
Npayload += 8 + int64(cfg.PreambleLength) + 5
|
|
// Calculate LoRa Transmission Parameter Relationship page 28.
|
|
chipsPerSymbol := cfg.SpreadingFactor.ChipsPerSymbol() // chips per symbol.
|
|
return time.Second * time.Duration(Npayload*int64(chipsPerSymbol)) /
|
|
time.Duration(cfg.Bandwidth.Hertz())
|
|
}
|
|
|
|
// HeaderType defines the presence of a header in the LoRa packet.
|
|
// An explicit header means that the packet contains a header with a length
|
|
// field. An implicit header means that the packet does not contain a header
|
|
// and the length is implicitly known, i.e. agreed upon between two modems in advance.
|
|
type HeaderType uint8
|
|
|
|
const (
|
|
HeaderExplicit HeaderType = 0
|
|
HeaderImplicit HeaderType = 1
|
|
)
|
|
|
|
// CodingRate defines the error correction scheme used by the LoRa modem.
|
|
// Higher coding rates imply more robust communications at the expense of
|
|
// less data throughput. A CR of 4/5 means that for every 4 bits of data,
|
|
// 1 bit of error correction is added to the total transmitted bits.
|
|
type CodingRate uint8
|
|
|
|
const (
|
|
// 4/5 coding rate.
|
|
CR4_5 CodingRate = 1
|
|
// 4/6 coding rate.
|
|
CR4_6 CodingRate = 2
|
|
// 4/7 coding rate.
|
|
CR4_7 CodingRate = 3
|
|
// 4/8 coding rate.
|
|
CR4_8 CodingRate = 4
|
|
)
|
|
|
|
// SpreadingFactor defines the number of chips per symbol. Higher spreading factors
|
|
// imply longer transmission times but more robust communications.
|
|
// The number of chips per symbol is 2^SF, so a spreading factor of 8 takes twice
|
|
// as long to transmit a symbol as a spreading factor of 7.
|
|
type SpreadingFactor uint8
|
|
|
|
// Common spreading factors. Decide the number of chips per symbol.
|
|
const (
|
|
SF5 SpreadingFactor = iota + 5
|
|
SF6
|
|
SF7
|
|
SF8
|
|
SF9
|
|
SF10
|
|
SF11
|
|
SF12
|
|
)
|
|
|
|
// ChipsPerSymbol returns the number of chips in a symbol. A chip is a subdivision
|
|
// of a symbol in the frequency domain rather than the time domain, which is why
|
|
// the units of this value is Hz, not Duration. A chip tells where to start
|
|
// the frequency sweep for a symbol.
|
|
func (sf SpreadingFactor) ChipsPerSymbol() int64 {
|
|
return 1 << sf
|
|
}
|
|
|
|
// Frequency defines the center frequency of the LoRa channel. The center frequency
|
|
// serves to avoid interference from other channels and has minimal effect on
|
|
// the data rate. The frequency in Config must match the modem's working frequency
|
|
// or the modem may fail to transmit or receive packets or even be damaged.
|
|
type Frequency int64
|
|
|
|
func (f Frequency) Hertz() int64 { return int64(f) }
|
|
|
|
// Common frequency units.
|
|
//
|
|
// To count the number of units in a Frequency, divide:
|
|
//
|
|
// // How many hertz in a kilohertz?
|
|
// kiloHertz := lora.Kilohertz
|
|
// fmt.Print(int64(kiloHertz/lora.Hertz)) // prints 1000 (1000 hertz in a kilohertz)
|
|
const (
|
|
Hertz Frequency = 1
|
|
Kilohertz Frequency = 1000 * Hertz
|
|
Megahertz Frequency = 1000 * Kilohertz
|
|
)
|
|
|
|
// Common LoRa bandwidths
|
|
const (
|
|
BW125k = 125 * Kilohertz
|
|
BW250k = 250 * Kilohertz
|
|
BW500k = 500 * Kilohertz
|
|
BW1625k = 1625 * Kilohertz
|
|
)
|
|
|
|
// Common LoRa frequencies
|
|
const (
|
|
// 433.05MHz Low limit medical, scientific and industrial band.
|
|
Freq433_0M = 433050000 * Hertz
|
|
// 434.8MHz High limit medical, scientific and industrial band.
|
|
Freq434_8M = 434790000 * Hertz
|
|
Freq868_1M = 868100000 * Hertz
|
|
Freq868_5M = 868500000 * Hertz
|
|
Freq916_8M = 916800000 * Hertz
|
|
Freq923_3M = 923300000 * Hertz
|
|
Freq2400_0M = 2400 * Megahertz
|
|
)
|
|
|
|
// 169.4MHz radio band ([Wize]), formerly known as ERMES band. Historically used by pagers.
|
|
//
|
|
// [Wize]: https://en.wikipedia.org/wiki/Wize_technology
|
|
const (
|
|
Freq169_4M = 169400000 * Hertz
|
|
Freq169_8M = 169812500 * Hertz
|
|
)
|
|
|
|
func b2u8(b bool) uint8 {
|
|
if b {
|
|
return 1
|
|
}
|
|
return 0
|
|
}
|