Archive for the Linux Category

More useful curve fitting – temperature compensation

Posted in Linux, math is fun on February 1, 2011 by makingthingswork

One of my current projects involves making a USB bathroom scale (I’ll post details later).

So as part of the process, I’ve been collecting the data and graphing it to make sure it gives a stable reading. It’s currently sitting in the garage with a 30lb bag of concrete on it, and the readings are not stable — they vary with temperature. Good to know, but how to fix it?

Here’s a day’s worth of data:

Since the weight is constant, the only variation (i’m assuming) is due to the fluctuations in temperature. It looks like a pretty close correlation. OK so far so good.

I exported the datapoints into a text file, looking like this:

258 158
258 157.269
258 157
...

Then loaded up gnuplot.

First I plotted them against each other, to see if there was a strong linear correlation

gnuplot> plot './tmpwght.txt' using 1:2 with points

It seems pretty linear.. (see the combined graph below). The temperature sensor in the attiny85 has only 1C increments, and the datapoints seem to land on ‘even’ numbers more often than not, creating groups of vertical points on the graph.

I then need to find the slope of the line that best fits the curve. I’m expecting a straight line of best fit, eg y=mx+b style, so i feed that into gnuplot’s “FIT” routines


gnuplot> f(x)=a*x+b
gnuplot> fit f(x) './tmpwght.txt' via a,b
....
Final set of parameters
a = 1.20761
b = -152.923

so it appears theres about a 1.2:1 correlation between temperature and weight reading (‘a’ from the curve fit). Let’s check the graph

gnuplot> f(x)=a*x+b
gnuplot> a=1.20761
gnuplot> b=-152.923
gnuplot> plot './tmpwght.txt' using 1:2 with points, f(x)

The fit looks good enough to try out. Let’s see if it does what we want.

Now, to adjust the weight reading to compensate for temperature fluctuations.

Since the ratio is based on the raw reading of temperature, we need to multiply the 2 together before we do any shifting: 1.20761 * temperature. Then, we need to make the temperature reading move the opposite way, so when the temp goes up, the number goes down. This is done by subtracting it from another number. I picked 325. This should give us a value that exactly contradicts the effect the temperature had on the weight measurement.

In equation form:
offset = 325 - (1.20761 * temperature)

Then we add that offset to the weight measurement, and we should get a straight line. Let’s see:

Looks pretty straight to me. The parts that aren’t straight can probably be attributed to poor temperature sensor data, which i will try and fix, either by adding a better temp sensor, or polling more frequently and averaging results.

I like math

Advertisements

curve fitting and graphing.

Posted in Linux, solar on January 16, 2011 by makingthingswork

Here’s how i do the curve fits and resulting graphing.

I use the free program ‘gnuplot’ which is pretty powerful, can do curve-fits, and graph everything.

Steps:

1: measure your curve and get some points. I created a file called ‘measured.dat’:

0  .0044
.1 .0123
.2 .0218
.3 .0382
.4 .0636
.5 .098
.6 .157
.7 .221
.8 .300
.9 .404
1 .495

2)  fit the curve. I use a cubic parabola: Ax3+Bx2+Cx+D

shell$ gnuplot
...
gnuplot> f(x)=a*x*x*x+b*x*x+c*x+d
gnuplot> fit f(x) './measured.dat' via a,b,c,d
...
Final set of parameters            Asymptotic Standard Error
=======================            ==========================a               = 0.142852         +/- 0.07749      (54.25%)
b               = 0.385536         +/- 0.1181       (30.63%)
c               = -0.035066        +/- 0.0493       (140.6%)
d               = 0.00833846       +/- 0.005414     (64.93%)
So now you have your coefficients: a,b,c,d

3) plug them into a graphing file. here’s mine:

# let's call this file "fit.gnuplot"
# you can keep the body of the file the same, and just
# modify a,b,c,d each time you run a new curve
a=0.142852
b=0.385536
c=-0.035066
d=0.00833846

#Cubic Parabola
#C=ax3 + bx2 + cx + d
B(x)=a*x**3+b*x**2+c*x+d
# this trick limits the curve to x=0 through x=1.05
C(x)= (x>0 && x<1.05) ? B(x) : 1/0

# tangent slope, derivative of cantilever
# D = B'=3ax2 + 2bx + c
D(x)=3*a*x**2+2*b*x+c

#focus (reflected) line slope (ts = tangent slope)
# y = 1/(TAN(2*(ATAN(1/ts))))
# incidence angle (0 = directly above,
#                  + means low in the sky
#                  - means high in the sky

# radians = degrees x PI / 180
# precalculate one, just showing the formula, not used
ideg= 0;
irad = ideg*pi/180

