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

Sx126x txpower #57

Merged
merged 6 commits into from
Nov 13, 2019
Merged

Sx126x txpower #57

merged 6 commits into from
Nov 13, 2019

Conversation

BarryPSmith
Copy link
Contributor

  • Adjusted use of setPaConfig in SX1262 setOutputPower to always use the SX1262 PA.
  • Adjusted available power output range of SX1262 to -9 to 22.
  • Implemented "PA Optimal Settings" described in datasheet section 13.1.14.1

Maybe it's a hardware issue with my devices? My (unnamed from banggood) SX1262 modules don't transmit when told to use the SX1261 PA (the logic did this for power less than or equal 14 dBm).

I also found that if I request less than -9 dBm output power, the module doesn't properly process the request and instead transmits at its maximum power.

To avoid breaking it for anyone using SX1261: Added (untested) SX1261 class to have different logic in the setOutputPower function.

Sadly when testing this I see imperfect behaviour when requested power is greater than 14 dBm:

  • More output power when 20 is requested than 22 or 21.
  • Power set by setTxParams has minimal effect; instead (approximately correct) jumps are seen when the values in setPaConfig changes.

I'm lacking the proper skills and hardware to take this further right now.

@jgromes jgromes added the help wanted If you're looking to help with RadioLib development, these are the issues to work on label Nov 8, 2019
@jgromes
Copy link
Owner

jgromes commented Nov 8, 2019

Hi, thanks for the contribution. Unfortunately, I can't merge this before it's properly tested, and I too lack the hardware.

If anyone knows of a simple way to meaure output power from these radio modules, that would be very much appreciated.

@BarryPSmith
Copy link
Contributor Author

After some more investigation I think it might be some other issue my side that results in the near constant power output above 17 dBm. It looks like I'm running into a ceiling of some sort.

I'll continue to investigate why my modules aren't responding properly to power requests above 17 (either on the trunk or this branch) then retest the code when I figure it out.

Gory details of testing follow...

Here's what I did: I made a small arduino sketch that loops the power from 22 to -9. At each power, it calls lora.transmitDirect(0) and delays for a few seconds.

