Skip to content

Fix Random function II #496

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

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
34 changes: 33 additions & 1 deletion API.md
Original file line number Diff line number Diff line change
Expand Up @@ -408,10 +408,42 @@ Supported values are between `0` and `6`. If gain is 0, AGC will be enabled and

### Random

Generate a random byte, based on the Wideband RSSI measurement.
Generate random bytes, based on Wideband RSSI measurements. The least significant bit (LSB) of each measurement is fed in a [Basic von Neumann extractor](https://en.wikipedia.org/wiki/Bernoulli_process#Bernoulli_sequence) for additional whitening.

```
byte b = LoRa.random();
```

Returns random byte.

```
LoRa.random(uint8_t *buffer, size_t size);
```


* `buffer` - pointer to an array of bytes
* `size` - size of the buffer in bytes

Returns nothing but the buffer will be filled with `size` random bytes.

If you need more than one byte it is recommended to use the second function due to some overhead.

The LoRa module is put in a receiving mode with configuration as suggested in AN1200.24 from Semtech. Before changing the mode all pending transmits are send. After collecting the random numbers the module is reconfigured to the state it has before calling this function. During the collection of the random numbers no packets can be received.

The pseudorandom number sequence test program [ENT](http://www.fourmilab.ch/random/) gives quite good results when fed with random bytes generated with a HopeRF RFW95 module and this function:

Entropy = 7.999980 bits per byte.

Optimum compression would reduce the size of this 8757184 byte file by 0 percent.

Chi square distribution for 8757184 samples is 247.56, and randomly
would exceed this value 61.91 percent of the times.

Arithmetic mean value of data bytes is 127.5110 (127.5 = random).

Monte Carlo value for Pi is 3.142532185 (error 0.03 percent).

Serial correlation coefficient is 0.000407 (totally uncorrelated = 0.0).

You can get around 1000 bytes/s with an ATmega328p and a HopeRF RFW95 module.
For more information see [RANDOM.md](https://github.com/plybrd/arduino-LoRa/blob/master/doc-random/RANDOM.md).
50 changes: 50 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,53 @@
# This Fork

adds support to **real random numbers** to this library.

The new methods are
``` c
byte rssi_wideband(); // RSSI wideband meassurement - the original "random" method
byte random();
void random(uint8_t *buffer, size_t size);
```
There is some overhead in creating random numbers. So if you need more then one you should use `random(uint8 *, size_t)`.

The improved method takes care if a packet is just to go over the air. It waits until the transmit is finished before setting up for random number generation.

In general you can not receive a wanted packet after setting up for random number generation. This is because of the fact that bandwidth, spreading factor, and coding rate is changed. For this reason I disable the interrupt IRQ_RX_DONE so no accidental receive will disturb us. In fact all interrupts on the SX127# will be disabled to be on the sure side. After finishing the collection of random numbers all the interrupts are restored to the previous state. Before returning all parameters are set to the original state.

A long time ago I suggested change request [#496](https://github.com/sandeepmistry/arduino-LoRa/pull/496) but until now nothing happend.

If you need not only a palmful random numbers you can have a look at library [LoRandom](https://github.com/Kongduino/LoRandom).

## How is it done?
It first renames the original function `byte random()` to `byte rssi_wideband()` as this is what this function does! It depends on the location of the meassurement and gives only random values from 0 to MAX with MAX<<255. The result (see [here](https://github.com/plybrd/arduino-LoRa/blob/master/doc-random/random-widebandRSSI.png)) looks strongly biased. In my case only numbers between 0 and 31 are returned! The cureves are for 20, 50, 200, 1000, ... samples. The more samples you have the better shows the bias up.

There is an description how to generate random numbers in Application Note AN1200.24 from Semtech. See Chapter 4 [Random Number Generation for Cryptography](https://semtech.my.salesforce.com/sfc/p/#E0000000JelG/a/440000001NAw/7YN8ZamV70_xR.vPDAAAshm.0Wt4jmRX0nOKkOzQqiI).

To generate an N bit random number, perform N read operation of the register RegRssiWideband (address 0x2c) and use the LSB of the fetched value. The value from RegRssiWideband is derived from a wideband (4MHz) signal strength at the receiver input and the LSB of this value constantly and randomly changes.

Doing so gives this [result](https://github.com/plybrd/arduino-LoRa/blob/master/doc-random/random-asAN1200.24.png). It looks like bit '1' is prefered in comparison to bit '0'. So binary numbers with many '1' occure more often. But we get the whole range of possible numbers.

Last, we add a basic von Neumann extractor which produce a uniform output even if the distribution of input bits is not uniform so long as each bit has the same probability of being one and there is no correlation between successive bits (see [Bernoulli_sequence@wikipedia](https://en.wikipedia.org/wiki/Bernoulli_process#Bernoulli_sequence) and [Randomness_extractor@wikipedia](https://en.wikipedia.org/wiki/Randomness_extractor)). This extractor is whitening some LSBs of RSSI wide-band measurements for each random byte. The [result](https://github.com/plybrd/arduino-LoRa/blob/master/doc-random/random-asAN1200.24-Neumann.png) shows random numbers without bias. The function reproduces the mean value when we pull enough numbers.

**Using the program [ENT](http://www.fourmilab.ch/random/) from fourmilab gives**

### For the new random function of this library
- Entropy = 7.999980 bits per byte.
- Optimum compression would reduce the size of this 8757184 byte file by 0 percent.
- Chi square distribution for 8757184 samples is 247.56, and randomly would exceed this value 61.91 percent of the times.
- Arithmetic mean value of data bytes is 127.5110 (127.5 = random).
- Monte Carlo value for Pi is 3.142532185 (error 0.03 percent).
- Serial correlation coefficient is 0.000407 (totally uncorrelated = 0.0).

### For rssi_wideband() ( sandeepmistry's "random" function)
- Entropy = 3.910721 bits per byte.
- Optimum compression would reduce the size of this 11800167 byte file by 51 percent.
- Chi square distribution for 11800167 samples is 311806630.27, and randomly would exceed this value less than 0.01 percent of the times.
- Arithmetic mean value of data bytes is 14.9272 (127.5 = random).
- Monte Carlo value for Pi is 4.000000000 (error 27.32 percent).
- Serial correlation coefficient is 0.000269 (totally uncorrelated = 0.0).


# Arduino LoRa

[![Build Status](https://travis-ci.org/sandeepmistry/arduino-LoRa.svg?branch=master)](https://travis-ci.org/sandeepmistry/arduino-LoRa)
Expand Down
Binary file added doc-random/random-asAN1200.24-Neumann.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc-random/random-asAN1200.24.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc-random/random-widebandRSSI.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
37 changes: 37 additions & 0 deletions examples/LoRaRandom/LoRaRandom.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#include <SPI.h>
#include <LoRa.h>

#define LRFRQNCY 866E6

void setup() {
Serial.begin(9600);
while (!Serial);
Serial.println("Start LoRaRandom");

if (!LoRa.begin(LRFRQNCY)) {
Serial.println("Starting LoRa failed!");
while(1);
}
Serial.println("LoRa init succeeded.");
}

#define NRANDOM 1024

void loop() {
uint8_t buffer[NRANDOM];

LoRa.random(buffer, NRANDOM);

// Just print them out
for(size_t i=0; i<NRANDOM; i++){
if(!(i%10)){
Serial.println();
Serial.print(i);
Serial.print(":\t");
}
Serial.print(buffer[i]);
Serial.print("\t");
}
Serial.println();
}

65 changes: 64 additions & 1 deletion src/LoRa.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#define REG_FIFO_TX_BASE_ADDR 0x0e
#define REG_FIFO_RX_BASE_ADDR 0x0f
#define REG_FIFO_RX_CURRENT_ADDR 0x10
#define REG_IRQ_FLAGS_MASK 0x11
#define REG_IRQ_FLAGS 0x12
#define REG_RX_NB_BYTES 0x13
#define REG_PKT_SNR_VALUE 0x19
Expand Down Expand Up @@ -65,6 +66,12 @@

#define MAX_PKT_LENGTH 255

// Setup random number generation
#define RNDM_SIGNAL_BANDWIDTH 0x70 // 125E3
#define RNDM_CODING_RATE 0x02 // 4/5
#define RNDM_HEADER_MODE 0x00 // explicit heade mode
#define RNDM_SPREADING_FACTOR 0x70 // 7

#if (ESP8266 || ESP32)
#define ISR_PREFIX ICACHE_RAM_ATTR
#else
Expand Down Expand Up @@ -688,11 +695,67 @@ void LoRaClass::setGain(uint8_t gain)
}
}

byte LoRaClass::random()
byte LoRaClass::rssi_wideband()
{
return readRegister(REG_RSSI_WIDEBAND);
}

// As suggested in AN1200.24 from Semtech with basic von Neumann
// extractor (see
// https://en.wikipedia.org/wiki/Bernoulli_process#Bernoulli_sequence)
void LoRaClass::random0(uint8_t *buffer, size_t size)
{
for(size_t i=size; i--;){
uint8_t bit, nbr=0, j=8;

while(j)
if((bit=readRegister(REG_RSSI_WIDEBAND) & 0x1)!=
(readRegister(REG_RSSI_WIDEBAND) & 0x1)){
nbr += nbr + bit;
j--;
}

*buffer++=nbr;
}
}

void LoRaClass::random(uint8_t *buffer, size_t size)
{
// Waiting until send is finished
while ((readRegister(REG_OP_MODE) & MODE_TX) == MODE_TX)
yield();

// Saving current state
uint8_t crntOpMode=readRegister(REG_OP_MODE);
uint8_t crntModemConfig1=readRegister(REG_MODEM_CONFIG_1);
uint8_t crntModemConfig2=readRegister(REG_MODEM_CONFIG_2);
uint8_t crntIRQFlagsMask=readRegister(REG_IRQ_FLAGS_MASK);

// Disable all interrupts (so we won't receive a packet)
writeRegister(REG_IRQ_FLAGS_MASK, 0xFF);

// Setup for random number generation
writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_RX_CONTINUOUS);
writeRegister(REG_MODEM_CONFIG_1,RNDM_SIGNAL_BANDWIDTH|RNDM_CODING_RATE|RNDM_HEADER_MODE);
writeRegister(REG_MODEM_CONFIG_2,RNDM_SPREADING_FACTOR);

// Collecting random numbers
random0(buffer, size);

// Restoring current state
writeRegister(REG_MODEM_CONFIG_1, crntModemConfig1);
writeRegister(REG_MODEM_CONFIG_2, crntModemConfig2);
writeRegister(REG_OP_MODE, crntOpMode);
writeRegister(REG_IRQ_FLAGS_MASK, crntIRQFlagsMask);
}

byte LoRaClass::random()
{
byte nbr;
random(&nbr, 1);
return nbr;
}

void LoRaClass::setPins(int ss, int reset, int dio0)
{
_ss = ss;
Expand Down
4 changes: 4 additions & 0 deletions src/LoRa.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,9 @@ class LoRaClass : public Stream {
void crc() { enableCrc(); }
void noCrc() { disableCrc(); }

byte rssi_wideband(); // RSSI wideband meassurement
byte random();
void random(uint8_t *buffer, size_t size);

void setPins(int ss = LORA_DEFAULT_SS_PIN, int reset = LORA_DEFAULT_RESET_PIN, int dio0 = LORA_DEFAULT_DIO0_PIN);
void setSPI(SPIClass& spi);
Expand All @@ -117,6 +119,8 @@ class LoRaClass : public Stream {

static void onDio0Rise();

void random0(uint8_t *buffer, size_t size);

private:
SPISettings _spiSettings;
SPIClass* _spi;
Expand Down