paddlec 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+