Go Down

Topic: Problem reading random noise generator (Read 11432 times) previous topic - next topic

acegard

Hello,
I am trying to make a HRNG (Hardware random number generator) and I'm using avalanche breakdown noise as the source of my random noise: Link
Here is my circuit:
Image Link
In order to test the distribution, I'm sampling it with the Arduino (an Uno), however, my distribution is skewed. I don't think it's the fault of my noise generator, because according to my University's scope, it looks pretty good:
Image Link
The yellow signal is the noise at the collector-emitter junction, and the green is it amplified (At the output of the second op-amp) and biased around 4.5V as virtual ground. Since I took that scope capture, I have changed the gain of the amplifier to give me roughly 3Vpp as opposed to the indicated 105 mVpp, but the signal should look the same. I then pass it through a voltage divider so that the Arduino can read it (to push it down from a 0-9V range to a 0-5V range) and capture it at Analog Out. I then sample it and map it from 1-20 (since that's what I need eventually anyway). However, my distribution is skewed:
Image Link
It seems to be clumped on the high and low ends of the spectrum, with 20 not even being mapped to. Any ideas why this could be? What I really want to do is show the entire distribution (from 0 to 1023) but the Arduino can't hold an array that large. Also, after some testing, it seems that my noise only ranges from 140 to 890 on the ADC's scale, so that's what I've mapped them by.
Here is my code:
Code: [Select]
///A sketch to sample and display the reading at analog 0 to test the generation of random noise

#define inPin A0

int distribution1 [20];
int distribution2 [20];
long times = 0;
int mini = 500;
int maxi = 500;
void setup()
{
  pinMode(inPin, INPUT);
  Serial.begin(9600);
  randomSeed(analogRead(1));
}

void loop()
{
  while(!Serial.available())
  {
    int inputLevel = analogRead(inPin);
    int mapped = map(inputLevel, 148,881,1,21);
    distribution1[mapped - 1]++;
    Serial.println(inputLevel
    );
    if(inputLevel > maxi)
      maxi = inputLevel;
     
    if(inputLevel<mini)
      mini = inputLevel;
     
      times ++;
  }
  Serial.print(times);Serial.println(" Samples taken");
  Serial.println("With noise:");
  for(int i = 0; i < 20; i++)
  {
    Serial.print(i+1);Serial.print(": ");Serial.println(distribution1[i]);
   
  }
  Serial.print("Minimum: ");Serial.println(mini);
  Serial.print("Maximum: ");Serial.println(maxi);
 
  for(long i = 0; i < times; i++)
  {
    int rand = random(1,21);
    distribution2[rand-1]++;
  }
 
  Serial.println("Built in:");
  for(int i = 0; i < 20; i++)
  {
    Serial.print(i+1);Serial.print(": ");Serial.println(distribution2[i]);
   
  }
 
  while(true)
  {}

}

jremington

#1
Jun 01, 2014, 02:48 am Last Edit: Jun 01, 2014, 02:59 am by jremington Reason: 1
Do you know for sure what the map() function actually does, in particular how does it handle numbers that are out of the specified input range? Try some other means of generating the numbers 0-20 from the input. For example, you could try using the remainder function,
Code: [Select]
newnumber = analogRead(inPin)%21;

Edit: here is the actual code for the map function. Clearly, there is a problem using the output as an array index, as the result can go out of the specified output range.
Code: [Select]
For the mathematically inclined, here's the whole function

long map(long x, long in_min, long in_max, long out_min, long out_max)
{
 return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}


Magician

Quote
140 to 890 on the ADC's scale
Thats exactly what I'd expect from 3Vpp input. Full range 5 Vpp.

acegard


Quote
140 to 890 on the ADC's scale
Thats exactly what I'd expect from 3Vpp input. Full range 5 Vpp.

I'm not sure what you're trying to say here. Yes, this is what I'm getting and this is what I've planned for. My problem stems, I think, from the mapping, but I'm not super sure.

Do you know for sure what the map() function actually does, in particular how does it handle numbers that are out of the specified input range? Try some other means of generating the numbers 0-20 from the input. For example, you could try using the remainder function,
Code: [Select]
newnumber = analogRead(inPin)%21;

Thanks for pointing that out to me. The map() function doesn't look like it will work. I tried your first suggestion with the modulo operator, and by clamping my results so that they don't go out of range:
Code: [Select]
if(newNumber <=19)
      distribution1[newNumber]++;

My distribution his still uneven:
Code: [Select]
1: 824
2: 697
3: 651
4: 633
5: 577
6: 577
7: 590
8: 591
9: 599
10: 650
11: 783
12: 782
13: 551
14: 428
15: 405
16: 345
17: 396
18: 416
19: 465
20: 1030

I'm honestly not sure if this is a hardware or software problem.

jremington

#4
Jun 01, 2014, 04:54 am Last Edit: Jun 01, 2014, 05:08 am by jremington Reason: 1
The bumps at 1 and 20 suggest that it is a software problem. The rest of the distribution looks pretty uniform. You just haven't yet found the appropriate algorithm for using the hardware to select truly random numbers. You might take a more careful look at what the author of this page http://holdenc.altervista.org/avalanche/ did to select streams of random 0s and 1s, which would be easy to map into any range you like.

As a first try, I suggested using the % operator, but many people recommend against using this method to reduce the range, as it can introduce bias. See this discussion http://stackoverflow.com/questions/2999075/generate-a-random-number-within-range/2999130#2999130

Edit: I note that in the original posted code, you do not initialize the two distribution arrays to zero. This is not a good idea, as some compilers (I haven't checked what the Arduino version of gcc does) just allocate memory space for an uninitialized variable, and that space almost certainly has some numbers already in it.



acegard

I certainly am going to try that, but I want to see first what the output looks like on the scope, and I can't get back into the university lab until it reopens on Monday. In the interim, do you know what might be causing the software issue?

jremington

The stackoverflow discussion I linked above suggests that you could try using the following function to limit the range of the hardware random number generator. You will need to modify it slightly (so as not to use the built in RAND_MAX and rand() function), but the general approach should work.
Code: [Select]
int rand_lim(int limit) {
/*
  return a random number between 0 and limit inclusive.
*/

    int divisor = RAND_MAX/(limit+1);
    int retval;

    do {
        retval = rand() / divisor;
    } while (retval > limit);

    return retval;
}


I used it to generate 100,000 integers between 0 and 19 and got the following uniform distribution
Quote

0 4976
1 4921
2 4980
3 5104
4 4862
5 4881
6 4970
7 5003
8 5064
9 5018
10 4930
11 5037
12 5205
13 5046
14 4906
15 4902
16 5010
17 5192
18 5005
19 4988

acegard

Interesting, I don't know why I didn't see the stackoverflow link earlier, my bad.
What does RAND_MAX do? If I'm going to replace it, I'll need to know haha. Also, would you suggest replacing rand() with my noise generator?

Also, initializing the arrays to zero didn't help. Of course, that's probably because I reset the sketch between each run, and (correct me if I'm wrong) the memory is initialized to zero.

jremington

RAND_MAX is the maximum number that the built in C/C++ function rand() outputs.

It may be that the Arduino gcc compiler initializes variable memory to zero, but many others don't. You should get into the habit of initializing ALL variables before using them.

acegard

You're right, I should, thank you. Should I replace rand() with the noise generator?

Grumpy_Mike

Don't forget that the noise will have a higher frequency in the noise that you can sample at. Therefor there will be a degree of avraging going on which will limit the readings at the top and bottom end. You need to add a low pass filter to your noise source to take it below the niquis rate.

acegard

If that's happening, why do they seem to be grouping at the top and bottom ends?

Coding Badly


An observation: most (maybe all) of the circuits I have seen like that use 12V or higher (including the one in your link which calls for 13V).  I vaguely recall one author claiming that 12V is the minimum necessary for the circuit to work correctly.  But, that could easily be because of the transistors he was using.

If you want continued help with the software I suggest you post the current version of your sketch.

Coding Badly


This may give you some troubleshooting ideas...
http://forum.arduino.cc/index.php?topic=161682.0

acegard

Thank you, Coding Badly, that thread will be very helpful. I'll post my sketch tomorrow when I get back to it. This is giving me a ton of ammunition with which to improve my design!

Go Up