summary refs log tree commit diff stats
path: root/src/main/kotlin/space/autistic/radio/fmsim/FmFullDemodulator.kt
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/kotlin/space/autistic/radio/fmsim/FmFullDemodulator.kt')
-rw-r--r--src/main/kotlin/space/autistic/radio/fmsim/FmFullDemodulator.kt158
1 files changed, 0 insertions, 158 deletions
diff --git a/src/main/kotlin/space/autistic/radio/fmsim/FmFullDemodulator.kt b/src/main/kotlin/space/autistic/radio/fmsim/FmFullDemodulator.kt
deleted file mode 100644
index de44e69..0000000
--- a/src/main/kotlin/space/autistic/radio/fmsim/FmFullDemodulator.kt
+++ /dev/null
@@ -1,158 +0,0 @@
-package space.autistic.radio.fmsim
-
-import org.joml.Vector2f
-import org.jtransforms.fft.FloatFFT_1D
-import space.autistic.radio.complex.I
-import space.autistic.radio.complex.cmul
-import space.autistic.radio.complex.conjugate
-import space.autistic.radio.dsp.Biquad1stOrder
-import java.nio.FloatBuffer
-import java.util.function.BiConsumer
-
-class FmFullDemodulator {
-    private val inputBuffer = FloatBuffer.allocate(FmFullConstants.FFT_SIZE_48K_300K)
-    private val fft300kBuf = FloatBuffer.allocate(FmFullConstants.FFT_SIZE_48K_300K)
-    private val fft48kBuf = FloatBuffer.allocate(FmFullConstants.FFT_SIZE_LPF_48K_15K_3K1)
-    private val outputBuffer = FloatBuffer.allocate(2 * FmFullConstants.FFT_DATA_BLOCK_SIZE_LPF_48K_15K_3K1)
-
-    init {
-        inputBuffer.position(2 * FmFullConstants.FFT_OVERLAP_48K_300K)
-    }
-
-    // yep.
-    private val boxcarI = Biquad1stOrder(1f, 1f, 0f)
-    private val boxcarQ = Biquad1stOrder(1f, 1f, 0f)
-    private val delayI = Biquad1stOrder(0f, 1f, 0f)
-    private val delayQ = Biquad1stOrder(0f, 1f, 0f)
-
-    private val deemphasisLeft = Biquad1stOrder(
-        FmFullConstants.FM_DEEMPAHSIS_B0_48K,
-        FmFullConstants.FM_DEEMPHASIS_B1_48K,
-        FmFullConstants.FM_DEEMPHASIS_A1_48K
-    )
-    private val deemphasisRight = Biquad1stOrder(
-        FmFullConstants.FM_DEEMPAHSIS_B0_48K,
-        FmFullConstants.FM_DEEMPHASIS_B1_48K,
-        FmFullConstants.FM_DEEMPHASIS_A1_48K
-    )
-
-    private val lastStereoPilot = Vector2f()
-    private val lastStereoPilotPolarDiscriminator = Vector2f()
-
-    /**
-     * Takes in samples at 300kHz, in I/Q format, and processes them for output.
-     *
-     * Calls consumer with processed samples at 48kHz, stereo.
-     */
-    fun process(input: FloatBuffer, stereo: Boolean, consumer: BiConsumer<Boolean, FloatBuffer>) {
-        while (input.remaining() >= 2) {
-            val z = Vector2f()
-            val w = Vector2f()
-            while (input.remaining() >= 2 && inputBuffer.hasRemaining()) {
-                z.x = boxcarI.process(input.get())
-                z.y = boxcarQ.process(input.get())
-                // quadrature demodulation = FM demodulation
-                // see https://wiki.gnuradio.org/index.php/Quadrature_Demod and such
-                w.x = delayI.process(z.x)
-                w.y = -delayQ.process(z.y)
-                z.cmul(w)
-                inputBuffer.put(org.joml.Math.atan2(z.y, z.x) * FmFullConstants.INVERSE_CORRECTION_FACTOR)
-            }
-            if (!inputBuffer.hasRemaining()) {
-                var stereoPilot = false
-                fft300kBuf.put(0, inputBuffer.array())
-                fft300k.realForward(fft300kBuf.array())
-                for (i in 0 until fft48kBuf.capacity()) {
-                    fft48kBuf.put(i, 0f)
-                }
-                for (i in 2 until (FmFullConstants.FREQUENCY_MIXING_BINS_38K - 2 and 1.inv()) step 2) {
-                    z.x = fft300kBuf.get(i)
-                    z.y = fft300kBuf.get(i + 1)
-                    w.x = fir48kLpf.get(i)
-                    w.y = fir48kLpf.get(i + 1)
-                    z.cmul(w)
-                    fft48kBuf.put(i, z.x)
-                    fft48kBuf.put(i + 1, z.y)
-                }
-                fft48kBuf.put(0, fft300kBuf.get(0) * fir48kLpf.get(0))
-                fft48k.realInverse(fft48kBuf.array(), false)
-                outputBuffer.clear()
-                fft48kBuf.position(FmFullConstants.FFT_OVERLAP_LPF_48K_15K_3K1)
-                for (i in 0 until FmFullConstants.FFT_DATA_BLOCK_SIZE_LPF_48K_15K_3K1) {
-                    val sample = fft48kBuf.get() * (1f / FmFullConstants.FFT_SIZE_48K_300K)
-                    outputBuffer.put(sample)
-                    outputBuffer.put(sample)
-                }
-                outputBuffer.clear()
-                if (stereo) {
-                    z.x = fft300kBuf.get(FmFullConstants.FREQUENCY_MIXING_BINS_38K)
-                    z.y = fft300kBuf.get(FmFullConstants.FREQUENCY_MIXING_BINS_38K + 1)
-                    z.conjugate(w).cmul(lastStereoPilot).conjugate().normalize()
-                    if (lastStereoPilotPolarDiscriminator.distanceSquared(w) < 0.5f && z.lengthSquared() >= FmFullConstants.FFT_SIZE_48K_300K) {
-                        stereoPilot = true
-                    }
-                    lastStereoPilot.set(z)
-                    lastStereoPilotPolarDiscriminator.set(w)
-                    if (stereoPilot) {
-                        // w is our phase offset
-                        // TODO check if this is mathematically sound
-                        z.normalize().cmul(z).cmul(w.conjugate()).conjugate()
-                        // z is our recovered 38kHz carrier, including phase offset
-                        for (i in 0 until fft48kBuf.capacity()) {
-                            fft48kBuf.put(i, 0f)
-                        }
-                        val base = FmFullConstants.FREQUENCY_MIXING_BINS_38K * 2
-                        val sz = Vector2f()
-                        val sw = Vector2f()
-                        for (i in 2 until (FmFullConstants.FREQUENCY_MIXING_BINS_38K - 2 and 1.inv()) step 2) {
-                            sz.x = fft300kBuf.get(base + i)
-                            sz.y = fft300kBuf.get(base + i + 1)
-                            sw.x = fft300kBuf.get(base - i)
-                            sw.y = fft300kBuf.get(base - i + 1)
-                            sz.cmul(z).add(sw.cmul(z).conjugate())
-                            sw.x = fir48kLpf.get(i)
-                            sw.y = fir48kLpf.get(i + 1)
-                            sz.cmul(sw)
-                            fft48kBuf.put(i, sz.x)
-                            fft48kBuf.put(i + 1, sz.y)
-                        }
-                        sz.x = fft300kBuf.get(base)
-                        sz.y = fft300kBuf.get(base + 1)
-                        sz.cmul(z)
-                        fft48kBuf.put(0, sz.x * fir48kLpf.get(0))
-                        fft48k.realInverse(fft48kBuf.array(), false)
-                        outputBuffer.clear()
-                        fft48kBuf.position(FmFullConstants.FFT_OVERLAP_LPF_48K_15K_3K1)
-                        for (i in 0 until FmFullConstants.FFT_DATA_BLOCK_SIZE_LPF_48K_15K_3K1) {
-                            val lmr = fft48kBuf.get() * (1f / FmFullConstants.FFT_SIZE_48K_300K)
-                            val lpr = outputBuffer.get(outputBuffer.position())
-                            outputBuffer.put((lpr + lmr) * 0.5f)
-                            outputBuffer.put((lpr - lmr) * 0.5f)
-                        }
-                        outputBuffer.clear()
-                    }
-                }
-                inputBuffer.position(FmFullConstants.FFT_DATA_BLOCK_SIZE_48K_300K)
-                inputBuffer.compact()
-                for (i in 0 until outputBuffer.capacity() step 2) {
-                    outputBuffer.put(i, deemphasisLeft.process(outputBuffer.get(i)))
-                }
-                for (i in 1 until outputBuffer.capacity() step 2) {
-                    outputBuffer.put(i, deemphasisRight.process(outputBuffer.get(i)))
-                }
-                consumer.accept(stereoPilot, outputBuffer)
-            }
-        }
-    }
-
-    companion object {
-        private val fft300k = FloatFFT_1D(FmFullConstants.FFT_SIZE_48K_300K.toLong())
-        private val fft48k = FloatFFT_1D(FmFullConstants.FFT_SIZE_LPF_48K_15K_3K1.toLong())
-        private val fir48kLpf = FloatBuffer.allocate(FmFullConstants.FFT_SIZE_LPF_48K_15K_3K1)
-
-        init {
-            fir48kLpf.put(0, FmFullConstants.FIR_LPF_48K_15K_3K1)
-            fft48k.realForward(fir48kLpf.array())
-        }
-    }
-}
\ No newline at end of file