rename spreadfactor and cleanup type change in sx127x

This commit is contained in:
Joel Wetzell
2026-05-13 08:52:30 -05:00
parent 2ce3e57401
commit aeff7af92a
4 changed files with 52 additions and 49 deletions
+18 -18
View File
@@ -22,9 +22,9 @@ type Config struct {
// Must be set when working with implicit headers. // Must be set when working with implicit headers.
MaxImplicitPayloadLength uint8 MaxImplicitPayloadLength uint8
CodingRate CodingRate CodingRate CodingRate
SpreadFactor SpreadFactor // this should probably be called "SpreadingFactor" to be consistent with LoRa terminology SpreadingFactor SpreadingFactor
SyncWord uint16 // for new chips sync word is full 16 bits SyncWord uint16 // for new chips sync word is full 16 bits
TxPower int8 // Tx Power in dBm. TxPower int8 // Tx Power in dBm.
CRC bool CRC bool
// Low data rate optimisation flag. The use of this flag is mandated when // Low data rate optimisation flag. The use of this flag is mandated when
// the symbol duration exceeds 16ms. Increases reliability at high spreading factors. // the symbol duration exceeds 16ms. Increases reliability at high spreading factors.
@@ -34,9 +34,9 @@ type Config struct {
} }
// SymbolPeriod returns the time it takes to transmit a single symbol given the // SymbolPeriod returns the time it takes to transmit a single symbol given the
// current configuration parameters. It depends on Spread factor and Bandwidth. // current configuration parameters. It depends on Spreading factor and Bandwidth.
func (cfg *Config) SymbolPeriod() time.Duration { func (cfg *Config) SymbolPeriod() time.Duration {
T_s := time.Second * time.Duration(cfg.SpreadFactor.ChipsPerSymbol()) / T_s := time.Second * time.Duration(cfg.SpreadingFactor.ChipsPerSymbol()) /
time.Duration(cfg.Bandwidth.Hertz()) time.Duration(cfg.Bandwidth.Hertz())
return T_s return T_s
} }
@@ -47,7 +47,7 @@ func (cfg *Config) SymbolPeriod() time.Duration {
// - CRC presence (presence == longer) // - CRC presence (presence == longer)
// - Header type (explicit == longer) // - Header type (explicit == longer)
// - Coding rate (proportional) // - Coding rate (proportional)
// - Spread factor (inversely proportional) // - Spreading factor (inversely proportional)
// - Preamble length (proportional) // - Preamble length (proportional)
// - Low data rate optimisation (presence == longer) // - Low data rate optimisation (presence == longer)
func (cfg *Config) TimeOnAir(payloadLength int) time.Duration { func (cfg *Config) TimeOnAir(payloadLength int) time.Duration {
@@ -58,10 +58,10 @@ func (cfg *Config) TimeOnAir(payloadLength int) time.Duration {
ih := int64(cfg.HeaderType) ih := int64(cfg.HeaderType)
ldr := int64(b2u8(cfg.LDRO)) ldr := int64(b2u8(cfg.LDRO))
cr := int64(cfg.CodingRate) cr := int64(cfg.CodingRate)
spread := int64(cfg.SpreadFactor) sf := int64(cfg.SpreadingFactor)
// Page 31 SX1276IMLTRT SEMTECH | Alldatasheet. // Page 31 SX1276IMLTRT SEMTECH | Alldatasheet.
Npayload := 8*int64(payloadLength) - 4*spread + 28 + 16*crc - 20*ih Npayload := 8*int64(payloadLength) - 4*sf + 28 + 16*crc - 20*ih
div := 4 * (spread - 2*ldr) div := 4 * (sf - 2*ldr)
// Apply Ceil and max with minimal branching. // Apply Ceil and max with minimal branching.
if Npayload < 0 || div <= 0 { if Npayload < 0 || div <= 0 {
Npayload = 0 Npayload = 0
@@ -77,7 +77,7 @@ func (cfg *Config) TimeOnAir(payloadLength int) time.Duration {
// time calculated. // time calculated.
Npayload += 8 + int64(cfg.PreambleLength) + 5 Npayload += 8 + int64(cfg.PreambleLength) + 5
// Calculate LoRa Transmission Parameter Relationship page 28. // Calculate LoRa Transmission Parameter Relationship page 28.
chipsPerSymbol := cfg.SpreadFactor.ChipsPerSymbol() // chips per symbol. chipsPerSymbol := cfg.SpreadingFactor.ChipsPerSymbol() // chips per symbol.
return time.Second * time.Duration(Npayload*int64(chipsPerSymbol)) / return time.Second * time.Duration(Npayload*int64(chipsPerSymbol)) /
time.Duration(cfg.Bandwidth.Hertz()) time.Duration(cfg.Bandwidth.Hertz())
} }
@@ -110,15 +110,15 @@ const (
CR4_8 CodingRate = 4 CR4_8 CodingRate = 4
) )
// SpreadFactor defines the number of chips per symbol. Higher spread factors // SpreadingFactor defines the number of chips per symbol. Higher spreading factors
// imply longer transmission times but more robust communications. // imply longer transmission times but more robust communications.
// The number of chips per symbol is 2^SF, so a spread factor of 8 takes twice // 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 spread factor of 7. // as long to transmit a symbol as a spreading factor of 7.
type SpreadFactor uint8 type SpreadingFactor uint8
// Common spread factors. Decide the number of chips per symbol. // Common spreading factors. Decide the number of chips per symbol.
const ( const (
SF5 SpreadFactor = iota + 5 SF5 SpreadingFactor = iota + 5
SF6 SF6
SF7 SF7
SF8 SF8
@@ -130,9 +130,9 @@ const (
// ChipsPerSymbol returns the number of chips in a symbol. A chip is a subdivision // 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 // 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 tells where to start // the units of this value is Hz, not Duration. A chip tells where to start
// the frequency sweep for a symbol. // the frequency sweep for a symbol.
func (sf SpreadFactor) ChipsPerSymbol() int64 { func (sf SpreadingFactor) ChipsPerSymbol() int64 {
return 1 << sf return 1 << sf
} }
+14 -14
View File
@@ -22,13 +22,13 @@ func TestTimeOnAir(t *testing.T) {
{ {
desc: "LoRaWAN 240bytes", // https://www.thethingsnetwork.org/airtime-calculator desc: "LoRaWAN 240bytes", // https://www.thethingsnetwork.org/airtime-calculator
cfg: lora.Config{ cfg: lora.Config{
Bandwidth: lora.BW125k, Bandwidth: lora.BW125k,
Frequency: 915e6, Frequency: 915e6,
SpreadFactor: lora.SF7, SpreadingFactor: lora.SF7,
HeaderType: lora.HeaderExplicit, HeaderType: lora.HeaderExplicit,
CodingRate: loraWANCodeRate, CodingRate: loraWANCodeRate,
CRC: true, CRC: true,
PreambleLength: loraWANPreambleLength, PreambleLength: loraWANPreambleLength,
}, },
plen: 240, plen: 240,
expected: 394.5e3 * time.Microsecond, expected: 394.5e3 * time.Microsecond,
@@ -36,13 +36,13 @@ func TestTimeOnAir(t *testing.T) {
{ {
desc: "LoRaWAN 5byte BW=500kHz", // https://www.thethingsnetwork.org/airtime-calculator desc: "LoRaWAN 5byte BW=500kHz", // https://www.thethingsnetwork.org/airtime-calculator
cfg: lora.Config{ cfg: lora.Config{
Bandwidth: lora.BW500k, Bandwidth: lora.BW500k,
Frequency: 915e6, Frequency: 915e6,
SpreadFactor: lora.SF7, SpreadingFactor: lora.SF7,
HeaderType: lora.HeaderExplicit, HeaderType: lora.HeaderExplicit,
CodingRate: loraWANCodeRate, CodingRate: loraWANCodeRate,
CRC: true, CRC: true,
PreambleLength: loraWANPreambleLength, PreambleLength: loraWANPreambleLength,
}, },
plen: 10, plen: 10,
expected: 15.4e3 * time.Microsecond, expected: 15.4e3 * time.Microsecond,
+16 -13
View File
@@ -81,7 +81,7 @@ type DeviceLoRa struct {
func DefaultConfig(freq lora.Frequency) lora.Config { func DefaultConfig(freq lora.Frequency) lora.Config {
return lora.Config{ return lora.Config{
Frequency: freq, Frequency: freq,
SpreadFactor: lora.SF7, SpreadingFactor: lora.SF7,
Bandwidth: lora.BW125k, Bandwidth: lora.BW125k,
CodingRate: lora.CR4_5, CodingRate: lora.CR4_5,
PreambleLength: 8, PreambleLength: 8,
@@ -103,7 +103,7 @@ func NewLoRa(bus SPI, cs, reset PinOutput) *DeviceLoRa {
} }
var ( var (
errBadSpread = errors.New("bad spread factor") errBadSpreadingFactor = errors.New("bad spreading factor")
errSF6Implicit = errors.New("SF6 can only be used with implicit header type") // Page 30: Implicit Header Mode. errSF6Implicit = errors.New("SF6 can only be used with implicit header type") // Page 30: Implicit Header Mode.
errPreambleTooShort = errors.New("preamble length too short") errPreambleTooShort = errors.New("preamble length too short")
ErrNotDetected = errors.New("sx127x not detected") ErrNotDetected = errors.New("sx127x not detected")
@@ -116,6 +116,7 @@ var (
ErrCRC = errors.New("crc error") ErrCRC = errors.New("crc error")
ErrRxTimeout = errors.New("rx timeout") ErrRxTimeout = errors.New("rx timeout")
errImplicitPacketTooLarge = errors.New("packet length exceeds MaxImplicitPayloadLength") errImplicitPacketTooLarge = errors.New("packet length exceeds MaxImplicitPayloadLength")
errSyncWordTooLarge = errors.New("sync word too long")
) )
func (d *DeviceLoRa) InitLoRaMode() (err error) { func (d *DeviceLoRa) InitLoRaMode() (err error) {
@@ -139,9 +140,9 @@ func (d *DeviceLoRa) InitLoRaMode() (err error) {
func (d *DeviceLoRa) Configure(cfg lora.Config) (err error) { func (d *DeviceLoRa) Configure(cfg lora.Config) (err error) {
switch { switch {
case cfg.SpreadFactor < lora.SF6 || cfg.SpreadFactor > lora.SF12: case cfg.SpreadingFactor < lora.SF6 || cfg.SpreadingFactor > lora.SF12:
err = errBadSpread err = errBadSpreadingFactor
case cfg.SpreadFactor == lora.SF6 && cfg.HeaderType != lora.HeaderImplicit: case cfg.SpreadingFactor == lora.SF6 && cfg.HeaderType != lora.HeaderImplicit:
err = errSF6Implicit err = errSF6Implicit
case cfg.PreambleLength < 6: case cfg.PreambleLength < 6:
err = errPreambleTooShort err = errPreambleTooShort
@@ -156,6 +157,8 @@ func (d *DeviceLoRa) Configure(cfg lora.Config) (err error) {
err = errors.New("MaxImplicitPayloadLength parameter must be set when working with implicit headers") err = errors.New("MaxImplicitPayloadLength parameter must be set when working with implicit headers")
case cfg.TxPower > 20 || cfg.TxPower < -4: case cfg.TxPower > 20 || cfg.TxPower < -4:
err = errors.New("tx power not in operating range -4..20") err = errors.New("tx power not in operating range -4..20")
case cfg.SyncWord > 0xff:
err = errSyncWordTooLarge
} }
if err != nil { if err != nil {
return err return err
@@ -189,7 +192,7 @@ func (d *DeviceLoRa) Configure(cfg lora.Config) (err error) {
return err return err
} }
// TODO(soypat): Use default OCP? // TODO(soypat): Use default OCP?
err = d.setSyncWord(cfg.SyncWord) err = d.setSyncWord(uint8(cfg.SyncWord))
if err != nil { if err != nil {
return err return err
} }
@@ -197,7 +200,7 @@ func (d *DeviceLoRa) Configure(cfg lora.Config) (err error) {
if err != nil { if err != nil {
return err return err
} }
err = d.setSpreadFactorConsistent(cfg.SpreadFactor) err = d.setSpreadingFactorConsistent(cfg.SpreadingFactor)
if err != nil { if err != nil {
return err return err
} }
@@ -706,7 +709,7 @@ func (d *DeviceLoRa) ReadConfig() (cfg lora.Config, err error) {
cfg.CodingRate = lora.CodingRate(cfg1>>1) & 0b111 cfg.CodingRate = lora.CodingRate(cfg1>>1) & 0b111
cfg.HeaderType = lora.HeaderType(cfg1 & 1) cfg.HeaderType = lora.HeaderType(cfg1 & 1)
cfg2 := buf[1] cfg2 := buf[1]
cfg.SpreadFactor = lora.SpreadFactor(cfg1 >> 4) cfg.SpreadingFactor = lora.SpreadingFactor(cfg1 >> 4)
cfg.CRC = cfg2&0x4 != 0 cfg.CRC = cfg2&0x4 != 0
// continuousMode = cfg2&0x8 != 0 // continuousMode = cfg2&0x8 != 0
cfg.PreambleLength = binary.BigEndian.Uint16(buf[3:]) cfg.PreambleLength = binary.BigEndian.Uint16(buf[3:])
@@ -715,7 +718,7 @@ func (d *DeviceLoRa) ReadConfig() (cfg lora.Config, err error) {
if err != nil { if err != nil {
return cfg, err return cfg, err
} }
cfg.SyncWord = sync cfg.SyncWord = uint16(sync)
// Read Frequency. // Read Frequency.
err = d.read(regFRF_MSB, buf[:3]) err = d.read(regFRF_MSB, buf[:3])
if err != nil { if err != nil {
@@ -864,9 +867,9 @@ func (d *DeviceLoRa) setFrequency(freq lora.Frequency) error {
return d.Write8(regFRF_LSB, freqReg[2]) // Write LSB last! return d.Write8(regFRF_LSB, freqReg[2]) // Write LSB last!
} }
// setSpreadFactorConsistent sets the spreading factor and closely related parameters // setSpreadingFactorConsistent sets the spreading factor and closely related parameters
// including Low Data Rate Optimization, DetectionOptimize, and DetectionThreshold. // including Low Data Rate Optimization, DetectionOptimize, and DetectionThreshold.
func (d *DeviceLoRa) setSpreadFactorConsistent(sf lora.SpreadFactor) (err error) { func (d *DeviceLoRa) setSpreadingFactorConsistent(sf lora.SpreadingFactor) (err error) {
err = d.setSpreadingFactor(sf) err = d.setSpreadingFactor(sf)
if err != nil { if err != nil {
return err return err
@@ -889,9 +892,9 @@ func (d *DeviceLoRa) setSpreadFactorConsistent(sf lora.SpreadFactor) (err error)
// It does not set parameters closely associated with the spreading factor such as // It does not set parameters closely associated with the spreading factor such as
// the Low Data Optimization, the Detection threshold, Detection Optimize and the // the Low Data Optimization, the Detection threshold, Detection Optimize and the
// Symbol Timeout. // Symbol Timeout.
func (d *DeviceLoRa) setSpreadingFactor(sf lora.SpreadFactor) error { func (d *DeviceLoRa) setSpreadingFactor(sf lora.SpreadingFactor) error {
if sf < 6 || sf > 12 { if sf < 6 || sf > 12 {
return errBadSpread return errBadSpreadingFactor
} }
const sfMask = 0b111 << 4 const sfMask = 0b111 << 4
return d.writeMasked8(regMODEM_CONFIG_2, sfMask, uint8(sf)<<4) return d.writeMasked8(regMODEM_CONFIG_2, sfMask, uint8(sf)<<4)
+4 -4
View File
@@ -31,7 +31,7 @@ type DeviceLoRa struct {
func DefaultConfig(freq lora.Frequency) lora.Config { func DefaultConfig(freq lora.Frequency) lora.Config {
return lora.Config{ return lora.Config{
Frequency: freq, Frequency: freq,
SpreadFactor: lora.SF9, SpreadingFactor: lora.SF9,
Bandwidth: lora.BW1625k, Bandwidth: lora.BW1625k,
CodingRate: lora.CR4_5, CodingRate: lora.CR4_5,
PreambleLength: 12, PreambleLength: 12,
@@ -90,13 +90,13 @@ func (d *DeviceLoRa) Configure(config lora.Config) error {
if err != nil { if err != nil {
return err return err
} }
err = d.setModulationParamsLoRa(config.SpreadFactor, config.Bandwidth, config.CodingRate) err = d.setModulationParamsLoRa(config.SpreadingFactor, config.Bandwidth, config.CodingRate)
if err != nil { if err != nil {
return err return err
} }
// special register setting depending on spreading factor chosen // special register setting depending on spreading factor chosen
switch config.SpreadFactor { switch config.SpreadingFactor {
case lora.SF5, lora.SF6: case lora.SF5, lora.SF6:
d.writeRegister(regSpreadingFactorAdditionalConfiguration, []byte{0x1E}) d.writeRegister(regSpreadingFactorAdditionalConfiguration, []byte{0x1E})
case lora.SF7, lora.SF8: case lora.SF7, lora.SF8:
@@ -418,7 +418,7 @@ func (d *DeviceLoRa) setBufferBaseAddress(txBase uint8, rxBase uint8) error {
return err return err
} }
func (d *DeviceLoRa) setModulationParamsLoRa(spreadingFactor lora.SpreadFactor, bandwidth lora.Frequency, codingRate lora.CodingRate) error { func (d *DeviceLoRa) setModulationParamsLoRa(spreadingFactor lora.SpreadingFactor, bandwidth lora.Frequency, codingRate lora.CodingRate) error {
err := d.waitWhileBusy(time.Second) err := d.waitWhileBusy(time.Second)
if err != nil { if err != nil {
return err return err