summary refs log tree commit diff stats
path: root/src/main/kotlin/space/autistic/radio/cli
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/kotlin/space/autistic/radio/cli')
-rw-r--r--src/main/kotlin/space/autistic/radio/cli/OfflineSimulator.kt237
1 files changed, 0 insertions, 237 deletions
diff --git a/src/main/kotlin/space/autistic/radio/cli/OfflineSimulator.kt b/src/main/kotlin/space/autistic/radio/cli/OfflineSimulator.kt
deleted file mode 100644
index bc16814..0000000
--- a/src/main/kotlin/space/autistic/radio/cli/OfflineSimulator.kt
+++ /dev/null
@@ -1,237 +0,0 @@
-package space.autistic.radio.cli
-
-import org.joml.Vector2f
-import space.autistic.radio.complex.cmul
-import space.autistic.radio.fmsim.FmFullConstants
-import space.autistic.radio.fmsim.FmFullDemodulator
-import space.autistic.radio.fmsim.FmFullModulator
-import java.io.FileInputStream
-import java.io.FileOutputStream
-import java.io.InputStream
-import java.net.URI
-import java.nio.ByteBuffer
-import java.nio.ByteOrder
-import java.nio.FloatBuffer
-import kotlin.io.path.inputStream
-import kotlin.io.path.toPath
-import kotlin.math.min
-import kotlin.system.exitProcess
-
-fun printUsage() {
-    println("Usage: OfflineSimulator <-o|-O> OUTFILE.raw {[-p POWER] [-l|-h] [-m] file:///FILE.raw} [-m]")
-    println("    file:///FILE.raw (or ./FILE.raw - the ./ is required)")
-    println("        The raw input file. two-channel (even with -m), 48kHz 32-bit float.")
-    println("    -o OUTFILE.raw")
-    println("        The raw RF stream to output, 2x300kHz 32-bit float.")
-    println("    -O OUTFILE.raw")
-    println("        The raw audio stream to output, 2x48kHz 32-bit float.")
-    println("    -p POWER")
-    println("        The signal amplitude (power level), e.g. 1.0.")
-    println("    -l")
-    println("        Simulate a partial overlap on the lower half of the tuned-into frequency.")
-    println("    -h")
-    println("        Simulate a partial overlap on the upper half of the tuned-into frequency.")
-    println("    -m")
-    println("        Downconvert to mono. As the last option, demodulate as mono.")
-}
-
-class SimFile(val power: Float, val band: Int, val filename: String, val stereo: Boolean) {
-    var closed: Boolean = false
-    val buffer: FloatBuffer = FloatBuffer.allocate(8192)
-    val modulator = FmFullModulator()
-    var stream: InputStream? = null
-}
-
-fun main(args: Array<String>) {
-    if (args.isEmpty()) {
-        printUsage()
-        exitProcess(1)
-    }
-    var hasOutput = false
-    var inArg = ""
-    var output = ""
-    var rfOutput = true
-    var power = 1.0f
-    var band = 2
-    var stereo = FmFullConstants.STEREO
-    val files: ArrayList<SimFile> = ArrayList()
-    for (arg in args) {
-        if (!hasOutput) {
-            if (arg == "-o" || arg == "-O") {
-                hasOutput = true
-                inArg = arg
-            } else {
-                printUsage()
-                exitProcess(1)
-            }
-        } else {
-            when (inArg) {
-                "-o" -> {
-                    output = arg
-                    rfOutput = true
-                    inArg = ""
-                }
-
-                "-O" -> {
-                    output = arg
-                    rfOutput = false
-                    inArg = ""
-                }
-
-                "-p" -> {
-                    power = arg.toFloatOrNull() ?: run {
-                        println("Error processing -p argument: not a valid float")
-                        printUsage()
-                        exitProcess(1)
-                    }
-                    inArg = ""
-                }
-
-                "" -> {
-                    if (!arg.startsWith("-")) {
-                        files.add(SimFile(power, band, arg, stereo))
-                        inArg = ""
-                        band = 2
-                        power = 1.0f
-                        stereo = FmFullConstants.STEREO
-                    } else {
-                        when (arg) {
-                            "-p" -> inArg = "-p"
-                            "-l" -> band = 1
-                            "-h" -> band = 3
-                            "-m" -> stereo = FmFullConstants.MONO
-                            else -> {
-                                println("Unknown option")
-                                printUsage()
-                                exitProcess(1)
-                            }
-                        }
-                    }
-                }
-
-                else -> throw NotImplementedError(inArg)
-            }
-        }
-    }
-
-    if (files.isEmpty()) {
-        printUsage()
-        exitProcess(1)
-    }
-
-    println(ProcessHandle.current().pid())
-
-    FileOutputStream(output).buffered().use { outputStream ->
-        for (inputFile in files) {
-            if (inputFile.filename != "file:///dev/zero") {
-                if (inputFile.filename.startsWith("./")) {
-                    inputFile.stream = FileInputStream(inputFile.filename)
-                } else {
-                    inputFile.stream = URI(inputFile.filename).toPath().inputStream()
-                }
-            }
-        }
-
-        val buffer = ByteBuffer.allocate(2 * 4 * FmFullConstants.FFT_DATA_BLOCK_SIZE_LPF_48K_15K_3K1)
-        val plus100k = FloatBuffer.wrap(FmFullConstants.CBUFFER_100K_300K)
-        val minus100k = FloatBuffer.wrap(FmFullConstants.CBUFFER_100K_300K)
-        val demodulator = FmFullDemodulator()
-        var lastStereoPilot = false
-        while (true) {
-            // initialized to maximum buffer size, trimmed down later
-            var minBuffer = 8192
-            for (inputFile in files) {
-                val stream = inputFile.stream
-                if (stream == null) {
-                    if (inputFile.buffer.remaining() > 2 * FmFullConstants.FFT_DATA_BLOCK_SIZE_48K_300K) {
-                        inputFile.modulator.flush(inputFile.power, inputFile.stereo) {
-                            inputFile.buffer.put(it)
-                        }
-                    }
-                } else {
-                    val bytes = stream.read(buffer.array())
-                    if (bytes <= 0) {
-                        stream.close()
-                        inputFile.stream = null
-                        inputFile.closed = true
-                        inputFile.modulator.flush(inputFile.power, inputFile.stereo) {
-                            inputFile.buffer.put(it)
-                        }
-                    } else {
-                        val floats = buffer.slice(0, bytes).order(ByteOrder.LITTLE_ENDIAN).asFloatBuffer()
-                        var shouldFlush = true
-                        inputFile.modulator.process(floats, inputFile.power, inputFile.stereo) {
-                            inputFile.buffer.put(it)
-                            shouldFlush = false
-                        }
-                        if (shouldFlush) {
-                            inputFile.modulator.flush(inputFile.power, inputFile.stereo) {
-                                inputFile.buffer.put(it)
-                            }
-                        }
-                    }
-                }
-                minBuffer = min(minBuffer, inputFile.buffer.position())
-            }
-
-            val outputBuffer = ByteBuffer.allocate(minBuffer * 4)
-            val floatView = outputBuffer.order(ByteOrder.LITTLE_ENDIAN).asFloatBuffer()
-            val floatBufferLo = FloatBuffer.allocate(minBuffer)
-            val floatBufferHi = FloatBuffer.allocate(minBuffer)
-            for (inputFile in files) {
-                inputFile.buffer.flip()
-                val floatBuffer = when (inputFile.band) {
-                    1 -> floatBufferLo
-                    2 -> floatView
-                    3 -> floatBufferHi
-                    else -> throw IllegalStateException()
-                }
-                for (i in 0 until floatBuffer.capacity()) {
-                    floatBuffer.put(i, floatBuffer.get(i) + inputFile.buffer.get())
-                }
-                inputFile.buffer.compact()
-            }
-            val z = Vector2f()
-            val w = Vector2f()
-            for (i in 0 until floatBufferHi.capacity() step 2) {
-                z.x = floatBufferHi.get(i)
-                z.y = floatBufferHi.get(i + 1)
-                if (!plus100k.hasRemaining()) {
-                    plus100k.clear()
-                }
-                w.x = plus100k.get()
-                w.y = plus100k.get()
-                z.cmul(w)
-                floatView.put(i, floatView.get(i) + z.x)
-                floatView.put(i, floatView.get(i) + z.y)
-            }
-            for (i in 0 until floatBufferLo.capacity() step 2) {
-                z.x = floatBufferLo.get(i)
-                z.y = floatBufferLo.get(i + 1)
-                if (!minus100k.hasRemaining()) {
-                    minus100k.clear()
-                }
-                w.x = minus100k.get()
-                w.y = -minus100k.get()
-                z.cmul(w)
-                floatView.put(i, floatView.get(i) + z.x)
-                floatView.put(i, floatView.get(i) + z.y)
-            }
-            if (rfOutput) {
-                outputStream.write(outputBuffer.array())
-            } else {
-                demodulator.process(floatView, stereo) { stereoPilot, it ->
-                    if (stereoPilot != lastStereoPilot) {
-                        println(if (stereoPilot) "stereo" else "mono")
-                    }
-                    lastStereoPilot = stereoPilot
-                    buffer.order(ByteOrder.LITTLE_ENDIAN).asFloatBuffer().put(0, it.array())
-                    outputStream.write(buffer.array())
-                }
-            }
-            if (files.all { it.closed }) {
-                break
-            }
-        }
-    }
-}
\ No newline at end of file