Converting logarithmic analog data to linear data

Greetings,

I guess this is more of a math question than anything else but I'm having a hard time figuring it out and I'm sure you guys can help.

I have a 10K ohm potentiometer plugged into an analog input. When I turn the pot from min to max, the value first increases very slowly and then very fast near the end. So I'm guessing it probably is logarithmic. My question is how can I convert this logarithmic-looking data back into linear data ?

TIA.

By working out the response of the pot, deriving the inverse function... If it really is logarithmic and not pseudo-logarithmic a call to pow() might do the job - but be aware that its unlikely to be truly log.

I do not need it to be extra precise. I just want it to respond in a somewhat linear fashion. You suggest to "derive the inverse function". Do you have an idea of how to do that ?

You seem to be suggesting that I exponentiate the value I receive ? Shouldn't it be the other way around (sort of like extracting the root of the value). Sorry for this poor display of math skills... :-[

Could you just use a linear pot?

It's actually not for me but for my 4 classes of students who were erroneously sold audio-type potentiometers instead of linear ones (at least that's my guess). I would have liked to suggest them an easy software-based fix. I'm sure this can be done easily by someone knowledgeable in math but it's just not my case...

You could do a lookup table type conversion....

You could do a lookup table type conversion....

Yes, I could do that and as a last resort this is probably what I'm going to do but I would have liked to know how to "linearize" logarithmic data...

Do you have an idea of how to do that ?

http://lmgtfy.com/?q=log+to+linear+conversion

I am certain you can find an answer in there, somewhere...

:slight_smile:

Not every question can be easily answered by lmgtfy.com. I'm sure you do realize that. If it was the case, there wouldn't be much of a point in setting up a forum...

What I am looking for is perhaps a formula I could start with to try to figure this thing out...

