Author Topic: RFM69 library extensions for finer power granularity with RFM69-HW (RFM69_ATC)  (Read 318518 times)

TomWS

  • Hero Member
  • *****
  • Posts: 1930
I've been working on some extensions to the RFM69 library to provide finer granularity to power control on HW devices.   These extensions, I believe, are backward compatible with existing code, but I'm willing to post them if anyone wants to try them.

The first change I made was to extend setHighPower to add a second argument for setting the PA control bits, PA1 & PA2.  The default, if the argument isn't specified, is to set the value to 0x60, which is the behavior of the original method.  With this new argument, the PAs can be selectively turned on and off.  Also, the OCP register is selectively controlled based on whether both PAs are turned on or not.

I also modified setHighPowerRegs so that the REG_TESTPAx registers are set based on whether BOTH PAs are turned on or not.  I didn't see much point in turning on the high power boost if one or the other of the PAs is off.

Finally, I modified setPowerLevel to allow a value from 0-127.  The function works exactly as before if the range is below 32 OR if it is not a HW device (as set by _isRFM69W).  If the power level is above 31 AND it is a HW device, then the level sets both the powerlevel register AND the PA select bits. In this case, however, the lowest 5 bits are divided by 2 making the range from 0-15, avoiding the wrap-around effect that occurs when PA1 or PA2 are used.  This should make the power level (at least with a selection of 32-127), if not linear, at least monotonic.

For example, if power level is 63, PA1 is on, PA2 is off, and the power level is set to 15. 

It's unfortunate that PA0 does not seem to be a selectable choice with the HW device, despite the impression one would get from reading the datasheet.  If we found a way to enable PA0 on and HW device, then the setPowerLevel method could have a full range of control...

Finally, I am planning to add an 'auto power control' that relies on the destination to include it's received RSSI in a special ACK back to the sender.  This way the sender knows how well his signal is being received and can drop back the power to a reasonably reliable level without wasting power.  I'll be working on that over the next couple of days...

Let me know what you think.  I'd be happy to turn this code over to the community if someone advises me on the proper way to do this...

Tom
« Last Edit: May 09, 2016, 08:59:54 AM by Felix »

emjay

  • Full Member
  • ***
  • Posts: 119
  • Country: nl
Re: RFM69 library extensions for finer granularity of power with HW devices
« Reply #1 on: October 22, 2014, 06:41:45 AM »
@TomWS,

 >  include it's received RSSI in a special ACK back to the sender

How consistent do you find successive RSSI readings to be?


TomWS

  • Hero Member
  • *****
  • Posts: 1930
Re: RFM69 library extensions for finer granularity of power with HW devices
« Reply #2 on: October 22, 2014, 09:03:22 PM »
@emjay The RSSI readings vary pretty widely (+/- 3-4 db), as you might expect in a relatively high frequency network, but, even so, if you don't have  an overly responsive compensation, the transmission power generally converges on a usable, and lowest power,  transmit power level.  One factor that seems to affect the RSSI response is the AGC logics in the receiving device.  When testing, I disabled LNA AGC, but not the DAGC and this tended to 'dither' the result a bit, depending on transmit rate.  Net is that you shouldn't be fixated on a particular target RSSI, but, at least, 'drift toward' one...

@felix, Of course, I'd be happy to send you the code.  I'll post some more info tomorrow, because, quite frankly, this exercise has 'educated' me quite a bit re the RFM69HW and I'll have some 'learnings' to add to this conversation.  However, I have resolved the power control to a usable logic and the ACK with RSSI seems to be working.  I've implemented the convergence logic in my test sketch and just need to fold this into the library, but I should have something I can send you tomorrow.  Is there a PM mechanism to do this???

Tom

TomWS

  • Hero Member
  • *****
  • Posts: 1930
Re: RFM69 library extensions for finer granularity of power with HW devices
« Reply #3 on: October 23, 2014, 11:41:58 AM »
I've attached the code and an updated Node.ino which demonstrates the modified and/or new methods.  For the auto power control to operate, the corresponding gateway will need to be rebuilt with the updated library...

This has been interesting as I TOTALLY misinterpreted the RFM69HW datasheet.  Without going into the gory details, I thought that the PA controls provided a wider range of power control than they do...  In any case, here's what I've learned (excuse the redundancy if you already know all this, but I need to provide this to explain the resulting function) and what I've implemented...

