added platformio project, not ready,just a dump

main
cheetah 4 years ago
parent 5f43c4fff8
commit de5e316b0f

@ -0,0 +1,5 @@
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch

@ -0,0 +1,7 @@
{
// See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": [
"platformio.platformio-ide"
]
}

@ -0,0 +1,8 @@
{
"C_Cpp.errorSquiggles": "Disabled",
"files.associations": {
"array": "cpp",
"string": "cpp",
"random": "cpp"
}
}

@ -0,0 +1,128 @@
#include "SX1276Better.h"
SX1276Better::SX1276Better(Module* mod) : SX1278(mod) {
pubmod = mod;
}
int16_t SX1276Better::begin(float freq, float bw, uint8_t sf, uint8_t cr, uint8_t syncWord, int8_t power, uint16_t preambleLength, uint8_t gain) {
// execute common part
int16_t state = SX127x::begin(SX1278_CHIP_VERSION, syncWord, preambleLength);
RADIOLIB_ASSERT(state);
// configure publicly accessible settings
state = setBandwidth(bw);
RADIOLIB_ASSERT(state);
state = setFrequency(freq);
RADIOLIB_ASSERT(state);
state = setSpreadingFactor(sf);
RADIOLIB_ASSERT(state);
state = setCodingRate(cr);
RADIOLIB_ASSERT(state);
state = setOutputPower(power);
RADIOLIB_ASSERT(state);
state = setGain(gain);
RADIOLIB_ASSERT(state);
return(state);
}
int16_t SX1276Better::beginFSK(float freq, float br, float freqDev, float rxBw, int8_t power, uint16_t preambleLength, bool enableOOK) {
// execute common part
int16_t state = SX127x::beginFSK(SX1278_CHIP_VERSION, br, freqDev, rxBw, preambleLength, enableOOK);
RADIOLIB_ASSERT(state);
// configure publicly accessible settings
state = setFrequency(freq);
RADIOLIB_ASSERT(state);
state = setOutputPower(power);
RADIOLIB_ASSERT(state);
state = setDataShaping(RADIOLIB_SHAPING_NONE);
RADIOLIB_ASSERT(state);
return(state);
}
int16_t SX1276Better::setFrequency(float freq) {
RADIOLIB_CHECK_RANGE(freq, 137.0, 1020.0, ERR_INVALID_FREQUENCY);
// SX1276/77/78 Errata fixes
if(getActiveModem() == SX127X_LORA) {
// sensitivity optimization for 500kHz bandwidth
// see SX1276/77/78 Errata, section 2.1 for details
if(abs(_bw - 500.0) <= 0.001) {
if((freq >= 862.0) && (freq <= 1020.0)) {
_mod->SPIwriteRegister(0x36, 0x02);
_mod->SPIwriteRegister(0x3a, 0x64);
} else if((freq >= 410.0) && (freq <= 525.0)) {
_mod->SPIwriteRegister(0x36, 0x02);
_mod->SPIwriteRegister(0x3a, 0x7F);
}
}
// mitigation of receiver spurious response
// see SX1276/77/78 Errata, section 2.3 for details
if(abs(_bw - 7.8) <= 0.001) {
_mod->SPIsetRegValue(0x31, 0b0000000, 7, 7);
_mod->SPIsetRegValue(0x2F, 0x48);
_mod->SPIsetRegValue(0x30, 0x00);
freq += 7.8;
} else if(abs(_bw - 10.4) <= 0.001) {
_mod->SPIsetRegValue(0x31, 0b0000000, 7, 7);
_mod->SPIsetRegValue(0x2F, 0x44);
_mod->SPIsetRegValue(0x30, 0x00);
freq += 10.4;
} else if(abs(_bw - 15.6) <= 0.001) {
_mod->SPIsetRegValue(0x31, 0b0000000, 7, 7);
_mod->SPIsetRegValue(0x2F, 0x44);
_mod->SPIsetRegValue(0x30, 0x00);
freq += 15.6;
} else if(abs(_bw - 20.8) <= 0.001) {
_mod->SPIsetRegValue(0x31, 0b0000000, 7, 7);
_mod->SPIsetRegValue(0x2F, 0x44);
_mod->SPIsetRegValue(0x30, 0x00);
freq += 20.8;
} else if(abs(_bw - 31.25) <= 0.001) {
_mod->SPIsetRegValue(0x31, 0b0000000, 7, 7);
_mod->SPIsetRegValue(0x2F, 0x44);
_mod->SPIsetRegValue(0x30, 0x00);
freq += 31.25;
} else if(abs(_bw - 41.7) <= 0.001) {
_mod->SPIsetRegValue(0x31, 0b0000000, 7, 7);
_mod->SPIsetRegValue(0x2F, 0x44);
_mod->SPIsetRegValue(0x30, 0x00);
freq += 41.7;
} else if(abs(_bw - 62.5) <= 0.001) {
_mod->SPIsetRegValue(0x31, 0b0000000, 7, 7);
_mod->SPIsetRegValue(0x2F, 0x40);
_mod->SPIsetRegValue(0x30, 0x00);
} else if(abs(_bw - 125.0) <= 0.001) {
_mod->SPIsetRegValue(0x31, 0b0000000, 7, 7);
_mod->SPIsetRegValue(0x2F, 0x40);
_mod->SPIsetRegValue(0x30, 0x00);
} else if(abs(_bw - 250.0) <= 0.001) {
_mod->SPIsetRegValue(0x31, 0b0000000, 7, 7);
_mod->SPIsetRegValue(0x2F, 0x40);
_mod->SPIsetRegValue(0x30, 0x00);
} else if(abs(_bw - 500.0) <= 0.001) {
_mod->SPIsetRegValue(0x31, 0b1000000, 7, 7);
}
}
// set frequency and if successful, save the new setting
int16_t state = SX127x::setFrequencyRaw(freq);
if(state == ERR_NONE) {
SX127x::_freq = freq;
}
return(state);
}
int16_t SX1276Better::setModeP(uint8_t mode) {
return(_mod->SPIsetRegValue(SX127X_REG_OP_MODE, mode, 2, 0, 5));
}

@ -0,0 +1,91 @@
#if !defined(_RADIOLIB_SX1276Better_H)
#define _RADIOLIB_SX1276Better_H
#include <RadioLib.h>
/*!
\class SX1276
\brief Derived class for %SX1276 modules. Overrides some methods from SX1278 due to different parameter ranges.
*/
class SX1276Better: public SX1278 {
public:
// constructor
/*!
\brief Default constructor. Called from Arduino sketch when creating new LoRa instance.
\param mod Instance of Module that will be used to communicate with the %LoRa chip.
*/
SX1276Better(Module* mod);
// basic methods
/*!
\brief %LoRa modem initialization method. Must be called at least once from Arduino sketch to initialize the module.
\param freq Carrier frequency in MHz. Allowed values range from 137.0 MHz to 1020.0 MHz.
\param bw %LoRa link bandwidth in kHz. Allowed values are 10.4, 15.6, 20.8, 31.25, 41.7, 62.5, 125, 250 and 500 kHz.
\param sf %LoRa link spreading factor. Allowed values range from 6 to 12.
\param cr %LoRa link coding rate denominator. Allowed values range from 5 to 8.
\param syncWord %LoRa sync word. Can be used to distinguish different networks. Note that value 0x34 is reserved for LoRaWAN networks.
\param power Transmission output power in dBm. Allowed values range from 2 to 17 dBm.
\param preambleLength Length of %LoRa transmission preamble in symbols. The actual preamble length is 4.25 symbols longer than the set number.
Allowed values range from 6 to 65535.
\param gain Gain of receiver LNA (low-noise amplifier). Can be set to any integer in range 1 to 6 where 1 is the highest gain.
Set to 0 to enable automatic gain control (recommended).
\returns \ref status_codes
*/
int16_t begin(float freq = 434.0, float bw = 125.0, uint8_t sf = 9, uint8_t cr = 7, uint8_t syncWord = SX127X_SYNC_WORD, int8_t power = 10, uint16_t preambleLength = 8, uint8_t gain = 0);
/*!
\brief FSK modem initialization method. Must be called at least once from Arduino sketch to initialize the module.
\param freq Carrier frequency in MHz. Allowed values range from 137.0 MHz to 525.0 MHz.
\param br Bit rate of the FSK transmission in kbps (kilobits per second). Allowed values range from 1.2 to 300.0 kbps.
\param freqDev Frequency deviation of the FSK transmission in kHz. Allowed values range from 0.6 to 200.0 kHz.
Note that the allowed range changes based on bit rate setting, so that the condition FreqDev + BitRate/2 <= 250 kHz is always met.
\param rxBw Receiver bandwidth in kHz. Allowed values are 2.6, 3.1, 3.9, 5.2, 6.3, 7.8, 10.4, 12.5, 15.6, 20.8, 25, 31.3, 41.7, 50, 62.5, 83.3, 100, 125, 166.7, 200 and 250 kHz.
\param power Transmission output power in dBm. Allowed values range from 2 to 17 dBm.
\param preambleLength Length of FSK preamble in bits.
\param enableOOK Use OOK modulation instead of FSK.
\returns \ref status_codes
*/
int16_t beginFSK(float freq = 434.0, float br = 48.0, float freqDev = 50.0, float rxBw = 125.0, int8_t power = 10, uint16_t preambleLength = 16, bool enableOOK = false);
// configuration methods
/*!
\brief Sets carrier frequency. Allowed values range from 137.0 MHz to 1020.0 MHz.
\param freq Carrier frequency to be set in MHz.
\returns \ref status_codes
*/
int16_t setFrequency(float freq);
Module* pubmod;
int16_t setModeP(uint8_t mode);
#if !defined(RADIOLIB_GODMODE)
private:
#endif
};
#endif

