<*+M2EXTENSIONS *> <*+STORAGE *> <*-CHECKDIV *> <*-CHECKRANGE *> <*-COVERFLOW *> <*-IOVERFLOW*> <*-PROCINLINE*> <*+NOPTRALIAS*> <*CPU="PENTIUM"*> IMPLEMENTATION MODULE aprstat; (* aprs statistc graphs by oe5dxl *) FROM osi IMPORT WrStrLn, WrStr, WrInt, sqrt, realcard, realint, WrFixed; FROM SYSTEM IMPORT INT16, INT8, CAST, MOVE, CARD8, CARD16, SHIFT, FILL, ADR; FROM maptool IMPORT pIMAGE, clr, Colset, drawchar, saveppm, waypoint, vistime, xsize, xytodeg; FROM libsrtm IMPORT getsrtm; FROM aprsdecode IMPORT DAT, pOPHIST, pFRAMEHIST, Decode, ERRSET, floor, ERRFLAGS, knottokmh, DEGSYM, sWXSET, WXSET, click, COLTYP, trunc, TYPES, ACKREJ, lums, MINTEMP, MAXTEMP; FROM aprsstr IMPORT POSITION, IntToStr, Append, Length, TIME, FixToStr, posinval; FROM aprspos IMPORT distance, WKNOTS, posvalid; FROM useri IMPORT debugmem, CONFSET, localtime, configon; FROM aprstext IMPORT DateLocToStr, sievert2str; CONST KMHTIME=60*10; colW=327C; colR=322C; colG=307C; colB=302C; colY=331C; colV=326C; SIEVMUL=1000000000.0; TYPE SET8=SET OF [0..7]; SET16=SET OF [0..15]; PROCEDURE setpix(img:pIMAGE; x, y:INTEGER; rr,gg,bb:INTEGER); BEGIN WITH img^[x][y] DO r:=rr; g:=gg; b:=bb END; END setpix; PROCEDURE str(img:pIMAGE; x, y:CARDINAL; s:ARRAY OF CHAR); VAR l, i:CARDINAL; inc:INTEGER; col:COLTYP; BEGIN l:=Length(s); i:=0; Colset(col, "W"); LOOP IF i>=l THEN EXIT END; WHILE s[i]>=CHR(128+ORD("A")) DO (* text colour switch *) Colset(col, CHR(ORD(s[i])-128)); INC(i); IF i>=l THEN EXIT END; END; drawchar(img, s[i], FLOAT(x), FLOAT(y), inc, 700, 1, col, FALSE); INC(x, inc); INC(i); END; END str; PROCEDURE num(img:pIMAGE; x, y, n:INTEGER; h:ARRAY OF CHAR); VAR s:ARRAY[0..50] OF CHAR; BEGIN IntToStr(n, 1, s); Append(s, h); str(img, x, y, s); END num; PROCEDURE sq(x:REAL):REAL; BEGIN RETURN x*x END sq; PROCEDURE pixl(x,y:REAL):CARDINAL; BEGIN x:=x*x + y*y; IF x<=0.0 THEN RETURN 0 END; RETURN trunc(256.0*sqrt(x)) END pixl; (* BEGIN RETURN TRUNC(256.0*(ABS(x) + ABS(y))) END pixl; *) PROCEDURE addpix(img:pIMAGE; x, y:REAL; rr,gg,bb:CARDINAL); CONST KL=512; VAR fx, fy:REAL; xx, yy, l:CARDINAL; BEGIN xx:=trunc(x); yy:=trunc(y); fx:=x-FLOAT(xx); fy:=y-FLOAT(yy); l:=pixl(1.0-fx, 1.0-fy); WITH img^[xx][yy] DO INC(r, rr*l DIV KL); INC(g, gg*l DIV KL);INC(b, bb*l DIV KL); END; l:=pixl(fx, 1.0-fy); WITH img^[xx+1][yy] DO INC(r, rr*l DIV KL); INC(g, gg*l DIV KL);INC(b, bb*l DIV KL); END; l:=pixl(1.0-fx, fy); WITH img^[xx][yy+1] DO INC(r, rr*l DIV KL); INC(g, gg*l DIV KL);INC(b, bb*l DIV KL); END; l:=pixl(fx, fy); WITH img^[xx+1][yy+1] DO INC(r, rr*l DIV KL); INC(g, gg*l DIV KL);INC(b, bb*l DIV KL); END; END addpix; PROCEDURE fillpix(img:pIMAGE; x, yfrom:CARDINAL; yto:REAL; rr,gg,bb:CARDINAL); CONST KL=512; VAR fy:REAL; yy:CARDINAL; BEGIN yy:=trunc(yto); fy:=yto-FLOAT(yy); WHILE yfrom<=yy DO WITH img^[x][yfrom] DO INC(r, rr); INC(g, gg);INC(b, bb) END; INC(yfrom); END; END fillpix; (* PROCEDURE sfact(y:REAL):CARDINAL; VAR s, m:CARDINAL; f:REAL; BEGIN s:=1; m:=2; f:=200.0/FLOAT(lums.fontysize+2); WHILE y/FLOAT(s)>f DO s:=s*m; IF m=2 THEN m:=5 ELSE m:=2 END; END; RETURN s END sfact; *) PROCEDURE sfact(y:REAL):REAL; VAR s, f:REAL; m:CARDINAL; BEGIN s:=1.0; m:=2; f:=200.0/FLOAT(lums.fontysize+2); WHILE y/s>f DO s:=s*FLOAT(m); IF m=2 THEN m:=5 ELSE m:=2 END; END; RETURN s END sfact; PROCEDURE dynmaxx(margin, min, max:CARDINAL):CARDINAL; BEGIN IF max+margin*2>VAL(CARDINAL,xsize) THEN IF VAL(CARDINAL,xsize)>min THEN max:=VAL(CARDINAL,xsize)-margin*2 ELSE max:=min-margin*2 END; END; RETURN max END dynmaxx; PROCEDURE btimehist(VAR img:pIMAGE; op:pOPHIST); CONST MAXT=7200; X=620; Y=150; MARGIN=16; XI=X+MARGIN; YI=Y+MARGIN; VAR t:ARRAY[0..MAXT-1] OF CARDINAL; pf:pFRAMEHIST; i,y,sum,xm,scale,expand,max,textx,textd:CARDINAL; dt:TIME; s:ARRAY[0..255] OF CHAR; BEGIN IF op=NIL THEN RETURN END; FOR i:=0 TO HIGH(t) DO t[i]:=0 END; pf:=op^.frames; sum:=0; IF pf<>NIL THEN WHILE pf^.next<>NIL DO IF vistime(pf^.time) & (pf^.next^.time>=pf^.time) THEN dt:=pf^.next^.time-pf^.time; IF dt<=HIGH(t) THEN INC(t[dt]); INC(sum); END; END; pf:=pf^.next; END; END; DEC(sum, sum DIV 20); xm:=0; y:=0; REPEAT INC(y, t[xm]); INC(xm) UNTIL (xm>=HIGH(t)) OR (y>=sum); INC(xm); IF xm<200 THEN xm:=200 END; FOR i:=xm+1 TO HIGH(t) DO INC(t[xm], t[i]) END; (* add not shown rest to last shown element *) scale:=(xm+X-1) DIV X; textd:=50; IF scale>30 THEN scale:=60; textx:=50; ELSIF scale>12 THEN scale:=30; textx:=20; ELSIF scale>6 THEN scale:=12; textx:=10; ELSIF scale>3 THEN scale:=6; textx:=5; ELSIF scale>2 THEN scale:=3; textx:=3; textd:=60; ELSIF scale>1 THEN scale:=2; textx:=2; textd:=60; ELSE scale:=1; textx:=1; textd:=60 END; IF scale>1 THEN FOR i:=1 TO HIGH(t) DO IF i MOD scale=0 THEN t[i DIV scale]:=0 END; INC(t[i DIV scale], t[i]); END; END; xm:=(xm+scale-1) DIV scale; expand:=1; IF xmX THEN xm:=X ELSIF xm=0 THEN RETURN END; xm:=xm*expand; max:=0; FOR i:=0 TO xm-1 DO IF t[i]>max THEN max:=t[i] END; END; IF max=0 THEN RETURN END; FOR i:=0 TO xm-1 DO t[i]:=t[i]*Y DIV max END; NEW(img, xm+MARGIN, YI); INC(debugmem.screens, SIZE(img^)); IF img=NIL THEN WrStrLn("error image alloc"); RETURN END; clr(img); FOR i:=0 TO xm-1 DO setpix(img, i+MARGIN DIV 2, MARGIN DIV 2-1, 0,500,800); IF i DIV expand MOD 10=0 THEN setpix(img, i+MARGIN DIV 2, MARGIN DIV 2-2, 0,500,800) END; IF i DIV expand MOD textd=0 THEN setpix(img, i+MARGIN DIV 2, MARGIN DIV 2-3, 0,500,800); setpix(img, i+MARGIN DIV 2, MARGIN DIV 2-4, 0,500,800); END; FOR y:=0 TO Y-1 DO setpix(img, i+MARGIN DIV 2, y+MARGIN DIV 2, 50,20*ORD(ODD(i DIV expand DIV 10))+30,30) END; y:=0; WHILE y res:=saveppm("/tmp/thist.ppm", img, xm+MARGIN, YI); <* END *> (* DISPOSE(img); *) END btimehist; PROCEDURE kmhist(VAR img:pIMAGE; op:pOPHIST; VAR test:BOOLEAN); CONST MAXX=1584; MINX=584; MAXY=120; MARGIN=8; XSTEPS=8; (* in 1 pixel *) FILTERLEN=6; (* median filter span in pixels *) NOINFOTIME=300; (* seconds timeout to no info *) MINSPAN=3600; MAXSPAN=3600*8; VAR fr, ffrom, fto, fold:pFRAMEHIST; t0, t1, ot:TIME; xt, xi, i, sp, tf, maxkm, maxkm10, maxx, markx, markkm:CARDINAL; maxy,dy,ys:REAL; s,h:ARRAY[0..255] OF CHAR; po:POSITION; vt:ARRAY[0..MAXX*XSTEPS-1+FILTERLEN*XSTEPS] OF REAL; marks:ARRAY[0..MAXX-1] OF BOOLEAN; tops:ARRAY[0..4] OF CARDINAL; dat:DAT; BEGIN IF (op=NIL) OR (op^.frames=NIL) THEN test:=FALSE; RETURN END; fto:=NIL; ffrom:=NIL; fr:=op^.frames; po:=fr^.vardat^.pos; fold:=NIL; REPEAT (* find first to last move *) IF vistime(fr^.time) & (fr^.nodraw<=ERRSET{eDIST}) & posvalid(fr^.vardat^.pos) & (distance(po, fr^.vardat^.pos)>0.05) THEN (* find last move *) fto:=fr; po:=fr^.vardat^.pos; IF ffrom=NIL THEN ffrom:=fold END; (* first move *) fold:=fr; END; fr:=fr^.next; UNTIL fr=NIL; IF (fto=NIL) OR (ffrom=NIL) THEN test:=FALSE; RETURN END; (* no move *) IF test THEN RETURN END; img:=NIL; FOR xi:=0 TO HIGH(marks) DO marks[xi]:=FALSE END; (* IF ffrom=NIL THEN ffrom:=op^.frames END; *) t1:=fto^.time; WHILE (ffrom^.next<>NIL) & ((ffrom^.nodraw>=ERRSET{eDIST}) OR (ffrom^.time+MAXSPAN=0) & (dat.speed=tops[i] THEN tops[i]:=dat.speed; EXIT END; INC(i); IF i>HIGH(tops) THEN EXIT END; END; END; IF fr=fto THEN EXIT END; fr:=fr^.next; IF fr=NIL THEN EXIT END; END; --FOR i:=0 TO HIGH(tops) DO WrInt(tops[i], 10) END; WHILE FLOAT(tops[0])>FLOAT(tops[1])*1.5 DO FOR i:=0 TO HIGH(tops)-1 DO tops[i]:=tops[i+1] END; END; markx:=0; markkm:=0; maxx:=MAXX*(t1-t0+MINSPAN) DIV MAXSPAN; (* guess picture width *) IF maxxMAXX THEN maxx:=MAXX END; (* and hard limit *) ot:=t0; fr:=ffrom; xi:=0; maxy:=0.0; dy:=0.0; tf:=0; LOOP IF (fr^.nodraw<=ERRSET{eDIST}) & (Decode(fr^.vardat^.raw, dat)>=0) & (NOT configon(fTRACKFILT) OR (dat.speed1000.0 THEN dy:=1000.0 END; IF fr^.time>ot+NOINFOTIME THEN tf:=XSTEPS*FILTERLEN END; (* gap in data *) xt:=trunc(FLOAT(fr^.time-t0)/FLOAT(t1-t0)*FLOAT(maxx*XSTEPS-1)); (* x position in picture *) IF xt>=maxx*XSTEPS-1 THEN xt:=maxx*XSTEPS END; IF xi DIV XSTEPS>FILTERLEN DIV 2 THEN marks[xi DIV XSTEPS-FILTERLEN DIV 2]:=TRUE END; (* waypoint *) IF dy>maxy THEN maxy:=dy END; WHILE xi0 THEN DEC(tf); IF tf=0 THEN dy:=0.0 END; (* extend value before no data to filter size *) END; END; ot:=fr^.time; IF (dat.pos.lat=click.markpos.lat) & (dat.pos.long=click.markpos.long) & (click.markpost=ot) THEN markx:=xi DIV XSTEPS; markkm:=knottokmh(dat.speed) END; END; IF fr=fto THEN EXIT END; fr:=fr^.next; IF fr=NIL THEN EXIT END; END; tf:=XSTEPS*FILTERLEN; WHILE xi<=HIGH(vt) DO vt[xi]:=dy; INC(xi); IF tf>0 THEN DEC(tf) ELSE dy:=0.0 END; (* extend last value to filter size then zero rest *) END; FOR xi:=0 TO maxx*XSTEPS-1 DO dy:=0.0; FOR xt:=0 TO XSTEPS*FILTERLEN-1 DO dy:=dy + vt[xi+xt]*(1.0-sq(FLOAT(xt)*(2.0/FLOAT(XSTEPS*FILTERLEN))-1.0)); (* FIR lowpass *) END; vt[xi]:=dy; END; maxkm:=trunc(maxy); maxkm10:=(maxkm+9) DIV 10*10; IF maxy>=2.0 THEN maxy:=(FLOAT(MAXY)*1.5/FLOAT(FILTERLEN*XSTEPS))/FLOAT(maxkm10); (* FIR gain integral 1-x^2 *) FOR xi:=0 TO HIGH(vt) DO vt[xi]:=vt[xi]*maxy END; (* fit y in picture *) NEW(img, maxx+2*MARGIN, MAXY+2*MARGIN); INC(debugmem.screens, SIZE(img^)); IF img=NIL THEN WrStrLn("error image alloc"); RETURN END; clr(img); (* FOR xt:=MARGIN TO MAXY+MARGIN-1 DO FOR xi:=MARGIN TO MAXX+MARGIN-1 DO WITH img^[xi+xt*(MAXX+2*MARGIN)] DO r:=30; g:=60+50*ORD(ODD(xt DIV 10)); b:=80+200*ORD(marks[xi-MARGIN]); END; END; END; *) dy:=FLOAT(MAXY)/FLOAT(maxkm10)*10.0; ys:=0.0; xt:=0; sp:=0; REPEAT (* draw y scale and paper and waypoints*) FOR i:=MARGIN-2-2*ORD(sp MOD 5=0) TO MARGIN DO setpix(img, i, trunc(ys+0.5)+MARGIN, 200, 1000, 200) END; ys:=ys+dy; WHILE (xt=FLOAT(MAXY+1); FOR xi:=0 TO maxx-1 DO setpix(img, xi+MARGIN, MARGIN, 200, 1000, 200) END; FOR xi:=0 TO MAXY DO setpix(img, MARGIN, xi+MARGIN, 200, 1000, 200) END; sp:=t0 DIV 3600 + 1; ot:=sp*3600; ys:=FLOAT(maxx*(ot-t0))/FLOAT(t1-t0); (* x of first full hour *) dy:=FLOAT(maxx*3600)/FLOAT(t1-t0); (* x size of 1h *) IF (dy>20.0) & (ys<=FLOAT(maxx)) THEN IF ys-dy*0.5>0.0 THEN (* draw 1/2 h mark *) FOR i:=MARGIN-2 TO MARGIN-1 DO setpix(img, trunc(ys-dy*0.5)+MARGIN, i, 200, 1000, 200) END; END; sp:=sp+24+localtime() DIV 3600; REPEAT (* draw h and rest of 1/2h marks *) FOR i:=MARGIN-4 TO MARGIN-1 DO setpix(img, trunc(ys)+MARGIN, i, 200, 1000, 200) END; IF ys+dy*0.5<=FLOAT(maxx) THEN FOR i:=MARGIN-2 TO MARGIN-1 DO setpix(img, trunc(ys+dy*0.5)+MARGIN, i, 200, 1000, 200) END; END; num(img, trunc(ys)+MARGIN-6, MARGIN, sp MOD 24, "h"); INC(sp); ys:=ys+dy; UNTIL ys>FLOAT(maxx); END; IF markx>FILTERLEN DIV 2 THEN DEC(markx, FILTERLEN DIV 2); FOR xi:=MARGIN TO MAXY+MARGIN DO setpix(img, markx+MARGIN, xi, 500, 200, 50) END; END; FOR xi:=XSTEPS TO maxx*XSTEPS-1 DO (* draw graph *) dy:=vt[xi]; IF dy>=0.1 THEN IF dy>MAXY THEN dy:=FLOAT(MAXY) END; addpix(img, FLOAT(xi)*(1.0/FLOAT(XSTEPS))+MARGIN, dy+MARGIN, 1000 DIV XSTEPS, 1000 DIV XSTEPS, 100 DIV XSTEPS); END; END; s:=" "; Append(s, op^.call); Append(s, " max="); IntToStr(maxkm, 1, h); Append(s, h); IF markx>0 THEN Append(s, " cursor="); IntToStr(markkm, 1, h); Append(s, h) END; Append(s, "km/h"); num(img, MARGIN+2, MAXY+MARGIN*2-lums.fontysize-1, maxkm10, s); <* IF SAVESTAT THEN *> res:=saveppm("/tmp/km.ppm", img, maxx+2*MARGIN, MAXY+2*MARGIN); <* END *> END; (* IF img<>NIL THEN DISPOSE(img) END; *) END kmhist; PROCEDURE paper(VAR img:pIMAGE; yax0, yax1, step, mul:REAL; margin, maxx, maxy:CARDINAL; name:ARRAY OF CHAR); VAR x, y:CARDINAL; s, so:INTEGER; v:REAL; BEGIN so:=realint(floor(yax0/step)); FOR y:=0 TO maxy-1 DO setpix(img, margin, y+margin, 200, 1000, 200); v:=yax0 + (yax1-yax0)*FLOAT(y)*(1.0/FLOAT(maxy)); s:=realint(floor(v/step)); FOR x:=margin+1 TO maxx-1+margin DO WITH img^[x][y+margin] DO INC(r, 60); INC(g, 50+40*ORD(ODD(s))); INC(b, 60); END; END; IF s<>so THEN FOR x:=margin-3 TO margin-1 DO setpix(img, x, y+margin, 200, 1000, 200); END; num(img, margin+1, y+margin-lums.fontysize DIV 2-2, s*VAL(INTEGER,step*mul+0.5), ""); so:=s; END; END; str(img, margin+50, maxy+margin*2-lums.fontysize-2, name); END paper; PROCEDURE althist(VAR img:pIMAGE; op:pOPHIST; VAR test:BOOLEAN; VAR way:REAL; VAR beacons, msgs, acks, rejs:CARDINAL); CONST MAXXX=720; MAXY=120; MARGIN=8; XSTEPS=8; (* in 1 pixel *) EMPTY=-10000; MAXALT=50000; VAR s,h :ARRAY[0..255] OF CHAR; minalt, maxalt, minaltd, maxaltd:REAL; alt, ground :ARRAY[0..MAXXX*XSTEPS-1] OF REAL; wdiv, hdiv, sc :REAL; Maxx, x, xc, markx, markx1 :CARDINAL; dat :DAT; waysum, resol :REAL; markalt :INTEGER; gndok, end :BOOLEAN; PROCEDURE decodealt(do:BOOLEAN); CONST E=FLOAT(EMPTY); VAR fr :pFRAMEHIST; opos :POSITION; a,a1,a2,a3,ognd :REAL; x, xc :CARDINAL; BEGIN markx:=0; markx1:=0; markalt:=MIN(INTEGER); fr:=op^.frames; posinval(opos); waysum:=0.0; xc:=1; a:=E; a2:=E; a3:=E; ognd:=E; end:=FALSE; FOR x:=0 TO HIGH(alt) DO alt[x]:=E END; FOR x:=0 TO HIGH(ground) DO ground[x]:=E END; LOOP (* sum up km driven *) IF vistime(fr^.time) & (Decode(fr^.vardat^.raw, dat)>=0) THEN IF dat.type=MSG THEN (* count msg stuff *) IF dat.ackrej=MSGMSG THEN INC(msgs) ELSIF dat.ackrej=MSGACK THEN INC(acks); ELSE INC(rejs) END; END; IF (NOT configon(fTRACKFILT) OR (fr^.nodraw<=ERRSET{eDIST})) THEN IF NOT posvalid(dat.pos) THEN dat.pos:=fr^.vardat^.pos END; IF posvalid(dat.pos) THEN x:=trunc(waysum*wdiv); IF posvalid(opos) THEN waysum:=waysum+distance(opos, dat.pos) END; IF (dat.altitude>EMPTY) & (dat.altitude<=MAXALT) THEN a1:=VAL(REAL, dat.altitude); IF do THEN IF x>HIGH(alt) THEN x:=HIGH(alt) END; IF a2>E THEN IF NOT configon(fTRACKFILT) OR (ABS(a-a1+a-a2)*0.25MIN(INTEGER)) & (markx=0) THEN markx:=x DIV XSTEPS END; IF (dat.pos.lat=click.markpos.lat) & (dat.pos.long=click.markpos.long) & (click.markpost=fr^.time) THEN markalt:=dat.altitude END; ground[x]:=ognd; (* delay same as gps filter *) ognd:=getsrtm(dat.pos, 0, resol); IF ognd>=20000.0 THEN ognd:=E END; END; IF NOT end THEN a3:=a2 END; a2:=a; a:=a1; END; opos:=dat.pos; END; IF NOT end THEN INC(beacons) END; END; END; IF end THEN EXIT END; IF fr^.next<>NIL THEN fr:=fr^.next ELSE end:=TRUE END; END; IF a3<=E THEN waysum:=0.0 END; (* too less values *) END decodealt; PROCEDURE interpol(VAR a:ARRAY OF REAL); CONST E=FLOAT(EMPTY); VAR i, j:CARDINAL; y,k:REAL; BEGIN i:=0; j:=0; y:=E; WHILE i<=HIGH(a) DO IF a[i]>E THEN IF y<=E THEN y:=a[i] END; IF i>j THEN k:=1.0/FLOAT(i-j); WHILE jE THEN WrFixed(alt[i], 10, 20); WrStrLn("=alt") END; IF (alt[i]>E) & (alt[i]maxaltd THEN maxaltd:=alt[i] END; IF (ground[i]>E) & (ground[i]maxalt THEN maxalt:=ground[i] END; END; IF (minaltd-(maxaltd-minaltd+100.0)>minalt) (* ground graph is too far below *) OR ((maxaltd-minaltd)*0.05>(maxalt-minalt)) THEN (* ground graph is a flat line *) minalt:=minaltd; (* make no ground graph *) maxalt:=maxaltd; ELSE IF minaltdmaxalt THEN maxalt:=maxaltd END; gndok:=TRUE; END; IF maxalt-minalt>1.0/MAXY THEN hdiv:=MAXY/(maxalt-minalt) ELSE hdiv:=1.0 END; (* normalize *) FOR i:=0 TO HIGH(alt) DO alt[i]:=(alt[i]-minalt)*hdiv; ground[i]:=(ground[i]-minalt)*hdiv; END; END norm; BEGIN way:=0.0; beacons:=0; msgs:=0; acks:=0; rejs:=0; gndok:=FALSE; minaltd:=0.0; maxaltd:=0.0; IF (op=NIL) OR (op^.frames=NIL) THEN test:=FALSE; RETURN END; Maxx:=dynmaxx(MARGIN, 400, MAXXX); decodealt(FALSE); way:=waysum; IF waysum<0.05 THEN test:=FALSE; RETURN END; (* no altitudes or km *) IF test THEN RETURN END; NEW(img, Maxx+2*MARGIN, MAXY+2*MARGIN); INC(debugmem.screens, SIZE(img^)); IF img=NIL THEN WrStrLn("error image alloc"); RETURN END; clr(img); wdiv:=FLOAT(Maxx*XSTEPS-1)/waysum; decodealt(TRUE); interpol(alt); interpol(ground); norm; (* FOR x:=0 TO HIGH(alt) DO IF alt[x]>=0.0 THEN WrFixed(alt[x], 3,11) ELSE WrStr(".") END; END; WrStrLn(""); *) sc:=sfact(maxalt-minalt); s:=" "; Append(s, op^.call); Append(s, " dist="); FixToStr(waysum, 2, h); Append(s, h); Append(s, "km min="); IntToStr(realint(minaltd), 1, h); Append(s, h); Append(s, "m max="); IntToStr(realint(maxaltd), 1, h); Append(s, h); IF markalt>MIN(INTEGER) THEN Append(s, "m curs="); IntToStr(markalt, 1, h); Append(s, h); END; Append(s, "m (NN)"); paper(img, minalt, maxalt, sc, 1.0, MARGIN, Maxx, MAXY, s); FOR x:=MARGIN-3 TO Maxx+MARGIN DO setpix(img, x, MARGIN, 200, 1000, 200) END; h:="km"; IF waysum<5.0 THEN waysum:=waysum*1000.0; h:="m"; END; sc:=sfact(waysum); hdiv:=sc*FLOAT(Maxx)/waysum; wdiv:=0.0; xc:=0; REPEAT FOR x:=MARGIN-3 TO MARGIN-1 DO setpix(img, trunc(wdiv)+MARGIN, x, 200, 1000, 200) END; IF (xc>0) & (trunc(wdiv)Maxx; IF markx>0 THEN FOR x:=MARGIN TO MAXY+MARGIN DO setpix(img, markx+MARGIN, x, 50, 400, 500) END; END; FOR x:=0 TO Maxx*XSTEPS-1 DO (* draw graph *) addpix(img, FLOAT(x)*(1.0/XSTEPS)+MARGIN, alt[x]+MARGIN, 500/XSTEPS, 600/XSTEPS, 700/XSTEPS); IF gndok THEN addpix(img, FLOAT(x)*(1.0/XSTEPS)+MARGIN, ground[x]+MARGIN, 250/XSTEPS, 150/XSTEPS, 0/XSTEPS); END; END; IF gndok THEN FOR x:=0 TO Maxx-1 DO fillpix(img, x+MARGIN, MARGIN, ground[x*XSTEPS]+(MARGIN-1), 63, 38, 0); END; END; <* IF SAVESTAT THEN *> res:=saveppm("/tmp/km.ppm", img, Maxx+2*MARGIN, MAXY+2*MARGIN); <* END *> END althist; PROCEDURE wxgraph(VAR img:pIMAGE; op:pOPHIST; stime:TIME; VAR what:sWXSET; VAR lastval:LASTVAL); CONST TIMESPAN=3600*24; MAXXX=720; MINXSIZE=320; MAXY=120; MARGIN=8; ARRSIZE=1440; -- XSTEP=FLOAT(MAXXX)/FLOAT(ARRSIZE); INCHMM=0.254; (* inch/100 to mm *) INVAL=-10000.0; MAXJOIN=65; (* maximum interpolated span min *) TYPE WX=RECORD temp,hyg,baro,wind,rain,lumi,siev:REAL; dust10, dust2, dust1, dust01:CARD16; END; VAR fr:pFRAMEHIST; xi, xt, Maxx, vc:CARDINAL; vh, yax0, yax1, XStep, step:REAL; max, min:WX; s,h,hh:ARRAY[0..255] OF CHAR; dat:DAT; temp:ARRAY[0..ARRSIZE-1] OF REAL; hyg:ARRAY[0..ARRSIZE-1] OF REAL; baro:ARRAY[0..ARRSIZE-1] OF REAL; winds:ARRAY[0..ARRSIZE-1] OF REAL; windd:ARRAY[0..ARRSIZE-1] OF REAL; gust:ARRAY[0..ARRSIZE-1] OF REAL; rain1:ARRAY[0..ARRSIZE-1] OF REAL; rain24:ARRAY[0..ARRSIZE-1] OF REAL; rain0:ARRAY[0..ARRSIZE-1] OF REAL; lumi:ARRAY[0..ARRSIZE-1] OF REAL; siev:ARRAY[0..ARRSIZE-1] OF REAL; dust10, dust2, dust1, dust01:ARRAY[0..ARRSIZE-1] OF REAL; have:sWXSET; dirvalid:BOOLEAN; PROCEDURE scale(VAR v:ARRAY OF REAL; min, max, ysize, maxamp:REAL; VAR smin, smax:REAL; VAR step:REAL); CONST F=0.9; VAR i:CARDINAL; k, a, d:REAL; BEGIN IF min=INVAL THEN min:=0.0; d:=max; IF d<=maxamp THEN d:=maxamp END; a:=ysize/d; k:=0.0; ELSE d:=max-min; IF d<=maxamp THEN d:=maxamp END; a:=ysize*F/d; k:=ysize*((1.0-F)*0.5); END; step:=sfact(d); smin:=min-k/a; smax:=min+k/a+d; FOR i:=0 TO HIGH(v) DO IF v[i]<>INVAL THEN v[i]:=(v[i]-min)*a+k END; END; END scale; PROCEDURE newimg():BOOLEAN; BEGIN IF img=NIL THEN NEW(img, Maxx+2*MARGIN, MAXY+2*MARGIN); INC(debugmem.screens, SIZE(img^)); END; IF img=NIL THEN RETURN FALSE END; clr(img); RETURN TRUE; END newimg; PROCEDURE timeline; VAR x,y:CARDINAL; t, to:TIME; BEGIN to:=0; FOR x:=0 TO Maxx-1 DO setpix(img, x+MARGIN, MARGIN, 200, 1000, 200); t:=(stime+localtime()-(Maxx-1-x)*TIMESPAN DIV Maxx) DIV 3600; IF to=0 THEN to:=t END; IF to<>t THEN to:=t; FOR y:=MARGIN-3 TO MARGIN-1 DO setpix(img, x+MARGIN, y, 200, 1000, 200); END; IF (x>28) & (t MOD 3=0) THEN num(img, x+MARGIN-6, MARGIN, t MOD 24, "h"); END; END; END; END timeline; PROCEDURE dots(v:ARRAY OF REAL; join:BOOLEAN; r,g,b:CARDINAL); VAR i:CARDINAL; yo,k,io,dx:REAL; BEGIN yo:=INVAL; io:=0.0; i:=0; REPEAT IF v[i]<>INVAL THEN waypoint(img, FLOAT(i)*XStep+MARGIN+0.5, v[i]+MARGIN, 2.0, r,g,b); IF join & (yo<>INVAL) & (i>trunc(io)) & (trunc(io)+MAXJOIN>=i) THEN k:=(v[i]-yo)/(FLOAT(i)-io+1.0); IF ABS(k)>2.0 THEN dx:=2.0/ABS(k); k:=k*dx ELSE dx:=1.0 END; WHILE trunc(io)<=i DO yo:=yo+k; waypoint(img, io*XStep+MARGIN, yo+MARGIN, 1.2, r DIV 2,g DIV 2,b DIV 2); io:=io+dx; END; END; yo:=v[i]; io:=FLOAT(i+1); END; INC(i); UNTIL i>HIGH(v); END dots; BEGIN IF (op=NIL) OR (op^.frames=NIL) (*OR (op^.lastinftyp<100)*) THEN RETURN END; Maxx:=dynmaxx(MARGIN, MINXSIZE, MAXXX); XStep:=FLOAT(Maxx)/FLOAT(ARRSIZE); img:=NIL; FOR xi:=0 TO ARRSIZE-1 DO temp[xi]:=INVAL; hyg[xi]:=INVAL; baro[xi]:=INVAL; winds[xi]:=INVAL; windd[xi]:=INVAL; gust[xi]:=INVAL; rain1[xi]:=INVAL; rain24[xi]:=INVAL; rain0[xi]:=INVAL; lumi[xi]:=INVAL; siev[xi]:=INVAL; dust10[xi]:=INVAL; dust2[xi]:=INVAL; dust1[xi]:=INVAL; dust01[xi]:=INVAL; END; WITH max DO temp:=INVAL; hyg:=INVAL; baro:=INVAL; wind:=INVAL; rain:=INVAL; lumi:=INVAL; siev:=INVAL; dust10:=0; END; dirvalid:=FALSE; min.temp:=MAX(REAL); min.baro:=MAX(REAL); FILL(ADR(lastval), 0C, SIZE(lastval)); fr:=op^.frames; REPEAT IF (fr^.time>stime-TIMESPAN) & (fr^.time<=stime) & (Decode(fr^.vardat^.raw, dat)>=0) & (dat.sym="_") THEN xt:=trunc(FLOAT(fr^.time-(stime-TIMESPAN))*(FLOAT(ARRSIZE)/FLOAT(TIMESPAN))); IF xt>=ARRSIZE THEN xt:=ARRSIZE-1 END; vh:=(dat.wx.temp-32.0)/1.8; IF (vh>=MINTEMP) & (vh<=MAXTEMP) THEN temp[xt]:=vh; IF vh>max.temp THEN max.temp:=vh END; IF vh=0.0) & (dat.wx.hygro<=100.0) THEN hyg[xt]:=dat.wx.hygro; IF dat.wx.hygro>max.hyg THEN max.hyg:=dat.wx.hygro END; lastval.hyg:=dat.wx.hygro; END; vh:=dat.wx.baro*0.1; IF (vh>=900.0) & (vh<=1100.0) THEN baro[xt]:=vh; IF vh>max.baro THEN max.baro:=vh END; IF vh=0.0) & (vh<300.0) THEN rain24[xt]:=vh; IF vh>max.rain THEN max.rain:=vh END; lastval.rain24:=vh; END; vh:=dat.wx.raintoday*INCHMM; IF (vh>=0.0) & (vh<300.0) THEN rain0[xt]:=vh; IF vh>max.rain THEN max.rain:=vh END; lastval.rain:=vh; END; vh:=dat.wx.rain1*INCHMM; IF (vh>=0.0) & (vh<300.0) THEN rain1[xt]:=vh; IF vh>max.rain THEN max.rain:=vh END; lastval.rain1:=vh; END; IF (dat.wx.lum>=0.0) & (dat.wx.lum<=2000.0) THEN lumi[xt]:=dat.wx.lum; IF dat.wx.lum>max.lumi THEN max.lumi:=dat.wx.lum END; lastval.lumi:=dat.wx.lum; END; IF (dat.course>0) & (dat.course<=360) THEN lastval.winddir:=FLOAT(dat.course MOD 360); windd[xt]:=lastval.winddir; dirvalid:=TRUE; END; vh:=FLOAT(dat.speed)*WKNOTS; IF (vh>=0.0) & (vh<=1000.0) THEN winds[xt]:=vh; lastval.winds:=vh; IF vh>max.wind THEN max.wind:=vh END; END; vh:=dat.wx.gust*WKNOTS; IF (vh>=0.0) & (vh<=1000.0) THEN gust[xt]:=vh; lastval.gust:=vh; IF vh>max.wind THEN max.wind:=vh END; END; vh:=dat.wx.sievert; IF (vh>=0.0) & (vh<1000.0) THEN siev[xt]:=vh*SIEVMUL; lastval.siev:=vh; IF vh>max.siev THEN max.siev:=vh END; END; IF dat.wx.dust10>=0 THEN vc:=dat.wx.dust10; dust10[xt]:=FLOAT(vc); lastval.dust10:=vc; IF vc>max.dust10 THEN max.dust10:=vc END; END; IF dat.wx.dust2>=0 THEN vc:=dat.wx.dust2; dust2[xt]:=FLOAT(vc); lastval.dust2:=vc; IF vc>max.dust10 THEN max.dust10:=vc END; END; IF dat.wx.dust1>=0 THEN vc:=dat.wx.dust1; dust1[xt]:=FLOAT(vc); lastval.dust1:=vc; IF vc>max.dust10 THEN max.dust10:=vc END; END; IF dat.wx.dust01>=0 THEN vc:=dat.wx.dust01; dust01[xt]:=FLOAT(vc); lastval.dust01:=vc; IF vc>max.dust10 THEN max.dust10:=vc END; END; END; fr:=fr^.next; UNTIL fr=NIL; DateLocToStr(stime, h); Append(h, " "); Append(h, op^.call); have:=sWXSET{}; IF max.temp<>INVAL THEN INCL(have, wTEMP); IF wTEMP IN what THEN IF NOT newimg() THEN RETURN END; scale(temp, min.temp, max.temp, FLOAT(MAXY), 10.0, yax0, yax1, step); FixToStr(lastval.temp, 2, s); Append(s, DEGSYM+"C "); Append(s, h); paper(img, yax0, yax1, step, 1.0, MARGIN, Maxx, MAXY, s); timeline; dots(temp, TRUE, 200, 700, 40); <* IF SAVESTAT THEN *> res:=saveppm("/tmp/temp.ppm", img, Maxx+2*MARGIN, MAXY+2*MARGIN); <* END *> END; END; IF max.baro<>INVAL THEN INCL(have, wBARO); IF wBARO IN what THEN IF NOT newimg() THEN RETURN END; scale(baro, min.baro, max.baro, FLOAT(MAXY), 2.0, yax0, yax1, step); FixToStr(lastval.baro, 2, s); Append(s, "hPa "); Append(s, h); paper(img, yax0, yax1, step, 1.0, MARGIN, Maxx, MAXY, s); timeline; dots(baro, TRUE, 500, 400, 500); <* IF SAVESTAT THEN *> res:=saveppm("/tmp/baro.ppm", img, Maxx+2*MARGIN, MAXY+2*MARGIN); <* END *> END; END; IF (max.wind<>INVAL) & (max.wind>0.0) THEN INCL(have, wWIND); IF wWIND IN what THEN IF NOT newimg() THEN RETURN END; scale(winds, INVAL, max.wind, FLOAT(MAXY), 20.0, yax0, yax1, step); scale(gust, INVAL, max.wind, FLOAT(MAXY), 20.0, yax0, yax1, step); s[0]:=0C; IF (lastval.winds<>0.0) OR (lastval.gust=0.0) THEN Append(s, colB); FixToStr(lastval.winds, 2, hh); Append(s, hh); Append(s, "km/h Wind "); END; IF lastval.gust<>0.0 THEN Append(s, colR); FixToStr(lastval.gust, 2, hh); Append(s, hh); Append(s, "km/h Gust "); END; Append(s, colW); Append(s, h); paper(img, yax0, yax1, step, 1.0, MARGIN, Maxx, MAXY, s); timeline; dots(winds, TRUE, 100, 500, 700); dots(gust, TRUE, 600, 100, 0); <* IF SAVESTAT THEN *> res:=saveppm("/tmp/wind.ppm", img, Maxx+2*MARGIN, MAXY+2*MARGIN); <* END *> END; END; IF dirvalid THEN INCL(have, wWINDDIR); IF wWINDDIR IN what THEN IF NOT newimg() THEN RETURN END; scale(windd, INVAL, 360.0, FLOAT(MAXY), 365.0, yax0, yax1, step); FixToStr(lastval.winddir, 0, s); Append(s, "deg Wind Direction "); Append(s, h); paper(img, yax0, yax1, 90.0, 1.0, MARGIN, Maxx, MAXY, s); timeline; dots(windd, FALSE, 200, 700, 700); <* IF SAVESTAT THEN *> res:=saveppm("/tmp/winddir.ppm", img, Maxx+2*MARGIN, MAXY+2*MARGIN); <* END *> END; END; IF max.hyg<>INVAL THEN INCL(have, wHYG); IF wHYG IN what THEN IF NOT newimg() THEN RETURN END; scale(hyg, INVAL, 100.0, FLOAT(MAXY), 101.0, yax0, yax1, step); FixToStr(lastval.hyg, 0, s); Append(s, "% Humidity "); Append(s, h); paper(img, yax0, yax1, step, 1.0, MARGIN, Maxx, MAXY, s); timeline; dots(hyg, TRUE, 0, 500, 700); <* IF SAVESTAT THEN *> res:=saveppm("/tmp/hygro.ppm", img, Maxx+2*MARGIN, MAXY+2*MARGIN); <* END *> END; END; IF max.lumi<>INVAL THEN INCL(have, wLUMI); IF wLUMI IN what THEN IF NOT newimg() THEN RETURN END; scale(lumi, INVAL, max.lumi, FLOAT(MAXY), 50.0, yax0, yax1, step); FixToStr(lastval.lumi, 0, s); Append(s, "W/m^2 Luminosity "); Append(s, h); paper(img, yax0, yax1, step, 1.0, MARGIN, Maxx, MAXY, s); timeline; dots(lumi, TRUE, 600, 600, 0); <* IF SAVESTAT THEN *> res:=saveppm("/tmp/lum.ppm", img, Maxx+2*MARGIN, MAXY+2*MARGIN); <* END *> END; END; IF max.rain<>INVAL THEN INCL(have, wRAIN); IF wRAIN IN what THEN IF NOT newimg() THEN RETURN END; scale(rain1, INVAL, max.rain, FLOAT(MAXY), 5.0, yax0, yax1, step); scale(rain24, INVAL, max.rain, FLOAT(MAXY), 5.0, yax0, yax1, step); scale(rain0, INVAL, max.rain, FLOAT(MAXY), 5.0, yax0, yax1, step); IF lastval.rain>=0.0 THEN FixToStr(lastval.rain, 2, hh); s:=colB+"today:"; Append(s, hh) END; IF lastval.rain1>=0.0 THEN FixToStr(lastval.rain1, 2, hh); Append(s, colR+" 1h:"); Append(s, hh) END; IF lastval.rain24>=0.0 THEN FixToStr(lastval.rain24, 2, hh); Append(s, colG+" 24h:"); Append(s, hh) END; Append(s, colW+"mm Rain "); Append(s, h); paper(img, yax0, yax1, step, 1.0, MARGIN, Maxx, MAXY, s); timeline; dots(rain1, TRUE, 500, 100, 0); dots(rain24,TRUE, 50, 600, 50); dots(rain0, TRUE, 100, 100, 700); <* IF SAVESTAT THEN *> res:=saveppm("/tmp/rain.ppm", img, Maxx+2*MARGIN, MAXY+2*MARGIN); <* END *> END; END; IF max.siev>0.0 THEN INCL(have, wSIEV); IF wSIEV IN what THEN IF NOT newimg() THEN RETURN END; IF max.siev<0.005 THEN vh:=1.0; s:="nSv/h" ELSE vh:=0.000001; s:="mSv/h" END; scale(siev, INVAL, max.siev*SIEVMUL, FLOAT(MAXY), 1.0, yax0, yax1, step); sievert2str(lastval.siev, hh); Append(s, " Gamma: "); Append(s, hh); sievert2str(max.siev, hh); Append(s," max:"); Append(s, hh); Append(s, " "); Append(s, h); paper(img, yax0, yax1, step, vh, MARGIN, Maxx, MAXY, s); timeline; dots(siev, TRUE, 700, 700, 0); END; END; IF max.dust10>0 THEN INCL(have, wFINEDUST); IF wFINEDUST IN what THEN IF NOT newimg() THEN RETURN END; scale(dust10,INVAL, FLOAT(max.dust10), FLOAT(MAXY), 2.0, yax0, yax1, step); scale(dust2, INVAL, FLOAT(max.dust10), FLOAT(MAXY), 2.0, yax0, yax1, step); scale(dust1, INVAL, FLOAT(max.dust10), FLOAT(MAXY), 2.0, yax0, yax1, step); scale(dust01,INVAL, FLOAT(max.dust10), FLOAT(MAXY), 2.0, yax0, yax1, step); IF lastval.dust10>0 THEN IntToStr(lastval.dust10, 0, hh); s:=colR+" PM10: "; Append(s, hh) END; IF lastval.dust2>0 THEN IntToStr(lastval.dust2, 0, hh); Append(s,colY+" PM2.5: "); Append(s, hh) END; IF lastval.dust1>0 THEN IntToStr(lastval.dust1, 0, hh); Append(s,colB+" PM1: "); Append(s, hh) END; IF lastval.dust01>0 THEN IntToStr(lastval.dust01, 0, hh); Append(s,colV+" PM0.1: "); Append(s, hh) END; IntToStr(max.dust10, 0, hh); Append(s,colW+" max: "); Append(s, hh); Append(s, "ug/m3 Finedust "); Append(s, h); paper(img, yax0, yax1, step, 1.0, MARGIN, Maxx, MAXY, s); timeline; dots(dust10, TRUE, 700, 50, 0); dots(dust2, TRUE, 700, 700, 0); dots(dust1, TRUE, 0, 200, 700); dots(dust01, TRUE, 400, 0, 700); END; END; what:=have; (* IF img<>NIL THEN DISPOSE(img) END; *) END wxgraph; END aprstat.