aboutsummaryrefslogtreecommitdiff
path: root/octave/qpsk.m
diff options
context:
space:
mode:
authorAuthor Name <[email protected]>2023-07-07 12:20:59 +0930
committerDavid Rowe <[email protected]>2023-07-07 12:29:06 +0930
commitac7c48b4dee99d4c772f133d70d8d1b38262fcd2 (patch)
treea2d0ace57a9c0e2e5b611c4987f6fed1b38b81e7 /octave/qpsk.m
shallow zip-file copy from codec2 e9d726bf20
Diffstat (limited to 'octave/qpsk.m')
-rw-r--r--octave/qpsk.m140
1 files changed, 140 insertions, 0 deletions
diff --git a/octave/qpsk.m b/octave/qpsk.m
new file mode 100644
index 0000000..b502e16
--- /dev/null
+++ b/octave/qpsk.m
@@ -0,0 +1,140 @@
+% qpsk.m
+%
+% David Rowe Sep 2015
+%
+% Octave functions to implement a QPSK modem
+
+1;
+
+% Gray coded QPSK modulation function
+
+function symbol = qpsk_mod(two_bits)
+ two_bits_decimal = sum(two_bits .* [2 1]);
+ switch(two_bits_decimal)
+ case (0) symbol = 1;
+ case (1) symbol = j;
+ case (2) symbol = -j;
+ case (3) symbol = -1;
+ endswitch
+endfunction
+
+
+% Gray coded QPSK demodulation function
+
+function two_bits = qpsk_demod(symbol)
+ bit0 = real(symbol*exp(j*pi/4)) < 0;
+ bit1 = imag(symbol*exp(j*pi/4)) < 0;
+ two_bits = [bit1 bit0];
+endfunction
+
+
+% Inserts pilot symbols a frame of symbols. The pilot symbols are
+% spread evenly throughout the input frame.
+
+function frameout = insert_pilots(framein, pilots, Npilotstep)
+
+ lpilots = length(pilots);
+ lframein = length(framein);
+ frameout = zeros(1, lframein + lpilots);
+
+ pin = 1; pout = 1; ppilots = 1;
+ while (lpilots)
+ %printf("pin %d pout %d ppilots %d lpilots %d\n", pin, pout, ppilots, lpilots);
+ frameout(pout:pout+Npilotstep-1) = framein(pin:pin+Npilotstep-1);
+ pin += Npilotstep;
+ pout += Npilotstep;
+ frameout(pout:pout) = pilots(ppilots);
+ ppilots++;
+ pout++;
+ lpilots--;
+ end
+endfunction
+
+
+% Removes the pilots symbols from a frame of symbols.
+
+function frameout = remove_pilots(framein, pilots, Npilotstep)
+
+ frameout = [];
+ lpilots = length(pilots);
+
+ pin = 1; pout = 1;
+ while (lpilots)
+ %printf("pin %d pout %d lpilots %d ", pin, pout, lpilots);
+ %printf("pin+spacing-1 %d lvd %d lframein: %d\n", pin+spacing-1, lvd, length(framein));
+ frameout(pout:pout+Npilotstep-1) = framein(pin:pin+Npilotstep-1);
+ pin += Npilotstep+1;
+ pout += Npilotstep;
+ lpilots--;
+ end
+
+endfunction
+
+
+% Estimate and correct phase offset using a window of Np pilots around
+% current symbol
+
+function symbpilot_rx = correct_phase_offset(aqpsk, symbpilot_rx)
+ rx_pilot_buf = aqpsk.rx_pilot_buf;
+ Npilotstep = aqpsk.Npilotstep;
+ Nsymb = aqpsk.Nsymb;
+
+ for ns=1:Npilotstep+1:Nsymb
+
+ % update buffer of recent pilots, note we need past ones
+
+ rx_pilot_buf(1) = rx_pilot_buf(2);
+ next_pilot_index = ceil(ns/(Npilotstep+1))*(Npilotstep+1);
+ rx_pilot_buf(2) = symbpilot_rx(next_pilot_index);
+
+ % average pilot symbols to get estimate of phase
+
+ phase_est = angle(sum(rx_pilot_buf));
+
+ %printf("next_pilot_index: %d phase_est: %f\n", next_pilot_index, phase_est);
+
+ % now correct the phase of each symbol
+
+ for s=ns:ns+Npilotstep
+ symbpilot_rx(s) *= exp(-j*phase_est);
+ end
+ end
+
+ aqpsk.rx_pilot_buf = rx_pilot_buf;
+endfunction
+
+
+% builds up a sparse QPSK modulated version version of the UW for use
+% in UW sync at the rx
+
+function mod_uw = build_mod_uw(uw, spacing)
+ luw = length(uw);
+
+ mod_uw = [];
+
+ pout = 1; puw = 1;
+ while (luw)
+ %printf("pin %d pout %d puw %d luw %d\n", pin, pout, puw, luw);
+ pout += spacing/2;
+ mod_uw(pout) = qpsk_mod(uw(puw:puw+1));
+ puw += 2;
+ pout += 1;
+ luw -= 2;
+ end
+endfunction
+
+
+% Uses the UW to determine when we have a full codeword ready for decoding
+
+function [found_uw corr] = look_for_uw(mem_rx_symbols, mod_uw)
+ sparse_mem_rx_symbols = mem_rx_symbols(find(mod_uw));
+
+ % correlate with ref UW
+
+ num = (mem_rx_symbols * mod_uw') .^ 2;
+ den = (sparse_mem_rx_symbols * sparse_mem_rx_symbols') * (mod_uw * mod_uw');
+
+ corr = abs(num/(den+1E-6));
+ found_uw = corr > 0.8;
+endfunction
+