I am plotting a curve in gnuplot. I would like to call out a couple of specific points on the curve.
This is what the curve looks like:
Shown below is what I'm hoping to get (but I had to draw in the points and lines to the axes with GIMP). I'd like to call out two specific points on the curve. The first, where Vgs (x-axis) equals -0.5. The second, where Id (y-axis) equals 2.5. If possible, I'd like to also have a dashed line over to the axis to aid in reading the values.
I found a reference that said to try plotting a circle using set object circle and the coordinates, but I must not have it right, because it complains about extra parameters. Browsing the manual is so far unsuccessful. I'm not even sure what term to search for.
Is there an easy way to call out a couple of points on the curve that I've drawn and have it look similar to the screenshot below?
Here are the commands I used to plot the curve:
set xlabel "Vgs (Volts)"
set ylabel "Id (mA)"
set grid
Idss=5
Vgs_off=-1
Id(Vgs) = Idss * (1 - (Vgs / Vgs_off) ) ** 2
plot [Vgs=Vgs_off:0][0:Idss + 1] Id(Vgs)
It's the characteristic curve for a Fairchild J112 JFET if anyone is curious.
Edited to replace the -1 in the denominator with Vgs_off to avoid confusion. This is a value from the JFET's datasheet and just happened to be -1 in this particular case.
ADDENDUM:
After incorporating the pieces from the answer given by #Eldrad, I came up with this much improved representation of the JFET characteristic curve:
Here are the commands used to create it:
# JFET parameters from data sheet
Vgs_off=-1
Idss=5
# JFET characteristic curve
Id(Vgs) = Idss * (1 - (Vgs / Vgs_off) ) ** 2
# Graph properties
set title "I_D vs V_{GS}"
set xlabel "V_{GS} (Volts)"
set ylabel "I_D (mA)"
set grid
set key off
set monochrome
# Plot the characteristic curve
plot [Vgs=Vgs_off:0][0:Idss] Id(Vgs)
# Plot interesting points
set object circle center 0.5 * Vgs_off, Id(0.5 * Vgs_off) radius char 0.33 fillstyle solid fillcolor rgb 'black'
set object circle center 0.293 * Vgs_off, Id(0.293 * Vgs_off) radius char 0.33 fillstyle solid fillcolor rgb 'black'
# Mark interesting points with dashed lines to where they intersect the x and y axes.
set arrow from first 0.5 * Vgs_off, graph 0 to first 0.5 * Vgs_off, graph 1 dashtype "-" nohead
set arrow from graph 0, first Id(0.5 * Vgs_off) to graph 1, first Id(0.5 * Vgs_off) dashtype "-" nohead
set arrow from 0.293 * Vgs_off, graph 0 to 0.293 * Vgs_off, graph 1 dashtype "_" nohead
set arrow from graph 0, first Id(0.293 * Vgs_off) to graph 1, first Id(0.293 * Vgs_off) dashtype "_" nohead
# Label the lines
set label "I_{DSS}" at graph 1.01, graph 1
set label "I_{DSS} / 2" at graph 1.01, graph 0.50
set label "I_{DSS} / 4" at graph 1.01, graph 0.25
set rmargin at screen 0.9
# Update the graph
replot
Getting specific values of a function is straightforward if the function y=f(x) and its inverse x=f(y) have an analytical expression, like it is in your case.
A couple of remarks about your function definition: You should use x as the variable, because this is the default gnuplot is looking for, it also makes the plot command shorter. Also, division by -1 is the same as putting a minus in front, so an easier function definition, including the inverse would be:
Idss = 5.0
Id (x) = Idss * (1 + x)**2
Id_inv (x) = sqrt(x/Idss) -1
Now the dashed lines can be drawn as arrows (check set arrow):
set arrow 11 from first -0.5, graph 0 to first -0.5, first Id(-0.5) lc "red" lw 2 dt 2 nohead
set arrow 12 from first -0.5, Id(-0.5) to graph 0, first Id(-0.5) lc "red" lw 2 dt 2 nohead
set arrow 21 from first Id_inv(2.5), graph 0 to Id_inv(2.5), 2.5 lc "green" lw 2 dt 2 nohead
set arrow 22 from first Id_inv(2.5), 2.5 to graph 0, first 2.5 lc "green" lw 2 dt 2 nohead
I would recommend drawing the horizontal lines to the axis where the tics are actually printed (i.e. to the left side). The reference points are the x- or y-values of the function and the border of the graphs, see the manual about coordinates for a detailed explanation.
You could add specific tic marks at those points:
set xtics add (-0.5, Id_inv(2.5))
set ytics add (Id(-0.5), 2.5)
You also asked about points in your comment and got the correct approach. The size of the circle can be chosen in any coordinate system (I think character is the width of a letter – x? – in the current font, but I'm not 100% sure)
set object 1 circle center -0.5,Id(-0.5) radius first 0.01 fs solid noborder fc "red"
set object 2 circle center Id_inv(2.5),2.5 radius first 0.01 fs solid noborder fc "green"
Now the remaining decoration can be added:
set title "{/:Italic I}_d vs {/:Italic V}_{gs}"
set xlabel "{/:Italic V}_{gs} /V"
set ylabel "{/:Italic I}_d /mA"
set xrange [-1:0]
plot Id(x)
Related
I need help with my polar plot shown below.
The plot was generated using the same code as in GNUPlot - Plotting a data set in polar form (r, θ, T(r,θ)) to a contour/heat map.
The code plot the whole of quadrant 1, but I only want the plot from 0 to 60 degrees, for radius up to 3000.
Can anyone help to suggest which part of the code that I should change?
Or what software that I can use to clean the unnecessary areas?
Thank you in advance.
Apparently, you can limit the polar graph to a quadrant.
However, I haven't found out how to get an other section than a quadrant.
So, maybe you have to draw it "manually"?
Check the following example as starting point.
Script:
### show only a section of a polar graph
reset session
set angle degrees
set size ratio 1
set margins 0,0,0,0
set origin 0.05,0.1
set size 0.9,0.9
set border 1
unset ytics
set xtics nomirror
# create some test data
set print $Data
do for [i=1:100] { print sprintf("%g %g",0.6+0.3*cos(7.2*i),i*0.6) }
set print
# draw polar graph "manually"
rmax = 1
rstep = 0.2
rcount = int(real(rmax)/rstep)+1
set xrange [0:rmax]
set yrange [0:rmax]
set for [r=1:rcount] obj 1+r circ at 0,0 size r*rstep arc [0:60] fc rgb r<rcount?0x777777:0x000000 dt r==rcount?1:3
amax = 60
astep = 10
acount = amax/astep
astepMin = 1
set for [a=0:acount] arrow 1+a \
from 0,0 angle a*astep length rmax lc rgb a<acount?0x777777:0x000000 dt a==acount?1:3 nohead
set for [a=0:amax:astepMin] arrow 1000+a \
from rmax*cos(a), rmax*sin(a) angle 180+a length rmax/100. lc "black" nohead
roff = 1.03 # factor for label offset
set for [a=0:amax:astep] label 1+a at cos(a)*rmax*roff, sin(a)*rmax*roff sprintf("%g°",a) center
# conversion polar to cartesian
xPtC(colR,colA) = column(colR)*cos(column(colA))
yPtC(colR,colA) = column(colR)*sin(column(colA))
plot $Data u (xPtC(1,2)):(yPtC(1,2)) w l lw 2 lc "red" ti "Data"
### end of script
Result:
I use the splot commadn to produce a heat map of the earth. The x- and y-values represent lattitude and longitude of a specific point on the Earth's surface, while the related z-value is the outcome of an analysis. The zrange is between 0 and 60. However, for some locations on Earth, there is no result available (which is correct) and z is set to 9999 for these cases.
I'm using the following script to produce the heat map:
set terminal png large size 1600,\
1200 font arial 24 crop
set output "map.png"
set palette model RGB defined (0 "dark-green",1 "forest-green",2 "green",3 "light-green",4 "dark-yellow",5 "yellow",6 "red",7 "dark-red")
set xrange[-180.00: 180.00]
set yrange[ -90.00: 90.00]
set zrange[ *: 60]
set grid
set pm3d map
set xlabel "Longitude [deg]"
set ylabel "Latitude [deg]"
unset key
set cblabel "Time [h]"
splot "output\\map.dat" u 5:6:8,\
"input\\world.dat" u 1:2:( .00) w l lw 1 lt -1
It works fine but because of the limitation in zrange, regions with z > 60 are shown in white.
I want to have something like a condition which enables that all 9999 z-values are shown in a specific colour like purple with a declaration like "no result" in the legend.
Any idea how to achieve this?
Thanks in advance,
Florian
Not exactly sure how to modify the style for the selected points, but you can use the ternary operator not to draw them at all. Something like:
splot "output\\map.dat" u 5:6:(($8<=60)?($8):(1/0))
You basically want to have 3 "ranges" of colors:
0 to 60 your defined palette colors
>60 "out of range" color
=9999 "no data" color
Not sure if splot ... w pm3d will allow an easy "independent" setting for z and color.
Furthermore, if you have NxN datapoints you will get (N-1)x(N-1) quadrangles and the color is determined by the z-values of the involved vertices (check help corners2color) and http://gnuplot.sourceforge.net/demo_5.5/pm3d.html (the very last graph). Maybe there is an easy way which I am not aware of.
That's why I would perfer the plotting style with boxxyerror (check help boxxyerror), maybe this is not the intended way, but it is rather flexible. If you are running gnuplt 5.4 you have the function palette() (check help palette).
I would take for missing data (backgroundcolor here:white) and for data out of range "grey", but you can easily change it. You can skip the random data generation part and in the plot command replace $Data with your filename and the corresponding columns. As well, replace 180./N and 90./N with the width (delta longitude) and height (delta latitude) of one data element.
Script: (requires gnuplot>=5.4)
### define separate color for missing values
reset session
set xrange[-180:180]
set yrange[-90:90]
# create some "random" test data
N = 90
set samples N
set isosamples N
set table $Data
c = 0.05
x0 = 70 # (rand(0)*360-180) # or random
y0 = -50 # (rand(0)*180-90) #
size0 = 2
x1 = -150 # (rand(0)*360-180) # or random
y1 = -20 # (rand(0)*180-90) #
size1 = 1
holeP0(x,y) = (1-erf((x-x0)*c/size0)**2) * (1-erf((y-y0)*c/size0)**2)
holeP1(x,y) = (1-erf((x-x1)*c/size1)**2) * (1-erf((y-y1)*c/size1)**2)
f(x,y) = rand(0)<holeP0(x,y) || rand(0)<holeP1(x,y) ? 9999 : (sin(1.3*x*c)*cos(.9*y*c)+cos(.8*x*c)*sin(1.9*y*c)+cos(y*.2*x*c**2))*11.5+33
splot f(x,y)
unset table
set palette model RGB defined (0 "dark-green",1 "forest-green",2 "green",3 "light-green",4 "dark-yellow",5 "yellow",6 "red",7 "dark-red")
myZmin = 0
myZmax = 60
myColorNoData = 0xffffff
myColorOutOfRange = 0x999999
set rmargin screen 0.8
set colorbox user origin screen 0.85,graph 0.2 size graph 0.05,graph 0.8
set cblabel "Amplitude"
set cbrange [myZmin:myZmax]
set tics out
set style fill solid 1.0 border
set key noautotitle at graph 1.27, graph 0.15 reverse Left samplen 2
myColor(col) = (z=column(col), z==9999 ? myColorNoData : z>myZmax ? myColorOutOfRange : palette(z))
plot $Data u 1:2:(180./N):(90./N):(myColor(3)) w boxxy lc rgb var, \
"world.dat" u 1:2:(0) w l lc "black", \
NaN w l lc palette, \
keyentry w boxes lc rgb 0x000000 fill empty ti "no data", \
keyentry w boxes lc rgb myColorOutOfRange ti "\ndata out\nof range"
### end of script
Result:
With palette it is easy to create color gradients
set view map
set samp 50,50
set palette defined (0 "blue", 1 "green", 2 "red")
spl "++" us 1:2:1 palette pt 5
Now I would like to apply transparency in vertical direction. The option lc rbg variable supports transparency via the alpha channel (see also here):
spl "++" us 1:2:1:(int(($2+5)/10*255)<<24) lc rgb var pt 5
But how can I translate the palette colors into rgb colors?
A second question: why I get only 10 horizontal rows, albeit I specified 50 in samp?
Easy answer first: When there is 2-dimensional sampling, either automatically from splot or explicitly from plot '++', the number of samples in the first dimension is controlled by set sample and the number of samples in the second dimension is controlled by set isosample.
Now the harder one. In gnuplot versions through the current 5.2.8 you cannot add transparency directly to the palette. You can, however, go through a multi-step process of saving the palette into a file or datablock and then reading it back it as an array of RGB colors. Once you have that array you can add an alpha channel value so that it expresses transparency as well. I will show this process using the datablock created by the command test palette. In older versions of gnuplot you may have to instead use the file created by set print "palette.save"; show palette palette 256;.
# save current palette to a datablock as a list of 256 RGB colors, one per line
set palette defined (0 "blue", 1 "green", 2 "red")
test palette
# print one line to show the format (cbval R G B NTSCval)
print $PALETTE[4]
# Create an array of packed RGB values
array RGB[256]
do for [i=1:256] {
Red = int(255. * word($PALETTE[i],2))
Green = int(255. * word($PALETTE[i],3))
Blue = int(255. * word($PALETTE[i],4))
RGB[i] = Red << 16 | Green << 8 | Blue
}
# Sample from '++' are generated to span ranges on the u and v axes
# I choose 1:256 so that the y coordinates match the range of array indices
set sample 50
set isosample 50
set urange [1:256]
set vrange [1:256]
set xrange [*:*] noextend
set yrange [*:*] noextend
# Now you can use colors stored in the array via colorspec `rgb variable`
# which will also accept an alpha channel in the high bits
plot "++" using 1:2:(RGB[int($2)]) with points pt 5 lc rgb variable
# The final step is to add an alpha channel as a function of y
# Here I go from opaque (Alpha = 0) to 50% transparent (Alpha = 127)
# This works because I know y will run from 1-256
ARGB(y) = RGB[int(y)] + (int(y/2)<<24)
plot "++" using 1:2:(ARGB($2)) with points pt 5 lc rgb variable
Output shown below.
The required command sequence, as you can see, is a mess.
It will be much easier in the next gnuplot release (5.4). The new version will provide a function palette(z) that converts from the current palette directly to a packed RGB value. Note that the palette() function isn't in the -rc1 testing version but will be in -rc2. So in version 5.4 all that palette/array/RGB manipulation can be replaced by
plot '++' using 1:2:(palette($2) + (int($2)<<24)) with points pt 5 lc rgb variable
Check also this: Gnuplot: transparency of data points when using palette
First of all, you can check what your defined palette is doing:
set palette defined (0 "blue", 1 "green", 2 "red")
test palette
You will get this:
Each channel (R,G,B) has a function with an input range [0:1] and an output range [0:1]. In this case it is a linear gradient.
So, you have to define such a function and put the channels together with the transparency (alpha) channel using the bit shift (see help operators binary).
The nice thing about a palette is that gnuplot takes care about the range. Here, you have to know minimum and maximum in advance and scale the color accordingly. You could use stats for this.
Code:
### your own palette with transparency
reset session
r(x) = x < 0.5 ? 0 : 2*x -1
g(x) = x < 0.5 ? 2*x : 2-2*x
b(x) = x < 0.5 ? 1-2*x : 0
a(y) = y
myColor(x,y) = (int(a((y-yMin)/(yMax-yMin))*0xff)<<24) + \
(int(r((x-xMin)/(xMax-xMin))*0xff)<<16) + \
(int(g((x-xMin)/(xMax-xMin))*0xff)<<8) + \
int(b((x-xMin)/(xMax-xMin))*0xff)
set samples 50
set isosamples 50
set size square
xMin=-5; xMax=5
yMin=-5; yMax=5
plot '++' u 1:2::(myColor($1,$2)) w p pt 5 ps 0.5 lc rgb var notitle
### end of code
Result:
Is it possible in Gnuplot to emulate the drawing style of an analogue oscilloscope, meaning thinner+dimmisher lines on larger amplitudes, like this:?
The effect you see in the oscilloscope trace is not due to amplitude, it is due to the rate of change as the trace is drawn. If you know that rate of change and can feed it to gnuplot as a third column of values, then you could use it to modulate the line color as it is drawn:
plot 'data' using 1:2:3 with lines linecolor palette z
I don't know what color palette would work best for your purpose, but here is an approximation using a function with an obvious, known, derivative.
set palette gray
set samples 1000
plot '+' using ($1):(sin($1)):(abs(cos($1))) with lines linecolor palette
For thickness variations, you could shift the curve slightly up and down, and fill the area between them.
f(x) = sin(2*x) * sin(30*x)
dy = 0.02
plot '+' u 1:(f(x)+dy):(f(x)-dy) w filledcurves ls 1 notitle
This does not allow variable colour, but the visual effect is similar.
Another approach:
As #Ethan already stated, the intensity is somehow proportional to the speed of movement, i.e. the derivative. If you have sin(x) as waveform, the derivative is cos(x). But what if you have given data? Then you have to calculate the derivative numerically.
Furthermore, depending on the background the line should fade from white (minimal derivative) to fully transparent (maximum derivative), i.e. you should change the transparency with the derivative.
Code:
### oscilloscope "imitation"
reset session
set term wxt size 500,400 butt # option butt, otherwise you will get overlap points
set size ratio 4./5
set samples 1000
set xrange[-5:5]
# create some test data
f(x) = 1.5*sin(15*x)*(cos(1.4*x)+1.5)
set table $Data
plot '+' u 1:(f($1)) w table
unset table
set xtics axis 1 format ""
set mxtics 5
set grid xtics ls -1
set yrange[-4:4]
set ytics axis 1 format ""
set mytics 5
set grid ytics ls -1
ColorScreen = 0x28a7e0
set obj 1 rect from screen 0,0 to screen 1,1 behind
set obj 1 fill solid 1.0 fc rgb ColorScreen
x0=y0=NaN
Derivative(x,y) = (dx=x-x0,x0=x,x-dx/2,dy=y-y0,y0=y,dy/dx) # approx. derivative
# get min/max derivative
set table $Dummy
plot n=0 $Data u (d=abs(Derivative($1,$2)),n=n+1,n<=2? (dmin=dmax=d) : \
(dmin>d ? dmin=d:dmin), (dmax<d?dmax=d:dmax)) w table
unset table
myColor(x,y) = (int((abs(Derivative(column(x),column(y)))-dmin)/(dmax-dmin)*0xff)<<24) +0xffffff
plot $Data u 1:2:(myColor(1,2)) w l lw 1.5 lc rgb var not
### end of code
Result:
I'm solving the pendulum's equation of motion, and I need to create an animation in GNUplot that shows the evolution of the system.
R4K provides me with the solution point by point, and this solution is stored in a .dat file named 'd' in columns 2 and 3. These are the orders given to GNUplot:
set xrange [-2:2]
set yrange [-2:2]
set pointsize 2
set style line 2 lc rgb '#0060ad' pt 7
set object circle at first 0,0 size scr 0.01 \
fillcolor rgb 'black’ fillstyle solid
do for [ii=1:3762] {
plot 'd.dat' using 2:3 every ::ii::ii linestyle 2
pause 0.002
}
I now want to connect the circle in [0,0] to the moving point plotted from d.dat with a moving line; how can I do it? I found a solution (kind of) here, but still I don't know how to tell GNUplot to search for a certain point in 'd.dat', different every time, and to draw the line between it and the centre.
The solution of user8153 is so far the best. Let me give a trial. If I were you I would have use arrows that update their positions with the moving end dynamically, since the every command loads one point at time. GNUPLOT provides environment variables such as GPVAL_X_MIN, GPVAL_X_MAX, GPVAL_Y_MIN,... that can be useful:
set xrange [-2:2]
set yrange [-2:2]
set pointsize 2
set style line 2 lc rgb '#0060ad' pt 7
set object circle at first 0,0 size scr 0.01 \
fillcolor rgb 'black’ fillstyle solid
do for [ii=1:3762] {
plot 'd.dat' using 2:3 every ::ii::ii linestyle 2
# makes GPVAL_ variables available
xpos = GPVAL_X_MIN; ypos = GPVAL_Y_MIN # or MAX does not matter since you are
# loading one point at time.
unset arrow # Remove the one of the previous graph
set arrow from 0, 0 to xpos, ypos nohead lc rgb 'black'
replot
pause 0.002
}
I have no data to debug this code snippet, but I would have done something along this line. Later versions of GNUPLOT allow to use stats command that avoids replot (replot could have an effect here on the persistence of images of the film but I am not sure). You can also try something along the line
set xrange [-2:2]
set yrange [-2:2]
set pointsize 2
set style line 2 lc rgb '#0060ad' pt 7
set object circle at first 0,0 size scr 0.01 \
fillcolor rgb 'black’ fillstyle solid
do for [ii=1:3762] {
stats 'd.dat' using 2:3 every ::ii::ii # applied the statistical summary of
# the data file
xp = STATS_min_x; yp = STATS_min_y
unset arrow
set arrow from 0, 0 to xp, yp nohead lc rgb 'black'
plot 'd.dat' using 2:3 every ::ii::ii linestyle 2
pause 0.002
}
I have never used STATS with the every command (let me know the output just for fun).
Hope that helps