added scan line demodulation

This commit is contained in:
Ahmet Inan 2024-04-15 14:18:26 +02:00
parent 7ad3fa6bd4
commit 6b255e4f5b
2 changed files with 46 additions and 8 deletions

View file

@ -0,0 +1,21 @@
/*
Frequency Modulation
Copyright 2024 Ahmet Inan <xdsopl@gmail.com>
*/
package xdsopl.robot36;
public class FrequencyModulation {
private float prev;
private final float scale;
FrequencyModulation(float bandwidth, float sampleRate) {
scale = sampleRate / (bandwidth * (float) Math.PI);
}
float demod(Complex input) {
float phase = input.arg();
float delta = (phase - prev) % (float) Math.PI;
prev = phase;
return scale * delta;
}
}

View file

@ -40,13 +40,18 @@ public class MainActivity extends AppCompatActivity {
private Delay powerDelay; private Delay powerDelay;
private SimpleMovingAverage powerAvg; private SimpleMovingAverage powerAvg;
private ComplexMovingAverage syncPulseFilter; private ComplexMovingAverage syncPulseFilter;
private ComplexMovingAverage scanLineFilter;
private ComplexMovingAverage baseBandLowPass; private ComplexMovingAverage baseBandLowPass;
private FrequencyModulation scanLineDemod;
private Phasor syncPulseOscillator; private Phasor syncPulseOscillator;
private Phasor scanLineOscillator;
private Phasor baseBandOscillator; private Phasor baseBandOscillator;
private Complex baseBand; private Complex baseBand;
private Complex syncPulse; private Complex syncPulse;
private Complex scanLine;
private int tint; private int tint;
private int curLine; private int curLine;
private int curColumn;
private void setStatus(int id) { private void setStatus(int id) {
status.setText(id); status.setText(id);
@ -68,15 +73,17 @@ public class MainActivity extends AppCompatActivity {
for (float v : recordBuffer) { for (float v : recordBuffer) {
baseBand = baseBandLowPass.avg(baseBand.set(v).mul(baseBandOscillator.rotate())); baseBand = baseBandLowPass.avg(baseBand.set(v).mul(baseBandOscillator.rotate()));
syncPulse = syncPulseFilter.avg(syncPulse.set(baseBand).mul(syncPulseOscillator.rotate())); syncPulse = syncPulseFilter.avg(syncPulse.set(baseBand).mul(syncPulseOscillator.rotate()));
scanLine = scanLineFilter.avg(scanLine.set(baseBand).mul(scanLineOscillator.rotate()));
float level = powerDelay.push(syncPulse.norm()) / powerAvg.avg(baseBand.norm()); float level = powerDelay.push(syncPulse.norm()) / powerAvg.avg(baseBand.norm());
int x = Math.min((int) (scopeWidth * level), scopeWidth); float value = 0.5f * (scanLineDemod.demod(scanLine) + 1);
for (int i = 0; i < x; ++i) int x = (int) Math.round(Math.min(Math.max(255 * Math.sqrt(value), 0), 255));
scopePixels[scopeWidth * curLine + i] = tint; scopePixels[scopeWidth * curLine + curColumn] = 0xff000000 | 0x00010101 * x;
for (int i = x; i < scopeWidth; ++i) if (++curColumn >= scopeWidth) {
scopePixels[scopeWidth * curLine + i] = 0; curColumn = 0;
for (int i = 0; i < scopeWidth; ++i) for (int i = 0; i < scopeWidth; ++i)
scopePixels[scopeWidth * (curLine + scopeHeight) + i] = scopePixels[scopeWidth * curLine + i]; scopePixels[scopeWidth * (curLine + scopeHeight) + i] = scopePixels[scopeWidth * curLine + i];
curLine = (curLine + 1) % scopeHeight; curLine = (curLine + 1) % scopeHeight;
}
} }
scopeBitmap.setPixels(scopePixels, scopeWidth * curLine, scopeWidth, 0, 0, scopeWidth, scopeHeight); scopeBitmap.setPixels(scopePixels, scopeWidth * curLine, scopeWidth, 0, 0, scopeWidth, scopeHeight);
scopeView.invalidate(); scopeView.invalidate();
@ -87,6 +94,13 @@ public class MainActivity extends AppCompatActivity {
int powerWindowSamples = (int) Math.round(powerWindowSeconds * sampleRate) | 1; int powerWindowSamples = (int) Math.round(powerWindowSeconds * sampleRate) | 1;
powerAvg = new SimpleMovingAverage(powerWindowSamples); powerAvg = new SimpleMovingAverage(powerWindowSamples);
powerDelay = new Delay((powerWindowSamples - 1) / 2); powerDelay = new Delay((powerWindowSamples - 1) / 2);
float blackFrequency = 1500;
float whiteFrequency = 2300;
float scanLineBandwidth = whiteFrequency - blackFrequency;
scanLineDemod = new FrequencyModulation(scanLineBandwidth, sampleRate);
float scanLineCutoff = scanLineBandwidth / 2;
int scanLineSamples = (int) Math.round(0.443 * sampleRate / scanLineCutoff) | 1;
scanLineFilter = new ComplexMovingAverage(scanLineSamples);
double syncPulseSeconds = 0.009; double syncPulseSeconds = 0.009;
int syncPulseSamples = (int) Math.round(syncPulseSeconds * sampleRate); int syncPulseSamples = (int) Math.round(syncPulseSeconds * sampleRate);
syncPulseFilter = new ComplexMovingAverage(syncPulseSamples); syncPulseFilter = new ComplexMovingAverage(syncPulseSamples);
@ -99,8 +113,11 @@ public class MainActivity extends AppCompatActivity {
baseBandOscillator = new Phasor(-centerFrequency, sampleRate); baseBandOscillator = new Phasor(-centerFrequency, sampleRate);
float syncPulseFrequency = 1200; float syncPulseFrequency = 1200;
syncPulseOscillator = new Phasor(-(syncPulseFrequency - centerFrequency), sampleRate); syncPulseOscillator = new Phasor(-(syncPulseFrequency - centerFrequency), sampleRate);
float grayFrequency = (blackFrequency + whiteFrequency) / 2;
scanLineOscillator = new Phasor(-(grayFrequency - centerFrequency), sampleRate);
baseBand = new Complex(); baseBand = new Complex();
syncPulse = new Complex(); syncPulse = new Complex();
scanLine = new Complex();
} }
private void initAudioRecord() { private void initAudioRecord() {