1: use strict;
2: # This module provides a random numeric sequence generator and a simple
3: # smoothing filter for use in simulation. You can specify ranges and periods.
4: # I use it to simulate the output of
5: # physical sensors for data acquisition testing.
6: #
7: # As HyperZonk said, don't use this for crypto (you know
8: # better than that, right?)
9: #
10: package RandomSequence;
11: require Exporter;
12: @RandomSequence::ISA = 'Exporter';
13: @RandomSequence::EXPORT_OK = qw(randomSequenceGenerator filterGenerator);
14: %RandomSequence::EXPORT_TAGS = (
15: 'ALL' => [ qw(randomSequenceGenerator filterGenerator) ]
16: );
17:
18: # Generate a closure that will generate a value that will vary
19: # randomly per the specifications you give it while staying within
20: # a given range.
21: #
22: # my $sub = randomSequenceGenerator( $min, $max, $maxSamples, $maxSlope, $minSamples );
23: # my $sample = &$sub; # repeat
24: #
25: # $min and $max are the range of the output values.
26: # $maxSamples is how many samples a continuous run of the same slope will go
27: # $maxSlope is how much (absolute) the value can change sample to sample
28: # $minSamples (optional, default=1) is how short the shortest run will be
29: #
30: sub randomSequenceGenerator
31: {
32: my ($min, $max, $maxSamples, $maxSlope, $minSamples) = @_;
33: $minSamples ||= 1;
34: my ($lastOutput, $samplesLeft, $slope) = (($max+$min)/2, 0, 0);
35: return sub
36: {
37: if ($samplesLeft-- <= 0) # re-calculate slope and samplesLeft
38: {
39: $samplesLeft = int( rand( $maxSamples - $minSamples ) + $minSamples );
40: $slope = rand( $maxSlope * 2 ) - $maxSlope;
41: }
42: $slope = -$slope if ($lastOutput + $slope > $max)
43: or ($lastOutput + $slope < $min);
44: return $lastOutput += $slope;
45: }
46: }
47:
48: # generate a simple exponential filter as a closure
49: # my $sub = filterGenerator( $coefficient, $firstSample );
50: # my $filtered = &$sub( $value ); # repeat
51: # $coefficient is how many samples to average over
52: # $firstSample (optional, default=first sample) is the preset output
53: sub filterGenerator
54: {
55: my $coefficient = shift; # time period in samples, larger=slower
56: my $firstSample = shift;
57: my $lastValue = $firstSample;
58: return sub
59: {
60: my $newSample = shift;
61: return $lastValue = $newSample if !defined($lastValue);
62: $lastValue += $newSample/$coefficient;
63: $lastValue *= $coefficient/($coefficient+1);
64: return $lastValue;
65: }
66: }
67:
68: 1;
69: __END__
70: # test program:
71: use RandomSequence ':ALL';
72: my $sequenceGenerator = randomSequenceGenerator(0, 360.0, 30, 1.0);
73: my $filter = filterGenerator(8);
74: for my $i (1..10000)
75: {
76: my $sample = &$sequenceGenerator;
77: my $filtered = $filter->($sample);
78: print "$sample,$filtered\n";
79: }