@ -0,0 +1,550 @@
#include "SX1278Better.h"
SX1278Better::SX1278Better(Module* mod) : SX127x(mod) {
}
int16_t SX1278Better::begin(float freq, float bw, uint8_t sf, uint8_t cr, uint8_t syncWord, int8_t power, uint16_t preambleLength, uint8_t gain) {
// execute common part
int16_t state = SX127x::begin(SX1278_CHIP_VERSION, syncWord, preambleLength);
RADIOLIB_ASSERT(state);
// configure publicly accessible settings
state = setBandwidth(bw);
RADIOLIB_ASSERT(state);
state = setFrequency(freq);
RADIOLIB_ASSERT(state);
state = setSpreadingFactor(sf);
RADIOLIB_ASSERT(state);
state = setCodingRate(cr);
RADIOLIB_ASSERT(state);
state = setOutputPower(power);
RADIOLIB_ASSERT(state);
state = setGain(gain);
RADIOLIB_ASSERT(state);
return(state);
}
int16_t SX1278Better::beginFSK(float freq, float br, float freqDev, float rxBw, int8_t power, uint16_t preambleLength, bool enableOOK) {
// execute common part
int16_t state = SX127x::beginFSK(SX1278_CHIP_VERSION, br, freqDev, rxBw, preambleLength, enableOOK);
RADIOLIB_ASSERT(state);
// configure settings not accessible by API
state = configFSK();
RADIOLIB_ASSERT(state);
// configure publicly accessible settings
state = setFrequency(freq);
RADIOLIB_ASSERT(state);
state = setOutputPower(power);
RADIOLIB_ASSERT(state);
state = setDataShaping(RADIOLIB_SHAPING_NONE);
RADIOLIB_ASSERT(state);
return(state);
}
void SX1278Better::reset() {
Module::pinMode(_mod->getRst(), OUTPUT);
Module::digitalWrite(_mod->getRst(), LOW);
Module::delay(1);
Module::digitalWrite(_mod->getRst(), HIGH);
Module::delay(5);
}
int16_t SX1278Better::setFrequency(float freq) {
RADIOLIB_CHECK_RANGE(freq, 137.0, 525.0, ERR_INVALID_FREQUENCY);
// SX1276/77/78 Errata fixes
if(getActiveModem() == SX127X_LORA) {
// sensitivity optimization for 500kHz bandwidth
// see SX1276/77/78 Errata, section 2.1 for details
if(abs(_bw - 500.0) <= 0.001) {
if((freq >= 862.0) && (freq <= 1020.0)) {
_mod->SPIwriteRegister(0x36, 0x02);
_mod->SPIwriteRegister(0x3a, 0x64);
} else if((freq >= 410.0) && (freq <= 525.0)) {
_mod->SPIwriteRegister(0x36, 0x02);
_mod->SPIwriteRegister(0x3a, 0x7F);
}
}
// mitigation of receiver spurious response
// see SX1276/77/78 Errata, section 2.3 for details
if(abs(_bw - 7.8) <= 0.001) {
_mod->SPIsetRegValue(0x31, 0b0000000, 7, 7);
_mod->SPIsetRegValue(0x2F, 0x48);
_mod->SPIsetRegValue(0x30, 0x00);
freq += 7.8;
} else if(abs(_bw - 10.4) <= 0.001) {
_mod->SPIsetRegValue(0x31, 0b0000000, 7, 7);
_mod->SPIsetRegValue(0x2F, 0x44);
_mod->SPIsetRegValue(0x30, 0x00);
freq += 10.4;
} else if(abs(_bw - 15.6) <= 0.001) {
_mod->SPIsetRegValue(0x31, 0b0000000, 7, 7);
_mod->SPIsetRegValue(0x2F, 0x44);
_mod->SPIsetRegValue(0x30, 0x00);
freq += 15.6;
} else if(abs(_bw - 20.8) <= 0.001) {
_mod->SPIsetRegValue(0x31, 0b0000000, 7, 7);
_mod->SPIsetRegValue(0x2F, 0x44);
_mod->SPIsetRegValue(0x30, 0x00);
freq += 20.8;
} else if(abs(_bw - 31.25) <= 0.001) {
_mod->SPIsetRegValue(0x31, 0b0000000, 7, 7);
_mod->SPIsetRegValue(0x2F, 0x44);
_mod->SPIsetRegValue(0x30, 0x00);
freq += 31.25;
} else if(abs(_bw - 41.7) <= 0.001) {
_mod->SPIsetRegValue(0x31, 0b0000000, 7, 7);
_mod->SPIsetRegValue(0x2F, 0x44);
_mod->SPIsetRegValue(0x30, 0x00);
freq += 41.7;
} else if(abs(_bw - 62.5) <= 0.001) {
_mod->SPIsetRegValue(0x31, 0b0000000, 7, 7);
_mod->SPIsetRegValue(0x2F, 0x40);
_mod->SPIsetRegValue(0x30, 0x00);
} else if(abs(_bw - 125.0) <= 0.001) {
_mod->SPIsetRegValue(0x31, 0b0000000, 7, 7);
_mod->SPIsetRegValue(0x2F, 0x40);
_mod->SPIsetRegValue(0x30, 0x00);
} else if(abs(_bw - 250.0) <= 0.001) {
_mod->SPIsetRegValue(0x31, 0b0000000, 7, 7);
_mod->SPIsetRegValue(0x2F, 0x40);
_mod->SPIsetRegValue(0x30, 0x00);
} else if(abs(_bw - 500.0) <= 0.001) {
_mod->SPIsetRegValue(0x31, 0b1000000, 7, 7);
}
}
// set frequency and if successful, save the new setting
int16_t state = SX127x::setFrequencyRaw(freq);
if(state == ERR_NONE) {
SX127x::_freq = freq;
}
return(state);
}
int16_t SX1278Better::setBandwidth(float bw) {
// check active modem
if(getActiveModem() != SX127X_LORA) {
return(ERR_WRONG_MODEM);
}
uint8_t newBandwidth;
// check allowed bandwidth values
if(abs(bw - 7.8) <= 0.001) {
newBandwidth = SX1278_BW_7_80_KHZ;
} else if(abs(bw - 10.4) <= 0.001) {
newBandwidth = SX1278_BW_10_40_KHZ;
} else if(abs(bw - 15.6) <= 0.001) {
newBandwidth = SX1278_BW_15_60_KHZ;
} else if(abs(bw - 20.8) <= 0.001) {
newBandwidth = SX1278_BW_20_80_KHZ;
} else if(abs(bw - 31.25) <= 0.001) {
newBandwidth = SX1278_BW_31_25_KHZ;
} else if(abs(bw - 41.7) <= 0.001) {
newBandwidth = SX1278_BW_41_70_KHZ;
} else if(abs(bw - 62.5) <= 0.001) {
newBandwidth = SX1278_BW_62_50_KHZ;
} else if(abs(bw - 125.0) <= 0.001) {
newBandwidth = SX1278_BW_125_00_KHZ;
} else if(abs(bw - 250.0) <= 0.001) {
newBandwidth = SX1278_BW_250_00_KHZ;
} else if(abs(bw - 500.0) <= 0.001) {
newBandwidth = SX1278_BW_500_00_KHZ;
} else {
return(ERR_INVALID_BANDWIDTH);
}
// set bandwidth and if successful, save the new setting
int16_t state = SX1278::setBandwidthRaw(newBandwidth);
if(state == ERR_NONE) {
SX127x::_bw = bw;
// calculate symbol length and set low data rate optimization, if auto-configuration is enabled
if(_ldroAuto) {
float symbolLength = (float)(uint32_t(1) << SX127x::_sf) / (float)SX127x::_bw;
RADIOLIB_DEBUG_PRINT("Symbol length: ");
RADIOLIB_DEBUG_PRINT(symbolLength);
RADIOLIB_DEBUG_PRINTLN(" ms");
if(symbolLength >= 16.0) {
state = _mod->SPIsetRegValue(SX1278_REG_MODEM_CONFIG_3, SX1278_LOW_DATA_RATE_OPT_ON, 3, 3);
} else {
state = _mod->SPIsetRegValue(SX1278_REG_MODEM_CONFIG_3, SX1278_LOW_DATA_RATE_OPT_OFF, 3, 3);
}
}
}
return(state);
}
int16_t SX1278Better::setSpreadingFactor(uint8_t sf) {
// check active modem
if(getActiveModem() != SX127X_LORA) {
return(ERR_WRONG_MODEM);
}
uint8_t newSpreadingFactor;
// check allowed spreading factor values
switch(sf) {
case 6:
newSpreadingFactor = SX127X_SF_6;
break;
case 7:
newSpreadingFactor = SX127X_SF_7;
break;
case 8:
newSpreadingFactor = SX127X_SF_8;
break;
case 9:
newSpreadingFactor = SX127X_SF_9;
break;
case 10:
newSpreadingFactor = SX127X_SF_10;
break;
case 11:
newSpreadingFactor = SX127X_SF_11;
break;
case 12:
newSpreadingFactor = SX127X_SF_12;
break;
default:
return(ERR_INVALID_SPREADING_FACTOR);
}
// set spreading factor and if successful, save the new setting
int16_t state = SX1278::setSpreadingFactorRaw(newSpreadingFactor);
if(state == ERR_NONE) {
SX127x::_sf = sf;
// calculate symbol length and set low data rate optimization, if auto-configuration is enabled
if(_ldroAuto) {
float symbolLength = (float)(uint32_t(1) << SX127x::_sf) / (float)SX127x::_bw;
RADIOLIB_DEBUG_PRINT("Symbol length: ");
RADIOLIB_DEBUG_PRINT(symbolLength);
RADIOLIB_DEBUG_PRINTLN(" ms");
if(symbolLength >= 16.0) {
state = _mod->SPIsetRegValue(SX1278_REG_MODEM_CONFIG_3, SX1278_LOW_DATA_RATE_OPT_ON, 3, 3);
} else {
state = _mod->SPIsetRegValue(SX1278_REG_MODEM_CONFIG_3, SX1278_LOW_DATA_RATE_OPT_OFF, 3, 3);
}
}
}
return(state);
}
int16_t SX1278Better::setCodingRate(uint8_t cr) {
// check active modem
if(getActiveModem() != SX127X_LORA) {
return(ERR_WRONG_MODEM);
}
uint8_t newCodingRate;
// check allowed coding rate values
switch(cr) {
case 5:
newCodingRate = SX1278_CR_4_5;
break;
case 6:
newCodingRate = SX1278_CR_4_6;
break;
case 7:
newCodingRate = SX1278_CR_4_7;
break;
case 8:
newCodingRate = SX1278_CR_4_8;
break;
default:
return(ERR_INVALID_CODING_RATE);
}
// set coding rate and if successful, save the new setting
int16_t state = SX1278::setCodingRateRaw(newCodingRate);
if(state == ERR_NONE) {
SX127x::_cr = cr;
}
return(state);
}
int16_t SX1278Better::setOutputPower(int8_t power) {
// check allowed power range
if(!(((power >= -3) && (power <= 17)) || (power == 20))) {
return(ERR_INVALID_OUTPUT_POWER);
}
// set mode to standby
int16_t state = SX127x::standby();
// set output power
if(power < 2) {
// power is less than 2 dBm, enable PA on RFO
state |= _mod->SPIsetRegValue(SX127X_REG_PA_CONFIG, SX127X_PA_SELECT_RFO, 7, 7);
state |= _mod->SPIsetRegValue(SX127X_REG_PA_CONFIG, SX1278_LOW_POWER | (power + 3), 6, 0);
state |= _mod->SPIsetRegValue(SX1278_REG_PA_DAC, SX127X_PA_BOOST_OFF, 2, 0);
} else if(power <= 17) {
// power is 2 - 17 dBm, enable PA1 + PA2 on PA_BOOST
state |= _mod->SPIsetRegValue(SX127X_REG_PA_CONFIG, SX127X_PA_SELECT_BOOST, 7, 7);
state |= _mod->SPIsetRegValue(SX127X_REG_PA_CONFIG, SX1278_MAX_POWER | (power - 2), 6, 0);
state |= _mod->SPIsetRegValue(SX1278_REG_PA_DAC, SX127X_PA_BOOST_OFF, 2, 0);
} else if(power == 20) {
// power is 20 dBm, enable PA1 + PA2 on PA_BOOST and enable high power mode
state |= _mod->SPIsetRegValue(SX127X_REG_PA_CONFIG, SX127X_PA_SELECT_BOOST, 7, 7);
state |= _mod->SPIsetRegValue(SX127X_REG_PA_CONFIG, SX1278_MAX_POWER | (power - 5), 6, 0);
state |= _mod->SPIsetRegValue(SX1278_REG_PA_DAC, SX127X_PA_BOOST_ON, 2, 0);
}
return(state);
}
int16_t SX1278Better::setGain(uint8_t gain) {
// check active modem
if(getActiveModem() != SX127X_LORA) {
return(ERR_WRONG_MODEM);
}
// check allowed range
if(gain > 6) {
return(ERR_INVALID_GAIN);
}
// set mode to standby
int16_t state = SX127x::standby();
// set gain
if(gain == 0) {
// gain set to 0, enable AGC loop
state |= _mod->SPIsetRegValue(SX1278_REG_MODEM_CONFIG_3, SX1278_AGC_AUTO_ON, 2, 2);
} else {
state |= _mod->SPIsetRegValue(SX1278_REG_MODEM_CONFIG_3, SX1278_AGC_AUTO_OFF, 2, 2);
state |= _mod->SPIsetRegValue(SX127X_REG_LNA, (gain << 5) | SX127X_LNA_BOOST_ON);
}
return(state);
}
int16_t SX1278Better::setDataShaping(uint8_t sh) {
// check active modem
if(getActiveModem() != SX127X_FSK_OOK) {
return(ERR_WRONG_MODEM);
}
// check modulation
if(SX127x::_ook) {
return(ERR_INVALID_MODULATION);
}
// set mode to standby
int16_t state = SX127x::standby();
RADIOLIB_ASSERT(state);
// set data shaping
switch(sh) {
case RADIOLIB_SHAPING_NONE:
return(_mod->SPIsetRegValue(SX127X_REG_PA_RAMP, SX1278_NO_SHAPING, 6, 5));
case RADIOLIB_SHAPING_0_3:
return(_mod->SPIsetRegValue(SX127X_REG_PA_RAMP, SX1278_FSK_GAUSSIAN_0_3, 6, 5));
case RADIOLIB_SHAPING_0_5:
return(_mod->SPIsetRegValue(SX127X_REG_PA_RAMP, SX1278_FSK_GAUSSIAN_0_5, 6, 5));
case RADIOLIB_SHAPING_1_0:
return(_mod->SPIsetRegValue(SX127X_REG_PA_RAMP, SX1278_FSK_GAUSSIAN_1_0, 6, 5));
default:
return(ERR_INVALID_DATA_SHAPING);
}
}
int16_t SX1278Better::setDataShapingOOK(uint8_t sh) {
// check active modem
if(getActiveModem() != SX127X_FSK_OOK) {
return(ERR_WRONG_MODEM);
}
// check modulation
if(!SX127x::_ook) {
return(ERR_INVALID_MODULATION);
}
// set mode to standby
int16_t state = SX127x::standby();
// set data shaping
switch(sh) {
case 0:
state |= _mod->SPIsetRegValue(SX127X_REG_PA_RAMP, SX1278_NO_SHAPING, 6, 5);
break;
case 1:
state |= _mod->SPIsetRegValue(SX127X_REG_PA_RAMP, SX1278_OOK_FILTER_BR, 6, 5);
break;
case 2:
state |= _mod->SPIsetRegValue(SX127X_REG_PA_RAMP, SX1278_OOK_FILTER_2BR, 6, 5);
break;
default:
return(ERR_INVALID_DATA_SHAPING);
}
return(state);
}
float SX1278Better::getRSSI() {
if(getActiveModem() == SX127X_LORA) {
// for LoRa, get RSSI of the last packet
float lastPacketRSSI;
// RSSI calculation uses different constant for low-frequency and high-frequency ports
if(_freq < 868.0) {
lastPacketRSSI = -164 + _mod->SPIgetRegValue(SX127X_REG_PKT_RSSI_VALUE);
} else {
lastPacketRSSI = -157 + _mod->SPIgetRegValue(SX127X_REG_PKT_RSSI_VALUE);
}
// spread-spectrum modulation signal can be received below noise floor
// check last packet SNR and if it's less than 0, add it to reported RSSI to get the correct value
float lastPacketSNR = SX127x::getSNR();
if(lastPacketSNR < 0.0) {
lastPacketRSSI += lastPacketSNR;
}
return(lastPacketRSSI);
} else {
// enable listen mode
startReceive();
// read the value for FSK
float rssi = (float)_mod->SPIgetRegValue(SX127X_REG_RSSI_VALUE_FSK) / -2.0;
// set mode back to standby
standby();
// return the value
return(rssi);
}
}
int16_t SX1278Better::setCRC(bool enableCRC) {
if(getActiveModem() == SX127X_LORA) {
// set LoRa CRC
SX127x::_crcEnabled = enableCRC;
if(enableCRC) {
return(_mod->SPIsetRegValue(SX127X_REG_MODEM_CONFIG_2, SX1278_RX_CRC_MODE_ON, 2, 2));
} else {
return(_mod->SPIsetRegValue(SX127X_REG_MODEM_CONFIG_2, SX1278_RX_CRC_MODE_OFF, 2, 2));
}
} else {
// set FSK CRC
if(enableCRC) {
return(_mod->SPIsetRegValue(SX127X_REG_PACKET_CONFIG_1, SX127X_CRC_ON, 4, 4));
} else {
return(_mod->SPIsetRegValue(SX127X_REG_PACKET_CONFIG_1, SX127X_CRC_OFF, 4, 4));
}
}
}
int16_t SX1278Better::forceLDRO(bool enable) {
if(getActiveModem() != SX127X_LORA) {
return(ERR_WRONG_MODEM);
}
_ldroAuto = false;
if(enable) {
return(_mod->SPIsetRegValue(SX1278_REG_MODEM_CONFIG_3, SX1278_LOW_DATA_RATE_OPT_ON, 3, 3));
} else {
return(_mod->SPIsetRegValue(SX1278_REG_MODEM_CONFIG_3, SX1278_LOW_DATA_RATE_OPT_OFF, 3, 3));
}
}
int16_t SX1278Better::autoLDRO() {
if(getActiveModem() != SX127X_LORA) {
return(ERR_WRONG_MODEM);
}
_ldroAuto = true;
return(ERR_NONE);
}
int16_t SX1278Better::implicitHeader(size_t len) {
return(setHeaderType(SX1278_HEADER_IMPL_MODE, len));
}
int16_t SX1278Better::explicitHeader() {
return(setHeaderType(SX1278_HEADER_EXPL_MODE));
}
int16_t SX1278Better::setBandwidthRaw(uint8_t newBandwidth) {
// set mode to standby
int16_t state = SX127x::standby();
// write register
state |= _mod->SPIsetRegValue(SX127X_REG_MODEM_CONFIG_1, newBandwidth, 7, 4);
return(state);
}
int16_t SX1278Better::setSpreadingFactorRaw(uint8_t newSpreadingFactor) {
// set mode to standby
int16_t state = SX127x::standby();
// write registers
if(newSpreadingFactor == SX127X_SF_6) {
state |= _mod->SPIsetRegValue(SX127X_REG_MODEM_CONFIG_1, SX1278_HEADER_IMPL_MODE, 0, 0);
state |= _mod->SPIsetRegValue(SX127X_REG_MODEM_CONFIG_2, SX127X_SF_6 | SX127X_TX_MODE_SINGLE | (SX127x::_crcEnabled ? SX1278_RX_CRC_MODE_ON : SX1278_RX_CRC_MODE_OFF), 7, 2);
state |= _mod->SPIsetRegValue(SX127X_REG_DETECT_OPTIMIZE, SX127X_DETECT_OPTIMIZE_SF_6, 2, 0);
state |= _mod->SPIsetRegValue(SX127X_REG_DETECTION_THRESHOLD, SX127X_DETECTION_THRESHOLD_SF_6);
} else {
state |= _mod->SPIsetRegValue(SX127X_REG_MODEM_CONFIG_1, SX1278_HEADER_EXPL_MODE, 0, 0);
state |= _mod->SPIsetRegValue(SX127X_REG_MODEM_CONFIG_2, newSpreadingFactor | SX127X_TX_MODE_SINGLE | (SX127x::_crcEnabled ? SX1278_RX_CRC_MODE_ON : SX1278_RX_CRC_MODE_OFF), 7, 2);
state |= _mod->SPIsetRegValue(SX127X_REG_DETECT_OPTIMIZE, SX127X_DETECT_OPTIMIZE_SF_7_12, 2, 0);
state |= _mod->SPIsetRegValue(SX127X_REG_DETECTION_THRESHOLD, SX127X_DETECTION_THRESHOLD_SF_7_12);
}
return(state);
}
int16_t SX1278Better::setCodingRateRaw(uint8_t newCodingRate) {
// set mode to standby
int16_t state = SX127x::standby();
// write register
state |= _mod->SPIsetRegValue(SX127X_REG_MODEM_CONFIG_1, newCodingRate, 3, 1);
return(state);
}
int16_t SX1278Better::setHeaderType(uint8_t headerType, size_t len) {
// check active modem
if(getActiveModem() != SX127X_LORA) {
return(ERR_WRONG_MODEM);
}
// set requested packet mode
int16_t state = _mod->SPIsetRegValue(SX127X_REG_MODEM_CONFIG_1, headerType, 0, 0);
RADIOLIB_ASSERT(state);
// set length to register
state = _mod->SPIsetRegValue(SX127X_REG_PAYLOAD_LENGTH, len);
RADIOLIB_ASSERT(state);
// update cached value
_packetLength = len;
return(state);
}
int16_t SX1278Better::configFSK() {
// configure common registers
int16_t state = SX127x::configFSK();
RADIOLIB_ASSERT(state);
// set fast PLL hop
state = _mod->SPIsetRegValue(SX1278_REG_PLL_HOP, SX127X_FAST_HOP_ON, 7, 7);
return(state);
}
#endif

@ -0,0 +1,314 @@
#if !defined(_RADIOLIB_SX1278Better_H)
#define _RADIOLIB_SX1278Better_H
#include <RadioLib.h>
// SX1278 specific register map
#define SX1278_REG_MODEM_CONFIG_3 0x26
#define SX1278_REG_PLL_HOP 0x44
#define SX1278_REG_TCXO 0x4B
#define SX1278_REG_PA_DAC 0x4D
#define SX1278_REG_FORMER_TEMP 0x5B
#define SX1278_REG_REG_BIT_RATE_FRAC 0x5D
#define SX1278_REG_AGC_REF 0x61
#define SX1278_REG_AGC_THRESH_1 0x62
#define SX1278_REG_AGC_THRESH_2 0x63
#define SX1278_REG_AGC_THRESH_3 0x64
#define SX1278_REG_PLL 0x70
// SX1278 LoRa modem settings
// SX1278_REG_OP_MODE MSB LSB DESCRIPTION
#define SX1278_HIGH_FREQ 0b00000000 // 3 3 access HF test registers
#define SX1278_LOW_FREQ 0b00001000 // 3 3 access LF test registers
// SX1278_REG_FRF_MSB + REG_FRF_MID + REG_FRF_LSB
#define SX1278_FRF_MSB 0x6C // 7 0 carrier frequency setting: f_RF = (F(XOSC) * FRF)/2^19
#define SX1278_FRF_MID 0x80 // 7 0 where F(XOSC) = 32 MHz
#define SX1278_FRF_LSB 0x00 // 7 0 FRF = 3 byte value of FRF registers
// SX1278_REG_PA_CONFIG
#define SX1278_MAX_POWER 0b01110000 // 6 4 max power: P_max = 10.8 + 0.6*MAX_POWER [dBm]; P_max(MAX_POWER = 0b111) = 15 dBm
#define SX1278_LOW_POWER 0b00100000 // 6 4
// SX1278_REG_LNA
#define SX1278_LNA_BOOST_LF_OFF 0b00000000 // 4 3 default LNA current
// SX127X_REG_MODEM_CONFIG_1
#define SX1278_BW_7_80_KHZ 0b00000000 // 7 4 bandwidth: 7.80 kHz
#define SX1278_BW_10_40_KHZ 0b00010000 // 7 4 10.40 kHz
#define SX1278_BW_15_60_KHZ 0b00100000 // 7 4 15.60 kHz
#define SX1278_BW_20_80_KHZ 0b00110000 // 7 4 20.80 kHz
#define SX1278_BW_31_25_KHZ 0b01000000 // 7 4 31.25 kHz
#define SX1278_BW_41_70_KHZ 0b01010000 // 7 4 41.70 kHz
#define SX1278_BW_62_50_KHZ 0b01100000 // 7 4 62.50 kHz
#define SX1278_BW_125_00_KHZ 0b01110000 // 7 4 125.00 kHz
#define SX1278_BW_250_00_KHZ 0b10000000 // 7 4 250.00 kHz
#define SX1278_BW_500_00_KHZ 0b10010000 // 7 4 500.00 kHz
#define SX1278_CR_4_5 0b00000010 // 3 1 error coding rate: 4/5
#define SX1278_CR_4_6 0b00000100 // 3 1 4/6
#define SX1278_CR_4_7 0b00000110 // 3 1 4/7
#define SX1278_CR_4_8 0b00001000 // 3 1 4/8
#define SX1278_HEADER_EXPL_MODE 0b00000000 // 0 0 explicit header mode
#define SX1278_HEADER_IMPL_MODE 0b00000001 // 0 0 implicit header mode
// SX127X_REG_MODEM_CONFIG_2
#define SX1278_RX_CRC_MODE_OFF 0b00000000 // 2 2 CRC disabled
#define SX1278_RX_CRC_MODE_ON 0b00000100 // 2 2 CRC enabled
// SX1278_REG_MODEM_CONFIG_3
#define SX1278_LOW_DATA_RATE_OPT_OFF 0b00000000 // 3 3 low data rate optimization disabled
#define SX1278_LOW_DATA_RATE_OPT_ON 0b00001000 // 3 3 low data rate optimization enabled
#define SX1278_AGC_AUTO_OFF 0b00000000 // 2 2 LNA gain set by REG_LNA
#define SX1278_AGC_AUTO_ON 0b00000100 // 2 2 LNA gain set by internal AGC loop
// SX127X_REG_VERSION
#define SX1278_CHIP_VERSION 0x12
// SX1278 FSK modem settings
// SX127X_REG_PA_RAMP
#define SX1278_NO_SHAPING 0b00000000 // 6 5 data shaping: no shaping (default)
#define SX1278_FSK_GAUSSIAN_1_0 0b00100000 // 6 5 FSK modulation Gaussian filter, BT = 1.0
#define SX1278_FSK_GAUSSIAN_0_5 0b01000000 // 6 5 FSK modulation Gaussian filter, BT = 0.5
#define SX1278_FSK_GAUSSIAN_0_3 0b01100000 // 6 5 FSK modulation Gaussian filter, BT = 0.3
#define SX1278_OOK_FILTER_BR 0b00100000 // 6 5 OOK modulation filter, f_cutoff = BR
#define SX1278_OOK_FILTER_2BR 0b01000000 // 6 5 OOK modulation filter, f_cutoff = 2*BR
// SX1278_REG_AGC_REF
#define SX1278_AGC_REFERENCE_LEVEL_LF 0x19 // 5 0 floor reference for AGC thresholds: AgcRef = -174 + 10*log(2*RxBw) + 8 + AGC_REFERENCE_LEVEL [dBm]: below 525 MHz
#define SX1278_AGC_REFERENCE_LEVEL_HF 0x1C // 5 0 above 779 MHz
// SX1278_REG_AGC_THRESH_1
#define SX1278_AGC_STEP_1_LF 0x0C // 4 0 1st AGC threshold: below 525 MHz
#define SX1278_AGC_STEP_1_HF 0x0E // 4 0 above 779 MHz
// SX1278_REG_AGC_THRESH_2
#define SX1278_AGC_STEP_2_LF 0x40 // 7 4 2nd AGC threshold: below 525 MHz
#define SX1278_AGC_STEP_2_HF 0x50 // 7 4 above 779 MHz
#define SX1278_AGC_STEP_3 0x0B // 3 0 3rd AGC threshold
// SX1278_REG_AGC_THRESH_3
#define SX1278_AGC_STEP_4 0xC0 // 7 4 4th AGC threshold
#define SX1278_AGC_STEP_5 0x0C // 4 0 5th AGC threshold
/*!
\class SX1278
\brief Derived class for %SX1278 modules. Also used as base class for SX1276, SX1277, SX1279, RFM95 and RFM96.
All of these modules use the same basic hardware and only differ in parameter ranges (and names).
*/
class SX1278Better: public SX127x {
public:
// constructor
/*!
\brief Default constructor. Called from Arduino sketch when creating new LoRa instance.
\param mod Instance of Module that will be used to communicate with the %LoRa chip.
*/
SX1278Better(Module* mod);
// basic methods
/*!
\brief %LoRa modem initialization method. Must be called at least once from Arduino sketch to initialize the module.
\param freq Carrier frequency in MHz. Allowed values range from 137.0 MHz to 525.0 MHz.
\param bw %LoRa link bandwidth in kHz. Allowed values are 10.4, 15.6, 20.8, 31.25, 41.7, 62.5, 125, 250 and 500 kHz.
\param sf %LoRa link spreading factor. Allowed values range from 6 to 12.
\param cr %LoRa link coding rate denominator. Allowed values range from 5 to 8.
\param syncWord %LoRa sync word. Can be used to distinguish different networks. Note that value 0x34 is reserved for LoRaWAN networks.
\param power Transmission output power in dBm. Allowed values range from 2 to 17 dBm.
\param preambleLength Length of %LoRa transmission preamble in symbols. The actual preamble length is 4.25 symbols longer than the set number.
Allowed values range from 6 to 65535.
\param gain Gain of receiver LNA (low-noise amplifier). Can be set to any integer in range 1 to 6 where 1 is the highest gain.
Set to 0 to enable automatic gain control (recommended).
\returns \ref status_codes
*/
int16_t begin(float freq = 434.0, float bw = 125.0, uint8_t sf = 9, uint8_t cr = 7, uint8_t syncWord = SX127X_SYNC_WORD, int8_t power = 10, uint16_t preambleLength = 8, uint8_t gain = 0);
/*!
\brief FSK modem initialization method. Must be called at least once from Arduino sketch to initialize the module.
\param freq Carrier frequency in MHz. Allowed values range from 137.0 MHz to 525.0 MHz.
\param br Bit rate of the FSK transmission in kbps (kilobits per second). Allowed values range from 1.2 to 300.0 kbps.
\param freqDev Frequency deviation of the FSK transmission in kHz. Allowed values range from 0.6 to 200.0 kHz.
Note that the allowed range changes based on bit rate setting, so that the condition FreqDev + BitRate/2 <= 250 kHz is always met.
\param rxBw Receiver bandwidth in kHz. Allowed values are 2.6, 3.1, 3.9, 5.2, 6.3, 7.8, 10.4, 12.5, 15.6, 20.8, 25, 31.3, 41.7, 50, 62.5, 83.3, 100, 125, 166.7, 200 and 250 kHz.
\param power Transmission output power in dBm. Allowed values range from 2 to 17 dBm.
\param preambleLength Length of FSK preamble in bits.
\param enableOOK Use OOK modulation instead of FSK.
\returns \ref status_codes
*/
int16_t beginFSK(float freq = 434.0, float br = 48.0, float freqDev = 50.0, float rxBw = 125.0, int8_t power = 10, uint16_t preambleLength = 16, bool enableOOK = false);
/*!
\brief Reset method. Will reset the chip to the default state using RST pin.
*/
void reset() override;
// configuration methods
/*!
\brief Sets carrier frequency. Allowed values range from 137.0 MHz to 525.0 MHz.
\param freq Carrier frequency to be set in MHz.
\returns \ref status_codes
*/
int16_t setFrequency(float freq);
/*!
\brief Sets %LoRa link bandwidth. Allowed values are 10.4, 15.6, 20.8, 31.25, 41.7, 62.5, 125, 250 and 500 kHz. Only available in %LoRa mode.
\param bw %LoRa link bandwidth to be set in kHz.
\returns \ref status_codes
*/
int16_t setBandwidth(float bw);
/*!
\brief Sets %LoRa link spreading factor. Allowed values range from 6 to 12. Only available in %LoRa mode.
\param sf %LoRa link spreading factor to be set.
\returns \ref status_codes
*/
int16_t setSpreadingFactor(uint8_t sf);
/*!
\brief Sets %LoRa link coding rate denominator. Allowed values range from 5 to 8. Only available in %LoRa mode.
\param cr %LoRa link coding rate denominator to be set.
\returns \ref status_codes
*/
int16_t setCodingRate(uint8_t cr);
/*!
\brief Sets transmission output power. Allowed values range from 2 to 17 dBm.
\param power Transmission output power in dBm.
\returns \ref status_codes
*/
int16_t setOutputPower(int8_t power);
/*!
\brief Sets gain of receiver LNA (low-noise amplifier). Can be set to any integer in range 1 to 6 where 1 is the highest gain.
Set to 0 to enable automatic gain control (recommended). Only available in %LoRa mode.
\param gain Gain of receiver LNA (low-noise amplifier) to be set.
\returns \ref status_codes
*/
int16_t setGain(uint8_t gain);
/*!
\brief Sets Gaussian filter bandwidth-time product that will be used for data shaping. Only available in FSK mode with FSK modulation.
Allowed values are RADIOLIB_SHAPING_0_3, RADIOLIB_SHAPING_0_5 or RADIOLIB_SHAPING_1_0. Set to RADIOLIB_SHAPING_NONE to disable data shaping.
\param sh Gaussian shaping bandwidth-time product that will be used for data shaping
\returns \ref status_codes
*/
int16_t setDataShaping(uint8_t sh) override;
/*!
\brief Sets filter cutoff frequency that will be used for data shaping.
Allowed values are 1 for frequency equal to bit rate and 2 for frequency equal to 2x bit rate. Set to 0 to disable data shaping.
Only available in FSK mode with OOK modulation.
\param sh Cutoff frequency that will be used for data shaping
\returns \ref status_codes
*/
int16_t setDataShapingOOK(uint8_t sh);
/*!
\brief Gets recorded signal strength indicator of the latest received packet for LoRa modem, or current RSSI level for FSK modem.
\returns Last packet RSSI for LoRa modem, or current RSSI level for FSK modem.
*/
float getRSSI();
/*!
\brief Enables/disables CRC check of received packets.
\param enableCRC Enable (true) or disable (false) CRC.
\returns \ref status_codes
*/
int16_t setCRC(bool enableCRC);
/*!
\brief Forces LoRa low data rate optimization. Only available in LoRa mode. After calling this method, LDRO will always be set to
the provided value, regardless of symbol length. To re-enable automatic LDRO configuration, call SX1278::autoLDRO()
\param enable Force LDRO to be always enabled (true) or disabled (false).
\returns \ref status_codes
*/
int16_t forceLDRO(bool enable);
/*!
\brief Re-enables automatic LDRO configuration. Only available in LoRa mode. After calling this method, LDRO will be enabled automatically
when symbol length exceeds 16 ms.
\returns \ref status_codes
*/
int16_t autoLDRO();
/*!
\brief Set implicit header mode for future reception/transmission.
\returns \ref status_codes
*/
int16_t implicitHeader(size_t len);
/*!
\brief Set explicit header mode for future reception/transmission.
\param len Payload length in bytes.
\returns \ref status_codes
*/
int16_t explicitHeader();
#if !defined(RADIOLIB_GODMODE)
protected:
#endif
int16_t setBandwidthRaw(uint8_t newBandwidth);
int16_t setSpreadingFactorRaw(uint8_t newSpreadingFactor);
int16_t setCodingRateRaw(uint8_t newCodingRate);
int16_t setHeaderType(uint8_t headerType, size_t len = 0xFF);
int16_t configFSK();
#if !defined(RADIOLIB_GODMODE)
private:
#endif
bool _ldroAuto = true;
bool _ldroEnabled = false;
};
#endif
#endif

