async def ncr_optdr(m:lm.Msg, freq:int) -> lm.Msg: self.lw_dnlink(m, port=0, payload=lo.pack_opts( [lo.NewChannelReq(Chnl=3, Freq=freq//100, MinDR=0, MaxDR=7)])) m = await self.lw_uplink() opts = self.get_opts(m) self.assert_equals(len(opts), 1) self.check_ncr_o(opts[0]) return m
async def ncr_optdr(m:LoraWanMsg, freq:int, msg:str) -> LoraWanMsg: dut.dndf(m, 0, lo.pack_opts([lo.NewChannelReq(Chnl=3, Freq=freq//100, MinDR=0, MaxDR=7)])) m = await dut.updf(explain=msg) opts = m.unpack_opts() assert len(opts) == 1, msg opt, = opts dut.check_ncr_o(opt, explain=msg) return m
async def ncr_add(m:lm.Msg, chans:List[Tuple[int,int]]) -> lm.Msg: opts = [lo.NewChannelReq(Chnl=ch, Freq=f//100, MinDR=0, MaxDR=5) for ch, f in chans] self.lw_dnlink(m, port=0, payload=lo.pack_opts(opts)) m = await self.lw_uplink() opts = self.get_opts(m) self.assert_equals(len(opts), len(chans)) for i,o in enumerate(opts): if chans[i][0] < len(self.region.upchannels): self.check_ncr_o(o, ChnlAck=0, DRAck=None) else: self.check_ncr_o(o) return await self.check_freqs(m, frozenset(it.chain( (ch[1] for ch in chans if ch[1]), (ch.freq for ch in self.region.upchannels))))
async def ncr_add(m:LoraWanMsg, chans:List[Tuple[int,int]]) -> LoraWanMsg: opts = [lo.NewChannelReq(Chnl=ch, Freq=f//100, MinDR=0, MaxDR=5) for ch, f in chans] dut.dndf(m, 0, lo.pack_opts(opts)) m = await dut.test_updf() opts = m.unpack_opts() assert len(opts) == len(chans) for i, o in enumerate(opts): if chans[i][0] < len(dut.session['region'].upchannels): dut.check_ncr_o(o, ChnlAck=0, DRAck=None) else: dut.check_ncr_o(o) return await dut.check_freqs(m, frozenset(itertools.chain( (ch[1] for ch in chans if ch[1]), (ch.freq for ch in dut.session['region'].upchannels))))
async def link_adr_req(self) -> bool: def check_laa(m: lm.Msg, explain: Optional[str] = None) -> None: opts = self.get_opts(m) self.assert_equals(len(opts), 1, explain) self.check_laa_o(opts[0], explain=explain) def check_laa_block(m: lm.Msg, n: int, ChAck: Optional[int] = 1, DRAck: Optional[int] = 1, TXPowAck: Optional[int] = 1, explain: Optional[str] = None) -> None: opts = self.get_opts(m) self.assert_equals(len(opts), n, explain) # check that all are of the same type self.assert_equals(len(set(type(o) for o in opts)), 1, explain) # check that all have the same value self.assert_equals(len(set(a.value for o in opts for a in o.args)), 1, explain) # verify last one (others are identical) self.check_laa_o(opts[-1], ChAck, DRAck, TXPowAck, explain) async def ncr_optdr(m: lm.Msg, freq: int) -> lm.Msg: self.lw_dnlink(m, port=0, payload=lo.pack_opts([ lo.NewChannelReq(Chnl=3, Freq=freq // 100, MinDR=0, MaxDR=7) ])) m = await self.lw_uplink() opts = self.get_opts(m) self.assert_equals(len(opts), 1) self.check_ncr_o(opts[0]) return m m = await self.start_testmode() # a. ADR bit assert self.isadren(m) # b. TXPower self.lw_dnlink(m, port=0, payload=lo.pack_opts( [lo.LinkADRReq(TXPow=7, DR=5, ChMaskCntl=6)])) m = await self.lw_uplink() check_laa(m) pstats = [0, 0] m = await self.ul_stats(m, 3, pstats=pstats) rssi0 = pstats[0] / pstats[1] self.lw_dnlink(m, port=0, payload=lo.pack_opts( [lo.LinkADRReq(TXPow=0, DR=5, ChMaskCntl=6)])) m = await self.lw_uplink() check_laa(m) pstats = [0, 0] m = await self.ul_stats(m, 3, pstats=pstats) rssi1 = pstats[0] / pstats[1] print(f'RSSI @ 2dBm: {rssi0:6.1f} dBm') print(f'RSSI @ 16dBm: {rssi1:6.1f} dBm') print(f'Difference: {rssi1-rssi0:6.1f} dBm') self.assert_range(rssi0, -80, -10) self.assert_range(rssi1, -80, -10) self.assert_ge(rssi1 - rssi0, 6) # c. Required DataRates for dr in range(6): self.lw_dnlink(m, port=0, payload=lo.pack_opts( [lo.LinkADRReq(TXPow=0, DR=dr, ChMaskCntl=6)])) m = await self.lw_uplink() check_laa(m) self.assert_equals(self.rps2dr(m['upmsg'].rps), dr) # d. Optional DataRates nchfreq = 869100000 m = await ncr_optdr(m, nchfreq) for dr in range(6, 8): self.lw_dnlink(m, port=0, payload=lo.pack_opts( [lo.LinkADRReq(TXPow=0, DR=dr, ChMaskCntl=6)])) m = await self.lw_uplink() check_laa(m) self.assert_equals(m['upmsg'].freq, nchfreq) self.assert_equals(self.rps2dr(m['upmsg'].rps), dr) m = await ncr_optdr(m, 0) self.assert_in(m['upmsg'].freq, list(ch.freq for ch in self.region.upchannels)) # e. ChannelMask self.lw_dnlink(m, port=0, payload=lo.pack_opts([ lo.NewChannelReq(Chnl=3, Freq=nchfreq // 100, MinDR=0, MaxDR=5), lo.LinkADRReq(TXPow=5, DR=5, ChMaskCntl=0, ChMask=7) ])) m = await self.lw_uplink() opts = self.get_opts(m) self.assert_equals(len(opts), 2) self.check_ncr_o(opts[0]) self.check_laa_o(opts[1]) m = await self.check_freqs( m, frozenset(ch.freq for ch in self.region.upchannels)) self.lw_dnlink(m, port=0, payload=lo.pack_opts([ lo.LinkADRReq(TXPow=5, DR=5, ChMaskCntl=0, ChMask=0xf) ])) m = await self.lw_uplink() opts = self.get_opts(m) self.assert_equals(len(opts), 1) self.check_laa_o(opts[0]) m = await self.check_freqs( m, frozenset( it.chain([nchfreq], (ch.freq for ch in self.region.upchannels)))) self.lw_dnlink(m, port=0, payload=lo.pack_opts([ lo.LinkADRReq(TXPow=5, DR=5, ChMaskCntl=0, ChMask=0) ])) m = await self.lw_uplink() opts = self.get_opts(m) self.assert_equals(len(opts), 1) self.check_laa_o(opts[0], ChAck=0, DRAck=None, TXPowAck=None) self.lw_dnlink(m, port=0, payload=lo.pack_opts([lo.NewChannelReq(Chnl=3, Freq=0)])) m = await self.lw_uplink() opts = self.get_opts(m) self.assert_equals(len(opts), 1) self.check_ncr_o(opts[0]) # f. Redundancy self.lw_dnlink(m, port=0, payload=lo.pack_opts( [lo.LinkADRReq(DR=5, ChMaskCntl=6, NbTrans=2)])) m = await self.lw_uplink() check_laa(m) l = [await self.lw_uplink() for _ in range(3)] self.assert_equals(l[0]['MIC'], m['MIC']) self.assert_equals(l[2]['MIC'], l[1]['MIC']) m = l[-1] self.lw_dnlink(m, port=0, payload=lo.pack_opts( [lo.LinkADRReq(DR=5, ChMaskCntl=6, NbTrans=1)])) m = await self.lw_uplink() check_laa(m) # g. ADRACKReq bit self.lw_dnlink(m) for i in range(64): m = await self.lw_uplink() self.assert_equals(self.isadrarq(m), False, explain=f'iter={i}') self.assert_equals(self.rps2dr(m['upmsg'].rps), 5) for dr in [5, 4, 3]: for i in range(32): m = await self.lw_uplink() self.assert_equals(self.isadrarq(m), True, explain=f'dr={dr}, iter={i}') self.assert_equals(self.rps2dr(m['upmsg'].rps), dr) self.lw_dnlink(m, port=0, payload=lo.pack_opts( [lo.LinkADRReq(DR=5, ChMaskCntl=6)])) m = await self.lw_uplink() check_laa(m) self.assert_equals(self.rps2dr(m['upmsg'].rps), 5) # h. a.. Successful LinkADRReq commands block self.lw_dnlink(m, port=0, payload=lo.pack_opts([ lo.LinkADRReq(ChMaskCntl=0, ChMask=0), lo.LinkADRReq(TXPow=4, DR=4, ChMaskCntl=0, ChMask=3, NbTrans=1), lo.LinkADRReq(TXPow=0, DR=3, ChMaskCntl=6, ChMask=0, NbTrans=1) ])) m = await self.lw_uplink() check_laa_block(m, 3) m = await self.check_freqs( m, frozenset(ch.freq for ch in self.region.upchannels)) self.lw_dnlink(m, port=0, payload=lo.pack_opts( [lo.LinkADRReq(DR=5, ChMaskCntl=6)])) m = await self.lw_uplink() check_laa(m) self.assert_equals(self.rps2dr(m['upmsg'].rps), 5) # h. b.. Unsuccessful LinkADRReq commands block self.lw_dnlink(m, port=0, payload=lo.pack_opts([ lo.LinkADRReq(ChMask=0x07, DR=4, TXPow=4), lo.LinkADRReq(ChMaskCntl=0, ChMask=0) ])) m = await self.lw_uplink() check_laa_block(m, 2, ChAck=0, DRAck=None, TXPowAck=None) self.assert_equals(self.rps2dr(m['upmsg'].rps), 5) self.lw_dnlink(m) # empty downlink to avoid timeout (?) m = await self.check_freqs( m, frozenset(ch.freq for ch in self.region.upchannels)) return True
async def _(dut=createtest): m = await dut.start_testmode() def check_laa(m:LoraWanMsg, msg:str) -> None: opts = m.unpack_opts() assert len(opts) == 1, msg opt, = opts dut.check_laa_o(opt, explain=msg) def check_laa_block(m:LoraWanMsg, n:int, *, ChAck:Optional[int]=1, DRAck:Optional[int]=1, TXPowAck:Optional[int]=1, msg:str) -> None: opts = m.unpack_opts() assert len(opts) == n, msg # check that all have the correct type assert [type(o) for o in opts] == [lo.LinkADRAns for _ in range(n)], msg # check that all have the same value opt = opts[-1] assert list(opts) == [opt for _ in range(n)], msg # verify last one (others are identical) dut.check_laa_o(opt, ChAck, DRAck, TXPowAck, explain=msg) async def ncr_optdr(m:LoraWanMsg, freq:int, msg:str) -> LoraWanMsg: dut.dndf(m, 0, lo.pack_opts([lo.NewChannelReq(Chnl=3, Freq=freq//100, MinDR=0, MaxDR=7)])) m = await dut.updf(explain=msg) opts = m.unpack_opts() assert len(opts) == 1, msg opt, = opts dut.check_ncr_o(opt, explain=msg) return m # a. ADR bit assert m.isadren() # b. TXPower dut.dndf(m, 0, lo.pack_opts([lo.LinkADRReq(TXPow=7, DR=5, ChMaskCntl=6)])) m = await dut.updf() check_laa(m, 'txpower=7') pstats = PowerStats() m = await dut.upstats(m, 3, pstats=pstats) rssi0 = pstats.avg() dut.dndf(m, 0, lo.pack_opts([lo.LinkADRReq(TXPow=0, DR=5, ChMaskCntl=6)])) m = await dut.updf() check_laa(m, 'txpower=0') pstats.reset() m = await dut.upstats(m, 3, pstats=pstats) rssi1 = pstats.avg() print(f'RSSI @ 2dBm: {rssi0:6.1f} dBm') print(f'RSSI @ 16dBm: {rssi1:6.1f} dBm') print(f'Difference: {rssi1-rssi0:6.1f} dBm') assert rssi0 > -80 and rssi0 < -10 assert rssi1 > -80 and rssi1 < -10 assert (rssi1 - rssi0) >= 6 # c. Required DataRates for dr in range(6): dut.dndf(m, 0, lo.pack_opts([lo.LinkADRReq(TXPow=0, DR=dr, ChMaskCntl=6)])) m = await dut.updf() check_laa(m, f'dr={dr}') assert m.dr == dr # d. Optional DataRates nchannel = ld.ChDef(freq=869100000, minDR=0, maxDR=7) reg = ld.Region_EU868() reg.upchannels.append(nchannel) dut.gateway.regions.append(reg) m = await ncr_optdr(m, nchannel.freq, 'create new channel') for dr in range(6, 8): dut.dndf(m, 0, lo.pack_opts([lo.LinkADRReq(TXPow=0, DR=dr, ChMaskCntl=6)])) m = await dut.updf() check_laa(m, f'dr={dr}') assert m.msg.freq == nchannel.freq, f'dr={dr}' assert m.dr == dr, f'dr={dr}' m = await ncr_optdr(m, 0, 'delete new channel') assert m.msg.freq in list(ch.freq for ch in dut.session['region'].upchannels) # e. ChannelMask dut.dndf(m, 0, lo.pack_opts([lo.NewChannelReq(Chnl=3, Freq=nchannel.freq//100, MinDR=0, MaxDR=5), lo.LinkADRReq(TXPow=5, DR=5, ChMaskCntl=0, ChMask=7)])) m = await dut.updf() opts = m.unpack_opts() assert len(opts) == 2 opt1, opt2 = opts dut.check_ncr_o(opt1) dut.check_laa_o(opt2) m = await dut.check_freqs(m, frozenset(ch.freq for ch in dut.session['region'].upchannels)) dut.dndf(m, 0, lo.pack_opts([lo.LinkADRReq(TXPow=5, DR=5, ChMaskCntl=0, ChMask=0xf)])) m = await dut.updf() check_laa(m, 'chmask=0xf') m = await dut.check_freqs(m, frozenset(ch.freq for ch in reg.upchannels)) dut.dndf(m, 0, lo.pack_opts([lo.LinkADRReq(TXPow=5, DR=5, ChMaskCntl=0, ChMask=0)])) m = await dut.updf() opts = m.unpack_opts() assert len(opts) == 1 opt, = opts dut.check_laa_o(opt, ChAck=0, DRAck=None, TXPowAck=None) dut.dndf(m, 0, lo.pack_opts([lo.NewChannelReq(Chnl=3, Freq=0)])) m = await dut.updf() opts = m.unpack_opts() assert len(opts) == 1 opt, = opts dut.check_ncr_o(opt) # f. Redundancy dut.dndf(m, 0, lo.pack_opts([lo.LinkADRReq(DR=5, ChMaskCntl=6, NbTrans=2)])) m = await dut.updf() check_laa(m, 'nbtrans=2') l = [await dut.updf() for _ in range(3)] assert l[0].rtm['MIC'] == m.rtm['MIC'] assert l[2].rtm['MIC'] == l[1].rtm['MIC'] m = l[-1] dut.dndf(m, 0, lo.pack_opts([lo.LinkADRReq(DR=5, ChMaskCntl=6, NbTrans=1)])) m = await dut.updf() check_laa(m, 'nbtrans=1') # g. ADRACKReq bit dut.dndf(m) for i in range(64): m = await dut.updf() assert m.isadrarq() == False, f'iter={i}' assert m.dr == 5, f'iter={i}' for dr in [5, 4, 3]: for i in range(32): m = await dut.updf() assert m.isadrarq() == True, f'dr={dr}, iter={i}' assert m.dr == dr, f'dr={dr}, iter={i}' dut.dndf(m, 0, lo.pack_opts([lo.LinkADRReq(DR=5, ChMaskCntl=6)])) m = await dut.updf() check_laa(m, 'dr=5') assert m.dr == 5 # h. a.. Successful LinkADRReq commands block dut.dndf(m, 0, lo.pack_opts([lo.LinkADRReq(ChMaskCntl=0, ChMask=0), lo.LinkADRReq(TXPow=4, DR=4, ChMaskCntl=0, ChMask=3, NbTrans=1), lo.LinkADRReq(TXPow=0, DR=3, ChMaskCntl=6, ChMask=0, NbTrans=1)])) m = await dut.updf() check_laa_block(m, 3, msg='linkadrreq block') m = await dut.check_freqs(m, frozenset(ch.freq for ch in dut.session['region'].upchannels)) dut.dndf(m, 0, lo.pack_opts([lo.LinkADRReq(DR=5, ChMaskCntl=6)])) m = await dut.updf() check_laa(m, 'dr=5') assert m.dr == 5 # h. b.. Unsuccessful LinkADRReq commands block dut.dndf(m, 0, lo.pack_opts([lo.LinkADRReq(ChMask=0x07, DR=4, TXPow=4), lo.LinkADRReq(ChMaskCntl=0, ChMask=0)])) m = await dut.updf() check_laa_block(m, 2, ChAck=0, DRAck=None, TXPowAck=None, msg='invalid linkadrreq block') assert m.dr == 5 dut.dndf(m) # empty downlink to avoid timeout (?) m = await dut.check_freqs(m, frozenset(ch.freq for ch in dut.session['region'].upchannels))