First, as everyone knows by now, there doesn't seem to be a way to use just PA0 if you have a RFM69HW.  Too bad, I would like to have this lower range (with it's corresponding power savings) as an option.   That being the case, then the only power ranges are controlled by enabling one or both of the PA1 & PA2 registers and, if both are on, adding the power boost to get an additional 3dbm of range. 

PA1 and PA2 are virtually identical with one very perplexing exception that I'll get into shortly.  They each add 4 dbm to the transmit power setting.  When either or both of these are used, then the power level setting in the lower five bits of the RegPALevel register provide 2 dbm steps for each lsb and will fold around at 16 (ie, 16=0, 17=1, etc) EXCEPT for when using PA1 only - in this case, you get NOTHING unless the level register is >= 16 - very odd...

So, given these facts, I've modified my original plan to have the setPowerLevel method accept a value from 0-127.  With this library, it accepts values from 0 to 51, with the following behavior:
With values 0-31, the method behaves exactly like the original version and is appropriate for either W, CW, or HW versions.
With values 32-47, the method sets PA2 on, PA1 off, no power boost, power level = 0-15, and is appropriate only for HW version.
With values of 48 & 49, the method sets both PA2 & PA1 on, no power boost, power level = 14 and 15 respectively, again, HW only.
Finally, with values of 50 & 51, the method sets both PA2 & PA1 on, with power boost, and power level = 14 and 15 respectively.

The result (with HW hardware) is a range of ~38 dbm in 2 dbm steps and is monotonic.  The last two steps, however, are slightly compressed at 1.5 dbm per step.

All original code 'should' work without change, unless you want to use the auto power control.  In this case you'll need to rebuild with this new library at both ends AND I recommend using the new form of setPowerLevel just to make sure internal variables are initialized properly - I think they are, but I can't claim to have thoroughly tested this case.

The following methods have been extended:
   setHighPower(bool onOff=true, byte PA_Ctl=0x60) - where PA_Ctl has been added as an optional argument to directly control the PA bits
   setPowerLevel(byte level) - the range of acceptable values for 'level' has been increased to 0-51 (for HW hardware)
internally, sendFrame() has been extended to add to new arguments, defaulted to maintain backward compatibility

New methods added are:
  enableAutoPower(int targetRSSI=-69) - new method to enable/disable AutoPower control and will work if the other end is using this new library, this 'should' work with W or CW devices but I don't have this configuration to test.
  int getAckRSSI(void) - returns the RSSI value ack'd by the far end if Autopower is enabled on this end AND the far end is built with new library. 
  byte setLNA(byte newReg) - sets the LNA register to control LNA gain/AGC.  I needed this to test Autopower without bumping into AGC effects.  Decided to keep it in the library...
  void setNetwork(byte networkID=1) - carried over from previous posting...

Several internal methods were modified with 'most' of the changes annotated with 'TWS' comments...

Auto Power seems to work pretty well and if you try the included 'Node...ino' the power output will dynamically adjust to try to achieve -59 RSSI at the far end.  Printing the return value from getAckRSSI() will show you how well it's tracking.  Note that the value WILL dither and give you a pretty good indication of how the RF signal is affected by all kinds of atmospheric and body effects...  :)

How AutoPower is achieved is a trade secret... just kidding.  A sender enables autopower with enableAutoPower(-59) where the value is the target RSSI.  Every send with retry will automatically request that the far end return the sender's RSSI with its Ack.  This is done with a new bit in the CTL field.  The far end, on seeing this in the Ack Request, will include the received RSSI in the first two bytes following the CTL field.  The DATALEN is adjusted accordingly, but is 'corrected' before the buffer length is passed to the receiver.  The only downside of this is that it shortens the max packet length by the two bytes.  You may want to disable Autopower (default condition) if you need the full length...  When the receive code receives the Ack'd RSSI, it compares it with the target RSSI and adjusts the transmit level accordingly.  It only changes the level by one on each adjustment to keep the gain from being thrashed about and will eventually converge on a suitable value IFF there is one, otherwise the power level will saturate at either the max or min level.  Finally, the power isn't adjusted immediately, the adjustment is deferred to the next TX mode...

I think that's it... or, at least, enough for now.

Tom
« Last Edit: October 01, 2015, 11:19:35 PM by Felix »

Justin

  • NewMember
  • *
  • Posts: 5