@ -0,0 +1,39 @@
This directory is intended for project header files.
A header file is a file containing C declarations and macro definitions
to be shared between several project source files. You request the use of a
header file in your project source file (C, C++, etc) located in `src` folder
by including it, with the C preprocessing directive `#include'.
```src/main.c
#include "header.h"
int main (void)
{
...
}
```
Including a header file produces the same results as copying the header file
into each source file that needs it. Such copying would be time-consuming
and error-prone. With a header file, the related declarations appear
in only one place. If they need to be changed, they can be changed in one
place, and programs that include the header file will automatically use the
new version when next recompiled. The header file eliminates the labor of
finding and changing all the copies as well as the risk that a failure to
find one copy will result in inconsistencies within a program.
In C, the usual convention is to give header files names that end with `.h'.
It is most portable to use only letters, digits, dashes, and underscores in
header file names, and at most one dot.
Read more about using header files in official GCC documentation:
* Include Syntax
* Include Operation
* Once-Only Headers
* Computed Includes
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html

@ -0,0 +1,46 @@
This directory is intended for project specific (private) libraries.
PlatformIO will compile them to static libraries and link into executable file.
The source code of each library should be placed in a an own separate directory
("lib/your_library_name/[here are source files]").
For example, see a structure of the following two libraries `Foo` and `Bar`:
|--lib
| |
| |--Bar
| | |--docs
| | |--examples
| | |--src
| | |- Bar.c
| | |- Bar.h
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
| |
| |--Foo
| | |- Foo.c
| | |- Foo.h
| |
| |- README --> THIS FILE
|
|- platformio.ini
|--src
|- main.c
and a contents of `src/main.c`:
```
#include <Foo.h>
#include <Bar.h>
int main (void)
{
...
}
```
PlatformIO Library Dependency Finder will find automatically dependent
libraries scanning project source files.
More information about PlatformIO Library Dependency Finder
- https://docs.platformio.org/page/librarymanager/ldf.html

