Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

10% duty cycle for EU #52

Closed
samuk opened this issue Feb 5, 2020 · 12 comments
Closed

10% duty cycle for EU #52

samuk opened this issue Feb 5, 2020 · 12 comments

Comments

@samuk
Copy link
Collaborator

samuk commented Feb 5, 2020

It looks like if we set 869.4-869.65 MHz we get 10% Duty cycle and 500mW (the best option)

868

Image from page 11 of this document https://www.ofcom.org.uk/__data/assets/pdf_file/0025/38095/final_report.pdf

Would it be feasible to implement a 10% duty cycle? Some kind of buffer that broadcasts messages after a few seconds delay?

Assuming a message takes 1 second. Where a node received two messages within ten seconds, one would be routed immediately and the second after a ten-second delay?

This may be a useful reference: https://www.thethingsnetwork.org/docs/lorawan/duty-cycle.html

@tlrobinson
Copy link
Contributor

tlrobinson commented Feb 5, 2020

Yeah, this would be good. I think LoRaLayer2 should enforce the duty cycle, since it also sends routing messages the disaster-radio code doesn’t know about.

We’d want a way for “clients” to receive an ack from LoRaLayer2 (WebSocketClient currently immediately sends returns an ack without feedback from LoRa) so we can show that the message is delayed, and also probably avoid broadcasting to other local clients until it’s ack’d (or at least display that it’s pending transmission)

I’m not sure if the buffering should take place in LoRaLayer2 or in LoRaClient, but I lean towards the latter. For the latter, if transmitting a message immediately would go above the allowed duty cycle then LL2.sendToLayer2() could return an error code and LoRaClient could implement the buffering logic. That seems better than complicating LoRaLayer2.

@samuk
Copy link
Collaborator Author

samuk commented Feb 6, 2020

Thanks, if we did end up with some kind of buffer system it might be interesting to add some logic;

Only broadcast message if at least x entries in the routing table.

It would add some rudimentary store and forward capability, eventually, you'd run out of memory and have to drop messages but might be interesting.

X could be user-configurable and maybe default to two connected nodes?

@matthewrwright
Copy link

My only concern here would be if you're in a situation where you have simplex communication. It may be that I'm running a better antenna or higher effective TX power and the other side can hear me, but I can't hear them possibly?

@dmth
Copy link

dmth commented Mar 18, 2020

Just for reference: This document describes the legal situation in Germany, 10% Duty Cycle seems to be ok for 869.4-869.65 MHz.

https://www.bundesnetzagentur.de/SharedDocs/Downloads/DE/Sachgebiete/Telekommunikation/Unternehmen_Institutionen/Frequenzen/Allgemeinzuteilungen/2018_05_SRD_pdf

@paidforby
Copy link
Contributor

I'm looking at working on this.

My thinking is,

Add a duty cycle setting to LoRaLayer2. Initially, for simplicity, I will assume a transmit time of 1s for every packet. Then given a desired duty cycle, LL2 would calculate an interval and only transmit a message once that interval has expired. So at a 10% duty cycle, you could transmit every 10s for a total of 6s on air per 60s.

