Skip to content

Commit

Permalink
Merge pull request #174 from mcci-catena/issue169
Browse files Browse the repository at this point in the history
Fix several issues with saved state
  • Loading branch information
terrillmoore authored Jul 5, 2021
2 parents 9b664f5 + ff209ca commit a035918
Show file tree
Hide file tree
Showing 3 changed files with 140 additions and 94 deletions.
10 changes: 7 additions & 3 deletions src/Arduino_LoRaWAN.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ Copyright notice:
/// \ref ARDUINO_LORAWAN_VERSION_COMPARE_LT() to compare relative versions.
///
#define ARDUINO_LORAWAN_VERSION \
ARDUINO_LORAWAN_VERSION_CALC(0, 9, 0, 0) /* v0.9.0 */
ARDUINO_LORAWAN_VERSION_CALC(0, 9, 1, 2) /* v0.9.1-pre2 */

#define ARDUINO_LORAWAN_VERSION_GET_MAJOR(v) \
(((v) >> 24u) & 0xFFu)
Expand Down Expand Up @@ -194,10 +194,10 @@ class Arduino_LoRaWAN

struct SessionChannelMask_Header
{
enum eMaskKind : uint8_t { kEUlike = 0, kUSlike = 1 };
enum eMaskKind : uint8_t { kEUlike = 0, kUSlike = 1, kCNlike = 2 };

uint8_t Tag; ///< discriminator, eMaskKind.
uint8_t Size; ///< size of SessionChannelMask, in bytes
uint8_t Size; ///< size of actual SessionChannelMask, in bytes
};

template <uint32_t a_nCh>
Expand Down Expand Up @@ -541,7 +541,9 @@ class Arduino_LoRaWAN
{
SessionStateHeader Header;
SessionStateV1 V1;
bool isValid() const;
} SessionState;

/*
|| the constructor.
*/
Expand Down Expand Up @@ -677,6 +679,8 @@ class Arduino_LoRaWAN
uint8_t *pBuf
);

bool IsValidState(const SessionState &state) const;

// return true iff network seems to be provisioned. Make
// it virtual so it can be overridden if needed.
virtual bool IsProvisioned(void)
Expand Down
18 changes: 16 additions & 2 deletions src/lib/arduino_lorawan_begin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ bool Arduino_LoRaWAN::begin(
LMIC_reset();

// if we can get saved state, go on.
if (! this->RestoreSessionState())
auto const fHaveSavedState = this->RestoreSessionState();
if (! fHaveSavedState)
{
// Otherwise set data rate and transmit power, based on regional considerations.
this->NetBeginRegionInit();
Expand All @@ -69,7 +70,8 @@ bool Arduino_LoRaWAN::begin(
//
AbpProvisioningInfo abpInfo;

if (this->GetAbpProvisioningInfo(&abpInfo))
if ((fHaveSavedState || this->GetProvisioningStyle() == ProvisioningStyle::kABP) &&
this->GetAbpProvisioningInfo(&abpInfo))
{
LMIC_setSession(
abpInfo.NetID,
Expand Down Expand Up @@ -222,6 +224,18 @@ void Arduino_LoRaWAN::StandardEventProcessor(
case EV_BEACON_TRACKED:
break;
case EV_JOINING:
{
// Set data rate and transmit power, based on regional considerations.
// Allows regions to change the channel mask (which otherwise will
// be reset to the region default -- possibly not what you want).
this->NetBeginRegionInit();

// Update the session info
this->SaveSessionInfo();

// save everything else of interest.
this->SaveSessionState();
}
break;

case EV_JOINED:
Expand Down
206 changes: 117 additions & 89 deletions src/lib/arduino_lorawan_sessionstate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ Arduino_LoRaWAN::BuildSessionState(
// handle EU like regions
#if CFG_LMIC_EU_like
State.V1.Channels.Header.Tag = State.V1.Channels.Header.kEUlike;
State.V1.Channels.Header.Tag = sizeof(State.V1.Channels.EUlike);
State.V1.Channels.Header.Size = sizeof(State.V1.Channels.EUlike);
State.V1.Channels.EUlike.clearAll();
constexpr unsigned maxCh = MAX_CHANNELS < State.V1.Channels.EUlike.nCh ? MAX_CHANNELS : State.V1.Channels.EUlike.nCh;
State.V1.Channels.EUlike.ChannelMap = LMIC.channelMap;
Expand Down Expand Up @@ -135,8 +135,8 @@ Arduino_LoRaWAN::BuildSessionState(
}

#elif CFG_LMIC_US_like
State.V1.Channels.Header.Tag = State.V1.Channels.Header.kEUlike;
State.V1.Channels.Header.Tag = sizeof(State.V1.Channels.USlike);
State.V1.Channels.Header.Tag = State.V1.Channels.Header.kUSlike;
State.V1.Channels.Header.Size = sizeof(State.V1.Channels.USlike);

#if ARDUINO_LMIC_VERSION_COMPARE_GE(ARDUINO_LMIC_VERSION, ARDUINO_LMIC_VERSION_CALC(3,99,0,1))
static_assert(
Expand Down Expand Up @@ -257,6 +257,43 @@ Name: Arduino_LoRaWAN::ApplySessionState()
*/

bool Arduino_LoRaWAN::IsValidState(const Arduino_LoRaWAN::SessionState &state) const
{
// do not apply the session state unless it roughly matches our configuration.
if (! state.isValid())
return false;

// make sure region and country match. TODO: make sure network matches.
if (! (Arduino_LoRaWAN::Region(state.V1.Region) == this->GetRegion() &&
state.V1.Country == uint16_t(this->GetCountryCode())))
return false;

// it matches!
return true;
}

bool Arduino_LoRaWAN::SessionState::isValid() const
{
if (! (this->Header.Tag == kSessionStateTag_V1 &&
this->Header.Size == sizeof(*this)))
return false;

switch (this->V1.Channels.Header.Tag)
{
case Arduino_LoRaWAN::SessionChannelMask_Header::eMaskKind::kEUlike:
return this->V1.Channels.Header.Size == sizeof(this->V1.Channels.EUlike);

case Arduino_LoRaWAN::SessionChannelMask_Header::eMaskKind::kUSlike:
return this->V1.Channels.Header.Size == sizeof(this->V1.Channels.USlike);

case Arduino_LoRaWAN::SessionChannelMask_Header::eMaskKind::kCNlike:
return this->V1.Channels.Header.Size == sizeof(this->V1.Channels.CNlike);

default:
return false;
}
}

#define FUNCTION "Arduino_LoRaWAN::ApplySessionState"

bool
Expand All @@ -265,124 +302,115 @@ Arduino_LoRaWAN::ApplySessionState(
)
{
// do not apply the session state unless it roughly matches our configuration.
if (State.Header.Tag == kSessionStateTag_V1 &&
Arduino_LoRaWAN::Region(State.V1.Region) == this->GetRegion() &&
State.V1.Country == uint16_t(this->GetCountryCode())
)
{
auto const tNow = os_getTime();
if (! this->IsValidState(State))
return false;

// record that we've done it.
this->m_savedSessionState = State;
auto const tNow = os_getTime();

// record that we've done it.
this->m_savedSessionState = State;

// set FcntUp, FcntDown, and session state
LMIC.datarate = State.V1.LinkDR;
// set FcntUp, FcntDown, and session state
LMIC.datarate = State.V1.LinkDR;

// set the uplink and downlink count
LMIC.seqnoDn = State.V1.FCntDown;
LMIC.seqnoUp = State.V1.FCntUp;
// set the uplink and downlink count
LMIC.seqnoDn = State.V1.FCntDown;
LMIC.seqnoUp = State.V1.FCntUp;

//
// TODO([email protected]): State.V1.gpsTime can be used to tweak the saved cycle
// time and also as a fallback if the system clock is not robust. but right
// now we ignore it.
//
//
// TODO([email protected]): State.V1.gpsTime can be used to tweak the saved cycle
// time and also as a fallback if the system clock is not robust. but right
// now we ignore it.
//

// conservatively set the global avail time.
LMIC.globalDutyAvail = tNow + State.V1.globalAvail;
// conservatively set the global avail time.
LMIC.globalDutyAvail = tNow + State.V1.globalAvail;

// set the Rx2 frequency
LMIC.dn2Freq = State.V1.Rx2Frequency;
// set the Rx2 frequency
LMIC.dn2Freq = State.V1.Rx2Frequency;

#if !defined(DISABLE_PING)
// set the ping frequency
LMIC.ping.freq = State.V1.PingFrequency;
// set the ping frequency
LMIC.ping.freq = State.V1.PingFrequency;
#endif

LMIC.adrAckReq = State.V1.LinkIntegrity;
LMIC.adrTxPow = State.V1.TxPower;
LMIC.upRepeat = State.V1.Redundancy;
LMIC.globalDutyRate = State.V1.DutyCycle;
LMIC.rx1DrOffset = State.V1.Rx1DRoffset;
LMIC.dn2Dr = State.V1.Rx2DataRate;
LMIC.rxDelay = State.V1.RxDelay;
LMIC.adrAckReq = State.V1.LinkIntegrity;
LMIC.adrTxPow = State.V1.TxPower;
LMIC.upRepeat = State.V1.Redundancy;
LMIC.globalDutyRate = State.V1.DutyCycle;
LMIC.rx1DrOffset = State.V1.Rx1DRoffset;
LMIC.dn2Dr = State.V1.Rx2DataRate;
LMIC.rxDelay = State.V1.RxDelay;

#if LMIC_ENABLE_TxParamSetupReq
LMIC.txParam = State.V1.TxParam;
LMIC.txParam = State.V1.TxParam;
#endif
#if !defined(DISABLE_BEACONS)
LMIC.bcnChnl = State.V1.BeaconChannel;
LMIC.bcnChnl = State.V1.BeaconChannel;
#endif
#if !defined(DISABLE_PING)
LMIC.ping.dr = State.V1.PingDr;
LMIC.ping.dr = State.V1.PingDr;
#endif
LMIC.dn2Ans = State.V1.MacRxParamAns;
LMIC.macDlChannelAns = State.V1.MacDlChannelAns;
LMIC.macRxTimingSetupAns = State.V1.MacRxTimingSetupAns;
LMIC.dn2Ans = State.V1.MacRxParamAns;
LMIC.macDlChannelAns = State.V1.MacDlChannelAns;
LMIC.macRxTimingSetupAns = State.V1.MacRxTimingSetupAns;

#if CFG_LMIC_EU_like
// don't turn off bits: user can't fool us here.
// we can get the immutable channels from the
// channelMap value after reset.
auto const resetMap = LMIC.channelMap;
auto const & euLike = State.V1.Channels.EUlike;
LMIC.channelMap |= euLike.ChannelMap;
// don't turn off bits: user can't fool us here.
// we can get the immutable channels from the
// channelMap value after reset.
auto const resetMap = LMIC.channelMap;
auto const & euLike = State.V1.Channels.EUlike;
LMIC.channelMap |= euLike.ChannelMap;
#if ARDUINO_LMIC_VERSION_COMPARE_GE(ARDUINO_LMIC_VERSION, ARDUINO_LMIC_VERSION_CALC(3,99,0,1))
LMIC.channelShuffleMap = euLike.ChannelShuffleMap;
LMIC.channelShuffleMap = euLike.ChannelShuffleMap;
#endif
for (unsigned ch = 0; ch < MAX_CHANNELS; ++ch)
for (unsigned ch = 0; ch < MAX_CHANNELS; ++ch)
{
if ((resetMap & (decltype(resetMap)(1) << ch)) == 0)
{
if ((resetMap & (decltype(resetMap)(1) << ch)) == 0)
{
// copy data -- note that the saved band number is encoded
LMIC_setupChannel(
ch,
euLike.getFrequency(euLike.UplinkFreq, ch),
euLike.ChannelDrMap[ch],
euLike.getBand(ch)
);
// copy data -- note that the saved band number is encoded
LMIC_setupChannel(
ch,
euLike.getFrequency(euLike.UplinkFreq, ch),
euLike.ChannelDrMap[ch],
euLike.getBand(ch)
);
#if !defined(DISABLE_MCMD_DlChannelReq)
LMIC.channelDlFreq[ch] = euLike.getFrequency(euLike.DownlinkFreq, ch);
LMIC.channelDlFreq[ch] = euLike.getFrequency(euLike.DownlinkFreq, ch);
#endif
}
}
}

for (unsigned band = 0; band < MAX_BANDS; ++band)
{
LMIC.bands[band].txcap = euLike.Bands[band].txDutyDenom;
LMIC.bands[band].txpow = euLike.Bands[band].txPower;
LMIC.bands[band].lastchnl = euLike.Bands[band].lastChannel;
// Heuristic; we don't know how long has passed since we saved
// this, because we don't currently have GPS time available.
// Conservatively reserve time from now.
LMIC.bands[band].avail = tNow + euLike.Bands[band].ostimeAvail;
}
for (unsigned band = 0; band < MAX_BANDS; ++band)
{
LMIC.bands[band].txcap = euLike.Bands[band].txDutyDenom;
LMIC.bands[band].txpow = euLike.Bands[band].txPower;
LMIC.bands[band].lastchnl = euLike.Bands[band].lastChannel;
// Heuristic; we don't know how long has passed since we saved
// this, because we don't currently have GPS time available.
// Conservatively reserve time from now.
LMIC.bands[band].avail = tNow + euLike.Bands[band].ostimeAvail;
}

#elif CFG_LMIC_US_like
# if ARDUINO_LMIC_VERSION_COMPARE_GE(ARDUINO_LMIC_VERSION, ARDUINO_LMIC_VERSION_CALC(3,99,0,1))
static_assert(sizeof(LMIC.channelShuffleMap) == sizeof(State.V1.Channels.USlike.ChannelShuffleMap),
"shuffle map doesn't match");
// copy the shuffle map bits
memcpy(LMIC.channelShuffleMap, State.V1.Channels.USlike.ChannelShuffleMap, sizeof(LMIC.channelShuffleMap));
static_assert(sizeof(LMIC.channelShuffleMap) == sizeof(State.V1.Channels.USlike.ChannelShuffleMap),
"shuffle map doesn't match");
// copy the shuffle map bits
memcpy(LMIC.channelShuffleMap, State.V1.Channels.USlike.ChannelShuffleMap, sizeof(LMIC.channelShuffleMap));
# endif
// copy the enabled states
for (unsigned ch = 0; ch < State.V1.Channels.USlike.nCh; ++ch)
{
const bool state = State.V1.Channels.USlike.isEnabled(ch);

if (state)
LMIC_enableChannel(ch);
else
LMIC_disableChannel(ch);
}
#endif

return true;
}
else
// copy the enabled states
for (unsigned ch = 0; ch < State.V1.Channels.USlike.nCh; ++ch)
{
return false;
const bool state = State.V1.Channels.USlike.isEnabled(ch);

if (state)
LMIC_enableChannel(ch);
else
LMIC_disableChannel(ch);
}
#endif
}

#undef FUNCTION

0 comments on commit a035918

Please sign in to comment.