Binary file not shown.

@ -0,0 +1,17 @@
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[env:ttgo-t-beam]
platform = espressif32
board = ttgo-t-beam
framework = arduino
lib_deps = jgromes/RadioLib@^4.3.0
monitor_speed = 115200
upload_speed = 115200

@ -0,0 +1,41 @@
#include <Arduino.h>
#define RADIOLIB_LOW_LEVEL
#define RADIOLIB_GODMODE
#include <RadioLib.h>
#include <pocsag_transmitter.h>
//Better
#define LORA_SCK 5
#define LORA_MISO 19
#define LORA_MOSI 27
#define LORA_SS 18
#define LORA_DIO0 26
#define LORA_DIO1 33
#define LORA_DIO2 32
#define LORA_RST 23
SX1276 radio = new Module(LORA_SS, LORA_DIO0, LORA_RST, LORA_DIO1);
POCSAGTransmitter transmitter;
void setup() {
Serial.begin(115200);
int state = radio.beginFSK(434.230,1.2,4.5);
if (state == ERR_NONE) {
Serial.println(F("success!"));
} else {
Serial.print(F("failed, code "));
Serial.println(state);
while (true);
}
//int state2 = radio.transmit("Hello World!");
Serial.println("initialized modem");
transmitter.begin(&radio);
Serial.println("initialized transmitter");
delay(2e2);
transmitter.test();
}
void loop() {
//Serial.print(".");
delay(50);
// put your main code here, to run repeatedly:
}

@ -0,0 +1,234 @@
#include "pocsag_encoder.h"
// encoder from here https://github.com/F5OEO/rpitx/blob/master/src/pocsag/pocsag.cpp
/**
* Calculate the CRC error checking code for the given word.
* Messages use a 10 bit CRC computed from the 21 data bits.
* This is calculated through a binary polynomial long division, returning
* the remainder.
* See https://en.wikipedia.org/wiki/Cyclic_redundancy_check#Computation
* for more information.
*/
uint32_t crc(uint32_t inputMsg) {
//Align MSB of denominatorerator with MSB of message
uint32_t denominator = CRC_GENERATOR << 20;
//Message is right-padded with zeroes to the message length + crc length
uint32_t msg = inputMsg << CRC_BITS;
//We iterate until denominator has been right-shifted back to it's original value.
for (int column = 0; column <= 20; column++)
{
//Bit for the column we're aligned to
int msgBit = (msg >> (30 - column)) & 1;
//If the current bit is zero, we don't modify the message this iteration
if (msgBit != 0)
{
//While we would normally subtract in long division, we XOR here.
msg ^= denominator;
}
//Shift the denominator over to align with the next column
denominator >>= 1;
}
//At this point 'msg' contains the CRC value we've calculated
return msg & 0x3FF;
}
/**
* Calculates the even parity bit for a message.
* If the number of bits in the message is even, return 0, else return 1.
*/
uint32_t parity(uint32_t x) {
//Our parity bit
uint32_t p = 0;
//We xor p with each bit of the input value. This works because
//xoring two one-bits will cancel out and leave a zero bit. Thus
//xoring any even number of one bits will result in zero, and xoring
//any odd number of one bits will result in one.
for (int i = 0; i < 32; i++) {
p ^= (x & 1);
x >>= 1;
}
return p;
}
/**
* Encodes a 21-bit message by calculating and adding a CRC code and parity bit.
*/
uint32_t encodeCodeword(uint32_t msg) {
uint32_t fullCRC = (msg << CRC_BITS) | crc(msg);
uint32_t p = parity(fullCRC);
return (fullCRC << 1) | p;
}
/**
* ASCII encode a null-terminated string as a series of codewords, written
* to (*out). Returns the number of codewords written. Caller should ensure
* that enough memory is allocated in (*out) to contain the message
*
* initial_offset indicates which word in the current batch the function is
* beginning at, so that it can insert SYNC words at appropriate locations.
*/
uint32_t encodeASCII(uint32_t initial_offset, char *str, uint32_t *out) {
//Number of words written to *out
uint32_t numWordsWritten = 0;
//Data for the current word we're writing
uint32_t currentWord = 0;
//Nnumber of bits we've written so far to the current word
uint32_t currentNumBits = 0;
//Position of current word in the current batch
uint32_t wordPosition = initial_offset;
while (*str != 0) {
unsigned char c = *str;
str++;
//Encode the character bits backwards
for (int i = 0; i < TEXT_BITS_PER_CHAR; i++) {
currentWord <<= 1;
currentWord |= (c >> i) & 1;
currentNumBits++;
if (currentNumBits == TEXT_BITS_PER_WORD) {
//Add the MESSAGE flag to our current word and encode it.
*out = encodeCodeword(currentWord | POCSAG_FLAG_MESSAGE);
out++;
currentWord = 0;
currentNumBits = 0;
numWordsWritten++;
wordPosition++;
if (wordPosition == POCSAG_BATCH_SIZE) {
//We've filled a full batch, time to insert a SYNC word
//and start a new one.
*out = POCSAG_SYNC;
out++;
numWordsWritten++;
wordPosition = 0;
}
}
}
}
//Write remainder of message
if (currentNumBits > 0)
{
//Pad out the word to 20 bits with zeroes
currentWord <<= 20 - currentNumBits;
*out = encodeCodeword(currentWord | POCSAG_FLAG_MESSAGE);
out++;
numWordsWritten++;
wordPosition++;
if (wordPosition == POCSAG_BATCH_SIZE)
{
//We've filled a full batch, time to insert a SYNC word
//and start a new one.
*out = POCSAG_SYNC;
out++;
numWordsWritten++;
wordPosition = 0;
}
}
return numWordsWritten;
}
/**
* An address of 21 bits, but only 18 of those bits are encoded in the address
* word itself. The remaining 3 bits are derived from which frame in the batch
* is the address word. This calculates the number of words (not frames!)
* which must precede the address word so that it is in the right spot. These
* words will be filled with the idle value.
*/
int addressOffset(int address) {
return (address & 0x7) * POCSAG_FRAME_SIZE;
}
/**
* Encode a full text POCSAG transmission addressed to (address).
* (*message) is a null terminated C string.
* (*out) is the destination to which the transmission will be written.
*/
void encodeTransmission(int repeatIndex, int address, int fb, char *message, uint32_t *out) {
//Encode preamble
//Alternating 1,0,1,0 bits for 576 bits, used for receiver to synchronize
//with transmitter
if (repeatIndex == 0)
for (int i = 0; i < POCSAG_PREAMBLE_LENGTH / 32; i++)
{
*out = 0xAAAAAAAA;
out++;
}
uint32_t *start = out;
//Sync
*out = POCSAG_SYNC;
out++;
//Write out padding before adderss word
int prefixLength = addressOffset(address);
for (int i = 0; i < prefixLength; i++)
{
*out = POCSAG_IDLE;
out++;
}
//Write address word.
//The last two bits of word's data contain the message type (function bits)
//The 3 least significant bits are dropped, as those are encoded by the
//word's location.
*out = encodeCodeword(((address >> 3) << 2) | fb);
out++;
out += encodeASCII(addressOffset(address) + 1, message, out);
//Finally, write an IDLE word indicating the end of the message
*out = POCSAG_IDLE;
out++;
//Pad out the last batch with IDLE to write multiple of BATCH_SIZE + 1
//words (+ 1 is there because of the SYNC words)
size_t written = out - start;
size_t padding = (POCSAG_BATCH_SIZE + 1) - written % (POCSAG_BATCH_SIZE + 1);
for (size_t i = 0; i < padding; i++)
{
*out = POCSAG_IDLE;
out++;
}
}
/**
* Calculates the length in words of a text POCSAG message, given the address
* and the number of characters to be transmitted.
*/
size_t textMessageLength(int repeatIndex, int address, int numChars) {
size_t numWords = 0;
//Padding before address word.
numWords += addressOffset(address);
//Address word itself
numWords++;
//numChars * 7 bits per character / 20 bits per word, rounding up
numWords += (numChars * TEXT_BITS_PER_CHAR + (TEXT_BITS_PER_WORD - 1)) / TEXT_BITS_PER_WORD;
//Idle word representing end of message
numWords++;
//Pad out last batch with idles
numWords += POCSAG_BATCH_SIZE - (numWords % POCSAG_BATCH_SIZE);
//Batches consist of 16 words each and are preceded by a sync word.
//So we add one word for every 16 message words
numWords += numWords / POCSAG_BATCH_SIZE;
//Preamble of 576 alternating 1,0,1,0 bits before the message
//Even though this comes first, we add it to the length last so it
//doesn't affect the other word-based calculations
if (repeatIndex == 0)
numWords += POCSAG_PREAMBLE_LENGTH / 32;
return numWords;
}

@ -0,0 +1,29 @@
#include <stdint.h>
#include <stdlib.h>
#define POCSAG_SYNC 0x7CD215D8
#define POCSAG_IDLE 0x7A89C197
#define POCSAG_FRAME_SIZE 2
#define POCSAG_BATCH_SIZE 16
#define POCSAG_PREAMBLE_LENGTH 576
#define POCSAG_FLAG_ADDRESS 0x000000
#define POCSAG_FLAG_MESSAGE 0x100000
#define POCSAG_FLAG_TEXT_DATA 0x3
#define POCSAG_FLAG_NUMERIC_DATA = 0x0
#define TEXT_BITS_PER_WORD 20
#define TEXT_BITS_PER_CHAR 7
#define CRC_BITS 10
#define CRC_GENERATOR 0b11101101001
uint32_t crc(uint32_t inputMsg);
uint32_t parity(uint32_t x);
uint32_t encodeCodeword(uint32_t msg);
uint32_t encodeASCII(uint32_t initial_offset, char *str, uint32_t *out);
int addressOffset(int address);
void encodeTransmission(int repeatIndex, int address, int fb, char *message, uint32_t *out);
size_t textMessageLength(int repeatIndex, int address, int numChars);

@ -0,0 +1,150 @@
#include "pocsag_transmitter.h"
#include "pocsag_encoder.h"
#define POCSAG_MAX_BUFFER_SIZE 2000 // 2000 QBYTEs
POCSAGTransmitter::POCSAGTransmitter() {
Serial.println("malloc");
transmitterData = (uint32_t *)malloc(sizeof(uint32_t) * POCSAG_MAX_BUFFER_SIZE); //TODO: make this static allocated
for (int z=0;z<POCSAG_MAX_BUFFER_SIZE;z++) transmitterData[z] = 0xFFFFFFFF;
encodingBuffer = (uint32_t *)malloc(sizeof(uint32_t) * POCSAG_MAX_BUFFER_SIZE); //TODO: make this static allocated
for (int z=0;z<POCSAG_MAX_BUFFER_SIZE;z++) encodingBuffer[z] = 0xFFFFFFFF;
}
void POCSAGTransmitter::begin(SX1278 *radio) {
_radio = radio;
_radio->setFrequencyDeviation(4.5);
/*_radio->_mod->SPIsetRegValue(SX127X_REG_FDEV_MSB, 0x00);
_radio->_mod->SPIsetRegValue(SX127X_REG_FDEV_LSB, 75);*/
// _radio->setEncoding(RADIOLIB_ENCODING_NRZ);
_radio->_mod->SPIsetRegValue(SX127X_REG_PAYLOAD_LENGTH, 0x00);
_radio->_mod->SPIsetRegValue(SX127X_REG_FIFO_THRESH, SX127X_TX_START_FIFO_NOT_EMPTY | 0x0F);
// _radio->pubmod->SPIsetRegValue(SX127X_REG_FIFO_THRESH, SX127X_TX_START_FIFO_NOT_EMPTY | SX127X_FIFO_THRESH);
//_radio->pubmod->SPIsetRegValue(SX127X_REG_PREAMBLE_LSB, 0x00);
//_radio->setPreambleLength(0);
uint16_t bitRate = 62500; //62500; //(SX127X_CRYSTAL_FREQ * 1000.0) / 0.512;
_radio->_mod->SPIsetRegValue(SX127X_REG_BITRATE_MSB, (bitRate & 0xFF00) >> 8, 7, 0);
_radio->_mod->SPIsetRegValue(SX127X_REG_BITRATE_LSB, bitRate & 0x00FF, 7, 0);
_radio->_mod->SPIsetRegValue(SX127X_REG_PA_RAMP, SX1278_NO_SHAPING);
_radio->_mod->SPIsetRegValue(SX127X_REG_PACKET_CONFIG_1, SX127X_PACKET_FIXED | SX127X_DC_FREE_NONE | SX127X_CRC_OFF | SX127X_CRC_AUTOCLEAR_ON | SX127X_ADDRESS_FILTERING_OFF, 7, 0);
_radio->_mod->SPIsetRegValue(SX127X_REG_PACKET_CONFIG_2, SX127X_DATA_MODE_PACKET, 6, 6);
_radio->setCRC(false);
_radio->setPreambleLength(0);
}
void POCSAGTransmitter::test() {
_radio->standby();
_radio->_mod->SPIsetRegValue(SX127X_REG_DIO_MAPPING_1, SX127X_DIO0_PACK_PACKET_SENT);
size_t completeLength = 0;
uint8_t * transmitterByteDataPointer = (uint8_t *)transmitterData;
Serial.print(F("transmitterData: ")); Serial.println ((unsigned long int)transmitterData, HEX);
Serial.print(F("transmitterByteDataPointer: ")); Serial.println ((unsigned long int)transmitterByteDataPointer, HEX);
/*
for (int y = 0; y < 13; y++) {
size_t beforeLength = completeLength + 0;
size_t messageLength = textMessageLength(y, 133701, 80);
uint32_t *transmission = (uint32_t *)malloc(sizeof(uint32_t) * messageLength);
encodeTransmission(y, 133701 + y, 3, "01234567890123456789012345678901234567890123456789012345678901234567890123456789", transmission);
completeLength += messageLength;
Serial.println();
Serial.print("messageLength="); Serial.println(messageLength, DEC);
Serial.println("MESSAGE encoded:");
for (size_t i = 0; i < messageLength; i++)
Serial.print(transmission[i], HEX);
Serial.println();
for (size_t i = 0; i < messageLength; i++) {
size_t baseOffset = (beforeLength * sizeof(uint32_t)) + (i * sizeof(uint32_t));
transmitterByteDataPointer[ baseOffset + 00 ] = ((transmission[ i ] >> 24) & 0xFF);
transmitterByteDataPointer[ baseOffset + 01 ] = ((transmission[ i ] >> 16) & 0xFF);
transmitterByteDataPointer[ baseOffset + 02 ] = ((transmission[ i ] >> 8) & 0xFF);
transmitterByteDataPointer[ baseOffset + 03 ] = (transmission[ i ] & 0xFF);
}
free(transmission);
/*Serial.println("TX encoded after free:");
for (size_t i = 0; i < messageLength; i++)
Serial.print(transmission[i], HEX);/
}
*/
uint32_t testTransmission[ 18 + 13 ] = {
// 18 words, 576 bits of preamble
0xAAAAAAAA, 0xAAAAAAAA,
0xAAAAAAAA, 0xAAAAAAAA, 0xAAAAAAAA, 0xAAAAAAAA, 0xAAAAAAAA, 0xAAAAAAAA, 0xAAAAAAAA, 0xAAAAAAAA,
0xAAAAAAAA, 0xAAAAAAAA, 0xAAAAAAAA, 0xAAAAAAAA, 0xAAAAAAAA, 0xAAAAAAAA, 0xAAAAAAAA, 0xAAAAAAAA,
// The real data starts here
0x7CD215D8, 0x7A89C197, 0x7A89C197, 0x7A89C197, 0x7A89C197, 0x7A89C197, 0x7A89C197, 0x7A89C197,
0x7A89C197, 0x4F5A0109, 0xC2619CE1, 0x7A89C197, 0x7A89C197,
};
completeLength = 18+13;
for (size_t i = 0; i < completeLength; i++) {
size_t baseOffset = (0 * sizeof(uint32_t)) + (i * sizeof(uint32_t));
transmitterByteDataPointer[ baseOffset + 00 ] = ((testTransmission[ i ] >> 24) & 0xFF);
transmitterByteDataPointer[ baseOffset + 01 ] = ((testTransmission[ i ] >> 16) & 0xFF);
transmitterByteDataPointer[ baseOffset + 02 ] = ((testTransmission[ i ] >> 8) & 0xFF);
transmitterByteDataPointer[ baseOffset + 03 ] = (testTransmission[ i ] & 0xFF);
}
Serial.print(F("transmitterData: ")); Serial.println ((unsigned long int)transmitterData, HEX);
Serial.print(F("transmitterByteDataPointer: ")); Serial.println ((unsigned long int)transmitterByteDataPointer, HEX);
Serial.println();
Serial.println("TX encoded in big buffer:");
for (size_t i = 0; i < completeLength*4; i++)
Serial.print(transmitterByteDataPointer[i], HEX);
Serial.println();
Serial.print("completeLength="); Serial.println(completeLength, DEC);
Serial.print(F("transmitterData: ")); Serial.println ((unsigned long int)transmitterData, HEX);
Serial.print(F("transmitterByteDataPointer: ")); Serial.println ((unsigned long int)transmitterByteDataPointer, HEX);
uint8_t * transmissionChunk = (uint8_t *)malloc(16);
for (int i=0;i<16;i++) transmissionChunk[i] = i%2==0?0x55 : 0xAA; // setting it all to 0xFF to see where memcpy works
// do encoding
byte irq2 = 0x00;
//memcpy(transmissionChunk, transmitterByteDataPointer, 16);
writeFIFOChunk(transmitterByteDataPointer, 16);
transmitterByteDataPointer+=16;
/*for (int i=0;i<16;i++) {
transmissionChunk[ i ] = *transmitterByteDataPointer;
transmitterByteDataPointer++;
}*/
//writeFIFOChunk(transmissionChunk, 16);
Serial.println("starting tx");
_radio->setMode(SX127X_TX);
/*for (int i=4; i < completeLength; i+=4) {
//memcpy(transmissionChunk, transmitterByteDataPointer, 16);
transmitterByteDataPointer+=16;
writeFIFOChunk(transmissionChunk, 16);
delay(20);
do {
irq2 = _radio->_mod->SPIgetRegValue(SX127X_REG_IRQ_FLAGS_2);
Serial.print(irq2 >> 4 & 1, HEX);
} while ((irq2 >> 4 & 1) > 0);
}*/
//Serial.println("free'd transmissionChunk, waiting for PacketSent");
do {
irq2 = _radio->_mod->SPIgetRegValue(SX127X_REG_IRQ_FLAGS_2);
// Serial.print(irq2 >> 3 & 1, HEX);
} while (((irq2 >> 3) & 1) == 0);
_radio->standby();
free(transmissionChunk);
Serial.println("done");
Serial.print(F("transmitterData: ")); Serial.println ((unsigned long int)transmitterData, HEX);
Serial.print(F("transmitterByteDataPointer: ")); Serial.println ((unsigned long int)transmitterByteDataPointer, HEX);
}
void POCSAGTransmitter::writeFIFOChunk(uint8_t* data, size_t len) {
for (int z=0;z<len;z++) Serial.print(data[ z ], HEX);
Serial.println();
_radio->_mod->SPItransfer(0x80, 0x00, data, NULL, len);
}

@ -0,0 +1,23 @@
#if !defined(_POCSAG_TRANSMITTER_H)
#define _POCSAG_TRANSMITTER_H
#include <RadioLib.h>
// #include <SX1278.h>
/*!
* POCSAG Transmitter
- catSIXe
*/
class POCSAGTransmitter {
public:
uint32_t *transmitterData;
uint32_t *encodingBuffer;
explicit POCSAGTransmitter();
void begin(SX1278 *radio);
void queuePage(uint32_t ric, char* text);
void transmitBatch();
void test();
void writeFIFOChunk(uint8_t* data, size_t len);
SX1278* _radio;
};
#endif

@ -0,0 +1,11 @@
This directory is intended for PlatformIO Unit Testing and project tests.
Unit Testing is a software testing method by which individual units of
source code, sets of one or more MCU program modules together with associated
control data, usage procedures, and operating procedures, are tested to
determine whether they are fit for use. Unit testing finds problems early
in the development cycle.
More information about PlatformIO Unit Testing:
- https://docs.platformio.org/page/plus/unit-testing.html
Loading…
Cancel
Save