Class: Digiproc::Strategies::PSK

Inherits:
Object
  • Object
show all
Defined in:
lib/strategies/modulation/phase_shift_keying_strategy.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(carrier_signal_eqn: ->(a, fo, t, theta){ a * Math.cos(2*Math::PI * fo * t + theta) }, modulating_signal:, coding_strategy: nil, carrier_frequency: 10000, pulse_length: 0.00015) ⇒ PSK

Initialize Args

modulating_signal

Array; each element is a “symbol” (should be a string reprisenting bits), where a symnbol is a number of bits symbolizing a single character (ie symbol means character)

carrier_signal_eqn (optional)

Lambda (or Proc) default value is: ->(a, fo, t, theta){ a * Math.cos(2*Math::PI * fo * t + theta) }

coding_strategy (optional)

Protocol, see Digiproc::Strategies::XORDifferentialEncodingZeroAngleStrategy, default value is nil

carrier_frequency (optional)::Numeric (in Hz), defaults to 10000

pulse_length (optional)

Float (in seconds), defaults to 0.00015 determines how long to apply a phase shift



12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# File 'lib/strategies/modulation/phase_shift_keying_strategy.rb', line 12

def initialize(carrier_signal_eqn: ->(a, fo, t, theta){ a * Math.cos(2*Math::PI * fo * t + theta) }, modulating_signal: ,coding_strategy: nil, carrier_frequency: 10000, pulse_length: 0.00015)
    @carrier_signal_eqn, @modulating_signal, @coding_strategy, @carrier_frequency, @pulse_length = carrier_signal_eqn, modulating_signal, coding_strategy, carrier_frequency, pulse_length
    @m = 2 ** modulating_signal.first.length 
    if coding_strategy.nil?
        @phase_shift_eqn = ->(i){ (2.0 * Math::PI * (i)) / @m }
        @coded_signal = @modulating_signal
    else
        @phase_shift_eqn = coding_strategy.phase_shift_eqn(@m)
        @coded_signal = coding_strategy.encode modulating_signal, @m, "0"
    end
    @signal_to_phase = {}
    for i in 0...@m do
        @signal_to_phase[i.to_s] = @phase_shift_eqn[i]
    end

    @phase_signal = @coded_signal.map{ |coded_symbol| @phase_shift_eqn[coded_symbol.to_i(2)] }

end

Instance Attribute Details

#carrier_frequencyObject

Returns the value of attribute carrier_frequency.



3
4
5
# File 'lib/strategies/modulation/phase_shift_keying_strategy.rb', line 3

def carrier_frequency
  @carrier_frequency
end

#carrier_signal_eqnObject

Returns the value of attribute carrier_signal_eqn.



3
4
5
# File 'lib/strategies/modulation/phase_shift_keying_strategy.rb', line 3

def carrier_signal_eqn
  @carrier_signal_eqn
end

#coded_signalObject

Returns the value of attribute coded_signal.



3
4
5
# File 'lib/strategies/modulation/phase_shift_keying_strategy.rb', line 3

def coded_signal
  @coded_signal
end

#coding_strategyObject

Returns the value of attribute coding_strategy.



3
4
5
# File 'lib/strategies/modulation/phase_shift_keying_strategy.rb', line 3

def coding_strategy
  @coding_strategy
end

#mObject

Returns the value of attribute m.



3
4
5
# File 'lib/strategies/modulation/phase_shift_keying_strategy.rb', line 3

def m
  @m
end

#modulating_signalObject

Returns the value of attribute modulating_signal.



3
4
5
# File 'lib/strategies/modulation/phase_shift_keying_strategy.rb', line 3

def modulating_signal
  @modulating_signal
end

#phase_shift_eqnObject

Returns the value of attribute phase_shift_eqn.



3
4
5
# File 'lib/strategies/modulation/phase_shift_keying_strategy.rb', line 3

def phase_shift_eqn
  @phase_shift_eqn
end

#phase_signalObject

Returns the value of attribute phase_signal.



3
4
5
# File 'lib/strategies/modulation/phase_shift_keying_strategy.rb', line 3

def phase_signal
  @phase_signal
end