I also have an RTL-SDR. I use GnuRadio Companion to view the received spectrum (it's a sharp spike since the unit is only transmitting on the carrier frequency). From this I can read off the peak emission spectral density.

Figuring that the only part of the system that changes is the software controlled power output, comparing the relative spectral density should give an the relative power output of the SX1262.

The table below shows what I found (With the pull request).

Requested power (dBm) Peak relative gain (dB - arbitrary units) Gain re requested @22
22 -28.8 0
21 -28.4 0.4
20 -28.4 0.4
19 -28.5 0.3
18 -28.5 0.3
17 -31.1 -2.3
16 -31.2 -2.4
15 -31.4 -2.6
14 -34.4 -5.6
13 -34.8 -6.0
12 -35.5 -6.7
11 -36.6 -7.8
10 -37.6 -8.8
9 -38.4 -9.6
8 -39.6 -10.8
7 -40.7 -11.9
6 -41.9 -13.1
5 -43.0 -14.2
4 -44 -15.2
3 -45.2 -16.4
2 -45.8 -17
1 -46.9 -18.1
0 -47.5 -18.7
-1 -48.7 -19.9
-2 -49.5 -20.7
-3 -50.9 -22.1
-4 -51.6 -22.8
-5 -52.4 -23.6
-6 -53.8 -25
-7 -54.8 -26
-8 -56 -27.2
-9 -57.1 -28.3

image

@BarryPSmith BarryPSmith closed this Nov 8, 2019
@BarryPSmith
Copy link
Contributor Author

Was I supposed to close that? Sorry I'm new to this whole git thing.

@jgromes
Copy link
Owner

jgromes commented Nov 9, 2019

Thanks for the thorough investigation - this is actually a pretty simple way to check how the ouput power changes, even though it doesn't give the actual power output.

What are using to power the radio? If you take a look at SX1262 datasheet, pages 29 - 30, you'll see curves that have similar shape to yours - perhaps your power source can't supply enough current? Also, what was the current limit set to?

I think we should keep this PR open.

@jgromes jgromes reopened this Nov 9, 2019
@BarryPSmith
Copy link
Contributor Author

So, I found my problem. I needed to call setDIO2AsRfSwitch().

I'm glad I tested it properly: The requested power needed to be scaled according to the maximum provided by setPaConfig. I updated my branch to do that, does this PR automatically update too?

Received signal power (~power output) now looks like this as I step down from +22 to -17 dBm:
image

The curve is imperfect, but it's close. I figure the units have probably been designed this way: these are low cost FM transmitters where exact TX power is less important than efficiency.

As an aside, this test also has the TX clamping fixed (section 15.2.2 of the datasheet). I can put that in a separate PR.

@jgromes
Copy link
Owner

jgromes commented Nov 11, 2019

Thanks for this - when I was writing the SX126x driver, there was no section 15 in the datasheet - might be worth it to go over the datasheet again, see if it's as bad as it was back then :D

does this PR automatically update too?

Yes, the changes you make in the branch you want to pull from are reflected in the PR.

Could you please take a look at SX1268 datasheet, if the same fixes should be applied there as well? I won't be able to merge this for some time anyway (since it looks like the latest update to ESP8266 Arduino core broke something in SoftwareSerial handling, which is why all Travis build fail now ... ).

@BarryPSmith
Copy link
Contributor Author

It looks like there's similar work to be done on SX1268. Also in #61

I hadn't looked much into the SX1268, but now that I look through its datasheet it looks identical to the SX1262 except for frequency range. It seems the code supports this idea.

What's your approach to code duplication in a situation like this? Frequency stuff looks the same in 1261 / 1262 and different in 1268, but power is the same in 1262 / 1268 and different in 1261. Write the stuff common to the majority of the classes in the base (SX126x) then override in the derived classes, or duplicate it in the derived classes?

@jgromes
Copy link
Owner

jgromes commented Nov 12, 2019

Code duplicity should be kept at minimum, so anything that is common to all the modules should be in the base class. The code in the derived classes (SX1262, SX1268 etc.) deals with things like parameter ranges etc. If there's something in common to just a few modules. it can be implemeted in the derived class, then other derived classes can inherit from that one - see SX127x/SX1278/SX1276, for example.

Ensured OCP is always restored when changing power.
Slight refactor to avoid duplicated SX1262 / SX1268 code.
@BarryPSmith
Copy link
Contributor Author

Alright, that one has the optimal high power PA code moved to SX126x so it can also be used by SX1268.

Copy link
Owner

@jgromes jgromes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I left a few comments. If those could be addressed, I think we can merge this.

src/modules/SX1261.cpp Outdated Show resolved Hide resolved
src/modules/SX1261.cpp Outdated Show resolved Hide resolved
src/modules/SX1261.cpp Show resolved Hide resolved
src/modules/SX1261.cpp Outdated Show resolved Hide resolved
src/modules/SX1261.h Outdated Show resolved Hide resolved
src/modules/SX1268.cpp Outdated Show resolved Hide resolved
src/modules/SX1268.cpp Outdated Show resolved Hide resolved
src/modules/SX126x.cpp Show resolved Hide resolved
src/modules/SX126x.cpp Outdated Show resolved Hide resolved
src/modules/SX126x.h Outdated Show resolved Hide resolved
Fixed power adjustment for SX1261
@BarryPSmith
Copy link
Contributor Author

BarryPSmith commented Nov 13, 2019

Requested changes are implemented. One minor functional change: When adding the comment about power scaling in SX1261.cpp I noticed I had not read the datasheet closely enough and was scaling by 4dB instead of the advised 3dB.

@jgromes jgromes merged commit db5b892 into jgromes:master Nov 13, 2019
@jgromes
Copy link
Owner

jgromes commented Nov 13, 2019

Thanks, I really appreciate this! Merging now, next release will be probably later this week.

@BarryPSmith BarryPSmith deleted the sx126x_txpower branch November 13, 2019 15:40
@jgromes jgromes removed the help wanted If you're looking to help with RadioLib development, these are the issues to work on label Nov 13, 2019
@jgromes
Copy link
Owner

jgromes commented Dec 7, 2019

@BarryPSmith could you please share the exact setup you used when testing this? It would help me a lot, thank you very much.

@BarryPSmith
Copy link
Contributor Author

No worries.

The hardware was fairly simple.

For the transmitter: An arduino nano controlling an SX1262, using a 3.3V linear regulator off the nano 5V line to power the SX1262, with some buffer capacitors. Voltage divider resistors to convert the 5V arduino logic levels to 3.3V for the radio. An antenna from a handheld radio I had laying about.
IMG_3227

For the receiver: A RTL-SDR dongle, (the RTL-SDRblog branded one). Another handheld antenna, but I used a SMA extension cable to put the antenna in another room because I was seeing clipping on the signal if they were next to each other.

@BarryPSmith
Copy link
Contributor Author

The arduino code is messy. I just took one of the radiolib examples (hard to tell which) and hacked at it until it did what I wanted. Then I hacked at it a bunch more while I was trying to figure out why I was seeing those weird steps (eventually realised it was the setDio2AsRfSwitch()). Usually my code starts out messy and only if it's something I figure with keeping do I clean it up.

This looks like the code I was using. It probably wasn't exactly the same, and it'll need to be modified to work with the newer version of radiolib (or probably rewritten with good style, unless you want to spend ages staring at the computer figuring out why a small change broke it).

/*
   RadioLib SX126x Transmit Example

   This example transmits packets using SX1262 LoRa radio module.
   Each packet contains up to 256 bytes of data, in the form of:
    - Arduino String
    - null-terminated char array (C-string)
    - arbitrary binary data (byte array)

   Other modules from SX126x family can also be used.

   For full API reference, see the GitHub Pages
   https://jgromes.github.io/RadioLib/
*/

// include the library
#include <RadioLib.h>

#define BUSY 4
// SX1262 has the following connections:
// NSS pin:   10
// DIO1 pin:  2
// DIO2 pin:  None
// BUSY pin:  4
SX1262 lora = new Module(9, 2, -1, BUSY);


// or using RadioShield
// https://github.com/jgromes/RadioShield
//SX1262 lora = RadioShield.ModuleA;


void Dio1Action();
void CheckDio1();

void setup() {
  Serial.begin(9600);

  // initialize SX1262 with default settings
  Serial.print(F("[SX1262] Initializing ... "));
  // carrier frequency:           434.0 MHz
  // bandwidth:                   125.0 kHz
  // spreading factor:            9
  // coding rate:                 7
  // sync word:                   0x1424 (private network)
  // output power:                14 dBm
  // current limit:               60 mA
  // preamble length:             8 symbols
  // CRC:                         enabled
  //while(1) Serial.println(digitalRead(BUSY));
  int state = lora.begin();
  
  if (state == ERR_NONE) {
    Serial.println(F("success!"));
  } else {
    Serial.print(F("failed, code "));
    Serial.println(state);
    while (true);
  }

  state = lora.standby();
  if (state)
  {
      Serial.print(F("standby failed, code "));
    Serial.println(state);
    while (true);
  }

  // NOTE: Some SX126x modules use TCXO
  //       (Temprature-Compensated Crystal Oscillator).
  //       To be able to use these modules, TCXO
  //       control must be enabled by calling
  //       setTCXO() and specifying the reference
  //       voltage.
  
  /*
    Serial.print(F("[SX1262] Setting TCXO reference ... "));
    // enable TCXO
    // reference voltage:           1.6 V
    // timeout:                     5000 us
    state = lora.setTCXO(1.6);
    if (state == ERR_NONE) {
      Serial.println(F("success!"));
    } else {
      Serial.print(F("failed, code "));
      Serial.println(state);
      while (true);
    }
  */

  // our unit doesn't work at 434 for some reason.
  // (We're using a licensed 425 frequency. In North America, it's in the UHF ham band for "Experimental". This seems like an experiment to me.
  Serial.println("== Setting Frequency ==");
  auto resF = lora.setFrequency(425);
  if (resF) {
    Serial.print("Error setting frequency: ");
    Serial.println(resF);
  }

  lora.setSpreadingFactor(5);
  lora.setCodingRate(5);
  lora.setBandwidth(10.4);
  lora.setDio2AsRfSwitch();
  lora.setCurrentLimit(140);

  for (int i = 22; i >= -17; i--)
  {
    Serial.print("Testing at...");
    Serial.println(i);
    Serial.println("== Setting power ==");
    auto resP = lora.setOutputPower(i);
    if (resP) {
      Serial.print("Error setting output power: ");
      Serial.println(resP);
    }

    // enable this block to test using a long packet
    #if 0
    Serial.println("== Transmitting Packet ==");
    char message[] = "<Callsign> Testing ###\t#.###############################################################################################################################################";
    message[15] = i < 0 ? '-' : '+';
    message[16] = '0' + abs(i) / 10;
    message[17] = '0' + abs(i) % 10;
    Serial.println(message);
    auto resT = lora.transmit(message);
    CheckDio1();
    if (resT) {
      Serial.print("Error transmitting packet: ");
      Serial.println(resT);
    }
    delay(1000);
    #endif

    //enable this block to test using a sine wave
  #if 1
    Serial.println("== Transmit Direct ==");
    auto resD = lora.transmitDirect(0);
    if (resD) {
      Serial.print("Error in transmit direct: ");
      Serial.println(resD);
    }
    delay(5000);

    auto resStd = lora.standby();
    if (resStd)
    {
      Serial.print("Error entering standby: ");
      Serial.println(resStd);
    }

    // enable this to stop it from automatically stepping through power levels.
    #if 0
    //Clear the serial buffer:
    while (Serial.read() >= 0);
    Serial.println("Send anything to continue");
    //Wait for a response:
    while (!Serial.available());
    #endif
  #endif
  } //i (power)
}

void loop() {

}

@BarryPSmith
Copy link
Contributor Author

BarryPSmith commented Dec 8, 2019

Here's the gnu radio companion setup I was using. This was my first time using GRC, so I was just figuring it out as I went along.

When I opened this project to check it before replying to this message, I had deleted the file sink for some reason (maybe some other testing I was doing?), so it might not be configured perfectly right now.

Power Monitor.zip

image

The system does:

  • Enable the RTL-SDR at 425MHz, with 2MHz bandwidth. This I just found from looking up what other people did.
  • Resample and low pass. It looks like the low pass filter is set for 20kHz, so that will have to be adjusted if you use the LoRa packet mode to test instead of pure sine wave.
  • Display the signal in a GUI sink for verification
  • Also, convert the signal to power (P ~ V^2),
  • then average the power
  • and only keep 1 in 10k samples (because we're still at a high sample rate).
  • Take 10log10(Power) to get decibels.
  • Then save the decibel levels to disk
  • (also display them in the GUI for verification).

@BarryPSmith
Copy link
Contributor Author

Sorry, I'm not a particularly organised person. Given that the things are unlikely to just work out of the box, if you care to share your intended use of this stuff I might be able to clean it up for that.

@BarryPSmith
Copy link
Contributor Author

Oh, I had to hack together a script to convert the binary data into something I could plot. I have an aversion to matlab, so I used C# -> text file -> excel.

This likely doesn't need to be shared, but for completeness I figure I'd paste it here. Saves having to look up the GRC binary format (It's just an array of 32 bit floating points.)

This just takes the input binary and writes a number on each line. From the shape of the data, you can see where the steps happen.

using System;
using System.IO;

namespace ConsoleApp4
{
    class Program
    {
        static void Main(string[] args)
        {
            var ifn = @"C:\Users\Barry_Main\Documents\425Mhz Power";
            var ofn = Path.ChangeExtension(ifn, "csv");
            using (var ifs = new FileStream(ifn, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
            using (var writer = new StreamWriter(ofn))
            using (var br = new BinaryReader(ifs))
            {
                while (br.BaseStream.Position < br.BaseStream.Length - sizeof(float))
                {
                    writer.WriteLine(br.ReadSingle());
                    Console.WriteLine(br.ReadSingle());
                }
            }
        }
    }
}

@jgromes
Copy link
Owner

jgromes commented Dec 8, 2019

Thank you, I wasn't expecting as much detail in the response :D

Could you please get in touch with me on [email protected]? I don't want to clog up issues and PRs with off-topic stuff and I don't want to expose your email publicly - mine is already in the library properties file.

@jgromes jgromes added enhancement New feature or request resolved Issue was resolved (e.g. bug fixed, or feature implemented) labels Jun 24, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request resolved Issue was resolved (e.g. bug fixed, or feature implemented)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants