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.kt57
1 files changed, 43 insertions, 14 deletions
diff --git a/src/main/kotlin/space/autistic/radio/cli/OfflineSimulator.kt b/src/main/kotlin/space/autistic/radio/cli/OfflineSimulator.kt
index 517957b..bc16814 100644
--- a/src/main/kotlin/space/autistic/radio/cli/OfflineSimulator.kt
+++ b/src/main/kotlin/space/autistic/radio/cli/OfflineSimulator.kt
@@ -3,6 +3,7 @@ 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
@@ -17,20 +18,24 @@ import kotlin.math.min
 import kotlin.system.exitProcess
 
 fun printUsage() {
-    println("Usage: OfflineSimulator -o OUTFILE.raw {[-p POWER] [-l|-h] file:///FILE.raw}")
+    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. 2x48kHz 32-bit float")
+    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, 2x200kHz 32-bit float")
+    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("        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) {
+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()
@@ -45,14 +50,16 @@ fun main(args: Array<String>) {
     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") {
+            if (arg == "-o" || arg == "-O") {
                 hasOutput = true
-                inArg = "-o"
+                inArg = arg
             } else {
                 printUsage()
                 exitProcess(1)
@@ -61,6 +68,13 @@ fun main(args: Array<String>) {
             when (inArg) {
                 "-o" -> {
                     output = arg
+                    rfOutput = true
+                    inArg = ""
+                }
+
+                "-O" -> {
+                    output = arg
+                    rfOutput = false
                     inArg = ""
                 }
 
@@ -75,15 +89,17 @@ fun main(args: Array<String>) {
 
                 "" -> {
                     if (!arg.startsWith("-")) {
-                        files.add(SimFile(power, band, arg))
+                        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()
@@ -119,14 +135,16 @@ fun main(args: Array<String>) {
         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.IFFT_DATA_BLOCK_SIZE_48K_300K) {
-                        inputFile.modulator.flush(inputFile.power) {
+                    if (inputFile.buffer.remaining() > 2 * FmFullConstants.FFT_DATA_BLOCK_SIZE_48K_300K) {
+                        inputFile.modulator.flush(inputFile.power, inputFile.stereo) {
                             inputFile.buffer.put(it)
                         }
                     }
@@ -136,18 +154,18 @@ fun main(args: Array<String>) {
                         stream.close()
                         inputFile.stream = null
                         inputFile.closed = true
-                        inputFile.modulator.flush(inputFile.power) {
+                        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.modulator.process(floats, inputFile.power, inputFile.stereo) {
                             inputFile.buffer.put(it)
                             shouldFlush = false
                         }
                         if (shouldFlush) {
-                            inputFile.modulator.flush(inputFile.power) {
+                            inputFile.modulator.flush(inputFile.power, inputFile.stereo) {
                                 inputFile.buffer.put(it)
                             }
                         }
@@ -199,7 +217,18 @@ fun main(args: Array<String>) {
                 floatView.put(i, floatView.get(i) + z.x)
                 floatView.put(i, floatView.get(i) + z.y)
             }
-            outputStream.write(outputBuffer.array())
+            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
             }