#pulse_lengthObject

Returns the value of attribute pulse_length.



3
4
5
# File 'lib/strategies/modulation/phase_shift_keying_strategy.rb', line 3

def pulse_length
  @pulse_length
end

#signal_to_phaseObject

Returns the value of attribute signal_to_phase.



3
4
5
# File 'lib/strategies/modulation/phase_shift_keying_strategy.rb', line 3

def signal_to_phase
  @signal_to_phase
end

Instance Method Details

#decodeObject

Apply inverse of the PSK strategy to decode the PSK signal



47
48
49
50
51
52
# File 'lib/strategies/modulation/phase_shift_keying_strategy.rb', line 47

def decode
    eqn = coding_strategy.nil? ? decode_eqn : coding_strategy.phase_to_sym(@m)
    sym = @phase_signal.map{ |phase| eqn[phase] }
    sym = coding_strategy.decode(sym) if not coding_strategy.nil?
    return sym
end

#output(a: 1) ⇒ Object

Input Args

a (optional)

Numeric for amplitude of the signal (default value = 1)

Return Digiproc::AnalogSignal of the Phase Shift Keyed signal



35
36
37
38
39
40
41
42
43
# File 'lib/strategies/modulation/phase_shift_keying_strategy.rb', line 35

def output(a: 1)
    eqn = Proc.new do |t|
        theta_index = (t.to_f / @pulse_length.to_f).floor
        @carrier_signal_eqn[a, @carrier_frequency, t, @phase_signal[theta_index]]
    end
    sample_rate = 0.01 / @carrier_frequency
    size = @pulse_length.to_f * @phase_signal.length / sample_rate
    Digiproc::AnalogSignal.new(eqn: eqn, sample_rate: sample_rate, size: size)
end

#reciever_decodeObject

Based off of reciever Figure 4-15 INTRODUCTION TO DIGITAL COMMUNICATIONS, ZIEMER, PETERSON pg 237 NOTE: SUBOPTIMUM DETECTOR, optimum detector referenced on the above page, different reference This simulates a real-life reciever recieving the siglans and decoding them



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/strategies/modulation/phase_shift_keying_strategy.rb', line 58

def reciever_decode
    recieved = output.to_ds.fft.magnitude
    ts = 1 / (@carrier_frequency / 0.01)
    max_freq = 1.0 / ts
    normalized_target = @carrier_frequency / max_freq.to_f
    bw = (0.57 / ts) / max_freq.to_f
    plt = Digiproc::QuickPlot
    factory = Digiproc::Factories::FilterFactory
    bpf = factory.filter_for(type: "bandpass", wo: normalized_target * Math::PI * 2, bw: bw * Math::PI * 2, transition_width: 0.0001, stopband_attenuation: 80)
    bpf.fft.calculate_at_size(2 ** 16)

    filtered = (bpf.fft * output.to_ds.fft(2** 16)).ifft.map(&:real)
    sample_interval = @pulse_length / (0.01 / @carrier_frequency)
    output = []
    for i in (sample_interval + 1).to_i...filtered.length do 
        output << (filtered[i] * filtered[i-sample_interval]) 
    end
    lpf = factory.filter_for(type: "lowpass", wc: 2 * Math::PI / 4, transition_width: 0.0001, stopband_attenuation: 80)
    outft = Digiproc::FFT.new(time_data: output, size: 2 ** 16)
    
    # TODO FIND WHERE SIGNAL BEGINS AND OUTPUT THE SIGNAL BASED OFF OF 
    # SIGNAL SIZE. ALSO, THERE SHOULD BE A RECIEVER METHOD OR SEPERATE CLASS
    # WHICH OUPUTS THE RAW DATA OF THE RECIEVERS
    data = (outft * lpf.fft).ifft.map(&:real)
    all_out = @phase_signal.size > 200 ? data[55000,  200 * sample_interval].concat(data[0, sample_interval * (@phase_signal.size - 200)]) : data[55000,  @phase_signal.size * sample_interval]
    final_out = []
    for i in 0...all_out.length do
        final_out << all_out[i] if (i + 10) %  sample_interval == 0 
    end
    final_out
end