Go Down

Topic: Random Function based upon internal temp sensor (Read 4801 times) previous topic - next topic

robtillaart

Jan 05, 2011, 03:50 pm Last Edit: Jan 05, 2011, 04:11 pm by robtillaart Reason: 1
Inspired by a discussion over the internal temperature sensor - http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1294205576 - and it outputting only rubbish I had the idea to use it for a random generator or for a random seed generator.

The code makes 32 calls to the internal temp sensor and strips of the "MNB = most noisy bit" and builds up a return value. Variations are possible, especially by adding a parameter representing the number of calls.

It generates quite random output on my UNO, but I haven't tested it on other platforms. I have all my 328's occupied in projects ... Furthermore I don't knw the quality of the randomness, but it seems to behave random enough for me. The speed seems a bit slow to me - no figures yet - therefor I think it is more usefull as a random seed generator than as a pure random generator.

Remarks, improvements, comments all welcome !

Todo:
* Test the distribution / randomness ??
* Test speed
* Wrap in a class
* Playground article.

Code: [Select]
//
//    FILE: RealRandom
//  AUTHOR: Rob Tillaart
// PURPOSE: Simple Random functions based upon unreliable internal temp sensor
// VERSION: 0.1
//       DATE: 2011-05-01
//
// Released to the public domain, use at own risk
//

long longRandom()
{
 analogReference(INTERNAL);
 unsigned long rv = 0;
 for (byte i=0; i< 32; i++) rv |= (analogRead(8) & 1L) << i;
 return rv;
}

float floatRandom()
{
 analogReference(INTERNAL);
 float rv = 0;
 float t = 0.5;
 for (byte i=0; i<32; i++)
 {
   if (analogRead(8) & 1L) rv += t;
   t /= 2;
 }
 return rv;
}

long randomRange(long _min, long _max)
{
 return _min + long ((_max - _min) * floatRandom());
}

void setup()
{
 Serial.begin(115200);
}

void loop()
{
 long t = randomRange(0,10);
 Serial.println(t);
 
 t = longRandom();
 Serial.println(t);
 
 float f = floatRandom();
 Serial.println(f);
 delay(500);  
}
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

AlphaBeta

Cool!

I would recommend you to adhere to the naming convention of Arduino that is lowerUpper camel casing.

longRandom, floatRandom, randomRanged



Just my 2 bits

robtillaart

#2
Jan 05, 2011, 04:09 pm Last Edit: Jan 05, 2011, 04:10 pm by robtillaart Reason: 1
Quote
Just my 2 bits

Only if they are random ;)

(done)
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)


Coding Badly


mowcius

ATmega328P in DIP, possibly others but the duemilanove and uno will do it at least.

robtillaart

#6
Jan 05, 2011, 11:41 pm Last Edit: Jan 05, 2011, 11:46 pm by robtillaart Reason: 1
Wrote a frequency test - change loop() in program above shows that on my UNO there are four numbers - 0, 13,  26, 39  - are more preferred than others. The others are more distributed evenly. So it is definitely not as random as I would like it to be, but very interesting why.

Hypothesis: Think it is because the same sensor is read out in a tight loop and the noise bit fluctuates not fast enough, resulting in similar noise bits => the  bits are not random enough (entropy/randomness).
in other words the readings are "too stable".

Code: [Select]

void loop()
{
 for (int i=1; i< 25; i++) frequency_test(40);
}

void frequency_test(int mx)
{
 int nr[40];  
 for(int i = 0; i < 40; i++) nr[i] = 0;

 for(int i = 0; i < 1000; i++) nr[ randomRange(0, mx)] ++;

 Serial.println();
 Serial.println(mx);
 Serial.println("=======================");
 for(int i = 0; i < 40; i++)
 {
   Serial.print(i);
   Serial.print("\t");
   Serial.print(nr[i]);;
   Serial.print("\t");
   for (int x=0; x < nr[i]; x++) Serial.print("]");
   Serial.println();
 }
}


