%! % A modified efficient digital sinewave algorithm scanner % ===================================== % Don Lancaster and Synergetics www.tinaja.com % Some ultra compact low end digital sinewave generators % were described in Column 85 of https://www.tinaja.com/glib/hackar4.pdf % It appears that better performance and lower distortion can be % had by making the initial triangle approximation look more like % a true cosine wave. This can be done by adding adjustments to a % simple clipping. At a penalty of only a few bytes extra. % This code scans for candidate sinewaves having no dc offset and % a "unity" cosine match % This file is http://www.tinaja.com/psutils/sincat1.psl It is normally % run by sending the file to Distiller by way of command line //acrodist /F. % It should also be GhostScript or Google Drive compatible. % The companion demo is http://www.tinaja.com/psutils/sincat1.pdf % All distortions are BEFORE filtering! % Development services available via don@tinaja.com. % ///////// (A) WEB FRIENDLY COLOR UTILITIES ///////////// % tintmat is a self-generating list of 216 triple color values /webtintmat [ 0 1 5 { /a exch store 0 1 5 { /b exch store 0 1 5 { 5 div b 5 div a 5 div }for } for } for ] def % setwebtint accepts a color number 0 to 215 and then % sets the PostScript color generator for later use... /setwebtint { abs cvi 216 cvi mod % restrict range webtintmat exch 3 mul 3 getinterval % get values from table aload pop setrgbcolor} def % and set them % The blocks are arranged as red to the right, green % up in order of increasing blue. % Some "pure color" sequences are % red: 0 1 2 3 4 5 % orange: 0 7 8 15 16 23 (sort of) % yellow: 0 7 14 21 28 35 % green: 0 6 12 18 24 30 % aqua: 0 42 84 126 168 210 % blue: 0 36 72 108 144 180 % magenta: 0 37 74 111 148 185 % purple 0 73 73 110 147 183 (sort of) % gray 0 43 86 129 172 215 % ////////////// GONZO EXCERPTS ////////////////// /setgrid { /blocksize exch def translate % simplified blocksize dup scale} def % repeats [ proc distance trips] xrpt /xrpt{gsave aload pop /trips exch def /dist exch def /rproc exch def trips { gsave rproc grestore dist 0 translate } repeat grestore} def /yrpt{gsave aload pop /trips exch def /dist exch def /rproc exch def trips { gsave rproc grestore 0 dist translate } repeat grestore} def % mergestr merges the two top stack strings into one top stack string /mergestr {2 copy length exch length add string dup dup 4 3 roll 4 index length exch putinterval 3 1 roll exch 0 exch putinterval} def % convert an array of low integers into a string /makestring {dup length string dup /NullEncode filter 3 -1 roll {1 index exch write} forall pop} def /showgrid {gsave /vblocks exch def /hblocks exch def thingridlines setlinewidth [{0 0 moveto 0 vblocks rlineto stroke} 1 hblocks 1 add] xrpt [{0 0 moveto hblocks 0 rlineto stroke} 1 vblocks 1 add] yrpt fatterborder { gsave newpath 0 0.96 blocksize div dtransform round idtransform setlinewidth pop 2 setlinecap 0 0 moveto hblocks 0 rlineto 0 vblocks rlineto hblocks neg 0 rlineto closepath stroke grestore} if fat5 { gsave newpath 0 0.48 blocksize div dtransform round idtransform setlinewidth pop mark {5 0 moveto 0 vblocks rlineto stroke} 5 hblocks 5 div cvi] xrpt mark {0 5 moveto hblocks 0 rlineto stroke} 5 vblocks 5 div cvi] yrpt grestore} if fatter10 { gsave newpath 0 0.96 blocksize div dtransform round idtransform setlinewidth pop mark {10 0 moveto 0 vblocks rlineto stroke} 10 hblocks 10 div cvi] xrpt mark {0 10 moveto hblocks 0 rlineto stroke} 10 vblocks 10 div cvi] yrpt grestore} if grestore} def /fatterborder {true } store /fat5 {true} store /fatter10 {true} store /thingridlines {0} store /dot { currentpoint newpath 0.150 0 360 arc fill } def /mdot { moveto dot} def %%%%%%%%%%%%%%%%%%%%%%%% Fourier Service Module %%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%% fourier begins here %%%%%%%%%%%%%%%%% zzz /findfourier { % based on skilling 443 % (\nentering findfor with signal of ) print signal == signal length 1 sub /points exch store 360 points div /inc exch store 0 % fundamental cosine 1 1 points { /posn exch store posn inc mul inc 2 div sub /ang exch store signal posn get signal posn 1 sub get add 2 div ang cos mul add } for /intf1 exch points div store 0 % third harmonic cosine 1 1 points { /posn exch store posn inc mul inc 2 div sub /ang exch store signal posn get signal posn 1 sub get add 2 div ang 3 mul cos mul add} for /intf3 exch points dup 0 eq {pop 0.00001} if div intf1 dup 0 eq {pop 0.00001} if div store % relative to fundamental 0 1 1 points { /posn exch store posn inc % fifth harmonic cosine mul inc 2 div sub /ang exch store signal posn get signal posn 1 sub get add 2 div ang 5 mul cos mul add } for /intf5 exch points dup 0 eq {pop 0.00001} if div intf1 dup 0 eq {pop 0.00001} if div store 0 1 1 points { /posn exch store posn inc % seventh harmonic cosine mul inc 2 div sub /ang exch store signal posn get signal posn 1 sub get add 2 div ang 7 mul cos mul add } for /intf7 exch points dup 0 eq {pop 0.00001} if div intf1 dup 0 eq {pop 0.00001} if div store 0 1 1 points { /posn exch store posn inc % ninth harmonic cosine mul inc 2 div sub /ang exch store signal posn get signal posn 1 sub get add 2 div ang 9 mul cos mul add } for /intf9 exch points dup 0 eq {pop 0.00001} if div intf1 dup 0 eq {pop 0.00001} if div store 0 1 1 points { /posn exch store posn inc % eleventh harmonic cosine mul inc 2 div sub /ang exch store signal posn get signal posn 1 sub get add 2 div ang 11 mul cos mul add } for /intf11 exch points dup 0 eq {pop 0.00001} if div intf1 dup 0 eq {pop 0.00001} if div store intf3 dup mul % totl distortion 3-9 percent intf5 dup mul add intf7 dup mul add intf9 dup mul add sqrt 100 mul /totaldist exch store } store /fourierdemo { % test module only /signal [ % triangle wave -1/9 1/25 -1/49 1/81 slight errors normal for this sample 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -19 -18 -17 -16 -15 -14 -13 -12 -11 -10 -9 -8 -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20] store findfourier intf1 == intf3 == intf5 == intf7 == intf9 == totaldist == (\n) print } store % fourierdemo % comment to stop fourier test /findharmonics { % make sure speed is exactly one cycle! /cosval size store /sinval 0 store mark % start signal array cosval 0 1 speed 2 sub {pop increment cosval } for ] /signal exch store signal == % comment to stop report signal length == findfourier intf3 == intf5 == intf7 == intf9 == totaldist == % reportharms } store %%%%%%%%%%%%%%%%%%%%%%%%%%% end Fourier service modules %%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%% triangle series increment code %%%%%%%%%%%%%%%%% /clipped true store /doublepeak true store /softclip true store /sinclip 10 store % softclip presently has to be manually overrided /increment { % softclip not yet valid /sinval sinval cosval 0 gt {-1}{+1}ifelse doublepeak {cosval 0 eq {pop 0} if} if % for double peak add store /cosval cosval sinval % softclip {sinval sinclip eq {pop sinclip 1 sub} if } store % softclip { sinval neg sinclip eq {pop sinclip neg 1 add} if } store clipped { sinval sinclip ge {pop sinclip} if } if clipped { sinval sinclip neg lt {pop sinclip neg} if } if add store } store /findsignal { % this version requires predefined speed /signal mark size 0 1 speed 1 sub {increment cosval} for ] store signal == signal == } store /makepage {50 50 10 setgrid 32 setwebtint 50 68 showgrid findsignaldelta gsave 1 14 translate refamplitudes showoutputcos showintsin showrefcos printsindata validtridata { printtridata } if showrefsin grestore printcosdata printcosdata printdata printtitle printharm showpage } store %%%%% /refamplitudes {5 setwebtint 0 setlinewidth 0 0 moveto 48 0 lineto stroke 0 sinclip 10 div moveto 48 sinclip 10 div lineto stroke 0 sinclip 10 div neg moveto 48 sinclip neg 10 div lineto stroke 0 size 10 div moveto 48 size 10 div lineto stroke 0 size 10 div neg moveto 48 size 10 div neg lineto stroke } store %%%% %%%%%%%%%%% print data %%%%%%%%%%%%%%% /printdata { gsave 0 27 translate /StoneSans-Bold findfont 1 scalefont setfont 0 setwebtint 1 10 moveto (Cosine Amplitude "size" : ) size 20 string cvs mergestr show 1 8.5 moveto (Cosine Time Period "speed" : ) speed 20 string cvs mergestr show 1 7 moveto (Clipped Sin Amplitude "sinclip" : ) sinclip 20 string cvs mergestr show 1 5.5 moveto (Cycles Displayed : ) cycles 1000 string cvs mergestr show 1 4 moveto (Soft Clip: ) softclip 10 string cvs mergestr show 1 2.5 moveto (Double Peak: ) doublepeak 10 string cvs mergestr show 1 1 moveto (Clipped: ) clipped 10 string cvs mergestr show grestore} store /printcosdata { gsave 0 26.5 translate % show the output cosine /StoneSans-Bold findfont 1 scalefont setfont 0 setwebtint /vpos1 19 store 1 vpos1 moveto (Approximate Cosine Wave Output:) show /vpos1 vpos1 1.5 sub store 3 vpos1 moveto ([ ) show /wide speed 4 div cvi store 0 1 signal length 1 sub {/posn1 exch store signal posn1 get 20 string cvs show ( ) show posn1 1 add wide eq posn1 1 add wide 2 mul eq or posn1 1 add wide 3 mul eq or {/vpos1 vpos1 1.5 sub store 4 vpos1 moveto} if } for ( ]) show grestore } store /printsindata { gsave -1 23 translate % show the output cosine /StoneSans-Bold findfont 1 scalefont setfont 0 setwebtint /vpos1 19 store 1 vpos1 moveto (Approximate Internal Sinewave:) show /vpos1 vpos1 1.5 sub store 3 vpos1 moveto ([ ) show /wide speed 4 div cvi store 0 1 signaldelta length 1 sub {/posn1 exch store signaldelta posn1 get 20 string cvs show ( ) show posn1 1 add wide eq posn1 1 add wide 2 mul eq or posn1 1 add wide 3 mul eq or {/vpos1 vpos1 1.5 sub store 4 vpos1 moveto} if } for ( ]) show grestore } store /printtridata { gsave -1 24 translate % show the output cosine /StoneSans-Bold findfont 1 scalefont setfont 0 setwebtint /vpos1 25 store 1 vpos1 moveto (Initial Triangle Series:) show /vpos1 vpos1 1.5 sub store 3 vpos1 moveto ([ ) show /wide speed 4 div cvi store 0 1 triangledata length 1 sub {/posn1 exch store triangledata posn1 get 20 string cvs show ( ) show posn1 1 add wide eq posn1 1 add wide 2 mul eq or posn1 1 add wide 3 mul eq or {/vpos1 vpos1 1.5 sub store 4 vpos1 moveto} if } for ( ]) show grestore } store %%%%%%%%%%%% print harmonics %%%%%%%% /printharm {gsave 0 27 translate /StoneSans-Bold findfont 1 scalefont setfont 0 setwebtint 25 10 moveto 156 setwebtint ( Unfiltered! ) show 0 setwebtint 25 8.5 moveto (Third Harmonic: ) show intf3 100 mul 15 string cvs show ( percent.) show 25 7 moveto (Fifth Harmonic: ) show intf5 100 mul 15 string cvs show ( percent.) show 25 5.5 moveto (Seventh Harmonic: ) show intf7 100 mul 15 string cvs show ( percent.) show 25 4 moveto (Ninth Harmonic: ) show intf9 100 mul 15 string cvs show ( percent.) show 25 2.5 moveto ( THD 3 - 9 : ) show totaldist 1.0 mul 15 string cvs show ( percent.) show grestore } store %%%% print title /printtitle { gsave 0 24 translate currentdict /title known { /StoneSans-Bold findfont 2 scalefont setfont 0 40 moveto ( ) show title 156 setwebtint show } if grestore} store /showoutputcos { gsave 204 setwebtint 0.17 setlinewidth 1 setlinejoin 1 setlinecap 0 size 10 div moveto cycles { 1 1 speed 1 sub { /posn exch store /val signal posn get store 0.1 0 rlineto posn 10 div val 10 div lineto } for currentpoint 0.2 0 rlineto stroke exch 0.15 add exch moveto speed 10 div 0 translate } repeat grestore } store /showintsin { gsave 0.05 0 translate % align with cosine 202 setwebtint 0.17 setlinewidth 1 setlinejoin 1 setlinecap 0 -.1 moveto cycles { 1 1 signaldelta length 1 sub { /posn exch store /val signaldelta posn get store 0.1 0 rlineto posn 10 div val 10 div lineto } for currentpoint 0 -0.01 rlineto .1 0 rlineto 0 -0.1 rlineto 0.1 0 rlineto stroke exch 0.15 add exch moveto speed 10 div 0 translate 0 -0.1 moveto } repeat grestore } store %%%%%%%%%%% show reference sin %%%%%%%%%%%%%% /showrefsin {gsave 0 setwebtint 1 setlinejoin 1 setlinecap cycles { 0 0 moveto 0 5 360 {/degs exch store /posn degs speed mul 360 div 10 div store /ampl degs sin neg sinclip mul 10 div store posn ampl lineto } for stroke speed 10 div 0 translate } repeat grestore } store %%%%%%%%%%% show reference cosine %%%%%%%%%%%%%% /showrefcos {gsave 0 setwebtint 1 setlinejoin 1 setlinecap cycles { 0 size 10 div moveto 0 5 360 {/degs exch store /posn degs speed mul 360 div 10 div store /ampl degs cos size mul 10 div store posn ampl lineto } for stroke speed 10 div 0 translate } repeat grestore } store %%%%%% /findsignaldelta { mark 0 1 signal length 2 sub {/posn exch store signal posn get signal posn 1 add get sub neg } for ] /signaldelta exch store signaldelta == } store /validtridata false store % temp may not be valid with soft clip %%%%%%%%%%%%%%%%%%%%%%%% quarter lookup signal generator %%%%%%%%%%%%%%%%%% /generate3 { dup 0 get /target exch store 1 get /title exch store /size 0 target {add} forall store /speed target length 4 mul 2 add store /sinclip target dup length 1 sub get store /cycles 490 speed div floor cvi store true { title == target == size == speed == sinclip == cycles == } if /signal mark size 0 1 target length 1 sub {/posn exch store % first quadrant dup target posn get sub} for target length 1 sub -1 0 { /posn1 exch store dup target posn1 get sub } for dup % double bottom peak 0 1 target length 1 sub {/posn exch store % third quadrant dup target posn get add} for target length 1 sub -1 0 { /posn1 exch store dup target posn1 get add } for ] store signal == /sinval 0 store /cosval size store findfourier intf3 == intf5 == intf7 == intf9 == totaldist == makepage (\n ) print } store %%%%% data %%%%% % while the /increment method internal approach above is often more % suitable for low end microcomputer use, the table lookup method % involving generate3 was used to create the follwing retults... /clipped true store /doublepeak true store /softclip false store %%%%%%%%%%%%%%%%%%%%%%% % clipped fours [[ 1 2 3 4 4] (14-18-04 hard clipped double peak ) ] generate3 % 0.0! 0.39 % clipped fives % none yet found suitable % clipped sixes [[ 1 2 3 4 5 6 6 6] (33-34-06 hard clipped dual peak) ] generate3 % 0.0! 0.55 [[ 1 2 3 4 5 5 6 6] (32-34-06 dual soft clipped dual peak) ] generate3 % .183 .251 % clipped sevens [[1 2 3 4 5 6 6 7 7 ] (41-38-07 single soft clip single peak)] generate3 % .362 .392 % clipped eghts [[ 1 2 3 4 5 6 7 8 8 8 8] (60-46-08 hard clip double peak) ] generate3 % .000 .628 [[ 1 2 3 4 5 6 7 7 8 8 8] (59-46-08 single soft clip double peak) ] generate3 % .073 .25 % clipped nines [[1 2 3 4 5 6 7 8 9 9 9 9 ] (72-50-09 hard clip single peak)] generate3 % .500 .737 [[1 2 3 4 5 6 7 8 8 9 9 9 ] (71-50-09 single soft clip single peak)] generate3 % .377 .448 % clipped tens [[1 2 3 4 5 6 7 8 9 10 10 10 10 10 ] (92-58-10 hard clip double peak)] generate3 % 0.0 0.67 [[1 2 3 4 5 6 7 8 9 9 10 10 10 10 ] (94-58-10 single soft clip double peak)] generate3 % .036 0.41 [[1 2 3 4 5 6 7 8 8 9 9 10 10 10 ] (92-58-10 double soft clip double peak)] generate3 % .109 0.141 % clipped elevens [[1 2 3 4 5 6 7 8 9 10 11 11 11 11 11 ] (110-62-11 hard clip single peak)] generate3 % .409 .727 [[1 2 3 4 5 6 7 8 9 10 10 10 11 11 11 ] (108-62-11 single clip single peak)] generate3 % .166 .361 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % original triangle series % except for the stunning [ 1 2 3 4 4...], results are mostly above one percent third. [[1 2 3 ] ( 06-14-03 triangle series no clip single peak)] generate3 % 1.23 1.32 [[1 2 3 3 ] ( 09-18-04 triangle series no clip double peak)] generate3 % 1.31 1.44 [[1 2 3 4 ] (10-18-04 triangle series no clip single peak)] generate3 % 1.90 2.01 [[1 2 3 4 4] (14-22-04 triangle series no clip double peak)] generate3 % 0.00 0.392 [[1 2 3 4 5] (15-22-05 triangle series no clip single peak)] generate3 % 2.30 2.42 [[1 2 3 4 5 5] (20-26-05 triangle series no clip double peak)] generate3 % 0.87 0.93 [[1 2 3 4 5 6] (21-26-06 triangle series no clip single peak)] generate3 % 2.56 2.68 [[1 2 3 4 5 6 6] (27-30-06 triangle series no clip double peak)] generate3 % 1.45 1.49 [[1 2 3 4 5 6 7 ] (28-30-07 triangle series no clip single peak)] generate3 % 2.74 2.86 [[1 2 3 4 5 6 7 7](35-34-07 triangle series no clip double peak)] generate3 % 0.86 0.90 [[1 2 3 4 5 6 7 8 ] (36-34-08 triangle series no clip single peak)] generate3 % 2.88 2.99 [[1 2 3 4 5 6 7 8 8] (44-38-08 triangle series no clip double peak)] generate3 % 2.16 2.20 [[1 2 3 4 5 6 7 8 9 ] (45-38-09 triangle series no clip single peak)] generate3 % 2.97 3.09 [[1 2 3 4 5 6 7 8 9 9] (54-42-09 triangle series no clip double peak)] generate3 % 2.38 2.43 [[1 2 3 4 5 6 7 8 9 10 ] (55-42-10 triangle series no clip single peak)] generate3 % 3.05 3.17 [[1 2 3 4 5 6 7 8 9 10 10] (65-46-10 triangle series no clip double peak)] generate3 % 2.56 2.61 [[1 2 3 4 5 6 7 8 9 10 11] (66-46-11 triangle series no clip single peak)] generate3 % 3.11 3.23 [[1 2 3 4 5 6 7 8 9 10 11 11] ( 77-50-11 triangle series no clip double peak)] generate3 % 2.70 2.75 [[1 2 3 4 5 6 7 8 9 10 11 12] (78-50-12 triangle series no clip single peak)] generate3 % 3.17 3.29 [[1 2 3 4 5 6 7 7 8 9 10 11 12 12] ( - -04 triangle series no clip single peak)] generate3 % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % 4 sinclip series magnifies [1 2 3 4 4...] % still has display glitches in display repeat cycles /softclip false store [[1 2 3 4 4] (14-22-04 triangle series no clip double peak)] generate3 % 0.0 0.39 [[2 4 6 8 8 ] (28-22-08 triangle mag series no clip single peak)] generate3 % 0.0 0.39 [[3 6 9 12 12 ] (42-22-12 triangle mag series no clip single peak)] generate3 % 0.0 0.39 [[4 8 12 16 16 ] (56-22-16 triangle mag series no clip single peak)] generate3 % 0.0 0.39 [[5 10 15 20 20 ] (70-22-20 triangle mag series no clip single peak)] generate3 % 0.0 0.39 [[6 12 18 24 24 ] (84-22-24 triangle mag series no clip single peak)] generate3 % 0.0 0.39 [[7 14 21 28 28 ] (98-22-28 triangle mag series no clip single peak)] generate3 % 0.0 0.39 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % results [ size speed sinclip 3H 3H-9H } /results [ [11 62 11 .166 .361 ] [108 62 11 .409 .727 ] [ 92 58 10 .109 .141 ] % best double soft [ 94 58 10 .036 .419 ] % good single soft [ 95 58 10 .000 .672 ] % ZERO third [ 71 50 09 .377 .448 ] [ 72 50 09 .500 .737 ] [ 59 46 08 .073 .251 ] % good single soft [ 60 46 08 .000 .628 ] % ZERO third [ 41 38 07 .362 .392 ] % [ 32 34 06 .183 .251 ] % good single soft [ 33 34 06 0.0 .551 ] % ZERO third [ 14 18 04 0.0 .390 ] % ZERO third ] store %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% /results4mag { % ultra fast and compact, low fifth [14-22-04 0.0 0.39] % ZERO third [28-22-04 0.0 0.39] % ZERO third 2x magnification limited sample rate [32-22-04 0.0 0.39] % ZERO third 3x magnification limited sample rate [56-22-04 0.0 0.39] % ZERO third 4x magnification limited sample rate [70-22-04 0.0 0.39] % ZERO third 5x magnification limited sample rate [84-22-04 0.0 0.39] % ZERO third 6x magnification limited sample rate [108-22-04 0.0 0.39] % ZERO third 7x magnification limited sample rate } store % eof