Compare commits

...

14 commits

Author SHA1 Message Date
marek-o bb7b66b358
Merge 46f376e101 into 7ad54873da 2025-08-22 00:49:19 +02:00
Marek Ossowski 46f376e101 Replaced more comments with renames 2025-08-22 00:45:58 +02:00
Marek Ossowski f745a6bddc This buffer can also be reverted now 2025-08-22 00:35:30 +02:00
Marek Ossowski e29f5728e3 Merge branch 'v2' into adding-hffax
# Conflicts:
#	app/src/main/java/xdsopl/robot36/MainActivity.java
2025-08-21 23:52:38 +02:00
Marek Ossowski a54b3f7fad Storing 640 pixel wide bitmap and stretching later 2025-08-21 22:57:45 +02:00
Marek Ossowski 1ed9f9094c Restored buffer sizes 2025-08-21 01:19:56 +02:00
Marek Ossowski df16e3c318 Removed comment 2025-08-21 01:06:59 +02:00
Marek Ossowski 4f647e14b4 Removed some comments and renamed methods in Mode 2025-08-21 01:02:26 +02:00
Marek Ossowski 4779d67180 Extracted Demodulator.normalizeFrequency() 2025-08-21 00:34:25 +02:00
Marek Ossowski 8173f147d8 Pull request fixes: Decoder.process() added newLinesPresent 2025-08-21 00:04:27 +02:00
Marek Ossowski 09d5f6dc5a Pull request fixes: moved string 2025-08-21 00:02:52 +02:00
Ahmet Inan 7ad54873da added frequency markers 2025-08-20 12:47:05 +02:00
Ahmet Inan 1502f20af1 updated libs and tools 2025-08-20 10:38:30 +02:00
Marek Ossowski 5aa3be6cc9 Fixed indents to tabs 2025-08-19 23:19:26 +02:00
14 changed files with 189 additions and 241 deletions

View file

