Normalizing signed values results in values outside 0 and 1 - c#

I am trying to create some kind of heatmap to visualize data I get from a sensor. So far this works but it looked odd. So I figured out that my code which normalizes the values (ranging from at least -100 to 100) must be invalid.
I often got exceptions because the factor (the normalized value) I use to calculate the color (byte) was negative or greater than 1.
So I modified my code and basically shifted min, max and the value by Math.Abs(min) to ensure that my values are positive. The negative issue is fixed but I sometimes end up with values like 1.02xxxx which must not happen. EDIT: Still getting negative values in some cases...
This is my code so far:
double min = data.Min();
double max = data.Max();
double avg = data.Average();
double v = max - min;
byte a, b, g, r;
// Set minimum and maximum
Dispatcher.InvokeAsync(() =>
{
Minimum = min;
Maximum = max;
Average = avg;
});
double shift = 0;
if (min < 0) // If minimum is negative, shift it to 0 and everything else positively by Abs(min)
{
max += shift = Math.Abs(min);
min += shift;
}
int skipFactor = (int)(1 / _pointResolution);
for (int i = 0; i < data.Length; i += skipFactor)
{
// If min == 0 then shift data
double value = Math.Abs(min) < 0.0001d ? (data[i] + shift) / max : (data[i] - min) / v;
...
}
How can I fix this without adding rounding or "edge-cases"? I assume the Math.Abs() combined with adding operations results in slightly off values... But there must be a solution to that, right?

Related

Dividing and spacing

I need to divide a variable distance in a very specific way. The spacing for the divisions must be 40 units minimum, and 80 units maximum.
I've tried several different various of this code but I am struggling to wrap my head around how to include the min/max variable in my division.
double totaldist = X;
double division = totaldist / 80;
double roundup = Math.Ceiling(division);
double space = totaldist / roundup;
double increment = 0;
while (increment < totaldist)
{
increment = increment + space;
}
The attached code is obviously short of what I want to accomplish, I'm not sure how to bridge the gap. Thank you
So all you have to do is loop over all the possible divisors and pick the best one. The simplest way to accomplish this is as follows:
public static int remainder(int totalDist)
{
double minRemainder = (totalDist % 40) / 40;
int bestDivision = 40;
for (var i = 40; i <= 80; i++)
{
double cRemainder = (totalDist % i) / i;
if (totalDist % i == 0) return i;
else if (cRemainder < minRemainder) { minRemainder = cRemainder; bestDivision = i; }
}
return bestDivision;
}
This will always return the closest result. Even if there is no real solution, it will still provide an approximate answer as a fallback.
I'd test every divisor for mod 0 (no remainder)
int d = 420;
int s = 40;
for(; s <= 80; s++){
if(d%s==0)
break;
}
if(s==81)
Console.Write("There is no suitable divisor");
else
Console.Write($"{d} divides into {s} segments of {d/s} with no remainder");
If you want to minimise the segment length (greater number of segments) start at 80 and work towards 40 in the loop instead - set your d to 480, start at 80 and you should get "80 segments of length 6" rather than "40 segments of length 12"
You can even get cute with your loop and have no body:
for(; s <= 80 && d%s > 0; s++){ }
But it's not quite so readable/self explanatory

How to get Bass, Mid, Treble data from FFT

I'm new to this whole audio processing area and I'm wondering how to extract Bass, Mid and treble from an FFT output. I'm currently using this to get the data: https://stackoverflow.com/a/20414331/2714577 which uses Naudio.
But I'm using a fftlength of 1024 (require speed). I'm trying to get these 3 sections in a format such as 0-255 for colour purposes.
I currently have this:
double[] data = new double[512];
void FftCalculated(object sender, FftEventArgs e)
{
for (int j = 0; j < e.Result.Length / 2; j++)
{
double magnitude = Math.Sqrt(e.Result[j].X * e.Result[j].X + e.Result[j].Y * e.Result[j].Y);
double dbValue = 20 * Math.Log10(magnitude);
data[j] = dbValue;
}
double d = 0;
for (int i = 20; i < 89; i++)
{
d += data[i];
}
double m = 0;
for (int i = 150; i < 255; i++)
{
m += data[i];
}
double t = 0;
for (int i = 300; i < 512; i++)
{
t += data[i];
}
Debug.Message(""+d+" |||| "+m+" |||| "+t);
}
Which returns:
Is this right? How do I get this data to something more usable?
The coefficients you get out of a Fourier transform can be positive or negative - what you're interested in is the magnitude (ie. the amount of each frequency), so you will want to take the absolute value in your summation.
Also, I would recommend normalizing - at the end of your summation do this:
double total = data.Sum(x => Math.Abs(x));
d /= total;
m /= total;
t /= total;
This way, your numbers will be confined to the range [0-1) and you will get the same information out if the sound is quieter (unless you don't want that). Actually, the range will be somewhat less than that because each of your summations covers a smaller individual range. So you may want to scale them by the largest one of them:
double largest = Math.Max(d, m, t);
d /= largest;
m /= largest;
t /= largest;
Now the range of each should be between 0 and 1. You can then multiply by 255 or 256 and truncate the decimal if you like.
The downside of the last step is if the values are all zero (because the inputs were all zero) then you will divide by zero. Oops! At this point you need to decide exactly what you want.. If you don't do this scaling, then a sound which is entirely treble (according to your breakdown above) will have (0,0,1) for (d,m,t). But a sound which is an even mixture of the three will be (0.3333, 0.3333, 0.3333) for (d,m,t). And a sound which is completely quiet would be (0,0,0). If that's not what you want, well then you need to define exactly what you want before I could help you any further.
Your dbValue is already a very good number, maesuring the level in decibel relative to 1.0 which becomes 0.0 dB
You should average instead of sum the individual (dB-Values at various) frequencies.
Then map the dB Range of about -80db .. 0.0dB to your color range.
Also note: Speach and music tend to have an average pink noise spectrum. This means that low frequencies tend to have higher dB than high frequencies.
You should compensate for this effect (probably before averaging the frequencies) to get a "better" display.

making double variables lower than 1

in my ASP.NET project i did a survey page that uses Application to save the votes. I have a problem with the making of the percentages amount. I've tried many things. here is the problematic part of my code:
double x = (count / sum) ;
double f = (count1 / sum) ;
double g = (count2 / sum) ;
double h = (count3 / sum) ;
if (sum > 0)
{
a = (int)x * 100;
b = (int)f * 100;
c = (int)g * 100;
d = (int)h * 100;
}
I used breakpoints and figured out that the problem was in the double variables: the (count/sum) equals 0 anyway.
I'm assuming count and sum are integer types.
The result of division of 2 integers is a truncated integer.
You need to cast one side of the division to a double, then the result will be double
So
((double)count)/sum
What are the datatypes of count, count[1-3] and sum? If they are integral types, then integer division is performed. This
int x = 100 ;
int y = 300 ;
double z = x / y ;
yields the value 0.0 for z.
Try something like
double h = (double) ( count3 / sum ) ;
You might also want to move your test for sum > 0 up: as coded, if sum is zero, you'll throw a DivideByZeroException before you get to your test, thus rendering your test moot.
Your count and sum variables are probably integers. Cast one of them to double:
double x = count / (double)sum;
UPDATE:
Actually, if you want the percentage as an integer, you can skip the doubles altogether:
int a = 100 * count / sum;

Oscillate or "ping pong" between two values?

I have a path that is evaluate at time 't' and returns an orientation and position based on the path type.
The value for time is affected by the path type:
switch (type)
{
case PathType.Closed:
time = ToolBox.Wrap(time, StartTime, EndTime);
break; // Wrap time around the path time to loop
case PathType.Open:
time = ToolBox.Min(time, EndTime);
break; // Clamp the time value to the max path time range
case PathType.Oscillating:
break;
}
The missing link is oscillating.
My question is what is a good, efficient way for oscillating between two values?
For example (2, 7). If time reaches 7 it reverses and decrements towards to 2 and once it reaches 2 it reverses and increases towards 7.
The algorithm should know whether to increase/decrease the value based on the original value so if the value is 9 it knows the answer is 7 - (Abs(7 - 9). If the value is 14 the value has wrapped around so it will result in an increase of 1.
Higher values will also increase or decrease the value depending on the number of times it wraps around the original range.
I hope that makes sense as I'm finding it difficult to explain.
EDIT:
Doesn't oscillate with floating point values:
for (float i = 0; i < 100; i += 0.1f)
{
Console.WriteLine("{0} {1}", i, Oscillate(2.5f, 7.5f, i));
}
private float Oscillate(float min, float max, float value)
{
float range = max - min;
float multiple = value / range;
bool ascending = multiple % 2 == 0;
float modulus = value % range;
return ascending ? modulus + min : max - modulus;
}
Here is what I came up with:
public static int Oscillate(int input, int min, int max)
{
int range = max - min ;
return min + Math.Abs(((input + range) % (range * 2)) - range);
}
I'm assuming input will be a counter starting at 0.
Ideally, you should be abstracting this functionality into some kind of a class and not be concerned about how the implementation actually works when you're using it. Here's an initial take on what that would look like in C++ (my C# is a little rusty). I think you can work it into C# with only little difficulty.
class oscillator
{
private:
float min;
float max;
static float mod(float num, float div)
{
float ratio = num / div;
return div * (ratio - std::floor(ratio));
}
public:
oscillator(float a, float b)
: min(a < b ? a : b), max(a > b ? a : b) {}
float range() ( return max-min; }
float cycle_length() { return 2*range(); }
float normalize(float val)
{
float state = mod(val-min, cycle_length());
if (state > range())
state = cycle_length()-state;
return state + min;
}
};
This will oscillate your numbers between 2 & 7, in this example, time is an int:
bool isIncreasing = time <= 7;
for (int i = 0; i < 20; i++) //some random loop
{
time = time + (isIncreasing ? 1 : -1);
if (time >= 7 || time <= 2) isIncreasing = !isIncreasing;
}
New answer to account for float values:
// Note: Increase FACTOR depending on how many decimal places of accuracy you need.
private const float FACTOR = 10;
public void Test()
{
for (float i = 0; i < 1000; i += 0.1F)
{
Console.WriteLine("{0} {1}", i, Oscillate(2.5F, 7.5F, i));
}
}
private float Oscillate(float min, float max, float time)
{
return (float)(Oscillate_Aux(Upscale(min), Upscale(max), Upscale(time))) / FACTOR;
}
private int Upscale(float value)
{
return (int)(value * FACTOR);
}
private int Oscillate_Aux(int min, int max, int time)
{
int range = max - min;
int multiple = time / range;
bool ascending = multiple % 2 == 0;
int modulus = time % range;
return ascending ? modulus + min : max - modulus;
}
What you're describing sounds a lot like periodic linear interpolation between two values. Consider using XNA's MathHelper.Lerp function as the basis for your oscillation.
Note that it uses a percentage to control the oscillation as its third parameter. You'll have to figure out how to translate your time t value into a percentile, but you could start with ex. sin(t) to see how things work.
If you're reluctant to import XNA into your project, the core equation is very simple:
value1 + (value2 - value1) * amount
Edit: If your question, at its heart, really is "What is a good, efficient way for oscillating between two values?", then Math.Sin(t) (or Cos) can provide you with regular oscillation between 0 and 1.

C# Formula to distribute numbers

I'm looking for a formula that can spread out numbers in a linear format based on a minimum number, max number and amount of numbers (or dots) between. The catch is, the closer you get to the max, the more numbers should be there.
An example (number will vary and will be about 100 times larger)
Min = 0
Max = 16
AmountOfNumbersToSpread = 6
0 1 2 3 4 5 6 7 8 9 A B C D E F
1 2 3 4 5 6
Thanks for the help in advance.
Based on the answer of Tal Pressman, you can write a distribution function like this:
IEnumerable<double> Spread(int min, int max, int count, Func<double, double> distribution)
{
double start = min;
double scale = max - min;
foreach (double offset in Redistribute(count, distribution))
yield return start + offset * scale;
}
IEnumerable<double> Redistribute(int count, Func<double, double> distribution)
{
double step = 1.0 / (count - 1);
for (int i = 0; i < count; i++)
yield return distribution(i * step);
}
You can use any kind of distribution function which maps [0;1] to [0;1] this way. Examples:
quadratic
Spread(0, 16, 6, x => 1-(1-x)*(1-x))
Output: 0 5.76 10.24 13.44 15.36 16
sine
Spread(0, 16, 6, x => Math.Sin(x * Math.PI / 2))
Output: 0 4.94427190999916 9.40456403667957 12.9442719099992 15.2169042607225 16
Basically, you should have something that looks like:
Generate a random number between 0 and 1.
Implement your desired distribution function (a 1:1 function from [0,1]->[0,1]).
Scale the result of the distribution function to match your desired range.
The exact function used for the second point is determined according to how exactly you want the numbers to be distributed, but according to your requirement, you'll want a function that has more values close to 1 than 0. For example, a sin or cos function.
Tried this on paper and it worked:
given MIN, MAX, AMOUNT:
Length = MAX - MIN
"mark" MIN and MAX
Length--, AMOUNT--
Current = MIN
While AMOUNT > 1
Space = Ceil(Length * Amount / (MAX - MIN))
Current += Space
"mark" Current
By "mark" I mean select that number, or whatever you need to do with it.
Close answer, not quite though, needs to work for larger numbers.
List<int> lstMin = new List<int>();
int Min = 1;
int Max = 1500;
int Length = Max - Min;
int Current = Min;
int ConnectedClient = 7;
double Space;
while(ConnectedClient > 0)
{
Space = Math.Ceiling((double)(Length * ConnectedClient / (Max - Min)));
Current += (int)Space;
ConnectedClient--;
Length--;
lstMin.Add(Current);
}

Categories