mirror of
https://github.com/xdsopl/robot36.git
synced 2025-12-06 07:12:07 +01:00
detect 5, 9 and 20 ms sync pulses
This commit is contained in:
parent
6d8ebca9da
commit
1a1f061e1c
|
|
@ -8,7 +8,9 @@ package xdsopl.robot36;
|
||||||
|
|
||||||
public class Demodulator {
|
public class Demodulator {
|
||||||
private final SimpleMovingAverage powerAvg;
|
private final SimpleMovingAverage powerAvg;
|
||||||
private final ComplexMovingAverage syncPulseFilter;
|
private final ComplexMovingAverage syncPulse5msFilter;
|
||||||
|
private final ComplexMovingAverage syncPulse9msFilter;
|
||||||
|
private final ComplexMovingAverage syncPulse20msFilter;
|
||||||
private final ComplexMovingAverage scanLineFilter;
|
private final ComplexMovingAverage scanLineFilter;
|
||||||
private final ComplexMovingAverage baseBandLowPass;
|
private final ComplexMovingAverage baseBandLowPass;
|
||||||
private final FrequencyModulation scanLineDemod;
|
private final FrequencyModulation scanLineDemod;
|
||||||
|
|
@ -16,16 +18,31 @@ public class Demodulator {
|
||||||
private final Phasor syncPulseOscillator;
|
private final Phasor syncPulseOscillator;
|
||||||
private final Phasor scanLineOscillator;
|
private final Phasor scanLineOscillator;
|
||||||
private final Phasor baseBandOscillator;
|
private final Phasor baseBandOscillator;
|
||||||
private final Delay syncPulseDelay;
|
private final Delay syncPulse5msDelay;
|
||||||
|
private final Delay syncPulse9msDelay;
|
||||||
|
private final Delay syncPulse20msDelay;
|
||||||
private final int syncPulseLowMark;
|
private final int syncPulseLowMark;
|
||||||
private final int syncPulseHighMark;
|
private final int syncPulseHighMark;
|
||||||
private float syncPulseMaxValue;
|
private float syncPulse5msMaxValue;
|
||||||
private int syncPulseMaxPosition;
|
private float syncPulse9msMaxValue;
|
||||||
|
private float syncPulse20msMaxValue;
|
||||||
|
private int syncPulse5msMaxPosition;
|
||||||
|
private int syncPulse9msMaxPosition;
|
||||||
|
private int syncPulse20msMaxPosition;
|
||||||
private int syncPulseCounter;
|
private int syncPulseCounter;
|
||||||
private Complex baseBand;
|
private Complex baseBand;
|
||||||
private Complex syncPulse;
|
private Complex syncPulse;
|
||||||
|
private Complex syncPulse5ms;
|
||||||
|
private Complex syncPulse9ms;
|
||||||
|
private Complex syncPulse20ms;
|
||||||
private Complex scanLine;
|
private Complex scanLine;
|
||||||
|
|
||||||
|
public enum SyncPulseWidth {
|
||||||
|
FiveMilliSeconds,
|
||||||
|
NineMilliSeconds,
|
||||||
|
TwentyMilliSeconds
|
||||||
|
}
|
||||||
|
public SyncPulseWidth syncPulseWidth;
|
||||||
public int syncPulseOffset;
|
public int syncPulseOffset;
|
||||||
|
|
||||||
Demodulator(int sampleRate) {
|
Demodulator(int sampleRate) {
|
||||||
|
|
@ -39,11 +56,17 @@ public class Demodulator {
|
||||||
float scanLineCutoff = scanLineBandwidth / 2;
|
float scanLineCutoff = scanLineBandwidth / 2;
|
||||||
int scanLineFilterSamples = (int) Math.round(0.443 * sampleRate / scanLineCutoff) | 1;
|
int scanLineFilterSamples = (int) Math.round(0.443 * sampleRate / scanLineCutoff) | 1;
|
||||||
scanLineFilter = new ComplexMovingAverage(scanLineFilterSamples);
|
scanLineFilter = new ComplexMovingAverage(scanLineFilterSamples);
|
||||||
double syncPulseSeconds = 0.009;
|
double syncPulse5msSeconds = 0.005;
|
||||||
int syncPulseSamples = (int) Math.round(syncPulseSeconds * sampleRate) | 1;
|
double syncPulse9msSeconds = 0.009;
|
||||||
syncPulseLowMark = syncPulseSamples / 2;
|
double syncPulse20msSeconds = 0.020;
|
||||||
syncPulseHighMark = syncPulseSamples * 2;
|
int syncPulse5msSamples = (int) Math.round(syncPulse5msSeconds * sampleRate) | 1;
|
||||||
syncPulseFilter = new ComplexMovingAverage(syncPulseSamples);
|
int syncPulse9msSamples = (int) Math.round(syncPulse9msSeconds * sampleRate) | 1;
|
||||||
|
int syncPulse20msSamples = (int) Math.round(syncPulse20msSeconds * sampleRate) | 1;
|
||||||
|
syncPulseLowMark = syncPulse5msSamples / 2;
|
||||||
|
syncPulseHighMark = syncPulse20msSamples * 2;
|
||||||
|
syncPulse5msFilter = new ComplexMovingAverage(syncPulse5msSamples);
|
||||||
|
syncPulse9msFilter = new ComplexMovingAverage(syncPulse9msSamples);
|
||||||
|
syncPulse20msFilter = new ComplexMovingAverage(syncPulse20msSamples);
|
||||||
float lowestFrequency = 1100;
|
float lowestFrequency = 1100;
|
||||||
float highestFrequency = 2300;
|
float highestFrequency = 2300;
|
||||||
float cutoffFrequency = (highestFrequency - lowestFrequency) / 2;
|
float cutoffFrequency = (highestFrequency - lowestFrequency) / 2;
|
||||||
|
|
@ -55,11 +78,18 @@ public class Demodulator {
|
||||||
syncPulseOscillator = new Phasor(-(syncPulseFrequency - centerFrequency), sampleRate);
|
syncPulseOscillator = new Phasor(-(syncPulseFrequency - centerFrequency), sampleRate);
|
||||||
float grayFrequency = (blackFrequency + whiteFrequency) / 2;
|
float grayFrequency = (blackFrequency + whiteFrequency) / 2;
|
||||||
scanLineOscillator = new Phasor(-(grayFrequency - centerFrequency), sampleRate);
|
scanLineOscillator = new Phasor(-(grayFrequency - centerFrequency), sampleRate);
|
||||||
int syncPulseDelaySamples = (powerWindowSamples - 1) / 2;
|
int syncPulse5msDelaySamples = (powerWindowSamples - 1) / 2 - (syncPulse5msSamples - 1) / 2;
|
||||||
syncPulseDelay = new Delay(syncPulseDelaySamples);
|
int syncPulse9msDelaySamples = (powerWindowSamples - 1) / 2 - (syncPulse9msSamples - 1) / 2;
|
||||||
|
int syncPulse20msDelaySamples = (powerWindowSamples - 1) / 2 - (syncPulse20msSamples - 1) / 2;
|
||||||
|
syncPulse5msDelay = new Delay(syncPulse5msDelaySamples);
|
||||||
|
syncPulse9msDelay = new Delay(syncPulse9msDelaySamples);
|
||||||
|
syncPulse20msDelay = new Delay(syncPulse20msDelaySamples);
|
||||||
syncPulseTrigger = new SchmittTrigger(0.17f, 0.19f);
|
syncPulseTrigger = new SchmittTrigger(0.17f, 0.19f);
|
||||||
baseBand = new Complex();
|
baseBand = new Complex();
|
||||||
syncPulse = new Complex();
|
syncPulse = new Complex();
|
||||||
|
syncPulse5ms = new Complex();
|
||||||
|
syncPulse9ms = new Complex();
|
||||||
|
syncPulse20ms = new Complex();
|
||||||
scanLine = new Complex();
|
scanLine = new Complex();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -67,28 +97,54 @@ public class Demodulator {
|
||||||
boolean syncPulseDetected = false;
|
boolean syncPulseDetected = false;
|
||||||
for (int i = 0; i < buffer.length; ++i) {
|
for (int i = 0; i < buffer.length; ++i) {
|
||||||
baseBand = baseBandLowPass.avg(baseBand.set(buffer[i]).mul(baseBandOscillator.rotate()));
|
baseBand = baseBandLowPass.avg(baseBand.set(buffer[i]).mul(baseBandOscillator.rotate()));
|
||||||
syncPulse = syncPulseFilter.avg(syncPulse.set(baseBand).mul(syncPulseOscillator.rotate()));
|
syncPulse = syncPulse.set(baseBand).mul(syncPulseOscillator.rotate());
|
||||||
|
syncPulse5ms = syncPulse5msFilter.avg(syncPulse5ms.set(syncPulse));
|
||||||
|
syncPulse9ms = syncPulse9msFilter.avg(syncPulse9ms.set(syncPulse));
|
||||||
|
syncPulse20ms = syncPulse20msFilter.avg(syncPulse20ms.set(syncPulse));
|
||||||
scanLine = scanLineFilter.avg(scanLine.set(baseBand).mul(scanLineOscillator.rotate()));
|
scanLine = scanLineFilter.avg(scanLine.set(baseBand).mul(scanLineOscillator.rotate()));
|
||||||
float syncPulseValue = syncPulseDelay.push(syncPulse.norm()) / powerAvg.avg(baseBand.norm());
|
float averagePower = powerAvg.avg(baseBand.norm());
|
||||||
|
float syncPulse5msValue = syncPulse5msDelay.push(syncPulse5ms.norm()) / averagePower;
|
||||||
|
float syncPulse9msValue = syncPulse9msDelay.push(syncPulse9ms.norm()) / averagePower;
|
||||||
|
float syncPulse20msValue = syncPulse20msDelay.push(syncPulse20ms.norm()) / averagePower;
|
||||||
float scanLineValue = scanLineDemod.demod(scanLine);
|
float scanLineValue = scanLineDemod.demod(scanLine);
|
||||||
float scanLineLevel = Math.min(Math.max(0.5f * (scanLineValue + 1), 0), 1);
|
float scanLineLevel = Math.min(Math.max(0.5f * (scanLineValue + 1), 0), 1);
|
||||||
if (syncPulseTrigger.latch(syncPulseValue)) {
|
if (syncPulseTrigger.latch(syncPulse5msValue)) {
|
||||||
if (syncPulseMaxValue < syncPulseValue) {
|
if (syncPulse5msMaxValue < syncPulse5msValue) {
|
||||||
syncPulseMaxValue = syncPulseValue;
|
syncPulse5msMaxValue = syncPulse5msValue;
|
||||||
syncPulseMaxPosition = syncPulseCounter;
|
syncPulse5msMaxPosition = syncPulseCounter;
|
||||||
|
}
|
||||||
|
if (syncPulse9msMaxValue < syncPulse9msValue) {
|
||||||
|
syncPulse9msMaxValue = syncPulse9msValue;
|
||||||
|
syncPulse9msMaxPosition = syncPulseCounter;
|
||||||
|
}
|
||||||
|
if (syncPulse20msMaxValue < syncPulse20msValue) {
|
||||||
|
syncPulse20msMaxValue = syncPulse20msValue;
|
||||||
|
syncPulse20msMaxPosition = syncPulseCounter;
|
||||||
}
|
}
|
||||||
++syncPulseCounter;
|
++syncPulseCounter;
|
||||||
} else if (syncPulseCounter > syncPulseLowMark && syncPulseCounter < syncPulseHighMark) {
|
} else if (syncPulseCounter > syncPulseLowMark && syncPulseCounter < syncPulseHighMark) {
|
||||||
int filterDelay = (powerAvg.length - 1) / 2
|
int filterDelay = (powerAvg.length - 1) / 2 - (scanLineFilter.length - 1) / 2;
|
||||||
+ (syncPulseFilter.length - 1) / 2
|
syncPulseOffset = i - syncPulseCounter - filterDelay;
|
||||||
- (scanLineFilter.length - 1) / 2;
|
if (syncPulse20msMaxValue > (9.f / 20.f) * syncPulse9msMaxValue) {
|
||||||
syncPulseOffset = i + syncPulseMaxPosition - syncPulseCounter - filterDelay;
|
syncPulseOffset += syncPulse20msMaxPosition;
|
||||||
|
syncPulseWidth = SyncPulseWidth.TwentyMilliSeconds;
|
||||||
|
} else if (syncPulse9msMaxValue > (5.f / 9.f) * syncPulse5msMaxValue) {
|
||||||
|
syncPulseOffset += syncPulse9msMaxPosition;
|
||||||
|
syncPulseWidth = SyncPulseWidth.NineMilliSeconds;
|
||||||
|
} else {
|
||||||
|
syncPulseOffset += syncPulse5msMaxPosition;
|
||||||
|
syncPulseWidth = SyncPulseWidth.FiveMilliSeconds;
|
||||||
|
}
|
||||||
syncPulseDetected = true;
|
syncPulseDetected = true;
|
||||||
syncPulseCounter = 0;
|
syncPulseCounter = 0;
|
||||||
syncPulseMaxValue = 0;
|
syncPulse5msMaxValue = 0;
|
||||||
|
syncPulse9msMaxValue = 0;
|
||||||
|
syncPulse20msMaxValue = 0;
|
||||||
} else {
|
} else {
|
||||||
syncPulseCounter = 0;
|
syncPulseCounter = 0;
|
||||||
syncPulseMaxValue = 0;
|
syncPulse5msMaxValue = 0;
|
||||||
|
syncPulse9msMaxValue = 0;
|
||||||
|
syncPulse20msMaxValue = 0;
|
||||||
}
|
}
|
||||||
buffer[i] = scanLineLevel;
|
buffer[i] = scanLineLevel;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -87,6 +87,17 @@ public class MainActivity extends AppCompatActivity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (syncPulseDetected && syncPulseIndex >= 0) {
|
if (syncPulseDetected && syncPulseIndex >= 0) {
|
||||||
|
switch (demodulator.syncPulseWidth) {
|
||||||
|
case FiveMilliSeconds:
|
||||||
|
status.setText("5 ms sync pulse");
|
||||||
|
break;
|
||||||
|
case NineMilliSeconds:
|
||||||
|
status.setText("9 ms sync pulse");
|
||||||
|
break;
|
||||||
|
case TwentyMilliSeconds:
|
||||||
|
status.setText("20 ms sync pulse");
|
||||||
|
break;
|
||||||
|
}
|
||||||
processOneLine(syncPulseIndex);
|
processOneLine(syncPulseIndex);
|
||||||
int count = curSample - syncPulseIndex;
|
int count = curSample - syncPulseIndex;
|
||||||
curSample = 0;
|
curSample = 0;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue