paddlec 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,30 @@
1
+ /* Copyright (C) 2019 Théotime Bollengier <theotime.bollengier@gmail.com>
2
+ *
3
+ * This file is part of PaddleC
4
+ *
5
+ * PaddleC is free software: you can redistribute it and/or modify
6
+ * it under the terms of the GNU General Public License as published by
7
+ * the Free Software Foundation, either version 3 of the License, or
8
+ * (at your option) any later version.
9
+ *
10
+ * PaddleC is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ * GNU General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU General Public License
16
+ * along with PaddleC. If not, see <https://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ #ifndef PADDLEC_PULSEAUDIO_H
20
+ #define PADDLEC_PULSEAUDIO_H
21
+
22
+ #include <ruby.h>
23
+ #include "libpaddlec.h"
24
+
25
+ extern VALUE m_PulseAudio;
26
+ extern VALUE c_PAConnection;
27
+
28
+ void Init_paddlec_pulseaudio();
29
+
30
+ #endif /* PADDLEC_PULSEAUDIO_H */
@@ -0,0 +1,26 @@
1
+ ## Copyright (C) 2019 Théotime Bollengier <theotime.bollengier@gmail.com>
2
+ ##
3
+ ## This file is part of PaddleC
4
+ ##
5
+ ## PaddleC is free software: you can redistribute it and/or modify
6
+ ## it under the terms of the GNU General Public License as published by
7
+ ## the Free Software Foundation, either version 3 of the License, or
8
+ ## (at your option) any later version.
9
+ ##
10
+ ## PaddleC is distributed in the hope that it will be useful,
11
+ ## but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ ## GNU General Public License for more details.
14
+ ##
15
+ ## You should have received a copy of the GNU General Public License
16
+ ## along with PaddleC. If not, see <https://www.gnu.org/licenses/>.
17
+
18
+
19
+ module PaddleC
20
+ end
21
+
22
+ require 'roctave'
23
+ require_relative 'paddlec/version.rb'
24
+ #require_relative 'paddlec/paddlec'
25
+ require_relative '../ext/paddlec/paddlec'
26
+
@@ -0,0 +1,3 @@
1
+ module PaddleC
2
+ VERSION = '0.0.1' # @return [String]
3
+ end
@@ -0,0 +1,55 @@
1
+ require File.expand_path('../lib/paddlec/version', __FILE__)
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = "paddlec"
5
+ s.version = PaddleC::VERSION
6
+ s.date = Time.now.strftime '%Y-%m-%d'
7
+ s.summary = "A Ruby C extension for real-time signal processing."
8
+ s.description = "PaddleC is a Ruby C extension attempting to provide objects and methods to rapidly set up real-time signal processing test benches in Ruby."
9
+ s.license = 'GPL-3.0+'
10
+ s.authors = ["Théotime Bollengier"]
11
+ s.email = 'theotime.bollengier@gmail.com'
12
+ s.homepage = 'https://gitlab.com/theotime_bollengier/paddlec'
13
+ s.add_runtime_dependency 'roctave', '~> 0.0', '>= 0.0.1'
14
+ s.files = [
15
+ 'ext/libpaddlec/arithmetic.c',
16
+ 'ext/libpaddlec/comparison.c',
17
+ 'ext/libpaddlec/complex.c',
18
+ 'ext/libpaddlec/delay.c',
19
+ 'ext/libpaddlec/fir_filter_avx.c',
20
+ 'ext/libpaddlec/fir_filter.c',
21
+ 'ext/libpaddlec/fir_filter_neon.c',
22
+ 'ext/libpaddlec/fir_filter_sse.c',
23
+ 'ext/libpaddlec/libpaddlec.c',
24
+ 'ext/libpaddlec/libpaddlec.h',
25
+ 'ext/libpaddlec/math.c',
26
+ 'ext/libpaddlec/no_fast_math.c',
27
+ 'ext/libpaddlec/rounding.c',
28
+ 'ext/paddlec/extconf.rb',
29
+ 'ext/paddlec/complex_buffer.c',
30
+ 'ext/paddlec/complex_buffer.h',
31
+ 'ext/paddlec/delay.c',
32
+ 'ext/paddlec/delay.h',
33
+ 'ext/paddlec/fir_filter.c',
34
+ 'ext/paddlec/fir_filter.h',
35
+ 'ext/paddlec/float_buffer.c',
36
+ 'ext/paddlec/float_buffer.h',
37
+ 'ext/paddlec/paddlec.c',
38
+ 'ext/paddlec/paddlec.h',
39
+ 'ext/paddlec/pulseaudio.c',
40
+ 'ext/paddlec/pulseaudio.h',
41
+ 'lib/paddlec/version.rb',
42
+ 'lib/paddlec.rb',
43
+ 'LICENSE',
44
+ 'paddlec.gemspec',
45
+ 'README.md',
46
+ 'samples/fmdemod_chunk_buffer.rb',
47
+ 'samples/fmdemod_chunk.rb',
48
+ 'samples/fmdemod.rb',
49
+ 'samples/stereo_chunk.rb'
50
+ ]
51
+ s.extensions = [
52
+ 'ext/paddlec/extconf.rb'
53
+ ]
54
+ end
55
+
@@ -0,0 +1,121 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'roctave'
4
+ require '../ext/paddlec/paddlec'
5
+
6
+
7
+ show_filters = false
8
+
9
+ fs = 2.048e6
10
+
11
+ # b = File.binread(File.expand_path '~/sdrrec/pouet.raw').unpack('C*').each_slice(2).collect{|a| Complex((a.first - 128) / 128.0, (a.last - 128) / 128.0)}.to_complex_buffer
12
+ # puts b.length
13
+ # File.binwrite('pouet_cf32le.raw', b.pack)
14
+ # exit
15
+
16
+
17
+ unless File.file? 'coefficients.f32le' then
18
+ STDOUT.write 'Generating filter coeficients... '; STDOUT.flush
19
+ now = Time.now
20
+
21
+ b_bbdecimator = Roctave.fir1(254, 95e3*2/fs)
22
+ Roctave.freqz(b_bbdecimator, :magnitude, nb_points: 2048, fs: fs) if show_filters
23
+
24
+ b_differentiator = Roctave.fir_differentiator(32)
25
+ Roctave.freqz(b_differentiator, :magnitude, nb_points: 2048, fs: fs/8) if show_filters
26
+
27
+ b_adecimator = Roctave.fir_low_pass(512, 15e3*2/256e3)
28
+ Roctave.freqz(b_adecimator, :magnitude, nb_points: 2048, fs: fs/8) if show_filters
29
+
30
+ tau = 50e-6
31
+ ts = 8*8/fs
32
+ b = [1 - Math.exp(-ts/tau)]
33
+ a = [1, -Math.exp(-ts/tau)]
34
+ h, w = Roctave.freqz(b, a, nb_points: 1024)
35
+ h.collect!{|v| v.abs}
36
+ w.collect!{|v| v/w.last}
37
+ n = 2048
38
+ b_deemphasis = Roctave.fir2(n, w, h, :odd_symmetry)
39
+ Roctave.freqz(b_deemphasis, :magnitude, nb_points: 2048, fs: fs/8/8) if show_filters
40
+
41
+ puts (Time.now - now).round(3)
42
+
43
+ File.open('coefficients.f32le', 'wb') do |ofile|
44
+ ofile.write b_bbdecimator.pack('f*')
45
+ ofile.write b_differentiator.pack('f*')
46
+ ofile.write b_adecimator.pack('f*')
47
+ ofile.write b_deemphasis.pack('f*')
48
+ end
49
+ else
50
+ ifile = File.open('coefficients.f32le', 'rb')
51
+ b = ifile.read.unpack('f*')
52
+ ifile.close
53
+ b_bbdecimator = b.shift(255)
54
+ b_differentiator = b.shift(33)
55
+ b_adecimator = b.shift(513)
56
+ b_deemphasis = b.shift(2049)
57
+ end
58
+
59
+
60
+ STDOUT.write 'Instantiating filters... '; STDOUT.flush
61
+ now = Time.now
62
+
63
+ bbdecimator = PaddleC::FirDecimator.new b_bbdecimator, 8
64
+ differentiator = PaddleC::FirFilter.new b_differentiator
65
+ adecimator = PaddleC::FirDecimator.new b_adecimator, 8
66
+ deemphasis = PaddleC::FirFilter.new b_deemphasis
67
+ puts (Time.now - now).round(3)
68
+
69
+
70
+ start = Time.now
71
+ STDOUT.write 'Reading input... '; STDOUT.flush
72
+ now = Time.now
73
+ #if2MHz_complex = PaddleC::ComplexBuffer.unpack(File.binread(File.expand_path('~/sdrrec/pouet.raw')), :u8)
74
+ if2MHz_complex = PaddleC::ComplexBuffer.unpack(File.binread(File.expand_path('~/sdrrec/95.0MHz_2.048Msps_cu8.raw')), :u8)
75
+ puts (Time.now - now).round(3)
76
+ puts if2MHz_complex.length
77
+
78
+ STDOUT.write 'Input decimation... '; STDOUT.flush
79
+ now = Time.now
80
+ baseband256kHz_complex = bbdecimator.decimate(if2MHz_complex)
81
+ puts (Time.now - now).round(3)
82
+
83
+ STDOUT.write 'Differentiation... '; STDOUT.flush
84
+ now = Time.now
85
+ baseband_diff_complex, baseband_delayed_complex = differentiator.filter(baseband256kHz_complex, delayed: true)
86
+ puts (Time.now - now).round(3)
87
+
88
+ STDOUT.write 'Demodulation... '; STDOUT.flush
89
+ now = Time.now
90
+ demodulated = (baseband_diff_complex.imag*baseband_delayed_complex.real - baseband_diff_complex.real*baseband_delayed_complex.imag) / baseband_delayed_complex.abs2
91
+ puts (Time.now - now).round(3)
92
+
93
+ STDOUT.write 'Decimation to audio... '; STDOUT.flush
94
+ now = Time.now
95
+ audioemph = adecimator.decimate(demodulated)
96
+ puts (Time.now - now).round(3)
97
+
98
+ STDOUT.write 'Deemphasis... '; STDOUT.flush
99
+ now = Time.now
100
+ audio = deemphasis.filter(audioemph) * 0.25
101
+ puts (Time.now - now).round(3)
102
+
103
+ puts "Processing took #{(Time.now - start).round 3} s"
104
+
105
+ PaddleC::PulseAudio::Simple::Sink.open(sample_spec: 'float32le 1ch 32000Hz') do |sink|
106
+ sink << audio
107
+ end
108
+
109
+ =begin
110
+ STDOUT.write 'Writing output to output.f32le... '; STDOUT.flush
111
+ now = Time.now
112
+ File.binwrite('output.f32le', audio.pack)
113
+ puts (Time.now - now).round(3)
114
+
115
+ puts "Processing took #{(Time.now - start).round 3} s"
116
+
117
+ system "ffmpeg -v error -ar 32k -ac 1 -f f32le -i output.f32le -y output.wav"
118
+
119
+ system "mpv output.wav"
120
+ =end
121
+
@@ -0,0 +1,120 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'roctave'
4
+ require '../ext/paddlec/paddlec'
5
+
6
+ def show_bar(f, t = 80.0)
7
+ p = (f.pos * t / f.size).round
8
+ STDOUT.write "\r[#{'='*p}>#{'-'*(t - p)}] "
9
+ STDOUT.flush
10
+ end
11
+
12
+
13
+ show_filters = false
14
+
15
+ fs = 2.048e6
16
+
17
+ # b = File.binread(File.expand_path '~/sdrrec/pouet.raw').unpack('C*').each_slice(2).collect{|a| Complex((a.first - 128) / 128.0, (a.last - 128) / 128.0)}.to_complex_buffer
18
+ # puts b.length
19
+ # File.binwrite('pouet_cf32le.raw', b.pack)
20
+ # exit
21
+
22
+
23
+ unless File.file? 'coefficients.f32le' then
24
+ STDOUT.write 'Generating filter coeficients... '; STDOUT.flush
25
+ now = Time.now
26
+
27
+ b_bbdecimator = Roctave.fir1(254, 95e3*2/fs)
28
+ Roctave.freqz(b_bbdecimator, :magnitude, nb_points: 2048, fs: fs) if show_filters
29
+
30
+ b_differentiator = Roctave.fir_differentiator(32)
31
+ Roctave.freqz(b_differentiator, :magnitude, nb_points: 2048, fs: fs/8) if show_filters
32
+
33
+ b_adecimator = Roctave.fir_low_pass(512, 15e3*2/256e3)
34
+ Roctave.freqz(b_adecimator, :magnitude, nb_points: 2048, fs: fs/8) if show_filters
35
+
36
+ tau = 50e-6
37
+ ts = 8*8/fs
38
+ b = [1 - Math.exp(-ts/tau)]
39
+ a = [1, -Math.exp(-ts/tau)]
40
+ h, w = Roctave.freqz(b, a, nb_points: 1024)
41
+ h.collect!{|v| v.abs}
42
+ w.collect!{|v| v/w.last}
43
+ n = 2048
44
+ b_deemphasis = Roctave.fir2(n, w, h, :odd_symmetry)
45
+ Roctave.freqz(b_deemphasis, :magnitude, nb_points: 2048, fs: fs/8/8) if show_filters
46
+
47
+ puts (Time.now - now).round(3)
48
+
49
+ File.open('coefficients.f32le', 'wb') do |ofile|
50
+ ofile.write b_bbdecimator.pack('f*')
51
+ ofile.write b_differentiator.pack('f*')
52
+ ofile.write b_adecimator.pack('f*')
53
+ ofile.write b_deemphasis.pack('f*')
54
+ end
55
+ else
56
+ ifile = File.open('coefficients.f32le', 'rb')
57
+ b = ifile.read.unpack('f*')
58
+ ifile.close
59
+ b_bbdecimator = b.shift(255)
60
+ b_differentiator = b.shift(33)
61
+ b_adecimator = b.shift(513)
62
+ b_deemphasis = b.shift(2049)
63
+ end
64
+
65
+
66
+
67
+ bbdecimator = PaddleC::FirDecimator.new b_bbdecimator, 8
68
+ differentiator = PaddleC::FirFilter.new b_differentiator
69
+ adecimator = PaddleC::FirDecimator.new b_adecimator, 8
70
+ deemphasis = PaddleC::FirFilter.new b_deemphasis
71
+
72
+ time_chunk = 100e-3
73
+ len = (32e3 * time_chunk).round
74
+
75
+ decoding = 0.0
76
+ waiting = 0.0
77
+ fname = 'pouet_cf32le.raw'
78
+ fname = '~/sdrrec/pouet.raw'
79
+ fname = '~/sdrrec/95.0MHz_2.048Msps_cu8.raw'
80
+ PaddleC::PulseAudio::Simple::Sink.open(stream_name: fname, sample_spec: 'float32 1ch 32000Hz') do |sink|
81
+ File.open(File.expand_path(fname), 'rb') do |f|
82
+ loop do
83
+ show_bar(f)
84
+
85
+ now = Time.now
86
+
87
+ if2MHz_complex = PaddleC::ComplexBuffer.unpack(f.read(8*8*len*2), :u8)
88
+
89
+ baseband256kHz_complex = bbdecimator.decimate(if2MHz_complex)
90
+
91
+ baseband_diff_complex, baseband_delayed_complex = differentiator.filter(baseband256kHz_complex, delayed: true)
92
+
93
+ demodulated = (baseband_diff_complex.imag*baseband_delayed_complex.real - baseband_diff_complex.real*baseband_delayed_complex.imag) / baseband_delayed_complex.abs2
94
+
95
+ audioemph = adecimator.decimate(demodulated)
96
+
97
+ audio = deemphasis.filter(audioemph) * 0.25
98
+
99
+ decoding += Time.now - now
100
+
101
+ now = Time.now
102
+
103
+ sink << audio
104
+
105
+ waiting += Time.now - now
106
+
107
+ break if f.eof?
108
+ end
109
+ show_bar(f)
110
+ puts
111
+ end
112
+ end
113
+
114
+
115
+ puts "decoding took #{decoding.round(3)}s"
116
+ puts "waiting took #{waiting.round(3)}s"
117
+
118
+
119
+
120
+
@@ -0,0 +1,144 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'roctave'
4
+ require '../ext/paddlec/paddlec'
5
+
6
+ def show_bar(f, t = 80.0)
7
+ p = (f.pos * t / f.size).round
8
+ STDOUT.write "\r[#{'='*p}>#{'-'*(t - p)}] "
9
+ STDOUT.flush
10
+ end
11
+
12
+
13
+ show_filters = false
14
+
15
+ fs = 2.048e6
16
+
17
+ # b = File.binread(File.expand_path '~/sdrrec/pouet.raw').unpack('C*').each_slice(2).collect{|a| Complex((a.first - 128) / 128.0, (a.last - 128) / 128.0)}.to_complex_buffer
18
+ # puts b.length
19
+ # File.binwrite('pouet_cf32le.raw', b.pack)
20
+ # exit
21
+
22
+
23
+ unless File.file? 'coefficients.f32le' then
24
+ STDOUT.write 'Generating filter coeficients... '; STDOUT.flush
25
+ now = Time.now
26
+
27
+ b_bbdecimator = Roctave.fir1(254, 95e3*2/fs)
28
+ Roctave.freqz(b_bbdecimator, :magnitude, nb_points: 2048, fs: fs) if show_filters
29
+
30
+ b_differentiator = Roctave.fir_differentiator(32)
31
+ Roctave.freqz(b_differentiator, :magnitude, nb_points: 2048, fs: fs/8) if show_filters
32
+
33
+ b_adecimator = Roctave.fir_low_pass(512, 15e3*2/256e3)
34
+ Roctave.freqz(b_adecimator, :magnitude, nb_points: 2048, fs: fs/8) if show_filters
35
+
36
+ tau = 50e-6
37
+ ts = 8*8/fs
38
+ b = [1 - Math.exp(-ts/tau)]
39
+ a = [1, -Math.exp(-ts/tau)]
40
+ h, w = Roctave.freqz(b, a, nb_points: 1024)
41
+ h.collect!{|v| v.abs}
42
+ w.collect!{|v| v/w.last}
43
+ n = 2048
44
+ b_deemphasis = Roctave.fir2(n, w, h, :odd_symmetry)
45
+ Roctave.freqz(b_deemphasis, :magnitude, nb_points: 2048, fs: fs/8/8) if show_filters
46
+
47
+ puts (Time.now - now).round(3)
48
+
49
+ File.open('coefficients.f32le', 'wb') do |ofile|
50
+ ofile.write b_bbdecimator.pack('f*')
51
+ ofile.write b_differentiator.pack('f*')
52
+ ofile.write b_adecimator.pack('f*')
53
+ ofile.write b_deemphasis.pack('f*')
54
+ end
55
+ else
56
+ ifile = File.open('coefficients.f32le', 'rb')
57
+ b = ifile.read.unpack('f*')
58
+ ifile.close
59
+ b_bbdecimator = b.shift(255)
60
+ b_differentiator = b.shift(33)
61
+ b_adecimator = b.shift(513)
62
+ b_deemphasis = b.shift(2049)
63
+ end
64
+
65
+
66
+
67
+ bbdecimator = PaddleC::FirDecimator.new b_bbdecimator, 8
68
+ differentiator = PaddleC::FirFilter.new b_differentiator
69
+ adecimator = PaddleC::FirDecimator.new b_adecimator, 8
70
+ deemphasis = PaddleC::FirFilter.new b_deemphasis
71
+
72
+ time_chunk = 100e-3
73
+ len = (32e3 * time_chunk).round
74
+ audio = PaddleC::FloatBuffer.new
75
+ audioemph = PaddleC::FloatBuffer.new
76
+ demodulated = PaddleC::FloatBuffer.new
77
+ baseband_diff_i = PaddleC::FloatBuffer.new
78
+ baseband_diff_r = PaddleC::FloatBuffer.new
79
+ baseband_del_i = PaddleC::FloatBuffer.new
80
+ baseband_del_r = PaddleC::FloatBuffer.new
81
+ baseband_del_abs2 = PaddleC::FloatBuffer.new
82
+ baseband_diff_complex = PaddleC::ComplexBuffer.new
83
+ baseband_delayed_complex = PaddleC::ComplexBuffer.new
84
+ baseband256kHz_complex = PaddleC::ComplexBuffer.new
85
+ if2MHz_complex = PaddleC::ComplexBuffer.new
86
+ strbuf = ''
87
+
88
+ decoding = 0.0
89
+ waiting = 0.0
90
+ fname = 'pouet_cf32le.raw'
91
+ fname = '~/sdrrec/95.0MHz_2.048Msps_cu8.raw'
92
+ PaddleC::PulseAudio::Simple::Sink.open(stream_name: fname, sample_spec: 'float32le 1ch 32000Hz') do |sink|
93
+ File.open(File.expand_path(fname), 'rb') do |f|
94
+ loop do
95
+ show_bar(f)
96
+
97
+ now = Time.now
98
+
99
+ strbuf = f.read(8*8*len*2, strbuf)
100
+ if2MHz_complex.unpack(strbuf, :u8)
101
+
102
+ bbdecimator.decimate(if2MHz_complex, buffer: baseband256kHz_complex)
103
+ if2MHz_complex.resize 0
104
+
105
+ differentiator.filter(baseband256kHz_complex, buffer: baseband_diff_complex, delayed: baseband_delayed_complex)
106
+
107
+ baseband_diff_complex.imag(baseband_diff_i)
108
+ baseband_diff_complex.real(baseband_diff_r)
109
+ baseband_delayed_complex.imag(baseband_del_i)
110
+ baseband_delayed_complex.real(baseband_del_r)
111
+ baseband_delayed_complex.abs2(baseband_del_abs2)
112
+
113
+ baseband_diff_i.mult!(baseband_del_r)
114
+ baseband_diff_r.mult!(baseband_del_i)
115
+ baseband_diff_i.sub!(baseband_diff_r)
116
+
117
+ baseband_diff_i.div(baseband_del_abs2, demodulated)
118
+
119
+ adecimator.decimate(demodulated, buffer: audioemph)
120
+
121
+ deemphasis.filter(audioemph, buffer: audio).mult!(0.25)
122
+
123
+ decoding += Time.now - now
124
+
125
+ now = Time.now
126
+
127
+ sink << audio
128
+
129
+ waiting += Time.now - now
130
+
131
+ break if f.eof?
132
+ end
133
+ show_bar(f)
134
+ puts
135
+ end
136
+ end
137
+
138
+
139
+ puts "decoding took #{decoding.round(3)}s"
140
+ puts "waiting took #{waiting.round(3)}s"
141
+
142
+
143
+
144
+