diff --git a/app/src/main/java/xdsopl/robot36/MainActivity.java b/app/src/main/java/xdsopl/robot36/MainActivity.java
index 18e4c9e..928c4f9 100644
--- a/app/src/main/java/xdsopl/robot36/MainActivity.java
+++ b/app/src/main/java/xdsopl/robot36/MainActivity.java
@@ -64,9 +64,9 @@ public class MainActivity extends AppCompatActivity {
private Bitmap scopeBitmap;
private PixelBuffer scopeBuffer;
private ImageView scopeView;
- private Bitmap freqPlotBitmap;
- private PixelBuffer freqPlotBuffer;
- private ImageView freqPlotView;
+ private Bitmap waterfallPlotBitmap;
+ private PixelBuffer waterfallPlotBuffer;
+ private ImageView waterfallPlotView;
private Bitmap peakMeterBitmap;
private PixelBuffer peakMeterBuffer;
private ImageView peakMeterView;
@@ -88,6 +88,7 @@ public class MainActivity extends AppCompatActivity {
private int thinColor;
private int tintColor;
private boolean autoSave;
+ private boolean showSpectrogram;
private void setStatus(int id) {
setTitle(id);
@@ -139,9 +140,11 @@ public class MainActivity extends AppCompatActivity {
recordBuffer[i] = .000030517578125f * shortBuffer[i];
}
processPeakMeter();
- processSpectrogram();
+ if (showSpectrogram)
+ processSpectrogram();
boolean newLines = decoder.process(recordBuffer, recordChannel);
- //processFreqPlot();
+ if (!showSpectrogram)
+ processFreqPlot();
if (newLines) {
processScope();
processImage();
@@ -214,50 +217,50 @@ public class MainActivity extends AppCompatActivity {
}
if (stft.push(input)) {
process = true;
- int stride = freqPlotBuffer.width;
- int line = stride * freqPlotBuffer.line;
+ int stride = waterfallPlotBuffer.width;
+ int line = stride * waterfallPlotBuffer.line;
double lowest = Math.log(1e-9);
double highest = Math.log(1);
double range = highest - lowest;
for (int i = 0; i < stride; ++i)
- freqPlotBuffer.pixels[line + i] = rainbow((Math.log(stft.power[i + 14]) - lowest) / range);
- System.arraycopy(freqPlotBuffer.pixels, line, freqPlotBuffer.pixels, line + stride * (freqPlotBuffer.height / 2), stride);
- freqPlotBuffer.line = (freqPlotBuffer.line + 1) % (freqPlotBuffer.height / 2);
+ waterfallPlotBuffer.pixels[line + i] = rainbow((Math.log(stft.power[i + 14]) - lowest) / range);
+ System.arraycopy(waterfallPlotBuffer.pixels, line, waterfallPlotBuffer.pixels, line + stride * (waterfallPlotBuffer.height / 2), stride);
+ waterfallPlotBuffer.line = (waterfallPlotBuffer.line + 1) % (waterfallPlotBuffer.height / 2);
}
}
if (process) {
- int width = freqPlotBitmap.getWidth();
- int height = freqPlotBitmap.getHeight();
- int stride = freqPlotBuffer.width;
- int offset = stride * (freqPlotBuffer.line + freqPlotBuffer.height / 2 - height);
- freqPlotBitmap.setPixels(freqPlotBuffer.pixels, offset, stride, 0, 0, width, height);
- freqPlotView.invalidate();
+ int width = waterfallPlotBitmap.getWidth();
+ int height = waterfallPlotBitmap.getHeight();
+ int stride = waterfallPlotBuffer.width;
+ int offset = stride * (waterfallPlotBuffer.line + waterfallPlotBuffer.height / 2 - height);
+ waterfallPlotBitmap.setPixels(waterfallPlotBuffer.pixels, offset, stride, 0, 0, width, height);
+ waterfallPlotView.invalidate();
}
}
private void processFreqPlot() {
- int width = freqPlotBitmap.getWidth();
- int height = freqPlotBitmap.getHeight();
- int stride = freqPlotBuffer.width;
- int line = stride * freqPlotBuffer.line;
+ int width = waterfallPlotBitmap.getWidth();
+ int height = waterfallPlotBitmap.getHeight();
+ int stride = waterfallPlotBuffer.width;
+ int line = stride * waterfallPlotBuffer.line;
int channels = recordChannel > 0 ? 2 : 1;
int samples = recordBuffer.length / channels;
int spread = 2;
- Arrays.fill(freqPlotBuffer.pixels, line, line + stride, 0);
+ Arrays.fill(waterfallPlotBuffer.pixels, line, line + stride, 0);
for (int i = 0; i < samples; ++i) {
int x = Math.round((recordBuffer[i] + 2.5f) * 0.25f * stride);
if (x >= spread && x < stride - spread)
for (int j = -spread; j <= spread; ++j)
- freqPlotBuffer.pixels[line + x + j] += 1 + spread * spread - j * j;
+ waterfallPlotBuffer.pixels[line + x + j] += 1 + spread * spread - j * j;
}
int factor = 960 / samples;
for (int i = 0; i < stride; ++i)
- freqPlotBuffer.pixels[line + i] = 0x00FFFFFF & fgColor | Math.min(factor * freqPlotBuffer.pixels[line + i], 255) << 24;
- System.arraycopy(freqPlotBuffer.pixels, line, freqPlotBuffer.pixels, line + stride * (freqPlotBuffer.height / 2), stride);
- freqPlotBuffer.line = (freqPlotBuffer.line + 1) % (freqPlotBuffer.height / 2);
- int offset = stride * (freqPlotBuffer.line + freqPlotBuffer.height / 2 - height);
- freqPlotBitmap.setPixels(freqPlotBuffer.pixels, offset, stride, 0, 0, width, height);
- freqPlotView.invalidate();
+ waterfallPlotBuffer.pixels[line + i] = 0x00FFFFFF & fgColor | Math.min(factor * waterfallPlotBuffer.pixels[line + i], 255) << 24;
+ System.arraycopy(waterfallPlotBuffer.pixels, line, waterfallPlotBuffer.pixels, line + stride * (waterfallPlotBuffer.height / 2), stride);
+ waterfallPlotBuffer.line = (waterfallPlotBuffer.line + 1) % (waterfallPlotBuffer.height / 2);
+ int offset = stride * (waterfallPlotBuffer.line + waterfallPlotBuffer.height / 2 - height);
+ waterfallPlotBitmap.setPixels(waterfallPlotBuffer.pixels, offset, stride, 0, 0, width, height);
+ waterfallPlotView.invalidate();
}
private void processScope() {
@@ -379,6 +382,20 @@ public class MainActivity extends AppCompatActivity {
initAudioRecord();
}
+ private void setShowSpectrogram(boolean newShowSpectrogram) {
+ if (showSpectrogram == newShowSpectrogram)
+ return;
+ showSpectrogram = newShowSpectrogram;
+ updateWaterfallPlotMenu();
+ }
+
+ private void updateWaterfallPlotMenu() {
+ if (showSpectrogram)
+ menu.findItem(R.id.action_show_spectrogram).setChecked(true);
+ else
+ menu.findItem(R.id.action_show_frequency_plot).setChecked(true);
+ }
+
private void setAutoSave(boolean newAutoSave) {
if (autoSave == newAutoSave)
return;
@@ -477,6 +494,7 @@ public class MainActivity extends AppCompatActivity {
state.putInt("audioSource", audioSource);
state.putInt("audioFormat", audioFormat);
state.putBoolean("autoSave", autoSave);
+ state.putBoolean("showSpectrogram", showSpectrogram);
state.putString("language", language);
super.onSaveInstanceState(state);
}
@@ -490,6 +508,7 @@ public class MainActivity extends AppCompatActivity {
edit.putInt("audioSource", audioSource);
edit.putInt("audioFormat", audioFormat);
edit.putBoolean("autoSave", autoSave);
+ edit.putBoolean("showSpectrogram", showSpectrogram);
edit.putString("language", language);
edit.apply();
}
@@ -501,6 +520,7 @@ public class MainActivity extends AppCompatActivity {
final int defaultAudioSource = MediaRecorder.AudioSource.MIC;
final int defaultAudioFormat = AudioFormat.ENCODING_PCM_16BIT;
final boolean defaultAutoSave = true;
+ final boolean defaultShowSpectrogram = true;
final String defaultLanguage = "system";
if (state == null) {
SharedPreferences pref = getPreferences(Context.MODE_PRIVATE);
@@ -510,6 +530,7 @@ public class MainActivity extends AppCompatActivity {
audioSource = pref.getInt("audioSource", defaultAudioSource);
audioFormat = pref.getInt("audioFormat", defaultAudioFormat);
autoSave = pref.getBoolean("autoSave", defaultAutoSave);
+ showSpectrogram = pref.getBoolean("showSpectrogram", defaultShowSpectrogram);
language = pref.getString("language", defaultLanguage);
} else {
AppCompatDelegate.setDefaultNightMode(state.getInt("nightMode", AppCompatDelegate.getDefaultNightMode()));
@@ -518,6 +539,7 @@ public class MainActivity extends AppCompatActivity {
audioSource = state.getInt("audioSource", defaultAudioSource);
audioFormat = state.getInt("audioFormat", defaultAudioFormat);
autoSave = state.getBoolean("autoSave", defaultAutoSave);
+ showSpectrogram = state.getBoolean("showSpectrogram", defaultShowSpectrogram);
language = state.getString("language", defaultLanguage);
}
super.onCreate(state);
@@ -530,12 +552,12 @@ public class MainActivity extends AppCompatActivity {
thinColor = getColor(R.color.thin);
tintColor = getColor(R.color.tint);
scopeBuffer = new PixelBuffer(640, 2 * 1280);
- freqPlotBuffer = new PixelBuffer(256, 2 * 256);
+ waterfallPlotBuffer = new PixelBuffer(256, 2 * 256);
peakMeterBuffer = new PixelBuffer(1, 16);
imageBuffer = new PixelBuffer(800, 616);
input = new Complex();
createScope(config);
- createFreqPlot(config);
+ createWaterfallPlot(config);
createPeakMeter();
List permissions = new ArrayList<>();
if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
@@ -566,6 +588,7 @@ public class MainActivity extends AppCompatActivity {
updateRecordChannelMenu();
updateAudioSourceMenu();
updateAudioFormatMenu();
+ updateWaterfallPlotMenu();
updateAutoSaveMenu();
return true;
}
@@ -717,6 +740,14 @@ public class MainActivity extends AppCompatActivity {
setAudioFormat(AudioFormat.ENCODING_PCM_16BIT);
return true;
}
+ if (id == R.id.action_show_spectrogram) {
+ setShowSpectrogram(true);
+ return true;
+ }
+ if (id == R.id.action_show_frequency_plot) {
+ setShowSpectrogram(false);
+ return true;
+ }
if (id == R.id.action_enable_auto_save) {
setAutoSave(true);
return true;
@@ -793,11 +824,11 @@ public class MainActivity extends AppCompatActivity {
private void createScope(Configuration config) {
int screenWidthDp = config.screenWidthDp;
int screenHeightDp = config.screenHeightDp;
- int freqPlotHeightDp = 64;
+ int waterfallPlotHeightDp = 64;
if (config.orientation == Configuration.ORIENTATION_LANDSCAPE)
screenWidthDp /= 2;
else
- screenHeightDp -= freqPlotHeightDp;
+ screenHeightDp -= waterfallPlotHeightDp;
int actionBarHeightDp = 64;
screenHeightDp -= actionBarHeightDp;
int width = scopeBuffer.width;
@@ -811,18 +842,18 @@ public class MainActivity extends AppCompatActivity {
scopeView.setImageBitmap(scopeBitmap);
}
- private void createFreqPlot(Configuration config) {
- int width = freqPlotBuffer.width;
- int height = freqPlotBuffer.height / 2;
+ private void createWaterfallPlot(Configuration config) {
+ int width = waterfallPlotBuffer.width;
+ int height = waterfallPlotBuffer.height / 2;
if (config.orientation != Configuration.ORIENTATION_LANDSCAPE)
height /= 4;
- freqPlotBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
- int stride = freqPlotBuffer.width;
- int offset = stride * (freqPlotBuffer.line + freqPlotBuffer.height / 2 - height);
- freqPlotBitmap.setPixels(freqPlotBuffer.pixels, offset, stride, 0, 0, width, height);
- freqPlotView = findViewById(R.id.freq_plot);
- freqPlotView.setScaleType(ImageView.ScaleType.FIT_XY);
- freqPlotView.setImageBitmap(freqPlotBitmap);
+ waterfallPlotBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ int stride = waterfallPlotBuffer.width;
+ int offset = stride * (waterfallPlotBuffer.line + waterfallPlotBuffer.height / 2 - height);
+ waterfallPlotBitmap.setPixels(waterfallPlotBuffer.pixels, offset, stride, 0, 0, width, height);
+ waterfallPlotView = findViewById(R.id.waterfall_plot);
+ waterfallPlotView.setScaleType(ImageView.ScaleType.FIT_XY);
+ waterfallPlotView.setImageBitmap(waterfallPlotBitmap);
}
private void createPeakMeter() {
@@ -839,7 +870,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(config.orientation == Configuration.ORIENTATION_LANDSCAPE ? R.layout.activity_main_land : R.layout.activity_main);
handleInsets();
createScope(config);
- createFreqPlot(config);
+ createWaterfallPlot(config);
createPeakMeter();
}
diff --git a/app/src/main/res/drawable/baseline_water_24.xml b/app/src/main/res/drawable/baseline_water_24.xml
new file mode 100644
index 0000000..18cd30e
--- /dev/null
+++ b/app/src/main/res/drawable/baseline_water_24.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index 11c647b..6c76183 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -13,16 +13,16 @@
android:layout_width="0dp"
android:layout_height="0dp"
android:contentDescription="@string/scope_description"
- app:layout_constraintBottom_toTopOf="@+id/freq_plot"
+ app:layout_constraintBottom_toTopOf="@+id/waterfall_plot"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_main_land.xml b/app/src/main/res/layout/activity_main_land.xml
index 00bc30a..8bb810e 100644
--- a/app/src/main/res/layout/activity_main_land.xml
+++ b/app/src/main/res/layout/activity_main_land.xml
@@ -14,15 +14,15 @@
android:layout_height="0dp"
android:contentDescription="@string/scope_description"
app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toStartOf="@+id/freq_plot"
+ app:layout_constraintEnd_toStartOf="@+id/waterfall_plot"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
\ No newline at end of file
diff --git a/app/src/main/res/menu/menu_main.xml b/app/src/main/res/menu/menu_main.xml
index 9cd9943..de8f621 100644
--- a/app/src/main/res/menu/menu_main.xml
+++ b/app/src/main/res/menu/menu_main.xml
@@ -208,6 +208,23 @@
app:iconTint="@color/tint" />
+ -
+
+
- Erstellen der Bilddatei fehlgeschlagen
Speichern des Bildes fehlgeschlagen
Dekodiertes SSTV-Bild
- Frequenzdiagramm
Spitzenpegel des Audiosignals
+ Wasserfalldiagramm
+ Frequenzdiagramm
+ Spektrogramm
Automatisches Speichern
Nachtmodus
Aktivieren
diff --git a/app/src/main/res/values-es-rUS/strings.xml b/app/src/main/res/values-es-rUS/strings.xml
index 0c404f5..e2211ff 100644
--- a/app/src/main/res/values-es-rUS/strings.xml
+++ b/app/src/main/res/values-es-rUS/strings.xml
@@ -34,8 +34,10 @@
Fallo en crear imagen
Fallo en guardar imagen
Imagen SSTV decodificada
- Gráfico de frecuencia
Nivel de señal de audio máximo
+ Gráfico de cascada
+ Gráfico de frecuencia
+ Espectrograma
Guardado automático
Modo nocturno
Habilitar
diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml
index 307eb92..0c414eb 100644
--- a/app/src/main/res/values-pl/strings.xml
+++ b/app/src/main/res/values-pl/strings.xml
@@ -34,8 +34,10 @@
Tworzenie pliku obrazu nie powiodło się
Zapisywanie obrazu nie powiodło się
Zdekodowano obraz SSTV
- Wykres częstotliwości
Szczytowy poziom sygnału audio
+ Wykres wodospadowy
+ Wykres częstotliwości
+ Spektrogram
Automatyczne zapisywanie
Tryb nocny
Włącz
diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml
index 10297fd..3db417c 100755
--- a/app/src/main/res/values-pt-rBR/strings.xml
+++ b/app/src/main/res/values-pt-rBR/strings.xml
@@ -34,8 +34,10 @@
Falha ao criar imagem
Falha ao salvar imagem
Imagem SSTV decodificada
- Gráfico de frequência
Nível de sinal de áudio máximo
+ Gráfico de cascata
+ Gráfico de frequência
+ Espectrograma
Salvamento automático
Modo noturno
Habilitar
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index bbd89a4..246d6ea 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -34,8 +34,10 @@
Ошибка создания файла изображения
Ошибка сохранения изображения
Декодированное изображение SSTV
- График частот
Пиковый уровень аудиосигнала
+ Водопадный график
+ График частот
+ Спектрограмма
Автосохранение
Ночной режим
Включить
diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml
index cd9c76e..c1fccbb 100644
--- a/app/src/main/res/values-uk/strings.xml
+++ b/app/src/main/res/values-uk/strings.xml
@@ -34,8 +34,10 @@
Помилка створення файлу зображення
Помилка збереження зображення
Декодоване зображення SSTV
- Графік частот
Піковий рівень аудіосигналу
+ Графік водоспаду
+ Графік частот
+ Спектрограма
Автозбереження
Нічний режим
Увімкнути
diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml
index 2b5811b..5d46199 100644
--- a/app/src/main/res/values-zh-rCN/strings.xml
+++ b/app/src/main/res/values-zh-rCN/strings.xml
@@ -34,8 +34,10 @@
创建图片文件出错
写入图像数据失败
解码的SSTV图像
- 频率图
音频峰值信号水平
+ 瀑布图
+ 频率图
+ 频谱图
自动保存
夜间模式
开启
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 9ff0250..b16e96e 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -69,8 +69,10 @@
Creating picture file failed
Storing picture failed
Decoded SSTV picture
- Frequency plot
Peak audio signal level
+ Waterfall plot
+ Frequency plot
+ Spectrogram
Auto Save
Night Mode
Enable