output sample
40
=======================
0       36      ]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
1       31      ]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
2       29      ]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
3       24      ]]]]]]]]]]]]]]]]]]]]]]]]
4       24      ]]]]]]]]]]]]]]]]]]]]]]]]
5       21      ]]]]]]]]]]]]]]]]]]]]]
6       27      ]]]]]]]]]]]]]]]]]]]]]]]]]]]
7       19      ]]]]]]]]]]]]]]]]]]]
8       21      ]]]]]]]]]]]]]]]]]]]]]
9       34      ]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
10      13      ]]]]]]]]]]]]]
11      23      ]]]]]]]]]]]]]]]]]]]]]]]
12      21      ]]]]]]]]]]]]]]]]]]]]]
13      43      ]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
14      30      ]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
15      16      ]]]]]]]]]]]]]]]]
16      18      ]]]]]]]]]]]]]]]]]]
17      26      ]]]]]]]]]]]]]]]]]]]]]]]]]]
18      11      ]]]]]]]]]]]
19      30      ]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
20      26      ]]]]]]]]]]]]]]]]]]]]]]]]]]
21      18      ]]]]]]]]]]]]]]]]]]
22      25      ]]]]]]]]]]]]]]]]]]]]]]]]]
23      20      ]]]]]]]]]]]]]]]]]]]]
24      15      ]]]]]]]]]]]]]]]
25      23      ]]]]]]]]]]]]]]]]]]]]]]]
26      59      ]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
27      35      ]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
28      23      ]]]]]]]]]]]]]]]]]]]]]]]
29      25      ]]]]]]]]]]]]]]]]]]]]]]]]]
30      27      ]]]]]]]]]]]]]]]]]]]]]]]]]]]
31      12      ]]]]]]]]]]]]
32      27      ]]]]]]]]]]]]]]]]]]]]]]]]]]]
33      17      ]]]]]]]]]]]]]]]]]
34      19      ]]]]]]]]]]]]]]]]]]]
35      20      ]]]]]]]]]]]]]]]]]]]]
36      23      ]]]]]]]]]]]]]]]]]]]]]]]
37      27      ]]]]]]]]]]]]]]]]]]]]]]]]]]]
38      19      ]]]]]]]]]]]]]]]]]]]
39      43      ]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]


To be continued ...
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

Coding Badly


robtillaart

No cap on AREF,  just an UNO with an ethernetshield (latter not used).
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

Coding Badly


I think you're going to have a difficult time using using the internal temperature sensor as an entropy source.  When I added a capacitor to AREF, the "randomness" was noticeably less.  When I used "noise reduction sleep mode", the "randomness" essentially disappeared.  

I suspect the "randomness" is a by-product of digital noise from the processor itself.  The datasheet mentions many times the need to reduce / eliminate / avoid digital noise when performing an A/D conversion.

I can think of two problems that result from using "digital noise" as an entropy source...

1. The "noise" is not at all random.  The processor is executing the exact same code every time a random bit is needed.  By executing the same code each time, the "noise" is the same each time.  This is bound to create patterns in the readings.

2. Holding my hand near AREF seems to introduce enough capacitance to reduce the randomness / noise.  I suspect other things (wire, PCB traces, whatever) will have the same effect.  This makes the technique unreliable.

For what it's worth, I really hope I'm wrong.  The Arduino sorely needs an entropy source.  The need for "truly random" values has come up many times.

robtillaart

#10
Jan 06, 2011, 11:56 am Last Edit: Jan 06, 2011, 01:08 pm by robtillaart Reason: 1
Thanks for the info, it is a pity the sensor is not noisy enough as the search for true randomness still continues

If your application has an ethernetshield one could call - http://www.random.org/bytes/

TODO: built an Arduino wrapper around this webform....
* fetch 4 bytes to make a long to be used
** in randomSeed()
** to generate a random
** to generate a float [0..1>
** to generate a number between n and M   (M is bigger than n)

think I have found a thing todo today :)

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

Coding Badly


They have a form for floating-point numbers.  You'll have to check for and exclude 1.00000000...

http://www.random.org/decimal-fractions/?num=1&dec=8&col=1&format=plain&rnd=new

robtillaart

#12
Jan 07, 2011, 06:16 pm Last Edit: Jan 07, 2011, 06:17 pm by robtillaart Reason: 1
Although the random function based upon the internal clock is not random enough I still did the speed test to get an impression of its performance. The sketch below produced timing results in micros for 1000 calls.

   
1000 * random used : 94568 (<0.01 sec)      
1000 * longRandom used : 3802480  (3.8 sec)
1000 * floatRandom used : 3981380  (4.0 sec)


longRandom() and floatRandom() are 40++ times slower than the native random() function. That is another indication it is more suitable as seedgenerator than as random function itself.

Code: [Select]
//
//    FILE: RealRandom
//  AUTHOR: Rob Tillaart
// PURPOSE: Simple Random functions based upon unreliable internal temp sensor
// VERSION: 0.1
//
// Released to the public domain, use at own risk
//

long longRandom()
{
 analogReference(INTERNAL);
 unsigned long rv = 0;
 for (byte i=0; i< 32; i++) rv |= (analogRead(8) & 1L) << i;
 return rv;
}

