fourier.tcl
Go to the documentation of this file.00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048 namespace ::math::fourier {
00049
00050
00051 namespace export dft inverse_dft lowpass highpass
00052 }
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065 ret ::math::fourier::dft (type in_, type data) {
00066 # First convert to internal format
00067 set dataL [list]
00068 set n 0
00069 foreach datum $in_data {
00070 if {[llength $datum] == 1} then {
00071 lappend dataL $datum 0.0
00072 } else {
00073 lappend dataL [lindex $datum 0] [lindex $datum 1]
00074 }
00075 incr n
00076 }
00077
00078 # Then compute a list of n'th roots of unity (explanation below)
00079 set rootL [DFT_make_roots $n -1]
00080
00081 # Check if the input length is a power of two.
00082 set p 1
00083 while {$p < $n} {set p [expr {$p << 1}]}
00084 # By construction, $p is a power of two. If $n==$p then $n is too.
00085
00086 # Finally compute the transform using Fast_DFT or Slow_DFT,
00087 # and convert back to the input format.
00088 set res [list]
00089 foreach {Re Im} [
00090 if {$p == $n} then {
00091 Fast_DFT $dataL $rootL
00092 } else {
00093 Slow_DFT $dataL $rootL
00094 }
00095 ] {
00096 lappend res [list $Re $Im]
00097 }
00098 return $res
00099 }
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113 ret ::math::fourier::inverse_dft (type in_, type data) {
00114 # First convert to internal format
00115 set dataL [list]
00116 set n 0
00117 foreach datum $in_data {
00118 if {[llength $datum] == 1} then {
00119 lappend dataL $datum 0.0
00120 } else {
00121 lappend dataL [lindex $datum 0] [lindex $datum 1]
00122 }
00123 incr n
00124 }
00125
00126 # Then compute a list of n'th roots of unity (explanation below)
00127 set rootL [DFT_make_roots $n 1]
00128
00129 # Check if the input length is a power of two.
00130 set p 1
00131 while {$p < $n} {set p [expr {$p << 1}]}
00132 # By construction, $p is a power of two. If $n==$p then $n is too.
00133
00134 # Finally compute the transform using Fast_DFT or Slow_DFT,
00135 # divide by input data length to correct the amplitudes,
00136 # and convert back to the input format.
00137 set res [list]
00138 foreach {Re Im} [
00139 # $p is power of two. If $n==$p then $n is too.
00140 if {$p == $n} then {
00141 Fast_DFT $dataL $rootL
00142 } else {
00143 Slow_DFT $dataL $rootL
00144 }
00145 ] {
00146 lappend res [list [expr {$Re/$n}] [expr {$Im/$n}]]
00147 }
00148 return $res
00149 }
00150
00151
00152
00153
00154
00155
00156
00157
00158
00159
00160 ret ::math::fourier::DFT_make_roots (type n , type sign) {
00161 set res [list]
00162 for {set k 0} {2*$k < $n} {incr k} {
00163 set alpha [expr {2*3.1415926535897931*$sign*$k/$n}]
00164 lappend res [expr {cos($alpha)}] [expr {sin($alpha)}]
00165 }
00166 return $res
00167 }
00168
00169
00170
00171
00172
00173
00174
00175
00176
00177
00178 ret ::math::fourier::Fast_DFT (type dataL , type rootL) {
00179 if {[llength $dataL] == 8} then {
00180 foreach {Re_z0 Im_z0 Re_z1 Im_z1 Re_z2 Im_z2 Re_z3 Im_z3} $dataL {break}
00181 if {[lindex $rootL 3] > 0} then {
00182 return [list\
00183 [expr {$Re_z0 + $Re_z1 + $Re_z2 + $Re_z3}] [expr {$Im_z0 + $Im_z1 + $Im_z2 + $Im_z3}]\
00184 [expr {$Re_z0 - $Im_z1 - $Re_z2 + $Im_z3}] [expr {$Im_z0 + $Re_z1 - $Im_z2 - $Re_z3}]\
00185 [expr {$Re_z0 - $Re_z1 + $Re_z2 - $Re_z3}] [expr {$Im_z0 - $Im_z1 + $Im_z2 - $Im_z3}]\
00186 [expr {$Re_z0 + $Im_z1 - $Re_z2 - $Im_z3}] [expr {$Im_z0 - $Re_z1 - $Im_z2 + $Re_z3}]]
00187 } else {
00188 return [list\
00189 [expr {$Re_z0 + $Re_z1 + $Re_z2 + $Re_z3}] [expr {$Im_z0 + $Im_z1 + $Im_z2 + $Im_z3}]\
00190 [expr {$Re_z0 + $Im_z1 - $Re_z2 - $Im_z3}] [expr {$Im_z0 - $Re_z1 - $Im_z2 + $Re_z3}]\
00191 [expr {$Re_z0 - $Re_z1 + $Re_z2 - $Re_z3}] [expr {$Im_z0 - $Im_z1 + $Im_z2 - $Im_z3}]\
00192 [expr {$Re_z0 - $Im_z1 - $Re_z2 + $Im_z3}] [expr {$Im_z0 + $Re_z1 - $Im_z2 - $Re_z3}]]
00193 }
00194 } elseif {[llength $dataL] > 8} then {
00195 set evenL [list]
00196 set oddL [list]
00197 foreach {Re_z0 Im_z0 Re_z1 Im_z1} $dataL {
00198 lappend evenL $Re_z0 $Im_z0
00199 lappend oddL $Re_z1 $Im_z1
00200 }
00201 set squarerootL [list]
00202 foreach {Re_omega0 Im_omega0 Re_omega1 Im_omega1} $rootL {
00203 lappend squarerootL $Re_omega0 $Im_omega0
00204 }
00205 set lowL [list]
00206 set highL [list]
00207 foreach\
00208 {Re_y0 Im_y0} [Fast_DFT $evenL $squarerootL]\
00209 {Re_y1 Im_y1} [Fast_DFT $oddL $squarerootL]\
00210 {Re_omega Im_omega} $rootL {
00211 set Re_y1t [expr {$Re_y1 * $Re_omega - $Im_y1 * $Im_omega}]
00212 set Im_y1t [expr {$Im_y1 * $Re_omega + $Re_y1 * $Im_omega}]
00213 lappend lowL [expr {$Re_y0 + $Re_y1t}] [expr {$Im_y0 + $Im_y1t}]
00214 lappend highL [expr {$Re_y0 - $Re_y1t}] [expr {$Im_y0 - $Im_y1t}]
00215 }
00216 return [concat $lowL $highL]
00217 } elseif {[llength $dataL] == 4} then {
00218 foreach {Re_z0 Im_z0 Re_z1 Im_z1} $dataL {break}
00219 return [list\
00220 [expr {$Re_z0 + $Re_z1}] [expr {$Im_z0 + $Im_z1}]\
00221 [expr {$Re_z0 - $Re_z1}] [expr {$Im_z0 - $Im_z1}]]
00222 } else {
00223 return $dataL
00224 }
00225 }
00226
00227
00228
00229
00230
00231
00232
00233
00234
00235
00236 ret ::math::fourier::Slow_DFT (type dataL , type rootL) {
00237 set n [expr {[llength $dataL] / 2}]
00238
00239 # The missing roots are computed by complex conjugating the given
00240 # roots. If $n is even then -1 is also needed; it is inserted explicitly.
00241 set k [llength $rootL]
00242 if {$n % 2 == 0} then {
00243 lappend rootL -1.0 0.0
00244 }
00245 for {incr k -2} {$k > 0} {incr k -2} {
00246 lappend rootL [lindex $rootL $k]\
00247 [expr {-[lindex $rootL [expr {$k+1}]]}]
00248 }
00249
00250 # This is strictly following the naive formula.
00251 # The product jk is kept as a separate counter variable.
00252 set res [list]
00253 for {set k 0} {$k < $n} {incr k} {
00254 set Re_sum 0.0
00255 set Im_sum 0.0
00256 set jk 0
00257 foreach {Re_z Im_z} $dataL {
00258 set Re_omega [lindex $rootL [expr {2*$jk}]]
00259 set Im_omega [lindex $rootL [expr {2*$jk+1}]]
00260 set Re_sum [expr {$Re_sum +
00261 $Re_z * $Re_omega - $Im_z * $Im_omega}]
00262 set Im_sum [expr {$Im_sum +
00263 $Im_z * $Re_omega + $Re_z * $Im_omega}]
00264 incr jk $k
00265 if {$jk >= $n} then {set jk [expr {$jk - $n}]}
00266 }
00267 lappend res $Re_sum $Im_sum
00268 }
00269 return $res
00270 }
00271
00272
00273
00274
00275
00276
00277
00278
00279
00280
00281 ret ::math::fourier::lowpass (type cutoff , type in_, type data) {
00282 package require math::complexnumbers
00283
00284 set res [list]
00285 set cutoff [list $cutoff 0.0]
00286 set f 0.0
00287 foreach a $in_data {
00288 set an [::math::complexnumbers::/ $a \
00289 [::math::complexnumbers::+ {1.0 0.0} \
00290 [::math::complexnumbers::/ [list 0.0 $f] $cutoff]]]
00291 lappend res $an
00292 set f [expr {$f+1.0}]
00293 }
00294
00295 return $res
00296 }
00297
00298
00299
00300
00301
00302
00303
00304
00305
00306
00307 ret ::math::fourier::highpass (type cutoff , type in_, type data) {
00308 package require math::complexnumbers
00309
00310 set res [list]
00311 set cutoff [list $cutoff 0.0]
00312 set f 0.0
00313 foreach a $in_data {
00314 set ff [::math::complexnumbers::/ [list 0.0 $f] $cutoff]
00315 set an [::math::complexnumbers::/ $ff \
00316 [::math::complexnumbers::+ {1.0 0.0} $ff]]
00317 lappend res $an
00318 set f [expr {$f+1.0}]
00319 }
00320
00321 return $res
00322 }
00323
00324
00325
00326
00327 package provide math::fourier 1.0.2
00328
00329
00330
00331 ret test_dft (type points , optional real =0 , optional iterations =20) {
00332 set in_dataL [list]
00333 for {set k 0} {$k < $points} {incr k} {
00334 if {$real} then {
00335 lappend in_dataL [expr {2*rand()-1}]
00336 } else {
00337 lappend in_dataL [list [expr {2*rand()-1}] [expr {2*rand()-1}]]
00338 }
00339 }
00340 set time1 [time {
00341 set conv_dataL [::math::fourier::dft $in_dataL]
00342 } $iterations]
00343 set time2 [time {
00344 set out_dataL [::math::fourier::inverse_dft $conv_dataL]
00345 } $iterations]
00346 set err 0.0
00347 foreach iz $in_dataL oz $out_dataL {
00348 if {$real} then {
00349 foreach {o1 o2} $oz {break}
00350 set err [expr {$err + ($i-$o1)*($i-$o1) + $o2*$o2}]
00351 } else {
00352 foreach i $iz o $oz {
00353 set err [expr {$err + ($i-$o)*($i-$o)}]
00354 }
00355 }
00356 }
00357 return [format "Forward: %s\nInverse: %s\nAverage error: %g"\
00358 $time1 $time2 [expr {sqrt($err/$points)}]]
00359 }
00360
00361
00362
00363
00364 if { 0 } {
00365 puts [::math::fourier::dft {1 2 3 4}]
00366 puts [::math::fourier::inverse_dft {{10 0.0} {-2.0 2.0} {-2 0.0} {-2.0 -2.0}}]
00367 puts [::math::fourier::dft {1 2 3 4 5}]
00368 puts [::math::fourier::inverse_dft {{15.0 0.0} {-2.5 3.44095480118} {-2.5 0.812299240582} {-2.5 -0.812299240582} {-2.5 -3.44095480118}}]
00369 puts [test_dft 10]
00370 puts [test_dft 16]
00371 puts [test_dft 100]
00372 puts [test_dft 128]
00373
00374 puts [::math::fourier::dft {1 2 3 4}]
00375 puts [::math::fourier::lowpass 1.5 [::math::fourier::dft {1 2 3 4}]]
00376 }
00377