Eventually, we would want LL2 to actually calculate the airtime for the packet (or there may be an easy way to detect airtime with sandeep's arduino-LoRa?). This would complicate the interval calculation and I would need to think more about how to actually use the airtime information.

I already have a buffer in the LL2 code, so I code just expand that (I also think it needs to be rewritten as a FIFO buffer) and perhaps LL2.sendToLayer2() could return your messages place in the buffer?

Alternatively, there is @tlrobinson suggestion that the buffering could happen in the Layer3 LoRaClient code. This would possibly make more sense if we implement the actual airtime calculation instead of just using the interval workaround. Then a node could keep track of how long it has been on air for the last 60s (or 30s or 10s?) and once it exceeds the specified duty cycle, start returning a wait error to the Layer3 code.

However, I would be concerned that this solution would result in a node transmitting in "one blast" everything in its buffer and then having to wait for some timeout to transmit again.

We’d want a way for “clients” to receive an ack from LoRaLayer2 (WebSocketClient currently >immediately sends returns an ack without feedback from LoRa)

I do agree with @tlrobinson that this ack'ing needs to be refactored. I will looking into passing an ack from Layer1 up to the Layer3 clients, should be relatively straightforward. It may be useful to have a standard for Layer3 clients sending acks to each other?

My only concern here would be if you're in a situation where you have simplex communication. It >may be that I'm running a better antenna or higher effective TX power and the other side can hear >me, but I can't hear them possibly?

@matthewrwright this is a valid concern and there is no good way to account for it. The "acking" that we are talking about is internal to the firmware. There is no plan to implement acking within the LoRaLayer2 protocol, specifically because of duty cycle concerns and the simplex situation you identified. Obviously, someone could implement acking at a higher layer, if they wanted to deal with the duty cycle and simplex possibility.

@Juul
Copy link
Member

Juul commented Apr 10, 2020

In case you all hadn't seen here is the airtime calculation algorithm implemented in a google spreadsheet.

@paidforby
Copy link
Contributor

@Juul thanks for the reminder about that spreadsheet, I've implemented these same calculation in C with this commit, sudomesh/LoRaLayer2@e41dd1a.

I was thinking more about how to use this calculation. I think it makes sense to calculate this once a packet is transmitted and then use it as the basis for the next interval.

For example, given default radio settings, a duty cycle of 10%, and a payload of 17 bytes. You would calculate airtime to be 114.688ms. Then calculate the time until you can transmit another packet like so, 114.688/.1 = 1146.88ms or 1.4688s. The interval would then be set to 1147ms. Then another packet of any size could be sent. i believe this will always keep the instantaneous airtime to the required 10% (or whatever set the duty cycle to). The bigger the packet the longer you wait to send the next packet and so on.

This has the draw back that it is more difficult (would have to inspect the entire buffer) for a Layer3 client to find out how long before it's message is sent. The easiest option is for the client to know what place in the buffer it has. Additionally, I need to work out how to return the necessary variables from Layer.transmit() because I need the length of the transmitted packet and the sequence number of the transmitted packet (or some identifier so Layer3 can know that it's packet has been sent).

@geeksville is working on a merge that will introduce async Layer1 functions, see sudomesh/LoRaLayer2#10 (comment) so I may wait until that work is complete to return to figure out some of these issues.

paidforby pushed a commit to sudomesh/LoRaLayer2 that referenced this issue Apr 15, 2020
@paidforby
Copy link
Contributor

I have successfully implemented a duty cycle option in LoRaLayer2. There are still something outstanding TODOs and considerations.

  • How to deal with the routing interval? What if you choose a short routing interval and a low duty cycle (long duty interval). Then the transmit buffer would eventually be filled with just routing packets. Maybe the routing interval should be a function of the duty cycle?

  • How to deal with a full transmit buffer? Currently all the fifo buffers hold 16 packet. If I try transmitting 17 messages at the same time, what happens to that 17th packet? Should Layer1 produce an error? Should Layer2 handle the retry? Should it be held in the L3toL2 buffer? Should the error propagate all the way to Layer3? If so, what does Layer3 do with that error message?

  • I need expose duty cycle as an option in the console and add it to the settings.cpp. And allow for the ability to turn of the duty cycle?

@geeksville
Copy link

btw: One way of solving the routing interval is to move to the other dominant mesh algorithm. It seems like disaster.radio is based (inspired?) by AODV? Which is a bit more optimal in lots of ways. But you might want to look into DSR algorithm? They are not as optimal, but a) no need to share routing tables.

@paidforby
Copy link
Contributor

disaster.radio's routing protocol was largely inspired by the babel routing protocol, which I suppose was inspired by both DSDV and AODV. That just what I was most familiar with at the time I began writing LL2. I'm going to look into the DSR alogrithm, I hadn't really consider it.

@samuk
Copy link
Collaborator Author

samuk commented May 21, 2020

Then the transmit buffer would eventually be filled with just routing packets.

Is this duty cycle stuff affected by the change to reactive routing #57 (comment)

@paidforby
Copy link
Contributor

No, the duty cycle still applies. The way I wrote the duty cycle, it just cares about the length of any transmission, not the contents of the transmission. Of course, not sending routing table packets will help avoid using up airtime and therefore make the duty cycle easier to observe. I think, once I expose the duty cycle in config.h and/or the console interface, we can close this issue. The other outstanding todos are more or less outside the scope of this issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants