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 }