%! % 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 %%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%% data below here %%%%%% %%%%%%%%%%%%%%%%%%%%% % temp triangleinput /triangledata [ -1 -2 -3 -4 -5 -6 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 6 5 4 3 2 1 ] store /validtridata false store % temp may not be valid with soft clip % hard coded 95 /clipped true store /doublepeak true store /softclip false store /sinclip 10 store /size 95 store /speed 58 store /sinval 0 store /cosval size store /cycles 8 store /title (95-58-10 hard clip double peak ZERO 3h) store /signal [ 95 94 92 89 85 80 74 67 59 50 40 30 20 10 0 -10 -20 -30 -40 -50 -59 -67 -74 -80 -85 -89 -92 -94 -95 -95 -94 -92 -89 -85 -80 -74 -67 -59 -50 -40 -30 -20 -10 0 10 20 30 40 50 59 67 74 80 85 89 92 94 95 ] store findfourier intf3 == intf5 == intf7 == intf9 == totaldist == (\n ) print makepage % hard coded soft 94 /clipped true store /doublepeak true store /softclip false store /sinclip 10 store /size 94 store /speed 58 store /sinval 0 store /cosval size store /cycles 8 store /title (94-58-10 soft clip double peak low 3h & thd) store /signal [ 94 93 91 88 84 79 73 66 58 49 40 30 20 10 0 -10 -20 -30 -40 -49 -58 -66 -73 -79 -84 -88 -91 -93 -94 -94 -93 -91 -88 -84 -79 -73 -66 -58 -49 -40 -30 -20 -10 0 10 20 30 40 49 58 66 73 79 84 88 91 93 94 ] store findfourier intf3 == intf5 == intf7 == intf9 == totaldist == makepage (\n ) print %%%%%%%%%%%%%%%%%%%%%%%%%%%% /clipped true store /doublepeak true store /softclip false store /sinclip 10 store /size 93 store /speed 58 store /sinval 0 store /cosval size store /cycles 8 store /title ( 93-58-10 soft clip double peak ( alternate )) store /signal [ 93 92 90 87 83 78 72 65 57 48 39 30 20 10 0 -10 -20 -30 -39 -48 -57 -65 -72 -78 -83 -87 -90 -92 -93 -93 -92 -90 -87 -83 -78 -72 -65 -57 -48 -39 -30 -20 -10 0 10 20 30 39 48 57 65 72 78 83 87 90 92 93 ] store findfourier intf3 == intf5 == intf7 == intf9 == totaldist == makepage (\n ) print %%%%%%%%%%%%%%%%%%%%%%%%%%%% triple soft /clipped true store /doublepeak true store /softclip false store /sinclip 10 store /size 92 store /speed 58 store /sinval 0 store /cosval size store /cycles 8 store /title ( 92-58-10 soft 2x clip double peak ( best so far!)) store /signal [ 92 91 89 86 82 77 71 64 56 48 39 30 20 10 0 -10 -20 -30 -39 -48 -56 -64 -71 -77 -82 -86 -89 -91 -92 -92 -91 -89 -86 -82 -77 -71 -64 -56 -48 -39 -30 -20 -10 0 10 20 30 39 48 56 64 71 77 82 86 89 91 92 ] store findfourier intf3 == intf5 == intf7 == intf9 == totaldist == makepage (\n ) print %%%%%%%%%%%%%%%%%%%%%%%%%%%% highest amplitude partial % autocoded 95 /clipped true store /doublepeak true store /softclip false store /sinclip 10 store /size 95 store /speed 58 store /sinval 0 store /cosval size store /cycles 8 store /title (95-58-10 hard clip double peak ZERO 3h) store /signal [ 95 94 92 89 85 80 74 67 59 50 40 30 20 10 0 -10 -20 -30 -40 -50 -59 -67 -74 -80 -85 -89 -92 -94 -95 -95 -94 -92 -89 -85 -80 -74 -67 -59 -50 -40 -30 -20 -10 0 10 20 30 40 50 59 67 74 80 85 89 92 94 95 ] store (\n\nhard coded ) print signal == (\n\n \n\n) print 25 { increment sinval cosval == ==} repeat (\n\n\n) print /size 95 store /speed 58 store /sinval 0 store /cosval size store /cycles 8 store /title (95-58-10 hard clip double peak ZERO 3h) store mark cosval speed 1 sub { increment cosval }repeat ] /signal exch store signal == findfourier intf3 == intf5 == intf7 == intf9 == totaldist == makepage (\n ) print % highest amplitude no soft /clipped true store /doublepeak true store /softclip false store /sinclip 12 store /size 126 store /speed 66 store /sinval 0 store /cosval size store /cycles 7 store /title (126-78-12 hard clip ( high amplitude )) store mark cosval speed 1 sub { increment cosval }repeat ] /signal exch store signal == findfourier intf3 == intf5 == intf7 == intf9 == totaldist == makepage (\n ) print %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % highest amplitude one soft /clipped true store /doublepeak true store /softclip false store /sinclip 12 store /size 125 store /speed 66 store /sinval 0 store /cosval size store /cycles 7 store /title (125-78-12 soft2 clip ( high amplitude )) store /signal [125 124 122 119 115 110 104 97 89 80 70 59 48 36 24 12 0 -12 -24 -36 -48 -59 -70 -80 -89 -97 -104 -110 -115 -119 -122 -124 -125 -125 -124 -122 -119 -115 -110 -104 -97 -89 -80 -70 -59 -48 -36 -24 -12 0 12 24 36 48 59 70 80 89 97 104 110 115 119 122 124 125] store findfourier intf3 == intf5 == intf7 == intf9 == totaldist == makepage (\n ) print %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % eof