digiproc 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (175) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +12 -0
  3. data/.rspec +3 -0
  4. data/.travis.yml +7 -0
  5. data/CODE_OF_CONDUCT.md +74 -0
  6. data/Gemfile +8 -0
  7. data/Gemfile.lock +48 -0
  8. data/LICENSE.txt +21 -0
  9. data/README.md +78 -0
  10. data/Rakefile +37 -0
  11. data/TODO.md +50 -0
  12. data/bin/console +14 -0
  13. data/bin/setup +8 -0
  14. data/config/environment.rb +118 -0
  15. data/console_tests.rb +44 -0
  16. data/digiproc.gemspec +49 -0
  17. data/examples/analog_signals/analog_to_digital.rb +31 -0
  18. data/examples/analog_signals/companded-signals.png +0 -0
  19. data/examples/analog_signals/companding.rb +68 -0
  20. data/examples/analog_signals/fft-plot.png +0 -0
  21. data/examples/analog_signals/plot_Digiproc::FFT.png +0 -0
  22. data/examples/analog_signals/plot_Dsp::FFT.png +0 -0
  23. data/examples/analog_signals/quantization-outputs.png +0 -0
  24. data/examples/analog_signals/quantize_compand.rb +69 -0
  25. data/examples/binomial_distribution/bit_error.rb +14 -0
  26. data/examples/binomial_distribution/dice.rb +35 -0
  27. data/examples/digital_signals/_coded_frequency_signal,_ts_=_1_s.png +0 -0
  28. data/examples/digital_signals/_coded_frequency_signal,_ts_=_2_s.png +0 -0
  29. data/examples/digital_signals/coded_power_spectral_density,__ts_=_1_s.png +0 -0
  30. data/examples/digital_signals/coded_power_spectral_density,__ts_=_2_s.png +0 -0
  31. data/examples/digital_signals/coded_time_signal,_ts_=_1_s.png +0 -0
  32. data/examples/digital_signals/coded_time_signal,_ts_=_2_s.png +0 -0
  33. data/examples/digital_signals/freq_sig_from_eqn,_ts_=_1_s.png +0 -0
  34. data/examples/digital_signals/freq_sig_from_eqn,_ts_=_2_s.png +0 -0
  35. data/examples/digital_signals/frequency_signal,_ts_=_1_s.png +0 -0
  36. data/examples/digital_signals/frequency_signal,_ts_=_2_s.png +0 -0
  37. data/examples/digital_signals/modulate_square_pulses.rb +9 -0
  38. data/examples/digital_signals/modulated_sq._pulses.png +0 -0
  39. data/examples/digital_signals/modulated_sq._pulses_alt.png +0 -0
  40. data/examples/digital_signals/power_spectral_density,__ts_=_1_s.png +0 -0
  41. data/examples/digital_signals/power_spectral_density,__ts_=_2_s.png +0 -0
  42. data/examples/digital_signals/square_signals.rb +90 -0
  43. data/examples/digital_signals/time_signal,_ts_=_1_s.png +0 -0
  44. data/examples/digital_signals/time_signal,_ts_=_2_s.png +0 -0
  45. data/examples/encoding/gray_code.rb +22 -0
  46. data/examples/encoding/psk.rb +91 -0
  47. data/examples/encoding/system_2_phase.png +0 -0
  48. data/examples/encoding/system_2_xmit_signal.png +0 -0
  49. data/examples/encoding/system_3_phase.png +0 -0
  50. data/examples/encoding/system_3_xmit_signal.png +0 -0
  51. data/examples/encoding/system_4_xmit_signal.png +0 -0
  52. data/examples/encoding/xor-dpsk-phase-signal-(sys1).png +0 -0
  53. data/examples/encoding/xor-dpsk-xmit-signal-(sys-1).png +0 -0
  54. data/examples/factories/Quickplot Graph.png +0 -0
  55. data/examples/factories/bandpass.rb +6 -0
  56. data/examples/fft/plot_Dsp::FFT.png +0 -0
  57. data/examples/fft/recieved_data_(time_domain).png +0 -0
  58. data/examples/fft/simple_fft_example.rb +47 -0
  59. data/examples/fft/unprocessed_fft.png +0 -0
  60. data/examples/filters/bandpass_filter.png +0 -0
  61. data/examples/filters/filter_a_signal.rb +38 -0
  62. data/examples/filters/white_noise_db_out_of_bp_filter.png +0 -0
  63. data/examples/filters/white_noise_mag_out_of_bp_filter.png +0 -0
  64. data/examples/filters/white_noise_spectra.png +0 -0
  65. data/examples/functions/compute_probability.rb +29 -0
  66. data/examples/functions/gram_schmidt.rb +10 -0
  67. data/examples/functions/minimize_energy.rb +29 -0
  68. data/examples/functions/orthoganalize.rb +18 -0
  69. data/examples/functions/simple_functions.rb +81 -0
  70. data/examples/linear_algebra/diverging_sys.rb +13 -0
  71. data/examples/linear_algebra/iterative_sys_of_eqns_methods.rb +27 -0
  72. data/examples/modulation_schemes/dpsk_2.png +0 -0
  73. data/examples/modulation_schemes/dpsk_256.png +0 -0
  74. data/examples/modulation_schemes/dpsk_freq_domain.rb +119 -0
  75. data/examples/modulation_schemes/psk.rb +36 -0
  76. data/examples/modulation_schemes/psk_2.png +0 -0
  77. data/examples/modulation_schemes/psk_256.png +0 -0
  78. data/examples/modulation_schemes/psksystem_1_xmit_signal.png +0 -0
  79. data/examples/modulation_schemes/psksystem_2_xmit_signal.png +0 -0
  80. data/examples/modulation_schemes/psksystem_3_xmit_signal.png +0 -0
  81. data/examples/modulation_schemes/system_1_xmit_signal.png +0 -0
  82. data/examples/modulation_schemes/system_2_xmit_signal.png +0 -0
  83. data/examples/modulation_schemes/system_3_xmit_signal.png +0 -0
  84. data/examples/quickplot/PlottableClass_plot.png +0 -0
  85. data/examples/quickplot/decorators.rb +13 -0
  86. data/examples/quickplot/direct_gruff.png +0 -0
  87. data/examples/quickplot/plot_PlottableClass.png +0 -0
  88. data/examples/quickplot/quickplot_vs_others.rb +85 -0
  89. data/examples/quickplot/random_data_quickplot,_dark.png +0 -0
  90. data/examples/quickplot/random_data_quickplot.png +0 -0
  91. data/examples/realized_gaussian/norm_dist_plot.png +0 -0
  92. data/examples/realized_gaussian/norm_dist_spectrum.png +0 -0
  93. data/examples/realized_gaussian/realized_gaussian_example.rb +23 -0
  94. data/lib/concerns/convolvable.rb +144 -0
  95. data/lib/concerns/data_properties.rb +223 -0
  96. data/lib/concerns/fourier_transformable.rb +178 -0
  97. data/lib/concerns/initializable.rb +43 -0
  98. data/lib/concerns/multipliable.rb +22 -0
  99. data/lib/concerns/os.rb +36 -0
  100. data/lib/concerns/plottable.rb +248 -0
  101. data/lib/concerns/requires_data.rb +8 -0
  102. data/lib/digiproc/version.rb +8 -0
  103. data/lib/digiproc.rb +2 -0
  104. data/lib/extensions/array_extension.rb +23 -0
  105. data/lib/extensions/core_extensions.rb +117 -0
  106. data/lib/factories/factories.rb +3 -0
  107. data/lib/factories/filter_factory.rb +83 -0
  108. data/lib/factories/window_factory.rb +22 -0
  109. data/lib/fft.rb +255 -0
  110. data/lib/filters/bandpass_filter.rb +43 -0
  111. data/lib/filters/bandstop_filter.rb +44 -0
  112. data/lib/filters/digital_filter.rb +59 -0
  113. data/lib/filters/highpass_filter.rb +27 -0
  114. data/lib/filters/lowpass_filter.rb +27 -0
  115. data/lib/functions.rb +221 -0
  116. data/lib/probability/binomial_distribution.rb +84 -0
  117. data/lib/probability/bit_generator.rb +94 -0
  118. data/lib/probability/gaussian_distribution.rb +29 -0
  119. data/lib/probability/probability.rb +234 -0
  120. data/lib/probability/theoretical_gaussian_distribution.rb +59 -0
  121. data/lib/quick_plot.rb +96 -0
  122. data/lib/rbplot.rb +219 -0
  123. data/lib/signals/analog_signal.rb +143 -0
  124. data/lib/signals/digital_signal.rb +181 -0
  125. data/lib/strategies/code/differential_encoding_strategy.rb +69 -0
  126. data/lib/strategies/code/gray_code.rb +75 -0
  127. data/lib/strategies/code/xor_differential_encoding_strategy.rb +100 -0
  128. data/lib/strategies/code/xor_differential_encoding_zero_angle_strategy.rb +103 -0
  129. data/lib/strategies/companding/custom_companding_strategy.rb +29 -0
  130. data/lib/strategies/convolution/bf_conv.rb +57 -0
  131. data/lib/strategies/fft/brute_force_dft_strategy.rb +31 -0
  132. data/lib/strategies/fft/inverse_fft_conjugate_strategy.rb +44 -0
  133. data/lib/strategies/fft/radix2_strategy.rb +84 -0
  134. data/lib/strategies/gaussian/gaussian_generator.rb +49 -0
  135. data/lib/strategies/linear_algebra/gauss_seidel_strategy.rb +90 -0
  136. data/lib/strategies/linear_algebra/jacobi_strategy.rb +81 -0
  137. data/lib/strategies/linear_algebra/sor2_strategy.rb +98 -0
  138. data/lib/strategies/linear_algebra/sor_strategy.rb +108 -0
  139. data/lib/strategies/modulation/phase_shift_keying_strategy.rb +96 -0
  140. data/lib/strategies/orthogonalize/gram_schmidt.rb +50 -0
  141. data/lib/strategies/strategies.rb +3 -0
  142. data/lib/strategies/window/blackman_window.rb +32 -0
  143. data/lib/strategies/window/hamming_window.rb +31 -0
  144. data/lib/strategies/window/hanning_window.rb +31 -0
  145. data/lib/strategies/window/kaiser_window.rb +27 -0
  146. data/lib/strategies/window/rectangular_window.rb +22 -0
  147. data/lib/strategies/window/window.rb +42 -0
  148. data/lib/systems/custom_system.rb +13 -0
  149. data/lib/systems/hilbert_transform.rb +6 -0
  150. data/lib/systems/matched_filter.rb +21 -0
  151. data/lib/systems/raised_cosine_filter.rb +11 -0
  152. data/lib/systems/system.rb +19 -0
  153. data/lib/systems/systems.rb +3 -0
  154. data/playground.rb +323 -0
  155. data/plots/_coded_frequency_signal,_ts_=_1_s.png +0 -0
  156. data/plots/_coded_frequency_signal,_ts_=_2_s.png +0 -0
  157. data/plots/coded_freq_sig_from_eqn,_ts_=_1_s.png +0 -0
  158. data/plots/coded_freq_sig_from_eqn,_ts_=_2_s.png +0 -0
  159. data/plots/coded_power_spectral_density,__ts_=_1_s.png +0 -0
  160. data/plots/coded_power_spectral_density,__ts_=_2_s.png +0 -0
  161. data/plots/coded_time_signal,_ts_=_1_s.png +0 -0
  162. data/plots/coded_time_signal,_ts_=_2_s.png +0 -0
  163. data/plots/dpsk_2.png +0 -0
  164. data/plots/freq_sig_from_eqn,_ts_=_1_s.png +0 -0
  165. data/plots/freq_sig_from_eqn,_ts_=_2_s.png +0 -0
  166. data/plots/frequency_signal,_ts_=_1_s.png +0 -0
  167. data/plots/frequency_signal,_ts_=_2_s.png +0 -0
  168. data/plots/power_spectral_density,__ts_=_1_s.png +0 -0
  169. data/plots/power_spectral_density,__ts_=_2_s.png +0 -0
  170. data/plots/psk_2.png +0 -0
  171. data/plots/time_signal,_ts_=_1_s.png +0 -0
  172. data/plots/time_signal,_ts_=_2_s.png +0 -0
  173. data/test-title-dark.png +0 -0
  174. data/test-title.png +0 -0
  175. metadata +322 -0
