This is a followup to
this blog post.
The figure below summarizes how three IQ data streams with equal frequency offsets Δf are combined into a single IQ data stream with sampling frequency 4Δf:

Coherent combination of three KiwiSDR IQ streams 
Note that the center frequencies are set to exact values, using GNSS timestamps to correct the local KiwiSDR oscillator, while the sampling frequencies of the IQ data streams are derived from the local KiwiSDR oscillator and are not exact. As a consequence the three IQ data streams are not coherent.
The three IQ data streams can be made coherent by 1) correcting for the frequency offsets and 2) aligning the relative phases.
1) Correcting for the frequency offset
One way of describing this correction is by comparing a signal in stream#1 with frequency ΔF/2 and another signal in stream#2 with frequency ΔF/2, taking into account that there are two different sampling rates: the true (GNSS aligned) sampling rate F
_{s} and the sampling rate according to the state of the local KiwiSDR oscillator, F′
_{s }:
z
_{1}(n) = exp{2πinΔF/2F′
_{s}}
z
_{2}(n) = exp{2πinΔF/F
_{s}  2πinΔF/2F′
_{s}} .
The beat offset signal is given by
z
_{1}^{*}(n) z
_{2}(n) = exp{2πinΔF(1/F
_{s}  1/F′
_{s})} ,
and is used to correct for the frequency offset, where F′
_{s} is determined from the GNSS time tags in the KiwiSDR IQ streams.
2) Relative phase alignment
Having corrected the frequency offsets, we are left with constant relative phase differences, Δϕ(0,1) and Δϕ(1,2). These global phase offsets are estimated by crosscorrelating the overlapping parts of the spectra, indicated in yellow in the figure above. GNURadio makes it easy to do this, using a combination of
freq_xlating_ccf and
conjugate__cc and a simple block which estimates the phase difference between two vectors of IQ samples.
Because the overlaps between IQ streams are needed to estimate the phase offsets, recordings with kiwirecorder.py should use the full available bandwidth.
The method described above has been implemented using GNURadio and is available as part of
grkiwisdr. Please note that this is work in progress and might need further improvements.
As can be seen in the updated GRC flowgraph below, IQ stream sample alignment, the correction for coherence, and the PFB synthesizer were combined into a single GNURadio block, called coh_stream_synth. In addition, exp{iΔϕ(0,1)} and exp{iΔϕ(1,2)} are shown in a constellation diagram display in order to monitor phase coherence (=stable relative phases).

GRC flowgraph 
Using the
GNURadio PFB synthesizer with 2× oversampling (
twox=True), edge effects at the boundaries between IQ data streams are avoided:

Coherent combination of three IQ streams @12 kHz into a single IQ stream @32 kHz. 