diff --git a/app/src/main/java/xdsopl/robot36/ColorConverter.java b/app/src/main/java/xdsopl/robot36/ColorConverter.java index cd64aaf..d5b18e5 100644 --- a/app/src/main/java/xdsopl/robot36/ColorConverter.java +++ b/app/src/main/java/xdsopl/robot36/ColorConverter.java @@ -6,6 +6,8 @@ Copyright 2024 Ahmet Inan package xdsopl.robot36; +import android.graphics.Color; + public final class ColorConverter { private static int clamp(int value) { @@ -36,6 +38,24 @@ public final class ColorConverter { return 0xff000000 | (R << 16) | (G << 8) | B; } + private static int[] argb2components(int argb) { + return new int[] { (argb >> 24) & 0xff, (argb >> 16) & 0xff, (argb >> 8) & 0xff, argb & 0xff }; + } + + public static int blend(int argbLeft, int argbRight, float ratioOfArgbRight) { + int[] componentsLeft = argb2components(argbLeft); + int[] componentsRight = argb2components(argbRight); + int[] output = new int[4]; + + ratioOfArgbRight = clamp(ratioOfArgbRight); + + for (int i = 0; i < 4; i++) { + output[i] = clamp(Math.round(componentsLeft[i] * (1 - ratioOfArgbRight) + componentsRight[i] * ratioOfArgbRight)); + } + + return Color.argb(output[0], output[1], output[2], output[3]); + } + public static int GRAY(float level) { return 0xff000000 | 0x00010101 * compress(level); } diff --git a/app/src/main/java/xdsopl/robot36/Demodulator.java b/app/src/main/java/xdsopl/robot36/Demodulator.java index 2bf882a..9f48001 100644 --- a/app/src/main/java/xdsopl/robot36/Demodulator.java +++ b/app/src/main/java/xdsopl/robot36/Demodulator.java @@ -33,9 +33,11 @@ public class Demodulator { public int syncPulseOffset; public float frequencyOffset; + public static final double syncPulseFrequency = 1200; + public static final double blackFrequency = 1500; + public static final double whiteFrequency = 2300; + Demodulator(int sampleRate) { - double blackFrequency = 1500; - double whiteFrequency = 2300; double scanLineBandwidth = whiteFrequency - blackFrequency; frequencyModulation = new FrequencyModulation(scanLineBandwidth, sampleRate); double syncPulse5msSeconds = 0.005; @@ -65,7 +67,6 @@ public class Demodulator { baseBandLowPass.taps[i] = (float) (kaiser.window(2.0, i, baseBandLowPass.length) * Filter.lowPass(cutoffFrequency, sampleRate, i, baseBandLowPass.length)); double centerFrequency = (lowestFrequency + highestFrequency) / 2; baseBandOscillator = new Phasor(-centerFrequency, sampleRate); - double syncPulseFrequency = 1200; syncPulseFrequencyValue = (float) ((syncPulseFrequency - centerFrequency) * 2 / scanLineBandwidth); syncPulseFrequencyTolerance = (float) (50 * 2 / scanLineBandwidth); double syncPorchFrequency = 1500; diff --git a/app/src/main/java/xdsopl/robot36/MainActivity.java b/app/src/main/java/xdsopl/robot36/MainActivity.java index 0a51384..d644c0b 100644 --- a/app/src/main/java/xdsopl/robot36/MainActivity.java +++ b/app/src/main/java/xdsopl/robot36/MainActivity.java @@ -15,6 +15,7 @@ import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.content.res.Configuration; import android.graphics.Bitmap; +import android.graphics.Color; import android.media.AudioFormat; import android.media.AudioRecord; import android.media.MediaRecorder; @@ -72,6 +73,7 @@ public class MainActivity extends AppCompatActivity { private ImageView peakMeterView; private PixelBuffer imageBuffer; private ShortTimeFourierTransform stft; + private final int binWidthHz = 10; private short[] shortBuffer; private float[] recordBuffer; private AudioRecord audioRecord; @@ -223,8 +225,22 @@ public class MainActivity extends AppCompatActivity { double lowest = Math.log(1e-9); double highest = Math.log(1); double range = highest - lowest; + int lowestBin = 14; for (int i = 0; i < stride; ++i) - waterfallPlotBuffer.pixels[line + i] = rainbow((Math.log(stft.power[i + 14]) - lowest) / range); + waterfallPlotBuffer.pixels[line + i] = rainbow((Math.log(stft.power[i + lowestBin]) - lowest) / range); + + int[] markerFrequencies = new int[] { + (int)Demodulator.syncPulseFrequency, + (int)Demodulator.blackFrequency, + (int)Demodulator.whiteFrequency, + }; + for (int freq: markerFrequencies) { + int marker = freq / binWidthHz - lowestBin; + waterfallPlotBuffer.pixels[line + marker - 1] = Color.BLACK; + waterfallPlotBuffer.pixels[line + marker] = ColorConverter.blend(waterfallPlotBuffer.pixels[line + marker], Color.GREEN, 0.8f); + waterfallPlotBuffer.pixels[line + marker + 1] = Color.BLACK; + } + System.arraycopy(waterfallPlotBuffer.pixels, line, waterfallPlotBuffer.pixels, line + stride * (waterfallPlotBuffer.height / 2), stride); } } @@ -315,7 +331,7 @@ public class MainActivity extends AppCompatActivity { if (rateChanged) { decoder = new Decoder(scopeBuffer, imageBuffer, getString(R.string.raw_mode), recordRate); decoder.setMode(currentMode); - stft = new ShortTimeFourierTransform(recordRate / 10, 3); + stft = new ShortTimeFourierTransform(recordRate / binWidthHz, 3); } startListening(); } else {