async def rx_timing_setup_req(self) -> bool: def check_rtsa(m:lm.Msg, explain:Optional[str]=None) -> None: opts = self.get_opts(m) self.assert_equals(len(opts), 1, explain) self.assert_equals(type(opts[0]), lo.RXTimingSetupAns, explain) m = await self.start_testmode() # -- Modify RX1 and RX2 timing to X second delay for delay in range(1, 16): self.lw_dnlink(m, port=0, payload=lo.pack_opts([lo.RXTimingSetupReq(Delay=delay)])) self.session['rx1delay'] = delay m = await self.lw_uplink() check_rtsa(m) m = await self.req_check_echo(m, b'\1\2\3') self.assert_equals(len(self.get_opts(m)), 0) m = await self.req_check_echo(m, b'\4\5\6', rx2=True) # -- Restore default timing self.lw_dnlink(m, port=0, payload=lo.pack_opts([lo.RXTimingSetupReq(Delay=0)])) self.session['rx1delay'] = 1 # -- Test reply transmission for i in range(2): m = await self.lw_uplink() check_rtsa(m, explain=f'iteration {i+1}') m = await self.req_check_echo(m, b'\1\2\3') self.assert_equals(len(self.get_opts(m)), 0) m = await self.req_check_echo(m, b'\4\5\6', rx2=True) return True
async def _(dut=createtest): m = await dut.start_testmode() def check_rtsa(m:LoraWanMsg, msg:str) -> None: opts = m.unpack_opts() assert len(opts) == 1, msg opt, = opts assert type(opt) == lo.RXTimingSetupAns, msg # -- Modify RX1 and RX2 timing to X second delay for delay in range(1, 16): dut.dndf(m, 0, lo.pack_opts([lo.RXTimingSetupReq(Delay=delay)])) dut.session['rx1delay'] = delay m = await dut.updf() check_rtsa(m, f'rxdelay={delay}') m = await dut.echo(m, b'\1\2\3') assert len(m.unpack_opts()) == 0, f'rxdelay={delay}' m = await dut.echo(m, b'\4\5\6', rx2=True) # -- Restore default timing dut.dndf(m, 0, lo.pack_opts([lo.RXTimingSetupReq(Delay=0)])) dut.session['rx1delay'] = 1 # -- Test reply transmission for i in range(2): m = await dut.updf() check_rtsa(m, f'iteration {i+1}') m = await dut.echo(m, b'\1\2\3') assert len(m.unpack_opts()) == 0 m = await dut.echo(m, b'\4\5\6', rx2=True)
async def rx_param_setup_req(self) -> bool: def check_rpsa(m: lm.Msg, explain: Optional[str] = None) -> None: opts = self.get_opts(m) self.assert_equals(len(opts), 1, explain) self.assert_equals(type(opts[0]), lo.RXParamSetupAns, explain) self.assert_equals(opts[0].FreqAck.value, 1, explain) self.assert_equals(opts[0].RX2DRAck.value, 1, explain) self.assert_equals(opts[0].RX1DRoffAck.value, 1, explain) m = await self.start_testmode() # Make sure we are DR5 so we can see the rx1droff effect self.assert_equals(self.rps2dr(m['upmsg'].rps), 5) # -- Modify RX1 and RX2 downlink parameters opt = lo.RXParamSetupReq(RX2DR=2, RX1DRoff=2, Freq=868525000 // 100) self.lw_dnlink(m, port=0, payload=lo.pack_opts([opt])) m = await self.lw_uplink() check_rpsa(m) m = await self.req_check_echo(m, b'\1\2\3', rx1droffset=opt.RX1DRoff.value) m = await self.req_check_echo(m, b'\4\5\6', rx2=True, rps=self.dndr2rps(opt.RX2DR.value), freq=opt.Freq.value * 100) # -- Restore default downlink parameters self.lw_dnlink(m, port=0, payload=lo.pack_opts([ lo.RXParamSetupReq(RX2DR=self.region.RX2DR, RX1DRoff=0, Freq=self.region.RX2Freq // 100) ]), rx1droffset=opt.RX1DRoff.value) # -- Test reply transmission for i in range(2): m = await self.lw_uplink() check_rpsa(m, explain=f'iteration {i+1}') m = await self.req_check_echo(m, b'\1\2\3') self.assert_equals(len(self.get_opts(m)), 0) m = await self.req_check_echo(m, b'\4\5\6', rx2=True) return True
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 _(dut=createtest): m = await dut.start_testmode() dut.dndf(m, 0, lo.pack_opts([lo.DevStatusReq()])) m = await dut.updf() opts = m.unpack_opts() assert len(opts) == 1 assert type(opts[0]) is lo.DevStatusAns print(opts[0])
async def _(dut=createtest): m = await dut.start_testmode() region = dut.session['region'] # helper function def check_rpsa(m:LoraWanMsg, msg:str) -> None: opts = m.unpack_opts() assert len(opts) == 1, msg opt, = opts assert type(opt) == lo.RXParamSetupAns, msg assert opt.FreqAck.value == 1, msg assert opt.RX2DRAck.value == 1, msg assert opt.RX1DRoffAck.value == 1, msg # Make sure we are DR5 so we can see the rx1droff effect assert m.dr == 5 # -- Modify RX1 and RX2 downlink parameters rx1droff, rx2dr, rx2freq = 2, 2, 868525000 opt = lo.RXParamSetupReq(RX2DR=rx2dr, RX1DRoff=rx1droff, Freq=rx2freq//100) dut.dndf(m, 0, lo.pack_opts([opt])) with dut.modified_session(rx1droff=rx1droff, rx2dr=rx2dr, rx2freq=rx2freq): m = await dut.updf() check_rpsa(m, None) m = await dut.echo(m, b'\1\2\3') m = await dut.echo(m, b'\4\5\6', rx2=True) # -- Restore default downlink parameters dut.dndf(m, 0, lo.pack_opts([lo.RXParamSetupReq(RX2DR=region.RX2DR, RX1DRoff=0, Freq=region.RX2Freq//100)])) # -- Test reply transmission for i in range(2): m = await dut.updf() check_rpsa(m, f'iteration {i+1}') m = await dut.echo(m, b'\1\2\3') assert len(m.unpack_opts()) == 0 m = await dut.echo(m, b'\1\2\3', rx2=True)
async def dev_status_req(self) -> bool: m = await self.start_testmode() self.lw_dnlink(m, port=0, payload=lo.pack_opts([lo.DevStatusReq()])) m = await self.lw_uplink() opts = self.get_opts(m) assert len(opts) == 1 assert type(opts[0]) is lo.DevStatusAns print(opts[0]) return True
async def _(dut=createtest): m = await dut.start_testmode() dc = dut.unpack_dnctr(m) for _ in range(2): cmd = lo.pack_opts([lo.DevStatusReq()]) dut.dndf(m, 0, cmd, fopts=cmd) m = await dut.updf() opts = m.unpack_opts() assert len(opts) == 0 and m.rtm['FPort'] != 0 m = await dut.test_updf() dc = dut.unpack_dnctr(m, expected=dc)
async def mcmd_invalid(self) -> bool: m = await self.start_testmode() dc = self.get_dnctr(m) for _ in range(2): cmd = lo.pack_opts([lo.DevStatusReq()]) self.lw_dnlink(m, port=0, payload=cmd, fopts=cmd) m = await self.lw_uplink() opts = self.get_opts(m) assert len(opts) == 0 and m['FPort'] != 0 m = await self.lw_uplink() self.get_dnctr(m, expect=dc) return True
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 dl_channel_req(self) -> bool: m = await self.start_testmode() for f in [868500000, self.region.upchannels[1].freq, 0]: # modify channel 1 RX1 frequency self.lw_dnlink(m, port=0, payload=lo.pack_opts( [lo.DlChannelReq(Chnl=1, Freq=f // 100)])) # wait until message is received on channel 1 AND # simultaneously ensure that the DlChannelAns is being # repeated while no DL is received for i in range(20): m = await self.lw_uplink() opts = self.get_opts(m) self.assert_equals(len(opts), 1, explain=f'i={i}, f={f}') o = opts[0] assert type(o) is lo.DlChannelAns self.assert_equals(o.ChnlAck.value, 1) self.assert_equals(o.FreqAck.value, 1 if f else 0) if i and self.getupch(m) == 1: break else: assert False, 'no message received on modified channel' # save current DL counter and send DL on modified channel freq dc = self.get_dnctr(m) self.req_mode(m, mode_conf=False, freq=f or None) # check uplink -- DlChannelAns must be cleared # *and* DL counter incremented m = await self.tst_uplink() opts = self.get_opts(m) self.assert_equals(len(opts), 0) self.get_dnctr(m, expect=dc + 1) # make sure we get a message on an unmodified channel, otherwise # next command won't be received.. while self.getupch(m) == 1: m = await self.tst_uplink() # Note: the following part of the test expands upon what's required... for ch, f in [(1, 333333333), (3, 868500000)]: # attempt to modify channel self.lw_dnlink(m, port=0, payload=lo.pack_opts( [lo.DlChannelReq(Chnl=ch, Freq=f // 100)])) # check that the command is rejected for the correct reason, and # simultaneously ensure that the DlChannelAns is being repeated # while no DL is received for i in range(16): m = await self.lw_uplink() opts = self.get_opts(m) self.assert_equals(len(opts), 1) o = opts[0] assert type(o) is lo.DlChannelAns if ch < len(self.region.upchannels): self.assert_equals(o.ChnlAck.value, 1) self.assert_equals(o.FreqAck.value, 0) else: self.assert_equals(o.ChnlAck.value, 0) self.assert_equals(o.FreqAck.value, 1) if i and self.getupch(m) == 1: break else: assert False, 'no message received on channel 1' # save current DL counter and send DL dc = self.get_dnctr(m) self.req_mode(m, mode_conf=False) # check uplink -- DlChannelAns must be cleared # *and* DL counter incremented m = await self.tst_uplink() opts = self.get_opts(m) self.assert_equals(len(opts), 0) self.get_dnctr(m, expect=dc + 1) # make sure invalid channel didn't get enabled somehow 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))
async def _(dut=createtest): m = await dut.start_testmode() region = dut.session['region'] for f in [ 868500000, region.upchannels[1].freq, 0 ]: # modify channel 1 RX1 frequency dut.dndf(m, 0, lo.pack_opts([lo.DlChannelReq(Chnl=1, Freq=f//100)])) # wait until message is received on channel 1 AND # simultaneously ensure that the DlChannelAns is being # repeated while no DL is received for i in range(32): m = await dut.updf() opts = m.unpack_opts() assert len(opts) == 1, f'i={i}, f={f}' o, = opts assert type(o) is lo.DlChannelAns assert o.ChnlAck.value == 1 assert o.FreqAck.value == (1 if f else 0) if i and m.ch == 1: break else: assert False, 'no message received on modified channel' # save current DL counter and send DL on modified channel freq dc = dut.unpack_dnctr(m) dut.request_mode(m, False, freq=f or None) # check uplink -- DlChannelAns must be cleared # *and* DL counter incremented m = await dut.test_updf() opts = m.unpack_opts() assert len(opts) == 0, f'f={f}' dut.unpack_dnctr(m, expected=dc+1) # make sure we get a message on an unmodified channel, otherwise # next command won't be received.. while m.ch == 1: m = await dut.test_updf() # Note: the following part of the test expands upon what's required... for ch, f in [(1, 333333333), (3, 868500000)]: # attempt to modify channel dut.dndf(m, 0, lo.pack_opts([lo.DlChannelReq(Chnl=ch, Freq=f//100)])) # check that the command is rejected for the correct reason, and # simultaneously ensure that the DlChannelAns is being repeated # while no DL is received for i in range(32): m = await dut.updf() opts = m.unpack_opts() assert len(opts) == 1, f'ch={ch}, f={f}' o, = opts assert type(o) is lo.DlChannelAns if ch < len(region.upchannels): assert o.ChnlAck.value == 1 assert o.FreqAck.value == 0 else: assert o.ChnlAck.value == 0 assert o.FreqAck.value == 1 if i and m.ch == 1: break else: assert False, 'no message received on channel 1' # save current DL counter and send DL dc = dut.unpack_dnctr(m) dut.request_mode(m, False) # check uplink -- DlChannelAns must be cleared # *and* DL counter incremented m = await dut.test_updf() opts = m.unpack_opts() assert len(opts) == 0, f'f={f}' dut.unpack_dnctr(m, expected=dc+1) # make sure invalid channel didn't get enabled somehow m = await dut.check_freqs(m, frozenset(ch.freq for ch in region.upchannels))