@@ -0,0 +1,68 @@
1
+ eqn1 = ->(t){ 8 * Math.cos(20 * Math::PI * t) }
2
+ eqn2 = ->(t){ Math.cos(20 * Math::PI * t) }
3
+ rate = 1.0 / 51.0
4
+
5
+ x1 = Digiproc::AnalogSignal.new(eqn: eqn1, sample_rate: rate, size: 6, quantization_bits: 4, quant_max: 8, quant_min: -8)
6
+ x2 = Digiproc::AnalogSignal.new(eqn: eqn2, sample_rate: rate, size: 6, quantization_bits: 4, quant_max: 8, quant_min: -8)
7
+
8
+ compression_eqn = ->(n){ n < 0 ? (-4 * ((-n) ** (1.0 / 3))) : (4 * n ** (1.0 / 3))}
9
+ expansion_eqn =->(n){ n < 0 ? ((-1.0 / 64) * ((-n) ** 3)) : ((1.0 / 64) * n ** 3)}
10
+
11
+ compander = Digiproc::Strategies::CustomCompandingStrategy.new(compression_eqn, expansion_eqn)
12
+
13
+ x3 = Digiproc::AnalogSignal.new(eqn: eqn1, sample_rate: rate, size: 6, quantization_bits: 4, quant_max: 8, quant_min: -8, companding_strategy: compander)
14
+ x4 = Digiproc::AnalogSignal.new(eqn: eqn2, sample_rate: rate, size: 6, quantization_bits: 4, quant_max: 8, quant_min: -8, companding_strategy: compander)
15
+
16
+ puts "x(1): "
17
+ puts x1.digitize.to_s
18
+ puts "Raw samples: #{x1.raw_samples}"
19
+ puts "Normalized Quantization RMS Error: #{x1.normalized_quantization_rms_error.round(3)}"
20
+ puts "\n\nx(2):"
21
+ puts x2.digitize.to_s
22
+ puts "Raw samples: #{x2.raw_samples}"
23
+ puts "Normalized Quantization RMS Error: #{x2.normalized_quantization_rms_error.round(3)}"
24
+ puts "\n\nx(1) with companding: "
25
+ puts x3.digitize.to_s
26
+ puts "Raw samples: #{x3.raw_samples}"
27
+ puts "Normalized Quantization RMS Error: #{x3.normalized_quantization_rms_error.round(3)}"
28
+ puts "\n\nx(2) with companding:"
29
+ puts x4.digitize.to_s
30
+ puts "Raw samples: #{x4.raw_samples}"
31
+ puts "Normalized Quantization RMS Error: #{x4.normalized_quantization_rms_error.round(3)}"
32
+ # binding.pry
33
+
34
+ signal = ->(t){Math.cos(2 * Math::PI * t)} #cos at 1 Hz
35
+ sample_times = (0..1000).map{ |int| int / 100.0} #Sample for 10 seconds every 0.01 seconds (100 times per period over 10 periods)
36
+ data = sample_times.map{ |time| signal.call(time) }
37
+ ft = []
38
+ for k in 0...data.length do
39
+ tot = 0
40
+ data.each_with_index do |x_n, n|
41
+ tot += x_n * Math::E ** (Complex(0,-1) * 2.0 * Math::PI * k * n / data.length.to_f)
42
+ end
43
+ ft << tot
44
+ end
45
+
46
+ fft = Digiproc::FFT.new(time_data: data)
47
+ t = Digiproc::Functions.linspace(0,1,fft.size)
48
+
49
+
50
+
51
+
52
+ plt = Digiproc::Rbplot.line(t, fft.dB, "discrete frequency")
53
+ plt.title('FFT Plot')
54
+ plt.path('./examples/analog_signals/')
55
+ plt.xlabel('Radians')
56
+ plt.ylabel('Decibles')
57
+ plt.xsteps(10)
58
+ plt.show
59
+
60
+ x = Digiproc::Functions.linspace(0, 6.0/51, 6)
61
+ plt = Digiproc::Rbplot.line(x, x1.digitize, 'x1')
62
+ plt.add_line(x, x2.digitize, 'x2')
63
+ plt.add_line(x, x3.digitize, 'x1 companded')
64
+ plt.add_line(x, x4.digitize, 'x2 companded')
65
+ plt.path('./examples/analog_signals/')
66
+ plt.title('Companded Signals')
67
+ plt.ylabel('Magnitude')
68
+ plt.show
@@ -0,0 +1,69 @@
1
+ # For the signals:
2
+ # x1(t)=8cos(20πt)
3
+ # x2(t)=cos(20πt)
4
+
5
+ # Find Normalized Quantization RMS Error:
6
+
7
+ # AnalogSignal class accepts an equation, sample rate, and size. Quantization bits, quantization max and min values, and a companding equation are optional.
8
+ # Once the #digitize method is called on the initialized class:
9
+ # The digital signal is sampled at the given sample rate for the given size number of samples
10
+ # If a companding strategy is given, the compression of bits is applied to the samples
11
+ # If a number of quantization bits are inputted, the values are quantized based on the number of bits desired as well as the max and min quantization values passed in.
12
+ # To perform the quantization, you can follow the progression which starts in the #quantize method. First, the samples are mapped from the passed in max and min values to −1⟺+1 Then, the value is multiplied by the number of bits squared divided by 2. That value is rounded to the nearest integer, and then mapped back down to the original max and min values.
13
+ # After quantization, if a companding strategy was given, the quantized samples are run through the expansion algorithm.
14
+ # This leaves you with the final answer, which is returned from the #digitize method
15
+ # The normalized RMS Error average value is calculated in the #normalized_quantization_rms_error method and is done so via the original sample values and the quantized output of the #digitize method.
16
+
17
+
18
+ x1 = Digiproc::AnalogSignal.new(eqn: ->(t){ 8 * Math.cos(20 * Math::PI * t) }, sample_rate: 1.0 / 51.0, size: 6,quantization_bits: 4, quant_max: 8, quant_min: -8)
19
+ x2 = Digiproc::AnalogSignal.new(eqn: ->(t){ Math.cos(20 * Math::PI * t) }, sample_rate: 1.0 / 51.0, size: 6,quantization_bits: 4, quant_max: 8, quant_min: -8)
20
+ puts "x(1): "
21
+ x1.digitize
22
+ puts "Normalized Quantization RMS Error: #{x1.normalized_quantization_rms_error.round(3)}"
23
+ puts "\n\nx(2):"
24
+ x2.digitize
25
+ puts "Normalized Quantization RMS Error: #{x2.normalized_quantization_rms_error.round(3)}"
26
+ puts
27
+ puts "x1 raw samples: #{x1.raw_samples.map{|s| s.round(2)}}"
28
+ puts "x1 quantized samples: #{x1.quantized_samples}"
29
+
30
+ puts
31
+
32
+ puts "x2 raw samples: #{x2.raw_samples.map{|s| s.round(2)}}"
33
+ puts "x2 quantized samples: #{x2.quantized_samples}"
34
+
35
+
36
+ # Now use a compander to attempt to reduce Quantization error:
37
+
38
+
39
+ compression_eqn = ->(n){ n < 0 ? (-4 * ((-n) ** (1.0 / 3))) : (4 * n ** (1.0 / 3))}
40
+ expansion_eqn =->(n){ n < 0 ? ((-1.0 / 64) * ((-n) ** 3)) : ((1.0 / 64) * n ** 3)}
41
+ compander = Digiproc::Strategies::CustomCompandingStrategy.new(compression_eqn, expansion_eqn)
42
+
43
+
44
+ x1 = Digiproc::AnalogSignal.new(eqn: ->(t){ 8 * Math.cos(20 * Math::PI * t) }, sample_rate: 1.0 / 51.0, size: 6,quantization_bits: 4, quant_max: 8, quant_min: -8, companding_strategy: compander)
45
+ x2 = Digiproc::AnalogSignal.new(eqn: ->(t){ Math.cos(20 * Math::PI * t) }, sample_rate: 1.0 / 51.0, size: 6,quantization_bits: 4, quant_max: 8, quant_min: -8, companding_strategy: compander)
46
+
47
+
48
+ puts "\n\nx(1) with companding: "
49
+
50
+ x1.digitize
51
+
52
+ puts "Normalized Quantization RMS Error: #{x1.normalized_quantization_rms_error.round(3)}"
53
+
54
+
55
+ puts "\n\nx(2) with companding:"
56
+
57
+ x2.digitize
58
+
59
+ puts "Normalized Quantization RMS Error: #{x2.normalized_quantization_rms_error.round(3)}"
60
+
61
+ puts "x3 raw samples: #{x1.raw_samples.map{|s| s.round(2)}}"
62
+ puts "x1 quantized samples: #{x1.quantized_samples}"
63
+
64
+ puts
65
+
66
+ puts "x2 raw samples: #{x2.raw_samples.map{|s| s.round(2)}}"
67
+ puts "x2 quantized samples: #{x2.quantized_samples}"
68
+
69
+ puts "Companding then reduced Quantization error for the low-amplitude signal x2, but raised it for the higher-amplitude signal x1"
@@ -0,0 +1,14 @@
1
+ #If a word consists of 10 bits with the ability to detect and correct any single bit error
2
+ # but not multiple bit errors, what is the probability of a word being in error?
3
+ # Compare the reduction in probability of a word error by using the error correcting code.
4
+
5
+ # Given that P(error)=0.00135, as it would be for a +- 1V signal with additive white gaussian noise
6
+ # with a variance of 1/9:
7
+
8
+
9
+ bd = Digiproc::Probability::TheoreticalBinomialDistribution.new(n: 10, p: 0.00135)
10
+ puts "Probability of no errors: #{bd.probability(0)}"
11
+ puts "Probability of one error: #{bd.probability(1)}"
12
+ puts "Probability of one or more errors(if no code fix): #{bd.probability(1..10)}"
13
+ puts "Probability of two or more errors (prob after code fix): #{bd.probability(2..10)}, should equal #{1 - bd.probability(0,1)}"
14
+ puts "Reduction in error by using error correcting code: #{bd.probability(1..10) - bd.probability(2..10)}"
@@ -0,0 +1,35 @@
1
+ binomial_dist = Digiproc::Probability::TheoreticalBinomialDistribution
2
+
3
+ six_die_roll = binomial_dist.new(n: 6, p: (1.0/6))
4
+
5
+ puts "For six die rolls:"
6
+
7
+ puts "At least one of a specific number: #{1 - (1 - (1.0/6)) ** 6}"
8
+ puts "Same as above with binomial dist: #{six_die_roll.probability(1..6)}"
9
+ puts "-------------------------------"
10
+ puts "Six of a kind: #{1 - ((six_die_roll.probability(0..5)) ** 6)}"
11
+ puts "At least 5 of a kind: #{1 - ((six_die_roll.probability(0..4)) ** 6)}"
12
+ puts "At least 3 of a predetermined number: #{six_die_roll.probability(3..6)}"
13
+ puts "-------------------------------"
14
+
15
+ puts "Experimental Data"
16
+
17
+ at_least = 3
18
+ die_number = 3
19
+ count = 0
20
+ r = Random.new
21
+ total = 5000000
22
+ total.times do
23
+ rolls = {1 => 0, 2 => 0, 3 => 0, 4 => 0, 5 => 0, 6 => 0}
24
+ 6.times do
25
+ num = r.rand(6) + 1
26
+ rolls[num] += 1
27
+ if rolls[die_number] == at_least
28
+ count += 1
29
+ break
30
+ end
31
+ end
32
+ end
33
+
34
+ puts "At least #{at_least} #{die_number}s (experimental data): #{count / total.to_f}"
35
+
@@ -0,0 +1,9 @@
1
+
2
+ input_data = Array.new(10,0) + Array.new(10,1) + Array.new(20,0) + Array.new(20,1) + Array.new(40,0)
3
+ x_n = Digiproc::DigitalSignal.new(data: input_data)
4
+ lpf = Digiproc::Factories::FilterFactory.filter_for(type: 'lowpass', wc: Math::PI / 10, transition_width: 0.01, stopband_attenuation: 80).to_ds
5
+ carrier = Digiproc::DigitalSignal.new_from_eqn(eqn: ->(n){Math.cos(10.0 * 2.0 * Math::PI * n / 100) }, size: lpf.data.length)
6
+ y_n = Digiproc::DigitalSignal.new(data: (x_n.fft(2**8) * lpf.fft(2**8)).ifft.map(&:real).take(100)) * carrier * carrier
7
+ y_n_alt = x_n.ds_conv(lpf) * carrier * carrier
8
+ Digiproc::QuickPlot.plot(data: y_n.data, title: "Modulated Sq. Pulses", path: './examples/digital_signals/')
9
+ Digiproc::QuickPlot.plot(data: y_n_alt.data[274,100], title: "Modulated Sq. Pulses Alt", path: './examples/digital_signals/')
@@ -0,0 +1,90 @@
1
+ # Define required objects and constants
2
+ r = Random.new
3
+ pulse_lens = [1,2]
4
+ plt = Digiproc::QuickPlot
5
+ fns = Digiproc::Functions
6
+ a = 1
7
+
8
+ # Random signal generator functions
9
+ def get_signal(rand_gen, a = 1)
10
+ rand_gen.rand(2) == 0 ? -a : a
11
+ end
12
+
13
+ # Create signal data
14
+ signal_size = 400
15
+ sample_rate = 0.05
16
+ signal_arr = []
17
+ signal_size.times { signal_arr << get_signal(r, a)}
18
+
19
+ #Get results with varied pulse length
20
+ pulse_lens.each do |pulse_len|
21
+ size = (1.0 / sample_rate) * signal_size * pulse_len
22
+ # Correct for sample rate so it can be compared to CT equation
23
+ signal_eqn = ->(t){ signal_arr[(t / pulse_len.to_f).floor] * sample_rate }
24
+ signal = Digiproc::AnalogSignal.new(eqn: signal_eqn, sample_rate: sample_rate, size: size)
25
+ freq_of_interest1 = 1.0 / (2 * pulse_len)
26
+ freq_of_interest2 = 1.0 / ( pulse_len)
27
+ # Test calculated equation
28
+ x_f = Proc.new do |f|
29
+ signal_arr.map.with_index{ |s, n| s.to_f * (a / (Complex(0, f * 2 * Math::PI))) * (Math::E ** (Complex(0, -f * Math::PI * 2 * n * pulse_len)) - Math::E ** (Complex(0,-f * 2 * Math::PI * pulse_len) * (1 + n))) }.sum
30
+ end
31
+ max_freq = 0.5 / (sample_rate)
32
+ freq_space = fns.linspace(0, max_freq, size)
33
+ xf_plot = fns.process(freq_space, x_f).map(&:abs)
34
+ #Equation DNE @ omega = 0
35
+ xf_plot[0] = 0
36
+
37
+ foi1_from_eqn = ((freq_of_interest1.to_f / max_freq) * xf_plot.length).to_i
38
+ foi2_from_eqn = ((freq_of_interest2.to_f / max_freq) * xf_plot.length).to_i
39
+
40
+
41
+ puts "Magnitude at 1/2T : #{xf_plot[foi1_from_eqn]}"
42
+ puts "Magnitude at 1/T : #{xf_plot[foi2_from_eqn]}"
43
+ puts
44
+
45
+ # Plot output from derived equation
46
+ plt.plot(data: xf_plot, title: "Freq Sig from Eqn, Ts = #{pulse_len} s", path: "./examples/digital_signals/")
47
+
48
+ # Plot experiment data
49
+ plt.plot(data: signal.digitize, title: "Time signal, Ts = #{pulse_len} s", path: "./examples/digital_signals/")
50
+ plt.plot(data: signal.to_ds.fft.magnitude, title: "Frequency Signal, Ts = #{pulse_len} s", path: "./examples/digital_signals/")
51
+ plt.plot(data: signal.to_ds.psd.data.map(&:real), title: "Power Spectral Density, Ts = #{pulse_len} s", path: "./examples/digital_signals/")
52
+ end
53
+
54
+ puts
55
+ puts
56
+
57
+ alpha = 1
58
+ coded_arr = Array.new(signal_size, 0)
59
+ coded_arr[0] = signal_arr.first
60
+ for i in 1...signal_size do
61
+ coded_arr[i] = signal_arr[i] + alpha * signal_arr[i-1]
62
+ end
63
+
64
+ pulse_lens.each do |pulse_len|
65
+ size = (1.0 / sample_rate) * signal_size * pulse_len
66
+ signal_eqn = ->(t){ coded_arr[(t / pulse_len.to_f).floor] * sample_rate }
67
+ signal = Digiproc::AnalogSignal.new(eqn: signal_eqn, sample_rate: sample_rate, size: size)
68
+ freq_of_interest1 = 1.0 / (2 * pulse_len)
69
+ freq_of_interest2 = 1.0 / ( pulse_len)
70
+
71
+ x_f = Proc.new do |f|
72
+ coded_arr.map.with_index{ |s, n| s.to_f * (a / (Complex(0, f * 2 * Math::PI))) * (Math::E ** (Complex(0, -f * Math::PI * 2 * n * pulse_len)) - Math::E ** (Complex(0,-f * 2 * Math::PI * pulse_len) * (1 + n))) }.sum
73
+ end
74
+ max_freq = 0.5 / (sample_rate)
75
+ freq_space = fns.linspace(0, max_freq, size)
76
+ xf_plot = fns.process(freq_space, x_f).map(&:abs)
77
+ xf_plot[0] = 0
78
+ # freq_unit = size.to_f / max_freq
79
+ foi1_from_eqn = ((freq_of_interest1.to_f / max_freq) * xf_plot.length).to_i
80
+ foi2_from_eqn = ((freq_of_interest2.to_f / max_freq) * xf_plot.length).to_i
81
+ # puts signal.to_ds.fft.magnitude[(foi1_from_eqn / 2 - 1).to_i]
82
+ puts "Coded Magnitude at 1/2T : #{xf_plot[foi1_from_eqn]}"
83
+ puts "Coded Magnitude at 1/T : #{xf_plot[foi2_from_eqn]}"
84
+ plt.plot(data: xf_plot, title: "Coded Freq Sig from Eqn, Ts = #{pulse_len} s")
85
+
86
+ #Plot test data
87
+ plt.plot(data: signal.digitize, title: "Coded Time signal, Ts = #{pulse_len} s", path: "./examples/digital_signals/")
88
+ plt.plot(data: signal.to_ds.fft.magnitude, title: " Coded Frequency Signal, Ts = #{pulse_len} s", path: "./examples/digital_signals/")
89
+ plt.plot(data: signal.to_ds.psd.data.map(&:real), title: "Coded Power Spectral Density, Ts = #{pulse_len} s", path: "./examples/digital_signals/")
90
+ end
@@ -0,0 +1,22 @@
1
+ ## Gray code maps "normal" numbers to a binary code in which adjacent numbers
2
+ ## only change by one bit. This reduced errors in the whole number when you
3
+ ## there is an error decoding a single bit
4
+
5
+ gc_gen = Digiproc::Strategies::GrayCode
6
+
7
+
8
+ three_bit_gray_code = gc_gen.generate(3)
9
+
10
+ puts three_bit_gray_code.to_s
11
+ puts "In decimal:"
12
+ gray_dec = three_bit_gray_code.map{ |gc| gc.to_i(2) }
13
+ puts gray_dec.to_s
14
+
15
+
16
+ puts "decode:"
17
+ puts "map gray code back to its original number"
18
+
19
+ orig_dec = gray_dec.map{ |dec| gc_gen.to_dec(dec) }
20
+ puts orig_dec.to_s
21
+
22
+ puts orig_bin = gray_dec.map{ |dec| gc_gen.to_binary(dec)}.to_s
@@ -0,0 +1,91 @@
1
+ # We want to transmit the data sequence 110100010110 using binary DPSK.
2
+ # Let s(t)=Acos(2πfot+θ) represent the transmitted signal in a signaling interval of duration Tb.
3
+ # Give the phase of the tranmitted signal. Begin with θ=0 for the phase of the first bit to be transmitted.
4
+
5
+
6
+
7
+ sig_str = "110100010110"
8
+ sig = sig_str.split('')
9
+ # diff_sig = Digiproc::Strategies::XORDifferentialEncodingStrategy.encode(sig, 2, "0")
10
+
11
+ # System 1:
12
+ #Use XOR to encode original signal with a delayed version of itself
13
+ # Map bits 0 => 0 rad, 1 => pi rad
14
+ bpsk = Digiproc::Strategies::PSK.new(modulating_signal: sig, coding_strategy: Digiproc::Strategies::XORDifferentialEncodingZeroAngleStrategy)
15
+
16
+ # System 2:
17
+ # Use XOR to encode original signal with a delayed version of itself
18
+ # Map bits 0 => pi, 1 => 3pi/2
19
+ bdpsk = Digiproc::Strategies::PSK.new(modulating_signal: sig, coding_strategy: Digiproc::Strategies::XORDifferentialEncodingStrategy)
20
+
21
+ # System 3:
22
+ # Do not XOR the bits but instead map them directly into frequencies which then are added to dealyed versions
23
+ # of themselves modulo 2pi (see the strategy used for more details)
24
+ dpsk = Digiproc::Strategies::PSK.new(modulating_signal: sig, coding_strategy:Digiproc::Strategies::DifferentialEncodingStrategy)
25
+
26
+ #System 4:
27
+ # Directly maps bits to phase without XORing and does not alter phase angles at all for transmission
28
+ dpsk2 = Digiproc::Strategies::PSK.new(modulating_signal: sig)
29
+
30
+ xvals = Digiproc::Functions.linspace(1,sig_str.length, sig_str.length + 1)
31
+
32
+
33
+ puts "System 1 Differential Signal: #{bpsk.coded_signal}"
34
+ puts "System 1 Phase: #{bpsk.phase_signal.map{ |ps| ps / Math::PI }} * pi"
35
+ puts "System 1 Decoded: #{bpsk.decode}"
36
+
37
+ dpskvals = bpsk.phase_signal.map{ |p| p / Math::PI}
38
+ path = "./examples/encoding/"
39
+ plt = Digiproc::Rbplot.line(xvals, dpskvals)
40
+ plt.xlabel("sample number")
41
+ plt.ylabel("phase ( *pi rad )")
42
+ plt.title("XOR DPSK Phase Signal (Sys1)")
43
+ plt.legend("DPSK angle")
44
+ plt.show(path)
45
+
46
+ qplt = Digiproc::QuickPlot
47
+ puts "\n\n"
48
+ puts "System 2 Differential Signal: #{bdpsk.coded_signal}"
49
+ puts "System 2 Coded Phase: #{bdpsk.phase_signal.map{ |ps| ps / Math::PI}} * pi"
50
+ puts "System 2 Decoded: #{bdpsk.decode}"
51
+ qplt.plot(data: bdpsk.phase_signal.map{ |p| p / Math::PI},y_label: "Phase", title: "System 2 Phase", path: path)
52
+ puts "\n\n"
53
+
54
+ puts "System 3 Signal: #{dpsk.coded_signal}"
55
+ puts "System 3 Coded Phase: #{dpsk.phase_signal.map{ |p| p / Math::PI}} * pi"
56
+ puts "System 3 Decoded: #{dpsk.decode}"
57
+ puts "(This is not correct)"
58
+ qplt.plot(data: dpsk.phase_signal.map{ |p| p / Math::PI},y_label: "Phase", title: "System 3 Phase", path: path)
59
+ puts "\n\n"
60
+
61
+
62
+ puts "System 4 Signal: #{dpsk2.coded_signal}"
63
+ puts "System 4 Coded Phase: #{dpsk2.phase_signal.map{ |p| p / Math::PI}} * pi"
64
+ puts "System 4 Decoded: #{dpsk2.decode}"
65
+ qplt.plot(data: dpsk2.phase_signal.map{ |p| p / Math::PI},y_label: "Phase", title: "System 3 Phase", path: path)
66
+ puts "\n\n"
67
+
68
+
69
+ yvals = bpsk.output.digitize
70
+ xvals = Digiproc::Functions.linspace(1,yvals.length, yvals.length)
71
+ plt = Digiproc::Rbplot.line(xvals, yvals)
72
+ plt.title("XOR DPSK Xmit Signal (Sys 1)")
73
+ plt.xlabel("Sample number")
74
+ plt.ylabel("Signal Amplitude")
75
+ plt.legend("DPSK Xmit Signal")
76
+ plt.theme(:dark)
77
+ plt.show(path)
78
+
79
+
80
+ qplt.plot(data: bdpsk.output.digitize, title: "System 2 Xmit Signal", y_label: "Magnitude", dark: true, path: path)
81
+ qplt.plot(data: dpsk.output.digitize, title: "System 3 Xmit Signal", y_label: "Magnitude", dark: true, path: path)
82
+ qplt.plot(data: dpsk2.output.digitize, title: "System 4 Xmit Signal", y_label: "Magnitude", dark: true, path: path)
83
+
84
+ puts "System 1 Output From Reciever: \n#{bpsk.reciever_decode}\n\n"
85
+ puts "Correctly decoded by reciever \n"
86
+ puts "System 2 Output From Reciever: \n#{bdpsk.reciever_decode}\n\n"
87
+ puts "Correctly decoded by reciever \n"
88
+ puts "System 3 Output From Reciever: \n#{dpsk.reciever_decode}\n\n"
89
+ puts "Not properly decoded by reciever"
90
+ puts "System 4 Output From Reciever: \n#{dpsk2.reciever_decode}\n\n"
91
+ puts 'Not properly decoded by reciever'
@@ -0,0 +1,6 @@
1
+ Filter_factory = Digiproc::Factories::FilterFactory
2
+ plt = Digiproc::QuickPlot
3
+ bpfilter = Filter_factory.filter_for(type: "bandpass", wo: Math::PI / 3, bw: Math::PI / 10, transition_width: 0.1, stopband_attenuation: 80)
4
+ freq_db = bpfilter.fft.dB
5
+ # Get x values with the linspace function (start, stop, number_of_points)
6
+ plt.plot(x: Digiproc::Functions.linspace(0,1,freq_db.length) ,y: freq_db, y_label: "dB", dark: true, path: './examples/factories/')
Binary file
@@ -0,0 +1,47 @@
1
+ plt = Digiproc::QuickPlot
2
+ prob = Digiproc::Probability
3
+ norm_dist = Digiproc::Probability::RealizedGaussianDistribution
4
+ window = BlackmanWindow.new(size: 16384)
5
+
6
+ #************ Find a signal in noise with FFT ****************
7
+
8
+ #sample every 1 ms
9
+ sample_rate = 0.001
10
+ datapoints = 16384 # use a power of 2 because FFT uses Radix 2 algorithm
11
+ #300Hz and 500Hz signals
12
+ signal = Digiproc::AnalogSignal.new(eqn: ->(t){0.2 * Math.cos(2*Math::PI * 300 * t) + 0.3 * Math.cos(2 * Math::PI * 400 * t)}, sample_rate: sample_rate, size: datapoints).to_ds
13
+ data = signal.data
14
+ noise = norm_dist.new(mean: 0, stddev: 1, size: datapoints)
15
+ #Add signal to noise data
16
+ recieved_signal = data.plus(noise.data)
17
+
18
+ #process signal with a blackman window (below shows that this is an optional step)
19
+ processed_signal_data = recieved_signal.times window.data
20
+
21
+ processed_signal = Digiproc::DigitalSignal.new(data: processed_signal_data)
22
+
23
+ fft = processed_signal.fft
24
+
25
+ #Get the
26
+ signal_locations = fft.maxima(4)
27
+
28
+ puts signal_locations
29
+
30
+ max_freq_hz = 0.5 / sample_rate
31
+ max_freq_datapoint = datapoints / 2
32
+
33
+ # Only use signals below Nyquist Frequency
34
+ signal_locations.select!{ |location| location.index < max_freq_datapoint }
35
+
36
+ signal_1_freq = signal_locations[0].index.to_f / max_freq_datapoint * max_freq_hz
37
+ signal_2_freq = signal_locations[1].index.to_f / max_freq_datapoint * max_freq_hz
38
+
39
+ puts "Most powerful signal at #{signal_1_freq} Hz"
40
+ puts "Second most powerful signal at #{signal_2_freq} Hz"
41
+
42
+ #Signal not viewable in time domain
43
+ plt.plot(data: recieved_signal, title: "Recieved data (time domain)", path: './examples/fft/' )
44
+ #Signal easily viewed in frequency domain (dB plot)
45
+ fft.plot_db(path: "./examples/fft/")
46
+ #plot unprocessed signal dB
47
+ plt.plot(data: Digiproc::DigitalSignal.new(data: recieved_signal).fft.dB , title: "Unprocessed FFT", path: './examples/fft/')
Binary file
@@ -0,0 +1,38 @@
1
+ plt = Digiproc::QuickPlot
2
+ fns = Digiproc::Functions
3
+ prob = Digiproc::Probability
4
+ norm_dist = Digiproc::Probability::RealizedGaussianDistribution
5
+ factory = Digiproc::Factories::FilterFactory
6
+
7
+ # Make a signal with gaussian noise:
8
+ # Create "white" Gaussian noise, 0 mean
9
+ dist = norm_dist.new(mean: 0, stddev: 1, size: 16384)
10
+
11
+ # Create a bandpass filter, make FT dimensions match
12
+ bpfilter = factory.filter_for(type: "bandpass", wo: Math::PI / 2, bw: Math::PI / 5, transition_width: 0.0001, stopband_attenuation: 80)
13
+ filter_dft = Digiproc::FFT.new(time_data: bpfilter.weights, size: 16384 * 4)
14
+
15
+ # Get FT of White noise, calculate No
16
+ dist_fft = Digiproc::FFT.new(time_data: dist.data, size: 16384 * 4)
17
+ n_o = 2 * (dist_fft.magnitude.map{ |val| val ** 2}.sum.to_f / dist_fft.data.length)
18
+
19
+ # Multiply freq domain of noise and filter to get output spectra
20
+ # Calculate output energy
21
+ filter_out = dist_fft * filter_dft
22
+ total_noise_out = filter_out.magnitude.map{ |val| (val ** 2) * (1.0/ (4 * 16384)) }.sum
23
+ time_data_out = fns.ifft(filter_out.data).map(&:real)
24
+ bw = 1.0 / 10
25
+
26
+ puts "Normal Dist. Input \n\tMean:#{prob.mean(dist.data)}, Stddev: #{prob.stddev(dist.data)}"
27
+ puts "No = #{n_o}"
28
+ puts "Total Noise Energy Out: #{total_noise_out}"
29
+ puts "Output: \n\tMean: #{prob.mean(time_data_out)}, Stddev: #{prob.stddev(time_data_out)}"
30
+ puts "Calculated Noise Energy Out: #{ n_o * bw}"
31
+
32
+ f = fns.linspace(0,1,16384)
33
+
34
+ path = './examples/filters/'
35
+ plt.plot(x: f, y: filter_dft.dB, title: "Bandpass Filter", path: path)
36
+ plt.plot(x: f, y: dist_fft.magnitude, title: "White Noise Spectra", path: path)
37
+ plt.plot(x: f, y: filter_out.magnitude, title: "White Noise Mag Out of BP Filter", y_label: "Magnitude", path: path)
38
+ plt.plot(x: f, y: filter_out.dB, title: "White Noise dB Out of BP Filter", y_label: "dB", path: path)
@@ -0,0 +1,29 @@
1
+ # Use Digiproc::Probability to calculate a QAM equation and find SNR
2
+
3
+
4
+ qam_eqn = Proc.new do |db|
5
+ peb_no = 10 ** (db / 10.0)
6
+ pes_no = peb_no * Math.log(256,2)
7
+ pq = Digiproc::Probability.normal_q( Math.sqrt(pes_no * 3.0 / 255))
8
+ psmo2 = (Math.sqrt(256) - 2)
9
+ ppc1 = (1 - 2 * pq) ** 2
10
+ ppc2 = (1 - 2 * pq) * (1 - pq)
11
+ ppc3 = (1 - pq) ** 2
12
+ ps = 1 - (1.0 / 256) * ((psmo2 ** 2) * ppc1 + 4 * psmo2 * ppc2 + 4 * ppc3)
13
+ ps / Math.log(256, 2)
14
+ end
15
+
16
+ def findval(eqn, value, desired_val, step = 0.001 , tolerance = 0.000000005)
17
+ output = eqn[value]
18
+ if (output >= (desired_val - tolerance)) and (output <= (desired_val + tolerance))
19
+ return value
20
+ else
21
+ slope = (output <=> eqn[value + tolerance])
22
+ dir_to_output = output <=> desired_val
23
+ next_val = value + slope * dir_to_output * step
24
+ return findval(eqn, next_val, desired_val)
25
+ end
26
+ end
27
+
28
+ qam = findval(qam_eqn, 25 , 0.000001)
29
+ puts "QAM Eb/No = #{qam}"
@@ -0,0 +1,10 @@
1
+ #GramSchmidtOrgonormalize calculates orthogonal vectors if possible
2
+ # translate_center_to_origin centeres points around the origin
3
+
4
+ messages = [[1,1], [1,-1], [-1,1]]
5
+ output_vectors = Digiproc::Strategies::GramSchmidtOrthonormalize.new(messages).output
6
+ puts output_vectors.to_s
7
+ # Therefore, this signal cannot be reprisented in 3-D Space
8
+
9
+ #Minimize energy in the 2-D reprisentation
10
+ puts Digiproc::Functions.translate_center_to_origin([[1,1],[1,-1],[-1,1]]).to_s