@ -4,12 +4,12 @@ plugins {
android {
namespace 'xdsopl.robot36'
compileSdk 35
compileSdk = 36
defaultConfig {
applicationId "xdsopl.robot36"
minSdk 24
targetSdk 35
targetSdk 36
versionCode 65
versionName "2.15"

View file

@ -3,8 +3,8 @@ package xdsopl.robot36;
import android.graphics.Bitmap;
public abstract class BaseMode implements Mode {
@Override
public Bitmap postProcessScopeImage(Bitmap bmp) {
return Bitmap.createScaledBitmap(bmp, bmp.getWidth() / 3, bmp.getHeight() / 3, true);
}
@Override
public Bitmap postProcessScopeImage(Bitmap bmp) {
return bmp;
}
}

View file

@ -55,7 +55,7 @@ public class Decoder {
this.scopeBuffer = scopeBuffer;
this.imageBuffer = imageBuffer;
imageBuffer.line = -1;
pixelBuffer = new PixelBuffer(2000, 2);
pixelBuffer = new PixelBuffer(800, 2);
demodulator = new Demodulator(sampleRate);
double pulseFilterSeconds = 0.0025;
int pulseFilterSamples = (int) Math.round(pulseFilterSeconds * sampleRate) | 1;
@ -96,7 +96,7 @@ public class Decoder {
double scanLineToleranceSeconds = 0.001;
scanLineToleranceSamples = (int) Math.round(scanLineToleranceSeconds * sampleRate);
rawMode = new RawDecoder(rawName, sampleRate);
hfFaxMode = new HFFax("HF Fax", sampleRate);
hfFaxMode = new HFFax(sampleRate);
Mode robot36 = new Robot_36_Color(sampleRate);
currentMode = robot36;
currentScanLineSamples = robot36.getScanLineSamples();
@ -159,7 +159,7 @@ public class Decoder {
private Mode findMode(ArrayList<Mode> modes, int code) {
for (Mode mode : modes)
if (mode.getCode() == code)
if (mode.getVISCode() == code)
return mode;
return null;
}
@ -334,7 +334,7 @@ public class Decoder {
}
if (lockMode && mode != currentMode)
return false;
mode.reset();
mode.resetState();
imageBuffer.width = mode.getWidth();
imageBuffer.height = mode.getHeight();
imageBuffer.line = 0;
@ -348,35 +348,29 @@ public class Decoder {
for (int i = 0; i < pulses.length; ++i)
pulses[i] = oldestSyncPulseIndex + i * currentScanLineSamples;
Arrays.fill(lines, currentScanLineSamples);
shiftSamples(lastSyncPulseIndex + mode.getBegin());
shiftSamples(lastSyncPulseIndex + mode.getFirstPixelSampleIndex());
drawLines(0xff00ff00, 8);
drawLines(0xff000000, 10);
return true;
}
/**
* @param freqOffs offsets from expected sync frequency
* @param pulses positions of sync pulses
* @param lines lengths of scan lines
* @param index position of latest sync pulse
*/
private boolean processSyncPulse(ArrayList<Mode> modes, float[] freqOffs, int[] pulses, int[] lines, int index) {
for (int i = 1; i < pulses.length; ++i)
pulses[i - 1] = pulses[i];
pulses[pulses.length - 1] = index;
for (int i = 1; i < lines.length; ++i)
lines[i - 1] = lines[i];
lines[lines.length - 1] = pulses[pulses.length - 1] - pulses[pulses.length - 2];
private boolean processSyncPulse(ArrayList<Mode> modes, float[] freqOffs, int[] syncIndexes, int[] lineLengths, int latestSyncIndex) {
for (int i = 1; i < syncIndexes.length; ++i)
syncIndexes[i - 1] = syncIndexes[i];
syncIndexes[syncIndexes.length - 1] = latestSyncIndex;
for (int i = 1; i < lineLengths.length; ++i)
lineLengths[i - 1] = lineLengths[i];
lineLengths[lineLengths.length - 1] = syncIndexes[syncIndexes.length - 1] - syncIndexes[syncIndexes.length - 2];
for (int i = 1; i < freqOffs.length; ++i)
freqOffs[i - 1] = freqOffs[i];
freqOffs[pulses.length - 1] = demodulator.frequencyOffset;
if (lines[0] == 0)
freqOffs[syncIndexes.length - 1] = demodulator.frequencyOffset;
if (lineLengths[0] == 0)
return false;
double mean = scanLineMean(lines);
double mean = scanLineMean(lineLengths);
int scanLineSamples = (int) Math.round(mean);
if (scanLineSamples < scanLineMinSamples || scanLineSamples > scratchBuffer.length)
return false;
if (scanLineStdDev(lines, mean) > scanLineToleranceSamples)
if (scanLineStdDev(lineLengths, mean) > scanLineToleranceSamples)
return false;
boolean pictureChanged = false;
if (lockMode || imageBuffer.line >= 0 && imageBuffer.line < imageBuffer.height) {
@ -387,7 +381,7 @@ public class Decoder {
currentMode = detectMode(modes, scanLineSamples);
pictureChanged = currentMode != prevMode
|| Math.abs(currentScanLineSamples - scanLineSamples) > scanLineToleranceSamples
|| Math.abs(lastSyncPulseIndex + scanLineSamples - pulses[pulses.length - 1]) > syncPulseToleranceSamples;
|| Math.abs(lastSyncPulseIndex + scanLineSamples - syncIndexes[syncIndexes.length - 1]) > syncPulseToleranceSamples;
}
if (pictureChanged) {
drawLines(0xff000000, 10);
@ -395,26 +389,24 @@ public class Decoder {
drawLines(0xff000000, 10);
}
float frequencyOffset = (float) frequencyOffsetMean(freqOffs);
if (pulses[0] >= scanLineSamples && pictureChanged) {
int endPulse = pulses[0];
if (syncIndexes[0] >= scanLineSamples && pictureChanged) {
int endPulse = syncIndexes[0];
int extrapolate = endPulse / scanLineSamples;
int firstPulse = endPulse - extrapolate * scanLineSamples;
for (int pulseIndex = firstPulse; pulseIndex < endPulse; pulseIndex += scanLineSamples)
copyLines(currentMode.decodeScanLine(pixelBuffer, scratchBuffer, scanLineBuffer, scopeBuffer.width, pulseIndex, scanLineSamples, frequencyOffset));
}
for (int i = pictureChanged ? 0 : lines.length - 1; i < lines.length; ++i)
copyLines(currentMode.decodeScanLine(pixelBuffer, scratchBuffer, scanLineBuffer, scopeBuffer.width, pulses[i], lines[i], frequencyOffset));
lastSyncPulseIndex = pulses[pulses.length - 1];
for (int i = pictureChanged ? 0 : lineLengths.length - 1; i < lineLengths.length; ++i)
copyLines(currentMode.decodeScanLine(pixelBuffer, scratchBuffer, scanLineBuffer, scopeBuffer.width, syncIndexes[i], lineLengths[i], frequencyOffset));
lastSyncPulseIndex = syncIndexes[syncIndexes.length - 1];
currentScanLineSamples = scanLineSamples;
lastFrequencyOffset = frequencyOffset;
shiftSamples(lastSyncPulseIndex + currentMode.getBegin());
shiftSamples(lastSyncPulseIndex + currentMode.getFirstPixelSampleIndex());
return true;
}
/**
@return true if new lines present
*/
public boolean process(float[] recordBuffer, int channelSelect) {
boolean newLinesPresent = false;
boolean syncPulseDetected = demodulator.process(recordBuffer, channelSelect);
int syncPulseIndex = currentSample + demodulator.syncPulseOffset;
int channels = channelSelect > 0 ? 2 : 1;
@ -428,25 +420,28 @@ public class Decoder {
if (syncPulseDetected) {
switch (demodulator.syncPulseWidth) {
case FiveMilliSeconds:
return processSyncPulse(syncPulse5msModes, last5msFrequencyOffsets, last5msSyncPulses, last5msScanLines, syncPulseIndex);
newLinesPresent = processSyncPulse(syncPulse5msModes, last5msFrequencyOffsets, last5msSyncPulses, last5msScanLines, syncPulseIndex);
break;
case NineMilliSeconds:
leaderBreakIndex = syncPulseIndex;
return processSyncPulse(syncPulse9msModes, last9msFrequencyOffsets, last9msSyncPulses, last9msScanLines, syncPulseIndex);
newLinesPresent = processSyncPulse(syncPulse9msModes, last9msFrequencyOffsets, last9msSyncPulses, last9msScanLines, syncPulseIndex);
break;
case TwentyMilliSeconds:
leaderBreakIndex = syncPulseIndex;
return processSyncPulse(syncPulse20msModes, last20msFrequencyOffsets, last20msSyncPulses, last20msScanLines, syncPulseIndex);
newLinesPresent = processSyncPulse(syncPulse20msModes, last20msFrequencyOffsets, last20msSyncPulses, last20msScanLines, syncPulseIndex);
break;
default:
return false;
break;
}
}
if (handleHeader())
return true;
if (currentSample > lastSyncPulseIndex + (currentScanLineSamples * 5) / 4) {
} else if (handleHeader()) {
newLinesPresent = true;
} else if (currentSample > lastSyncPulseIndex + (currentScanLineSamples * 5) / 4) {
copyLines(currentMode.decodeScanLine(pixelBuffer, scratchBuffer, scanLineBuffer, scopeBuffer.width, lastSyncPulseIndex, currentScanLineSamples, lastFrequencyOffset));
lastSyncPulseIndex += currentScanLineSamples;
return true;
newLinesPresent = true;
}
return false;
return newLinesPresent;
}
public void setMode(String name) {

View file

@ -13,6 +13,8 @@ public class Demodulator {
private final SchmittTrigger syncPulseTrigger;
private final Phasor baseBandOscillator;
private final Delay syncPulseValueDelay;
private final double scanLineBandwidth;
private final double centerFrequency;
private final float syncPulseFrequencyValue;
private final float syncPulseFrequencyTolerance;
private final int syncPulse5msMinSamples;
@ -38,7 +40,7 @@ public class Demodulator {
public static final double whiteFrequency = 2300;
Demodulator(int sampleRate) {
double scanLineBandwidth = whiteFrequency - blackFrequency;
scanLineBandwidth = whiteFrequency - blackFrequency;
frequencyModulation = new FrequencyModulation(scanLineBandwidth, sampleRate);
double syncPulse5msSeconds = 0.005;
double syncPulse9msSeconds = 0.009;
@ -65,22 +67,23 @@ public class Demodulator {
Kaiser kaiser = new Kaiser();
for (int i = 0; i < baseBandLowPass.length; ++i)
baseBandLowPass.taps[i] = (float) (kaiser.window(2.0, i, baseBandLowPass.length) * Filter.lowPass(cutoffFrequency, sampleRate, i, baseBandLowPass.length));
double centerFrequency = (lowestFrequency + highestFrequency) / 2;
centerFrequency = (lowestFrequency + highestFrequency) / 2;
baseBandOscillator = new Phasor(-centerFrequency, sampleRate);
syncPulseFrequencyValue = (float) ((syncPulseFrequency - centerFrequency) * 2 / scanLineBandwidth); //converts to range from -1 to 1
syncPulseFrequencyValue = (float) normalizeFrequency(syncPulseFrequency);
syncPulseFrequencyTolerance = (float) (50 * 2 / scanLineBandwidth);
double syncPorchFrequency = 1500;
double syncHighFrequency = (syncPulseFrequency + syncPorchFrequency) / 2;
double syncLowFrequency = (syncPulseFrequency + syncHighFrequency) / 2;
double syncLowValue = (syncLowFrequency - centerFrequency) * 2 / scanLineBandwidth;
double syncHighValue = (syncHighFrequency - centerFrequency) * 2 / scanLineBandwidth;
double syncLowValue = normalizeFrequency(syncLowFrequency);
double syncHighValue = normalizeFrequency(syncHighFrequency);
syncPulseTrigger = new SchmittTrigger((float) syncLowValue, (float) syncHighValue);
baseBand = new Complex();
}
/**
* @return true if sync pulse detected
*/
private double normalizeFrequency(double frequency) {
return (frequency - centerFrequency) * 2 / scanLineBandwidth;
}
public boolean process(float[] buffer, int channelSelect) {
boolean syncPulseDetected = false;
int channels = channelSelect > 0 ? 2 : 1;

View file

@ -9,123 +9,123 @@ import android.graphics.Rect;
* HF Fax, IOC 576, 120 lines per minute
*/
public class HFFax extends BaseMode {
private final ExponentialMovingAverage lowPassFilter;
private final String name;
private final int sampleRate;
private final float[] cumulated;
private int horizontalShift = 0;
private final ExponentialMovingAverage lowPassFilter;
private final String name;
private final int sampleRate;
private final float[] cumulated;
private int horizontalShift = 0;
HFFax(String name, int sampleRate) {
this.name = name;
lowPassFilter = new ExponentialMovingAverage();
this.sampleRate = sampleRate;
cumulated = new float[getWidth()];
}
HFFax(int sampleRate) {
this.name = "HF Fax";
lowPassFilter = new ExponentialMovingAverage();
this.sampleRate = sampleRate;
cumulated = new float[getWidth()];
}
private float freqToLevel(float frequency, float offset) {
return 0.5f * (frequency - offset + 1.f);
}
private float freqToLevel(float frequency, float offset) {
return 0.5f * (frequency - offset + 1.f);
}
@Override
public String getName() {
return name;
}
@Override
public String getName() {
return name;
}
@Override
public int getCode() {
return -1;
}
@Override
public int getVISCode() {
return -1;
}
@Override
public int getWidth() {
return 1808;
}
@Override
public int getWidth() {
return 640;
}
@Override
public int getHeight() {
return 1200;
}
@Override
public int getHeight() {
return 1200;
}
@Override
public int getBegin() {
return 0;
}
@Override
public int getFirstPixelSampleIndex() {
return 0;
}
@Override
public int getFirstSyncPulseIndex() {
return -1;
}
@Override
public int getFirstSyncPulseIndex() {
return -1;
}
@Override
public int getScanLineSamples() {
return sampleRate / 2;
}
@Override
public int getScanLineSamples() {
return sampleRate / 2;
}
@Override
public void reset() {
}
@Override
public void resetState() {
}
@Override
public Bitmap postProcessScopeImage(Bitmap bmp) {
if (horizontalShift > 0) {
Bitmap bmpMutable = Bitmap.createBitmap(getWidth(), bmp.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bmpMutable);
canvas.drawBitmap(
bmp,
new Rect(0, 0, horizontalShift, bmp.getHeight()),
new Rect(getWidth() - horizontalShift, 0, getWidth(), bmp.getHeight()),
null);
canvas.drawBitmap(
bmp,
new Rect(horizontalShift, 0, getWidth(), bmp.getHeight()),
new Rect(0, 1, getWidth() - horizontalShift, bmp.getHeight() + 1),
null);
@Override
public Bitmap postProcessScopeImage(Bitmap bmp) {
int realWidth = 1808;
int realHorizontalShift = horizontalShift * realWidth / getWidth();
Bitmap bmpMutable = Bitmap.createBitmap(realWidth, bmp.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bmpMutable);
if (horizontalShift > 0) {
canvas.drawBitmap(
bmp,
new Rect(0, 0, horizontalShift, bmp.getHeight()),
new Rect(realWidth - realHorizontalShift, 0, realWidth, bmp.getHeight()),
null);
}
canvas.drawBitmap(
bmp,
new Rect(horizontalShift, 0, getWidth(), bmp.getHeight()),
new Rect(0, 1, realWidth - realHorizontalShift, bmp.getHeight() + 1),
null);
return bmpMutable;
}
return bmpMutable;
}
return bmp;
}
@Override
public boolean decodeScanLine(PixelBuffer pixelBuffer, float[] scratchBuffer, float[] scanLineBuffer, int scopeBufferWidth, int syncPulseIndex, int scanLineSamples, float frequencyOffset) {
if (syncPulseIndex < 0 || syncPulseIndex + scanLineSamples > scanLineBuffer.length)
return false;
int horizontalPixels = getWidth();
lowPassFilter.cutoff(horizontalPixels, 2 * scanLineSamples, 2);
lowPassFilter.reset();
for (int i = 0; i < scanLineSamples; ++i)
scratchBuffer[i] = lowPassFilter.avg(scanLineBuffer[i]);
lowPassFilter.reset();
for (int i = scanLineSamples - 1; i >= 0; --i)
scratchBuffer[i] = freqToLevel(lowPassFilter.avg(scratchBuffer[i]), frequencyOffset);
for (int i = 0; i < horizontalPixels; ++i) {
int position = (i * scanLineSamples) / horizontalPixels;
int color = ColorConverter.GRAY(scratchBuffer[position]);
pixelBuffer.pixels[i] = color;
@Override
public boolean decodeScanLine(PixelBuffer pixelBuffer, float[] scratchBuffer, float[] scanLineBuffer, int scopeBufferWidth, int syncPulseIndex, int scanLineSamples, float frequencyOffset) {
if (syncPulseIndex < 0 || syncPulseIndex + scanLineSamples > scanLineBuffer.length)
return false;
int horizontalPixels = getWidth();
lowPassFilter.cutoff(horizontalPixels, 2 * scanLineSamples, 2);
lowPassFilter.reset();
for (int i = 0; i < scanLineSamples; ++i)
scratchBuffer[i] = lowPassFilter.avg(scanLineBuffer[i]);
lowPassFilter.reset();
for (int i = scanLineSamples - 1; i >= 0; --i)
scratchBuffer[i] = freqToLevel(lowPassFilter.avg(scratchBuffer[i]), frequencyOffset);
for (int i = 0; i < horizontalPixels; ++i) {
int position = (i * scanLineSamples) / horizontalPixels;
int color = ColorConverter.GRAY(scratchBuffer[position]);
pixelBuffer.pixels[i] = color;
//accumulate recent values, forget old
float decay = 0.99f;
cumulated[i] = cumulated[i] * decay + Color.luminance(color) * (1 - decay);
}
//accumulate recent values, forget old
float decay = 0.99f;
cumulated[i] = cumulated[i] * decay + Color.luminance(color) * (1 - decay);
}
//try to detect "sync": thick white margin
int bestIndex = 0;
float bestValue = 0;
for (int x = 0; x < getWidth(); ++x)
{
float val = cumulated[x];
if (val > bestValue)
{
bestIndex = x;
bestValue = val;
}
}
//try to detect "sync": thick white margin
int bestIndex = 0;
float bestValue = 0;
for (int x = 0; x < getWidth(); ++x)
{
float val = cumulated[x];
if (val > bestValue)
{
bestIndex = x;
bestValue = val;
}
}
horizontalShift = bestIndex;
horizontalShift = bestIndex;
pixelBuffer.width = horizontalPixels;
pixelBuffer.height = 1;
return true;
}
pixelBuffer.width = horizontalPixels;
pixelBuffer.height = 1;
return true;
}
}

View file

@ -15,7 +15,6 @@ 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;
@ -44,7 +43,6 @@ import androidx.appcompat.app.AppCompatDelegate;
import androidx.appcompat.widget.ShareActionProvider;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.core.graphics.ColorUtils;
import androidx.core.graphics.Insets;
import androidx.core.os.LocaleListCompat;
import androidx.core.view.MenuItemCompat;
@ -74,7 +72,6 @@ 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;
@ -92,6 +89,8 @@ public class MainActivity extends AppCompatActivity {
private int tintColor;
private boolean autoSave;
private boolean showSpectrogram;
private final int binWidthHz = 10;
private final int[] freqMarkers = { 1100, 1300, 1500, 2300 };
private void setStatus(int id) {
setTitle(id);
@ -226,22 +225,12 @@ public class MainActivity extends AppCompatActivity {
double lowest = Math.log(1e-9);
double highest = Math.log(1);
double range = highest - lowest;
int lowestBin = 14;
int minFreq = 140;
int minBin = minFreq / binWidthHz;
for (int i = 0; i < stride; ++i)
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] = ColorUtils.blendARGB(waterfallPlotBuffer.pixels[line + marker], Color.GREEN, 0.8f);
waterfallPlotBuffer.pixels[line + marker + 1] = Color.BLACK;
}
waterfallPlotBuffer.pixels[line + i] = rainbow((Math.log(stft.power[i + minBin]) - lowest) / range);
for (int freq : freqMarkers)
waterfallPlotBuffer.pixels[line + (freq - minFreq) / binWidthHz] = fgColor;
System.arraycopy(waterfallPlotBuffer.pixels, line, waterfallPlotBuffer.pixels, line + stride * (waterfallPlotBuffer.height / 2), stride);
}
}
@ -319,7 +308,7 @@ public class MainActivity extends AppCompatActivity {
int sampleSize = audioFormat == AudioFormat.ENCODING_PCM_FLOAT ? 4 : 2;
int frameSize = sampleSize * channelCount;
int readsPerSecond = 50;
int bufferSize = Integer.highestOneBit(recordRate) * frameSize * 4;
int bufferSize = Integer.highestOneBit(recordRate) * frameSize;
int frameCount = recordRate / readsPerSecond;
int bufferCount = frameCount * channelCount;
recordBuffer = new float[bufferCount];
@ -568,7 +557,7 @@ public class MainActivity extends AppCompatActivity {
fgColor = getColor(R.color.fg);
thinColor = getColor(R.color.thin);
tintColor = getColor(R.color.tint);
scopeBuffer = new PixelBuffer(640 * 3, 2 * 1280 * 3);
scopeBuffer = new PixelBuffer(640, 2 * 1280);
waterfallPlotBuffer = new PixelBuffer(256, 2 * 256);
peakMeterBuffer = new PixelBuffer(1, 16);
imageBuffer = new PixelBuffer(800, 616);

View file

@ -9,59 +9,26 @@ package xdsopl.robot36;
import android.graphics.Bitmap;
public interface Mode {
/**
* @return mode name
*/
String getName();
/**
* @return VIS code
*/
int getCode();
int getVISCode();
/**
* @return image width
*/
int getWidth();
/**
* @return image height
*/
int getHeight();
/**
* @return number of samples from sync pulse to start of image data
*/
int getBegin();
int getFirstPixelSampleIndex();
/**
* @return number of samples from start of first scanline to first sync pulse?, nonzero for Scottie
*/
int getFirstSyncPulseIndex();
/**
* @return number of samples in a scanline
*/
int getScanLineSamples();
/**
* Adjust scope image before saving
*/
Bitmap postProcessScopeImage(Bitmap bmp);
/**
* Reset internal state.
*/
void reset();
void resetState();
/**
* @param pixelBuffer buffer to store decoded pixels
* @param scratchBuffer buffer for temporary data
* @param scanLineBuffer raw samples to be decoded, can contain more than one scanline
* @param scopeBufferWidth used in RawDecoder, initializes width?
* @param syncPulseIndex number of samples from array start to sync pulse
* @param scanLineSamples number of samples per scanline
* @param frequencyOffset correction of frequency of expected vs actual sync pulse (normalized to range (-1, 1))
* @param frequencyOffset normalized correction of frequency (expected vs actual)
* @return true if scanline was decoded
*/
boolean decodeScanLine(PixelBuffer pixelBuffer, float[] scratchBuffer, float[] scanLineBuffer, int scopeBufferWidth, int syncPulseIndex, int scanLineSamples, float frequencyOffset);

View file

@ -56,7 +56,7 @@ public class PaulDon extends BaseMode {
}
@Override
public int getCode() {
public int getVISCode() {
return code;
}
@ -71,7 +71,7 @@ public class PaulDon extends BaseMode {
}
@Override
public int getBegin() {
public int getFirstPixelSampleIndex() {
return beginSamples;
}
@ -86,7 +86,7 @@ public class PaulDon extends BaseMode {
}
@Override
public void reset() {
public void resetState() {
}
@Override

View file

@ -51,7 +51,7 @@ public class RGBDecoder extends BaseMode {
}
@Override
public int getCode() {
public int getVISCode() {
return code;
}
@ -66,7 +66,7 @@ public class RGBDecoder extends BaseMode {
}
@Override
public int getBegin() {
public int getFirstPixelSampleIndex() {
return beginSamples;
}
@ -81,7 +81,7 @@ public class RGBDecoder extends BaseMode {
}
@Override
public void reset() {
public void resetState() {
}
@Override

View file

@ -29,7 +29,7 @@ public class RawDecoder extends BaseMode {
}
@Override
public int getCode() {
public int getVISCode() {
return -1;
}
@ -44,7 +44,7 @@ public class RawDecoder extends BaseMode {
}
@Override
public int getBegin() {
public int getFirstPixelSampleIndex() {
return 0;
}
@ -59,7 +59,7 @@ public class RawDecoder extends BaseMode {
}
@Override
public void reset() {
public void resetState() {
}
@Override

View file

@ -59,7 +59,7 @@ public class Robot_36_Color extends BaseMode {
}
@Override
public int getCode() {
public int getVISCode() {
return 8;
}
@ -74,7 +74,7 @@ public class Robot_36_Color extends BaseMode {
}
@Override
public int getBegin() {
public int getFirstPixelSampleIndex() {
return beginSamples;
}
@ -89,7 +89,7 @@ public class Robot_36_Color extends BaseMode {
}
@Override
public void reset() {
public void resetState() {
lastEven = false;
}

View file

@ -47,12 +47,6 @@ public class Robot_72_Color extends BaseMode {
lowPassFilter = new ExponentialMovingAverage();
}
/**
* FIXME same in other modes (copy&paste)
* @param frequency frequency, range (-1,1)
* @param offset correction, range (-1,1)
* @return pixel value, range (0,1)
*/
private float freqToLevel(float frequency, float offset) {
return 0.5f * (frequency - offset + 1.f);
}
@ -63,7 +57,7 @@ public class Robot_72_Color extends BaseMode {
}
@Override
public int getCode() {
public int getVISCode() {
return 12;
}
@ -78,7 +72,7 @@ public class Robot_72_Color extends BaseMode {
}
@Override
public int getBegin() {
public int getFirstPixelSampleIndex() {
return beginSamples;
}
@ -93,7 +87,7 @@ public class Robot_72_Color extends BaseMode {
}
@Override
public void reset() {
public void resetState() {
}
@Override

View file

@ -1,9 +1,9 @@
[versions]
agp = "8.9.0"
agp = "8.12.1"
junit = "4.13.2"
junitVersion = "1.2.1"
espressoCore = "3.6.1"
appcompat = "1.7.0"
junitVersion = "1.3.0"
espressoCore = "3.7.0"
appcompat = "1.7.1"
material = "1.12.0"
activity = "1.10.1"
constraintlayout = "2.2.1"

View file

@ -1,6 +1,6 @@
#Fri Apr 12 11:35:07 CEST 2024
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists