From 89772839f4a09e8e12d0bc95d5e791d7039b1249 Mon Sep 17 00:00:00 2001 From: Ahmet Inan Date: Tue, 16 Apr 2024 18:45:43 +0200 Subject: [PATCH] synchronize using sync pulses --- .../main/java/xdsopl/robot36/Demodulator.java | 15 +++--- .../java/xdsopl/robot36/MainActivity.java | 50 +++++++++++++------ 2 files changed, 43 insertions(+), 22 deletions(-) diff --git a/app/src/main/java/xdsopl/robot36/Demodulator.java b/app/src/main/java/xdsopl/robot36/Demodulator.java index 3171236..784c08c 100644 --- a/app/src/main/java/xdsopl/robot36/Demodulator.java +++ b/app/src/main/java/xdsopl/robot36/Demodulator.java @@ -26,6 +26,8 @@ public class Demodulator { private Complex syncPulse; private Complex scanLine; + public int syncPulseOffset; + Demodulator(int sampleRate) { double powerWindowSeconds = 0.5; int powerWindowSamples = (int) Math.round(powerWindowSeconds * sampleRate) | 1; @@ -61,7 +63,8 @@ public class Demodulator { scanLine = new Complex(); } - public void process(float[] buffer) { + public boolean process(float[] buffer) { + boolean syncPulseDetected = false; for (int i = 0; i < buffer.length; ++i) { baseBand = baseBandLowPass.avg(baseBand.set(buffer[i]).mul(baseBandOscillator.rotate())); syncPulse = syncPulseFilter.avg(syncPulse.set(baseBand).mul(syncPulseOscillator.rotate())); @@ -76,19 +79,17 @@ public class Demodulator { syncPulseMaxPosition = syncPulseCounter; } ++syncPulseCounter; - buffer[i] = syncPulseLevel; } else if (syncPulseCounter > 0 && syncPulseCounter < syncPulseSamples) { - int offset = syncPulseCounter - syncPulseMaxPosition; - if (offset <= i) - buffer[i - offset] = -1; + syncPulseOffset = i + syncPulseMaxPosition - syncPulseCounter; + syncPulseDetected = true; syncPulseCounter = 0; syncPulseMaxLevel = 0; - buffer[i] = scanLineLevel; } else { syncPulseCounter = 0; syncPulseMaxLevel = 0; - buffer[i] = scanLineLevel; } + buffer[i] = scanLineLevel; } + return syncPulseDetected; } } diff --git a/app/src/main/java/xdsopl/robot36/MainActivity.java b/app/src/main/java/xdsopl/robot36/MainActivity.java index 8a4f9e8..8666bbb 100644 --- a/app/src/main/java/xdsopl/robot36/MainActivity.java +++ b/app/src/main/java/xdsopl/robot36/MainActivity.java @@ -42,6 +42,7 @@ public class MainActivity extends AppCompatActivity { private int tint; private int curLine; private int curColumn; + private int syncPulseToleranceSamples; private void setStatus(int id) { status.setText(id); @@ -55,30 +56,49 @@ public class MainActivity extends AppCompatActivity { @Override public void onPeriodicNotification(AudioRecord audioRecord) { audioRecord.read(recordBuffer, 0, recordBuffer.length, AudioRecord.READ_BLOCKING); - demodulator.process(recordBuffer); - visualizeSignal(); + visualizeSignal(demodulator.process(recordBuffer)); } }; - private void visualizeSignal() { + private void slideUpOneLine() { + for (int i = 0; i < scopeWidth; ++i) + scopePixels[scopeWidth * (curLine + scopeHeight) + i] = scopePixels[scopeWidth * curLine + i]; + curLine = (curLine + 1) % scopeHeight; + scopeBitmap.setPixels(scopePixels, scopeWidth * curLine, scopeWidth, 0, 0, scopeWidth, scopeHeight); + scopeView.invalidate(); + } + + private void visualizeSignal(boolean syncPulseDetected) { + int syncPulseOffset = curColumn + demodulator.syncPulseOffset; + if (syncPulseDetected && syncPulseOffset >= syncPulseToleranceSamples && syncPulseOffset < curColumn) { + syncPulseDetected = false; + int nextLine = (curLine + 1) % scopeHeight; + for (int i = 0; i < curColumn - syncPulseOffset; ++i) + scopePixels[scopeWidth * nextLine + i] = scopePixels[scopeWidth * curLine + syncPulseOffset + i]; + for (int i = syncPulseOffset; i < scopeWidth; ++i) + scopePixels[scopeWidth * curLine + i] = 0xff00ff00; + curColumn -= syncPulseOffset; + slideUpOneLine(); + } for (float v : recordBuffer) { - int pixelColor = 0xff00ff00; - if (v >= 0) { - int intensity = (int) Math.round(255 * Math.sqrt(v)); - pixelColor = 0xff000000 | 0x00010101 * intensity; - } + int intensity = (int) Math.round(255 * Math.sqrt(v)); + int pixelColor = 0xff000000 | 0x00010101 * intensity; scopePixels[scopeWidth * curLine + curColumn] = pixelColor; - if (++curColumn >= scopeWidth) { + boolean syncNow = syncPulseDetected && syncPulseOffset == curColumn; + if (syncNow || ++curColumn >= scopeWidth) { + syncPulseDetected = false; curColumn = 0; - for (int i = 0; i < scopeWidth; ++i) - scopePixels[scopeWidth * (curLine + scopeHeight) + i] = scopePixels[scopeWidth * curLine + i]; - curLine = (curLine + 1) % scopeHeight; - scopeBitmap.setPixels(scopePixels, scopeWidth * curLine, scopeWidth, 0, 0, scopeWidth, scopeHeight); - scopeView.invalidate(); + slideUpOneLine(); } } } + void initTools(int sampleRate) { + demodulator = new Demodulator(sampleRate); + double syncPulseToleranceSeconds = 0.001; + syncPulseToleranceSamples = (int) Math.round(syncPulseToleranceSeconds * sampleRate); + } + private void initAudioRecord() { int audioSource = MediaRecorder.AudioSource.UNPROCESSED; int channelConfig = AudioFormat.CHANNEL_IN_MONO; @@ -95,7 +115,7 @@ public class MainActivity extends AppCompatActivity { if (audioRecord.getState() == AudioRecord.STATE_INITIALIZED) { audioRecord.setRecordPositionUpdateListener(recordListener); audioRecord.setPositionNotificationPeriod(recordBuffer.length); - demodulator = new Demodulator(sampleRate); + initTools(sampleRate); startListening(); } else { setStatus(R.string.audio_init_failed);