Re: RFM69 library extensions for finer granularity of power with HW devices
« Reply #4 on: November 26, 2014, 04:25:14 PM »
Tom, I'm trying to do RSSI-based localization. Do you have any recommendations for how I might take advantage of your
Code: [Select]
enableAutoPower() 
method but still get meaningful readings that (somewhat) correspond to distance? (In general, I'm aware of the many obstacles to achieving robust localization based on signal strength alone -- no need to school me!)

Also, what are good practices for calling the
Code: [Select]
readRSSI()
method, i.e., when to call it? Depending on what code is executed around it, the Rssi value returned is different. Why?

Thank you!
« Last Edit: November 26, 2014, 04:32:27 PM by Justin »

TomWS

  • Hero Member
  • *****
  • Posts: 1930
Re: RFM69 library extensions for finer granularity of power with HW devices
« Reply #5 on: November 26, 2014, 04:38:16 PM »
Justin,
answering your last question first, readRSSI() only returns a valid value immediately after a true return from receiveDone(), however the value you get is the RSSI from the transmitter and you have no idea what transmit power level its using (unless you specifically code it to send that info).

If you're using the code that I added, the intent is to adjust 'your' transmit power so that the other end of the link receives somewhere around the 'targetRSSI' level.  So, if everything it within adjustment range, the value returned from getAckRSSI() should converge to the target level.  What is meaningful in this case, however, is your transmit level needed to achieve that RSSI value...

Tom

Justin

  • NewMember
  • *
  • Posts: 5
Re: RFM69 library extensions for finer granularity of power with HW devices
« Reply #6 on: December 01, 2014, 11:55:00 AM »
This makes sense: so in order to retrieve
Quote
the transmit level needed to achieve the RSSI value
I should just look at the value of the global variable _transmitLevel, correct?

But in order to make my distance calculation, I need an adjusted RSSI value. Would I subtract _transmit level from the result of getAckRSSI()? Here is my ranging code:

Code: [Select]
float getRange(boolean inFeet) {

    float rssi = average; 
    float ratio_db = txCalibratedPower - rssi;
    float ratio_linear = pow(10, ratio_db / 10);
    r = sqrt(ratio_linear);
   
    if(inFeet) r=r*3.2;
   
    return r;
}

Thanks for your help.

TomWS

  • Hero Member
  • *****
  • Posts: 1930
Re: RFM69 library extensions for finer granularity of power with HW devices
« Reply #7 on: December 01, 2014, 09:20:31 PM »
@Justin, I'll warn you that I've had a lovely dinner with a bit (maybe more than a bit) of wine, but, frankly, I think you're working assumption that you can apply scientific principles to this problem is, uh, begging for a serious learning experience...

From my perspective, if you want 'relative' metrics in range measurements AND you're tweaking transmit power to try to maintain a constant RSSI on the receiving end, then looking at the _transmitLevel will give you a good 'idea' of what it takes to get the resulting RSSI (over time). 

If you want precise measurements and control, you've come to the wrong place.  If you spend any time in Wireless technology, you will learn that the ONLY way to get 'meaningful' quantitative measurements is spend quality time in an RF anechoic chamber with a lot of very expensive test equipment, make all kinds of polar measurements, and this and that, and learn that it probably doesn't matter much 'cuz, in the end, the real question is, 'are you getting what you want?'.

One point WRT to reading _transmitLevel, I don't think the code I posted had this member as a 'public'.  Consequently, to get the value from application code, you might have to either move _transmitLevel to the 'public' area or add a method to RFM69 to get the value as in:
Code: [Select]
int RFM69::getTransmitLevel(void) {
   return _transmitLevel;
}
I'd prefer the latter approach.  No point in making the _transmitLevel public if all you need to do is read it.  I think I've done this on my recent revs to the library. 

Net: I'm glad that you want to 'know' what your 'proximity' to the end point is and that you're pursuing it methodically.  This would be really useful in some interesting use cases.  However, from my experience, it will be really tough to get 'actual' range.  It will be easy to get relative range values.

Tom

Justin

  • NewMember
  • *
  • Posts: 5
Re: RFM69 library extensions for finer granularity of power with HW devices
« Reply #8 on: December 08, 2014, 06:31:58 PM »
I'll wager that your experience in RF engineering greatly exceeds mine, so, of course, I'll gladly consider your warning. Thank you. If you're curious: broadly speaking, I need accuracy within a factor of 3.  So, if I measure the RSSI every second over the course of a minute, average those values, and then infer that a 100 ft distance between them, I need the actual distance to be no less than 33', and no more than 300'.

Good suggestion, I'll add a new method. I would hope to report my results back here, although I have had to return to using the Radiohead library -- I am using an SPI OLED screen, and making any calls to screws up the RSSI reading, no matter where I put it.

TomWS

  • Hero Member
  • *****
  • Posts: 1930
Re: RFM69 library extensions for finer granularity of power with HW devices
« Reply #9 on: December 08, 2014, 09:57:43 PM »
@Justin, I think, if you have a reasonable perspective then you might be able to calibrate your system to a usable range. The tricky part is, if you simply monitor _transmit level while you physically 'hover' over the receiver or transmitter you will find that you get +/- 5 dB of signal variation. If your environment doesn't have human bodies 'floating' around, then you might have a chance of discerning distance with 'some' accuracy in using transmit level value if you've 'calibrated' the value to distance. I, personally, would be surprised if you could get better than 5 foot resolution in the best case. Please let me know if you can do better than that.

Felix

  • Administrator
  • Hero Member
  • *****
  • Posts: 6866
  • Country: us
    • LowPowerLab
Re: RFM69 library extensions for finer granularity of power with HW devices
« Reply #10 on: December 08, 2014, 10:52:54 PM »
Just my 2 cents: the receiver sensitivity is highly dependent on the antenna orientation with respect to the transmitter antenna. Like TomWS mentioned, simply hovering or adding obstacles will start to change the RSSI quite a bit. The more obstacles the harder it will be (or more impossible rather) to tell how far the transmitter is. This is a hard problem and RSSI is simply a signal strength level and it tells nothing about the distance of the transmitter. There are atmospheric conditions, obstacles, flora, interference and other factors you need to consider, to me RSSI just sounds like too unreliable of a reading to calculate distance. If you have a controlled environment then a lot of the factors are constant or under control and then you get closer to the direct relationship (logarithmic, not linear!) between RSSI and distance.

TomWS

  • Hero Member
  • *****
  • Posts: 1930
Re: RFM69 library extensions for finer granularity of power with HW devices
« Reply #11 on: December 09, 2014, 05:06:17 PM »
<...snip> If you have a controlled environment then a lot of the factors are constant or under control and then you get closer to the direct relationship (logarithmic, not linear!) between RSSI and distance.
Just to clarify one point, if you're using the autogain control and using _transmitlevel as the metric, then this is a logarithmic value (changing transmit power by ~3db for each increment) so can be 'almost' usable as a linear measure (IFF all the conditions Felix mentioned are met, which is to say, near impossible  :)

Tom

syrinxtech

  • Sr. Member
  • ****
  • Posts: 347
  • Country: us
    • Syrinx Technologies
Re: RFM69 library extensions for finer granularity of power with HW devices
« Reply #12 on: November 13, 2015, 11:59:30 AM »
Tom, many thanks for the work on ATC.  I just added the code to my indoor/outdoor weather station.  With the outdoor unit sitting next to the indoor unit the RSSI was -27.  I had set the threshold to be -85.  Within about 6 transmits the RSSI was holding between -84 and -86.  Hopefully over the next couple of days I'll have some time to try and measure the current savings.

TomWS

  • Hero Member
  • *****
  • Posts: 1930
Re: RFM69 library extensions for finer granularity of power with HW devices
« Reply #13 on: November 14, 2015, 07:48:00 AM »
Tom, many thanks for the work on ATC.  I just added the code to my indoor/outdoor weather station.  With the outdoor unit sitting next to the indoor unit the RSSI was -27.  I had set the threshold to be -85.  Within about 6 transmits the RSSI was holding between -84 and -86.  Hopefully over the next couple of days I'll have some time to try and measure the current savings.
Thanks for the acknowledgement, I appreciate it.

It's curious that the RSSI dropped so dramatically in just 6 transmits (or infinite for that matter).  The code only adjusts the transmit level by one unit per ack (purposefully slow response) and, with the RFM69HW, there are 19 steps from max signal to min (transmit level 51->32) with a  22dB range (+20dBm->-2dBm).   Are you sure you have the IS_RFM69HW defined correctly?

Tom

syrinxtech

  • Sr. Member
  • ****
  • Posts: 347
  • Country: us
    • Syrinx Technologies
Re: RFM69 library extensions for finer granularity of power with HW devices
« Reply #14 on: November 14, 2015, 11:23:22 AM »
Yes, I'm not using the high-powered radio so there is no define or setHighPower() function called.

I do have one other question.  Is there any possibility of OTA updates not working now?  I used to update the outdoor unit OTA before the ATC upgrade.  I just tried this morning and I can't seem to connect OTA.  Do I need to setup the Moteino that I use as the OTA gateway to also use ATC?

Or did I break something somewhere else?