## slope of reflected ray
F(ts)=1/tan(2*atan(1/ts)-irad)
# focus y-int
G(tp)=C(tp)-(tp*F(D(tp)))

## where will the graph be drawn?
set xrange [-.5:1.2]
set yrange [-.1:1.2]

## setup graphing
set multiplot
# offset (first ray will be ioff+istep)
ioff=-3
istep=1.5
# each ray will offset on x axis by this much
xstep=.1
# arrow number
ar=0
# tangent point
tp=0
ideg=ioff
irad = ideg*pi/180

## how far to the left of the x-axis to extend arrows?
xp = -.35

## increment various counters
ar=ar+1; tp=tp+xstep

## downward ray
set arrow ar from tp,5 to tp,C(tp) lt rgb 'black'

## normal reflected ray
ideg=ideg+istep; irad = ideg*pi/180; ar=ar+1;
set arrow ar from tp,C(tp) to xp,F(D(tp))*xp+G(tp) lt rgb 'red'

## plus 1 step
ideg=ideg+istep; irad = ideg*pi/180; ar=ar+1;
set arrow ar from tp,C(tp) to xp,F(D(tp))*xp+G(tp) lt rgb 'black'

## plus another step
ideg=ideg+istep; irad = ideg*pi/180; ar=ar+1;
set arrow ar from tp,C(tp) to xp,F(D(tp))*xp+G(tp) lt rgb 'green'

## repeat this stanza as many times as you need
## next set
ideg=ioff; irad = ideg*pi/180
ar=ar+1; tp=tp+xstep
set arrow ar from tp,5 to tp,C(tp)  lt rgb 'black'   
ideg=ideg+istep; irad = ideg*pi/180; ar=ar+1;
set arrow ar from tp,C(tp) to xp,F(D(tp))*xp+G(tp) lt rgb 'red'
ideg=ideg+istep; irad = ideg*pi/180; ar=ar+1;
set arrow ar from tp,C(tp) to xp,F(D(tp))*xp+G(tp) lt rgb 'black'
ideg=ideg+istep; irad = ideg*pi/180; ar=ar+1;
set arrow ar from tp,C(tp) to xp,F(D(tp))*xp+G(tp) lt rgb 'green'

set label "  red: 1.5 degrees high\nblack: perfectly aimed\ngreen: 1.5 degrees low" at -.4,.2

set key at 1,.05
plot C(x) lt 2 title 'best fit line for measured sheet'
set key at 1,0
plot 'measured.dat' with points title 'measured points'

unset multiplot
pause -1

Now you have a graph definition that will do all the fancy arrows and such.

4) Show the graph:

shell$ gnuplot fit.gnuplot

Here’s what it makes:

You’ll need to duplicate the ‘repeat this stanza’ section once more for each point without a reflection on it.

Native resolution on my IN72

Posted in kevo, Linux, solved on January 21, 2008 by makingthingswork

My 4805 died a bit ago – threw a segment on the colorwheel, and that was it.

I found an IN72 on craigslist, and the price was right so I picked it up.

When I connected the DVI out on my mythtv box (aka the ‘kevo’), I got no love. No signal, nothing. This was strange since the 4805 worked fine, and they are basically the same.

I restarted xorg, and it came up ok, but it was at 720×480, not 848×480. I want to run native resolution if I can, so I spent the rest of the afternoon fighting with it.

After much troubleshooting the problems were:

the IN72 has a native resoltution of ~848×480, but it seems to report that it has a native resolution of 720×480 for some reason. Why they decided to do this, I have no idea.

Fortunately, the nvidia driver had an option to ignore the ‘native resolution’ setting.

I configured that, then found another problem. It couldn’t figure out the timing for the new mode for some reason. Internet to the rescue.

Here’s the nvidia config i ended up using:

Section "Device"
Identifier "nVidia Corporation NV34 [GeForce FX 5200]"
Driver "nvidia"
# some stuff omitted, not relevant
Option "ExactModeTimingsDVI" "true"
Option "ModeValidation" "NoDFPNativeResolutionCheck"
EndSection

And the modeline:

Section "Monitor"
Identifier "InFocus"
Modeline "848x480@60" 37.293 848 936 984 1104 480 508 509 563
EndSection

Some helpful commands:

## start Xwindows/xorg with verbose logging (logs to /var/log/Xorg.0.log):
startx -- -logverbose 6

Related error messages:
(WW) NVIDIA(0): Mode is rejected: Mode (848 x 480) is too large for DFP
(WW) NVIDIA(0): Native Resolution (Max: 720 x 480).

(WW) NVIDIA(0): Unable to use mode "848x480@60" for IFS InFocusIN72-D (DFP-0);
(WW) NVIDIA(0): cannot compute backend DFP timings (mode is larger than
(WW) NVIDIA(0): native backend 720 x 480).