float floatRandom()
{
 analogReference(INTERNAL);
 float rv = 0;
 float t = 0.5;
 for (byte i=0; i<32; i++)
 {
   if (analogRead(8) & 1L) rv += t;
   t /= 2.0;
   //delay(1); // improves flatness but costs 32 mills
 }
 return rv;
}

long randomRange(long _min, long _max)
{
 return _min + long ((_max - _min) * floatRandom() );
}

void setup()
{
 Serial.begin(115200);
}

void loop()
{
 long x;
 float f;
 unsigned long start;
 unsigned long time;

 start = micros();
 for (int i=1; i< 1000; i++)
 {
   x = random();
 }
 time = micros() - start;
 Serial.print("1000 * random used : ");
 Serial.println(time);
 
 start = micros();
 for (int i=1; i< 1000; i++)
 {
   x = longRandom();
 }
 time = micros() - start;
 Serial.print("1000 * longRandom used : ");
 Serial.println(time);
 
   start = micros();
 for (int i=1; i< 1000; i++)
 {
   f = floatRandom();
 }
 time = micros() - start;
 Serial.print("1000 * floatRandom used : ");
 Serial.println(time);
 
 while (1);
}
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

robtillaart

#13
Jan 07, 2011, 07:02 pm Last Edit: Jan 07, 2011, 07:11 pm by robtillaart Reason: 1
Quote
If your application has an ethernetshield one could call - http://www.random.org/bytes/


And so I build this prototype . It is a simple webclient that asks for 4 bytes and converts this output to a long. If the network connection fails the internal random() function is called as a backup.

Code: [Select]
/*
*    FILE: randomNet
*  AUTHOR: Rob van den Tillaart
*    DATE: 2011-01-06
* VERSION: 0.2
* PURPOSE: fetch a random long over the net from www.random.org
*/

#include <SPI.h>
#include <Ethernet.h>

byte mac[6] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte ip[4] = { 192, 168, 1, 25 };
byte server[4] = { 174, 143, 173, 125 };  // nslookup www.random.org

long fetchRandomLong()
{
 long rv = -1;
 byte ch;
 int state = 0;
 int x = 0;
 
 Client client(server, 80);

 if (client.connect())
 {
   // client.println("GET /decimal-fractions/?num=1&dec=8&col=1&format=plain&rnd=new");
   client.println("GET /integers/?num=4&min=0&max=255&col=1&base=10&format=plain&rnd=new");

   // wait max 1 second to read the answer
   for (int i=0; i< 1000; i++)
   {
     if (client.available()) break;
     delay(1);
   }
   // fetch the string
   while (client.available())
   {
     ch = client.read();
     if ('0' <= ch && ch <= '9')
     {
       state = 1;
       x = x*10 + ch - '0';
     }
     else
     {
       if (state == 1)
       {
         state = 0;
         rv <<= 8;
         rv += x;
         x = 0;
       }
     }
   }
   client.stop();
   for (int i=0; i<100; i++)
   {
     if (client.status() == 0) break;
     delay(1);
   }
 }
 //  network did not work => use internal as backup
 if (rv == -1)    
 {
   rv = random();
 }
 return rv;
}

////////////////////////////////////////////////////////
void setup()
{
 Serial.begin(115200);
 Serial.println("Demo - random from www.random.org");
 Ethernet.begin(mac, ip);
 delay(1000);
}

void loop()
{
 unsigned long start = millis();
 long rd = fetchRandomLong();
 unsigned long timeUsed = millis() - start;
 Serial.print("random : ");
 Serial.println(rd);
 Serial.print("time : ");
 Serial.println(timeUsed);
}
// END OF FILE


Sample of the output

Demo Random via net
random : 1346747051
time : 1033
random : -1992743329
time : 705
random : -629485075
time : 795
random : 491946319
time : 532
random : -1748141667
time : 623
random : -1086788461
time : 1096
random : 907126690
time : 1187
random : -1270226305
time : 911
random : 605004460
time : 851
random : -1837384756
time : 692

The randomness of the numbers is guaranteed by the organization behind the webserver www.random.org. The timing is almost a random generator in itself as it differs quite alot. Average timing varies around 750-800 millis(). The code size is 6840 (IDE 21 - UNO).

The code is bigger and slower than the random function based upon the internal temperature sensor ans so we can conclude that true randomness has its price.

TODO:
* a minimal improvement would be ask for two 16 bit numbers
* fetchRandomFloat()
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

Go Up