Alright; this is from those links - and granted, its the reverse of what you want to do (and I am no maths whiz, so I can't tell you off the top of my head how to reverse it) - maybe there is some way to go the opposite direction using it as a base?

:slight_smile:

Last post here has an interesting hardware method to "fake it":

http://www.electronicspoint.com/convert-log-pots-linear-pots-t70898.html

Not perfect, though, from what I am reading...

:slight_smile:

This does look like a valid starting point. I had not seen this one so far. I'll try it as soon as I get the chance and report back. Thanks.

Last post I am making on this subject in this thread...

It seems like (reading google) that going from linear to log is something that is done a lot, and is fairly "easy"; going the other way, though, is either difficult, or not something that people tend to do; not much in the way of references.

There are a lot of references to something called "Linear-Log Modelling", but the math is way above my pay scale, so I can't tell if it would help, nor if it did, which method is a good method and which are cr*p.

Something I do wonder, though, is if you can fake it in software with some math and some other code like that hardware fakery done with resistors. Perhaps if you had a program that ran, and built up a look up table (LUT) to help flatten the curve; you would run it, and it would have you rotate the pot between the endpoints, have you press a button when you are at the middle vs the ends or something, maybe quarter-way points, then it would synthesize a table of your size choice (256, 512, 1024 elements) based on interpolation of the mid-point flattening values...

:slight_smile:

The look up table idea was suggested earlier in this post. I simply would have prefered a "nicer" solution. But hey, that may well be what I end up doing. Maybe I was naive in thinking there was an easy software/math fix for that. The math involved is probably above my payscale also...

You seem to be suggesting that I exponentiate the value I receive ? Shouldn't it be the other way around (sort of like extracting the root of the value). Sorry for this poor display of math skills...

I belive you are right. It's not pow() you need, but rather log(). Probably best to convert (cast) the reading to a float too.

Something like

float potLogType = 10; // wild guess base-10 logarithmic
int scale = 330; // experiment...

int reading = analogRead(pin);

int linearized = (int)((log((float)reading) / log(potLogType)) * scale);

Completely untested, and I don't have much experience with audio and logarithmic potmeters.

As for the "scale" variable, my guess is, since the value returned from analgRead is from 0 - 1023, and log(1000)/log(10) = 3, it would need to be somewhere around 330. For a base-10 logarithmic that is, and if you want the same range value. But I don't know if that is the case.

Unless it is a cheap "fake" logarithmic as described in this wikipedia article Potentiometer - Wikipedia and also in one of the links above by cr0sh. Which btw also had an interesting hardware way of faking it back:

Set the log pot at its physical midpoint. Take resistance measurements
from the wiper to the endpoints of the pot, call them R1 and R2. Get a
resistor with a value approximately equal to the absolute value of
R1R2/(R2-R1). Connect this resistor between the wiper and the end of
the pot where you measured the higher resistance, you now have a pot
with a resistance centered at its physical midpoint.

Yes, probably crappy. But what's the harm? If the guy wants to take
log pots and try to "convert" them, let him.

Great! That's the kind of math I was looking for. :slight_smile: I'll give it a try with various settings and report back.

Please do (report back) :slight_smile:

I did make some assumptions before going to sleep last night though; for one that the potentiometer was connected as a simple voltage divider and fed to the analog input directly. And the other was that the inputs were useful at low values.. which I now think they are not, since the values change slowly at the beginning.

However it turns out it isn't as straight-forward as I first thought :stuck_out_tongue: For one, any log(x) for x between 0 and 1 will be a negative number.. which can be "solved" by adding a little offset, making sure the reading is never below 1 (aka a small-ish series resistor from the pot to GND). However, because of the slow changing values at the beginning, the potmeter's physical range would be limited to about the last 1/2 of its movement (probably dependent on the logarithmic function it has).

Well.. in my theoretical little tests, that is. Please note I havent tested this in practice yet!

Since I had some other test code handy, I made some small tests in processing:
Red line: logarithmic potentiometer
Blue line: "linearized" values

First without any offsets, as you can see the blue line goes below zero...

An offset of 1, to make the log() positive (equivalent of a series resistor of about 1/1023rd of the total resistance of the pot, I would think):

And a somewhat bigger offset (I used 10 here, = 10/1023rd total potmeter resistance), seems to make it somewhat more linear, and loosing some resolution. So you'd have to remap the Y axis range to useful values:

But it would seem, from the X axis, you will loose at least 1/2 of the physical range of the log pots. If they are really logarithmic that is and not the fake log type. I'm not sure how those would be.

Btw, here is my processing code, just in case.

// Testing logarithmic to linear conversion of potmeter values

// 2010.04.24 raron


int[] logarithmic = new int[1024];  // the "logarithmic" potmeter values
int[] linearized  = new int[1024];  // attempted linearizing the exponential values



void setup()
{
  size(400,400);
  int xOrigo = 100;
  int yOrigo = height-100;
  int xsize = 256;
  int ysize = 256;
  
  // make coordinate system
  line(xOrigo, yOrigo, xOrigo+xsize, yOrigo);
  line(xOrigo, yOrigo, xOrigo, yOrigo-ysize);

  // tick marks
  for(int x=0; x<xsize; x+=10)
  {
    int tick = 10;
    if(x%50 == 0) tick = 20;
    line(xOrigo+x,yOrigo,xOrigo+x,yOrigo+tick);
  }
  for(int y=0; y<ysize; y+=10)
  {
    int tick = 10;
    if(y%50 == 0) tick = 20;
    line(xOrigo,yOrigo-y,xOrigo-tick,yOrigo-y);
  }
  


  // test
  for (int i=0; i<1024; i++)
  {
    logarithmic[i] = [glow]1 +[/glow] (int) (1022.0 * pow(i/1023.0,10));              // added a small offset +1, to not get negative logarithm on the next line
    linearized[i]  = (int) (log((float)(logarithmic[i]))/log(10.0)*[glow]340[/glow]); // Important that the scale-up of 340 is inside the parentesis!
  }
  
  
  // Draw functions
  for (int b=1; b<1024; b++)
  {
    // Draw logarithmic curve red
    stroke(255,0,0);
    line(xOrigo+b/4-1,yOrigo-(logarithmic[b-1]/4),xOrigo+b/4,yOrigo-(logarithmic[b]/4));

    // Draw "linearized" curve blue
    stroke(0,0,255);
    line(xOrigo+b/4-1,yOrigo-(linearized[b-1]/4),xOrigo+b/4,yOrigo-(linearized[b]/4));
  }
}



void draw() 
{
}

Note that the scaleup-variable (I used a 340 directly here) needs to be inside the parenthesis, otherwise it wont work much at all.

Maybe time for someone actually knowledgeable in this to answer instead :slight_smile: Maybe the results could be better if combined with the method cr0sh linked to, for example?

raron,

As you predicted, your method works fine for the second half of the potentiometer. However, the first half reports either 0 or 1 which is basically unusable. I'm guessing Arduino does not have enough resolution to report something useful for the first half. The solution would have to be via hardware, I suppose.

As for the second half, I implemented your suggestion in ActionScript code and it works pretty good :

var linear = Math.log(input) / Math.log(1000) * 1023;

This code returns a value between 0 and close to 1023. As you can see, the potLogType value had to be increased quite a bit.


I just realized that if I plug the potentiometer differently, I get much better readings...

I had my potentiometer plugged in according to the tutorial on the Arduino website (http://www.arduino.cc/en/Tutorial/Potentiometer). However, if I send the current to the 3rd potentiometer pin and pickup the signal from the middle pin I get an almost linear result. Anyway, linear enough for what I want to do.

I guess I really do not understand how potentiometers work...