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