From 90a2aad491038513c6e871739df5988a1e460d72 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 11 May 2023 16:21:59 +0200 Subject: [PATCH 1/6] new class Mikrofon --- .../main/java/com/example/ueberwachungssystem/Mikrofon.java | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 app/src/main/java/com/example/ueberwachungssystem/Mikrofon.java diff --git a/app/src/main/java/com/example/ueberwachungssystem/Mikrofon.java b/app/src/main/java/com/example/ueberwachungssystem/Mikrofon.java new file mode 100644 index 0000000..510d104 --- /dev/null +++ b/app/src/main/java/com/example/ueberwachungssystem/Mikrofon.java @@ -0,0 +1,4 @@ +package com.example.ueberwachungssystem; + +public class Mikrofon { +} From 7c8610facbab4424aa5b4b0ec15f4c633e3ef115 Mon Sep 17 00:00:00 2001 From: Tobias Wolz Date: Thu, 25 May 2023 16:58:08 +0200 Subject: [PATCH 2/6] Move from Mikrofon to MicrophoneDetector class and add of abstract Detector class --- app/src/main/AndroidManifest.xml | 7 + .../ueberwachungssystem/DetectionReport.java | 36 ++ .../example/ueberwachungssystem/Detector.java | 40 +++ .../ueberwachungssystem/MainActivity.java | 55 +++ .../MicrophoneDetector.java | 333 ++++++++++++++++++ .../example/ueberwachungssystem/Mikrofon.java | 325 +++++++++++++++++ .../ueberwachungssystem/logger/Logger.java | 61 ++++ app/src/main/res/layout/activity_main.xml | 14 +- 8 files changed, 869 insertions(+), 2 deletions(-) create mode 100644 app/src/main/java/com/example/ueberwachungssystem/DetectionReport.java create mode 100644 app/src/main/java/com/example/ueberwachungssystem/Detector.java create mode 100644 app/src/main/java/com/example/ueberwachungssystem/MicrophoneDetector.java create mode 100644 app/src/main/java/com/example/ueberwachungssystem/logger/Logger.java diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index cef8f3d..397d997 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,6 +2,13 @@ + + + + + + + { + private AudioRecord recorder; + private final int sampleRateInHz = 44100; + private final int channelConfig = AudioFormat.CHANNEL_IN_MONO; + private final int audioFormat = AudioFormat.ENCODING_PCM_16BIT; + private int minPufferGroesseInBytes; + private int pufferGroesseInBytes; + private RingPuffer ringPuffer = new RingPuffer(10); + + AufnahmeTask() { + minPufferGroesseInBytes = AudioRecord.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat); + pufferGroesseInBytes = minPufferGroesseInBytes * 2; + recorder = new AudioRecord(MediaRecorder.AudioSource.MIC, sampleRateInHz, channelConfig, audioFormat, pufferGroesseInBytes); + +// textViewMinPufferGroesseInBytes.setText("" + minPufferGroesseInBytes); +// textViewPufferGroesseInBytes.setText("" + pufferGroesseInBytes); +// textViewAbtastrate.setText("" + recorder.getSampleRate()); +// textViewAnzahlKanaele.setText("" + recorder.getChannelCount()); + + logger.log("Puffergroeße: "+ minPufferGroesseInBytes + " " + pufferGroesseInBytes); + logger.log("Recorder (SR, CH): "+ recorder.getSampleRate() + " " + recorder.getChannelCount()); + + int anzahlBytesProAbtastwert; + String s; + switch (recorder.getAudioFormat()) { + case AudioFormat.ENCODING_PCM_8BIT: + s = "8 Bit PCM "; + anzahlBytesProAbtastwert = 1; + break; + case AudioFormat.ENCODING_PCM_16BIT: + s = "16 Bit PCM"; + anzahlBytesProAbtastwert = 2; + break; + case AudioFormat.ENCODING_PCM_FLOAT: + s = "Float PCM"; + anzahlBytesProAbtastwert = 4; + break; + default: + throw new IllegalArgumentException(); + } +// textViewAudioFormat.setText(s); + + switch (recorder.getChannelConfiguration()) { + case AudioFormat.CHANNEL_IN_MONO: + s = "Mono"; + break; + case AudioFormat.CHANNEL_IN_STEREO: + s = "Stereo"; + anzahlBytesProAbtastwert *= 2; + break; + case AudioFormat.CHANNEL_INVALID: + s = "ungültig"; + break; + default: + throw new IllegalArgumentException(); + } +// textViewKanalKonfiguration.setText(s); + logger.log("Konfiguration: "+ s); + + int pufferGroesseInAnzahlAbtastwerten = pufferGroesseInBytes / anzahlBytesProAbtastwert; + int pufferGroesseInMillisekunden = 1000 * pufferGroesseInAnzahlAbtastwerten / recorder.getSampleRate(); + +// textViewPufferGroesseInAnzahlAbtastwerte.setText("" + pufferGroesseInAnzahlAbtastwerten); +// textViewPufferGroesseInMillisekunden.setText("" + pufferGroesseInMillisekunden); + } + + @Override + protected Void doInBackground(Long... params) { + recorder.startRecording(); + short[] puffer = new short[pufferGroesseInBytes / 2]; + long lastTime = System.currentTimeMillis(); + float verarbeitungsrate = 0; + final int maxZaehlerZeitMessung = 10; + int zaehlerZeitMessung = 0; + int anzahlVerarbeitet = 0; + GleitenderMittelwert gleitenderMittelwert = new GleitenderMittelwert(0.3f); + + for (; ; ) { + if (aufnahmeTask.isCancelled()) { + break; + } else { + int n = recorder.read(puffer, 0, puffer.length); + Verarbeitungsergebnis ergebnis = verarbeiten(puffer, n); + anzahlVerarbeitet += n; + + zaehlerZeitMessung++; + if (zaehlerZeitMessung == maxZaehlerZeitMessung) { + long time = System.currentTimeMillis(); + long deltaTime = time - lastTime; + verarbeitungsrate = 1000.0f * anzahlVerarbeitet / deltaTime; + verarbeitungsrate = gleitenderMittelwert.mittel(verarbeitungsrate); + zaehlerZeitMessung = 0; + anzahlVerarbeitet = 0; + lastTime = time; + } + + float noiseLevel = gleitenderMittelwert.MittelwertPuffer(puffer); + + // logger.log("Noise Level:" + noiseLevel); + ergebnis.noiseLevel = noiseLevel; + ergebnis.verarbeitungsrate = (int) verarbeitungsrate; + publishProgress(ergebnis); + + try { + Thread.sleep(100); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + recorder.release(); + return null; + } + + private Verarbeitungsergebnis verarbeiten(short[] daten, int n) { + String status; + short maxAmp = -1; + if (n == AudioRecord.ERROR_INVALID_OPERATION) { + status = "ERROR_INVALID_OPERATION"; + } else if (n == AudioRecord.ERROR_BAD_VALUE) { + status = "ERROR_BAD_VALUE"; + } else { + status = "OK"; + short max = 0; + for (int i = 0; i < n; i++) { + if (daten[i] > max) { + max = daten[i]; + } + } + + ringPuffer.hinzufuegen(max); + maxAmp = ringPuffer.maximum(); + } + + return new Verarbeitungsergebnis(status, maxAmp, 0, 0); + } + + @Override + protected void onProgressUpdate(Verarbeitungsergebnis... progress) { + super.onProgressUpdate(progress); +// textViewMaxAmp.setText("" + progress[0].maxAmp); +// textViewVerarbeitungsrate.setText("" + progress[0].verarbeitungsrate); + logger.overwriteLastlog("VR, Max, NL:" + progress[0].verarbeitungsrate + ", " + progress[0].maxAmp + + ", " + progress[0].noiseLevel); + + if (progress[0].maxAmp >= Schwellwert_Alarm) { + Alarm = true; + } + } + } + + class Verarbeitungsergebnis { + String status; + short maxAmp; + int verarbeitungsrate; + float noiseLevel; + + Verarbeitungsergebnis(String status, short maxAmp, int verarbeitungsrate, float noiseLevel) { + this.status = status; + this.maxAmp = maxAmp; + this.verarbeitungsrate = verarbeitungsrate; + this.noiseLevel = noiseLevel; + } + } + + class RingPuffer { + private short[] puffer; + private final int laenge; + private int anzahlEnthaltenerDaten; + private int position; + + public RingPuffer(int n) { + laenge = n; + anzahlEnthaltenerDaten = 0; + position = 0; + puffer = new short[laenge]; + } + + public void hinzufuegen(short wert) { + puffer[position] = wert; + position++; + if (position >= laenge) { + position = 0; + } + if (anzahlEnthaltenerDaten < laenge) { + anzahlEnthaltenerDaten++; + } + } + + public void hinzufuegen(short[] daten) { + for (short d : daten) { + puffer[position] = d; + position++; + if (position >= laenge) { + position = 0; + } + } + if (anzahlEnthaltenerDaten < laenge) { + anzahlEnthaltenerDaten += daten.length; + if (anzahlEnthaltenerDaten >= laenge) { + anzahlEnthaltenerDaten = laenge; + } + } + } + + public short maximum() { + short max = 0; + for (int i = 0; i < anzahlEnthaltenerDaten; i++) { + if (puffer[i] > max) { + max = puffer[i]; + } + } + return max; + } + + public float mittelwert() { + float summe = 0; + for (int i = 0; i < anzahlEnthaltenerDaten; i++) { + summe += puffer[i]; + } + return summe / anzahlEnthaltenerDaten; + } + } + + class GleitenderMittelwert { + private final float wichtungNeuerWert; + private final float wichtungAlterWert; + private float mittelwert = 0; + private boolean istMittelwertGesetzt = false; + + GleitenderMittelwert(float wichtungNeuerWert) { + this.wichtungNeuerWert = wichtungNeuerWert; + this.wichtungAlterWert = 1 - this.wichtungNeuerWert; + } + + float MittelwertPuffer(short[] puffer) { + + for (int i = 0; i < puffer.length; i++) { + mittelwert = Math.abs(puffer[i]); + } + mittelwert = mittelwert/puffer.length; + + return mittelwert; + } + + float mittel(float wert) { + if (istMittelwertGesetzt) { + mittelwert = wert * wichtungNeuerWert + mittelwert * wichtungAlterWert; + } else { + mittelwert = wert; + istMittelwertGesetzt = true; + } + return mittelwert; + } + } +} diff --git a/app/src/main/java/com/example/ueberwachungssystem/Mikrofon.java b/app/src/main/java/com/example/ueberwachungssystem/Mikrofon.java index 510d104..2d3c815 100644 --- a/app/src/main/java/com/example/ueberwachungssystem/Mikrofon.java +++ b/app/src/main/java/com/example/ueberwachungssystem/Mikrofon.java @@ -1,4 +1,329 @@ package com.example.ueberwachungssystem; +import android.Manifest; +import android.app.Activity; +import android.content.Context; +import android.content.pm.PackageManager; +import android.media.AudioFormat; +import android.media.AudioRecord; +import android.media.MediaRecorder; +import android.os.AsyncTask; +import android.os.Bundle; +import android.os.Environment; +import android.widget.TextView; + +import androidx.core.app.ActivityCompat; +import androidx.core.content.ContextCompat; + +import com.example.ueberwachungssystem.logger.Logger; + +import java.io.File; + public class Mikrofon { + + Logger logger; + private static final int RECHTEANFORDERUNG_MIKROFON = 1; + + private Activity MainActivityForClass; + private AufnahmeTask aufnahmeTask; + public boolean Alarm = false; + public int Schwellwert_Alarm = 500; + + public void onCreate(Activity MainActivity, Logger MainLogger) { + MainActivityForClass = MainActivity; + logger = MainLogger; //Class uses the same logger as the MainActivity + logger.log(this.getClass().getSimpleName() + ".onCreate"); + + if (!istZugriffAufMikrofonErlaubt()) { + zugriffAufMikrofonAnfordern(); + } + } + + public void onResume() { + logger.log(this.getClass().getSimpleName() + ".onResume"); + if (!istZugriffAufMikrofonErlaubt()) { + zugriffAufMikrofonAnfordern(); + } + if (istZugriffAufMikrofonErlaubt()) { + aufnahmeTask = new AufnahmeTask(); + aufnahmeTask.execute(); + } + } + + public void onPause() { + logger.log(this.getClass().getSimpleName() + ".onPause"); + if(aufnahmeTask!=null) { + aufnahmeTask.cancel(true); + // aufnahmeTask = null; // if aufnahmeTask = null, break in for loop would not work (Nullpointer Exception) + } + } + + private boolean istZugriffAufMikrofonErlaubt() { + if (ContextCompat.checkSelfPermission(MainActivityForClass, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) { + logger.log("Zugriff auf Mikrofon ist verboten."); + return false; + } else { + logger.log("Zugriff auf Mikrofon ist erlaubt."); + return true; + } + } + private void zugriffAufMikrofonAnfordern() { + ActivityCompat.requestPermissions(MainActivityForClass, new String[]{Manifest.permission.RECORD_AUDIO}, RECHTEANFORDERUNG_MIKROFON); + } + + class AufnahmeTask extends AsyncTask { + private AudioRecord recorder; + private final int sampleRateInHz = 44100; + private final int channelConfig = AudioFormat.CHANNEL_IN_MONO; + private final int audioFormat = AudioFormat.ENCODING_PCM_16BIT; + private int minPufferGroesseInBytes; + private int pufferGroesseInBytes; + private RingPuffer ringPuffer = new RingPuffer(10); + + AufnahmeTask() { + minPufferGroesseInBytes = AudioRecord.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat); + pufferGroesseInBytes = minPufferGroesseInBytes * 2; + recorder = new AudioRecord(MediaRecorder.AudioSource.MIC, sampleRateInHz, channelConfig, audioFormat, pufferGroesseInBytes); + +// textViewMinPufferGroesseInBytes.setText("" + minPufferGroesseInBytes); +// textViewPufferGroesseInBytes.setText("" + pufferGroesseInBytes); +// textViewAbtastrate.setText("" + recorder.getSampleRate()); +// textViewAnzahlKanaele.setText("" + recorder.getChannelCount()); + + logger.log("Puffergroeße: "+ minPufferGroesseInBytes + " " + pufferGroesseInBytes); + logger.log("Recorder (SR, CH): "+ recorder.getSampleRate() + " " + recorder.getChannelCount()); + + int anzahlBytesProAbtastwert; + String s; + switch (recorder.getAudioFormat()) { + case AudioFormat.ENCODING_PCM_8BIT: + s = "8 Bit PCM "; + anzahlBytesProAbtastwert = 1; + break; + case AudioFormat.ENCODING_PCM_16BIT: + s = "16 Bit PCM"; + anzahlBytesProAbtastwert = 2; + break; + case AudioFormat.ENCODING_PCM_FLOAT: + s = "Float PCM"; + anzahlBytesProAbtastwert = 4; + break; + default: + throw new IllegalArgumentException(); + } +// textViewAudioFormat.setText(s); + + switch (recorder.getChannelConfiguration()) { + case AudioFormat.CHANNEL_IN_MONO: + s = "Mono"; + break; + case AudioFormat.CHANNEL_IN_STEREO: + s = "Stereo"; + anzahlBytesProAbtastwert *= 2; + break; + case AudioFormat.CHANNEL_INVALID: + s = "ungültig"; + break; + default: + throw new IllegalArgumentException(); + } +// textViewKanalKonfiguration.setText(s); + logger.log("Konfiguration: "+ s); + + int pufferGroesseInAnzahlAbtastwerten = pufferGroesseInBytes / anzahlBytesProAbtastwert; + int pufferGroesseInMillisekunden = 1000 * pufferGroesseInAnzahlAbtastwerten / recorder.getSampleRate(); + +// textViewPufferGroesseInAnzahlAbtastwerte.setText("" + pufferGroesseInAnzahlAbtastwerten); +// textViewPufferGroesseInMillisekunden.setText("" + pufferGroesseInMillisekunden); + } + + @Override + protected Void doInBackground(Long... params) { + recorder.startRecording(); + short[] puffer = new short[pufferGroesseInBytes / 2]; + long lastTime = System.currentTimeMillis(); + float verarbeitungsrate = 0; + final int maxZaehlerZeitMessung = 10; + int zaehlerZeitMessung = 0; + int anzahlVerarbeitet = 0; + GleitenderMittelwert gleitenderMittelwert = new GleitenderMittelwert(0.3f); + + for (; ; ) { + if (aufnahmeTask.isCancelled()) { + break; + } else { + int n = recorder.read(puffer, 0, puffer.length); + Verarbeitungsergebnis ergebnis = verarbeiten(puffer, n); + anzahlVerarbeitet += n; + + zaehlerZeitMessung++; + if (zaehlerZeitMessung == maxZaehlerZeitMessung) { + long time = System.currentTimeMillis(); + long deltaTime = time - lastTime; + verarbeitungsrate = 1000.0f * anzahlVerarbeitet / deltaTime; + verarbeitungsrate = gleitenderMittelwert.mittel(verarbeitungsrate); + zaehlerZeitMessung = 0; + anzahlVerarbeitet = 0; + lastTime = time; + } + + float noiseLevel = gleitenderMittelwert.MittelwertPuffer(puffer); + + // logger.log("Noise Level:" + noiseLevel); + ergebnis.noiseLevel = noiseLevel; + ergebnis.verarbeitungsrate = (int) verarbeitungsrate; + publishProgress(ergebnis); + + try { + Thread.sleep(100); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + recorder.release(); + return null; + } + + private Verarbeitungsergebnis verarbeiten(short[] daten, int n) { + String status; + short maxAmp = -1; + if (n == AudioRecord.ERROR_INVALID_OPERATION) { + status = "ERROR_INVALID_OPERATION"; + } else if (n == AudioRecord.ERROR_BAD_VALUE) { + status = "ERROR_BAD_VALUE"; + } else { + status = "OK"; + short max = 0; + for (int i = 0; i < n; i++) { + if (daten[i] > max) { + max = daten[i]; + } + } + + ringPuffer.hinzufuegen(max); + maxAmp = ringPuffer.maximum(); + } + + return new Verarbeitungsergebnis(status, maxAmp, 0, 0); + } + + @Override + protected void onProgressUpdate(Verarbeitungsergebnis... progress) { + super.onProgressUpdate(progress); +// textViewMaxAmp.setText("" + progress[0].maxAmp); +// textViewVerarbeitungsrate.setText("" + progress[0].verarbeitungsrate); + logger.overwriteLastlog("VR, Max, NL:" + progress[0].verarbeitungsrate + ", " + progress[0].maxAmp + + ", " + progress[0].noiseLevel); + + if (progress[0].maxAmp >= Schwellwert_Alarm) { + Alarm = true; + } + } + } + + class Verarbeitungsergebnis { + String status; + short maxAmp; + int verarbeitungsrate; + float noiseLevel; + + Verarbeitungsergebnis(String status, short maxAmp, int verarbeitungsrate, float noiseLevel) { + this.status = status; + this.maxAmp = maxAmp; + this.verarbeitungsrate = verarbeitungsrate; + this.noiseLevel = noiseLevel; + } + } + + class RingPuffer { + private short[] puffer; + private final int laenge; + private int anzahlEnthaltenerDaten; + private int position; + + public RingPuffer(int n) { + laenge = n; + anzahlEnthaltenerDaten = 0; + position = 0; + puffer = new short[laenge]; + } + + public void hinzufuegen(short wert) { + puffer[position] = wert; + position++; + if (position >= laenge) { + position = 0; + } + if (anzahlEnthaltenerDaten < laenge) { + anzahlEnthaltenerDaten++; + } + } + + public void hinzufuegen(short[] daten) { + for (short d : daten) { + puffer[position] = d; + position++; + if (position >= laenge) { + position = 0; + } + } + if (anzahlEnthaltenerDaten < laenge) { + anzahlEnthaltenerDaten += daten.length; + if (anzahlEnthaltenerDaten >= laenge) { + anzahlEnthaltenerDaten = laenge; + } + } + } + + public short maximum() { + short max = 0; + for (int i = 0; i < anzahlEnthaltenerDaten; i++) { + if (puffer[i] > max) { + max = puffer[i]; + } + } + return max; + } + + public float mittelwert() { + float summe = 0; + for (int i = 0; i < anzahlEnthaltenerDaten; i++) { + summe += puffer[i]; + } + return summe / anzahlEnthaltenerDaten; + } + } + + class GleitenderMittelwert { + private final float wichtungNeuerWert; + private final float wichtungAlterWert; + private float mittelwert = 0; + private boolean istMittelwertGesetzt = false; + + GleitenderMittelwert(float wichtungNeuerWert) { + this.wichtungNeuerWert = wichtungNeuerWert; + this.wichtungAlterWert = 1 - this.wichtungNeuerWert; + } + + float MittelwertPuffer(short[] puffer) { + + for (int i = 0; i < puffer.length; i++) { + mittelwert = Math.abs(puffer[i]); + } + mittelwert = mittelwert/puffer.length; + + return mittelwert; + } + + float mittel(float wert) { + if (istMittelwertGesetzt) { + mittelwert = wert * wichtungNeuerWert + mittelwert * wichtungAlterWert; + } else { + mittelwert = wert; + istMittelwertGesetzt = true; + } + return mittelwert; + } + } } diff --git a/app/src/main/java/com/example/ueberwachungssystem/logger/Logger.java b/app/src/main/java/com/example/ueberwachungssystem/logger/Logger.java new file mode 100644 index 0000000..07af51a --- /dev/null +++ b/app/src/main/java/com/example/ueberwachungssystem/logger/Logger.java @@ -0,0 +1,61 @@ +package com.example.ueberwachungssystem.logger; + +import android.util.Log; +import android.widget.TextView; + +import java.io.PrintWriter; +import java.io.StringWriter; + +public class Logger { + private TextView textView; + private StringBuffer sb = new StringBuffer(); + private String tag; + private int lengthOfLastLog = 0; + private boolean overwrite = false; + + public Logger(String tag, TextView textView, String logInitText) { + this.tag = tag; + this.textView = textView; + sb.append(logInitText); + } + + public void log(String s) { + overwrite = false; + Log.d(tag, s); + sb.append(s).append("\n"); + if (textView != null) { + textView.setText(sb.toString()); + } + } + + public void overwriteLastlog(String s) { + Log.d(tag, s); + lengthOfLastLog = s.length(); + if (overwrite) + { + sb.setLength(sb.length() - (lengthOfLastLog + 1)); + } + sb.append(s).append("\n"); + overwrite = true; + if (textView != null) { + textView.setText(sb.toString()); + } + } + + public void log(Exception e) { + StringWriter sw = new StringWriter(); + e.printStackTrace(new PrintWriter(sw)); + log(sw.toString()); + } + + public void clearLog() { + sb.setLength(0); + if (textView != null) { + textView.setText(""); + } + } + + public String getLoggedText() { + return sb.toString(); + } +} diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 17eab17..bea580a 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -7,12 +7,22 @@ tools:context=".MainActivity"> + + \ No newline at end of file From b94a1d98b7a35a77d5099857d992f774e2d90182 Mon Sep 17 00:00:00 2001 From: Tobias Wolz Date: Fri, 16 Jun 2023 16:49:20 +0200 Subject: [PATCH 3/6] Working detection with calibration and dB signals. FFT also implemented, but does not work properly at the moment. (Added Complex class, FFT class and imported jjoe64 Graphview (in gradle and gradle.properties) for visualising FFT results) --- app/build.gradle | 1 + .../ueberwachungssystem/MainActivity.java | 22 +- .../MicrophoneDetector.java | 148 +++++++- .../example/ueberwachungssystem/Mikrofon.java | 329 ------------------ .../Signalverarbeitung/Complex.java | 148 ++++++++ .../Signalverarbeitung/FFT.java | 248 +++++++++++++ app/src/main/res/layout/activity_main.xml | 19 +- gradle.properties | 3 +- 8 files changed, 551 insertions(+), 367 deletions(-) delete mode 100644 app/src/main/java/com/example/ueberwachungssystem/Mikrofon.java create mode 100644 app/src/main/java/com/example/ueberwachungssystem/Signalverarbeitung/Complex.java create mode 100644 app/src/main/java/com/example/ueberwachungssystem/Signalverarbeitung/FFT.java diff --git a/app/build.gradle b/app/build.gradle index 631248a..784bb8a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -36,4 +36,5 @@ dependencies { testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.5' androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' + implementation 'com.jjoe64:graphview:4.2.2' } \ No newline at end of file diff --git a/app/src/main/java/com/example/ueberwachungssystem/MainActivity.java b/app/src/main/java/com/example/ueberwachungssystem/MainActivity.java index ab47b23..ee9c096 100644 --- a/app/src/main/java/com/example/ueberwachungssystem/MainActivity.java +++ b/app/src/main/java/com/example/ueberwachungssystem/MainActivity.java @@ -1,36 +1,32 @@ package com.example.ueberwachungssystem; import androidx.appcompat.app.AppCompatActivity; -import androidx.core.app.ActivityCompat; -import android.Manifest; import android.os.Bundle; import android.view.View; -import android.widget.Button; import android.widget.Switch; import android.widget.TextView; -import android.widget.ToggleButton; -import com.example.ueberwachungssystem.Mikrofon; import com.example.ueberwachungssystem.logger.Logger; +import com.jjoe64.graphview.GraphView; + public class MainActivity extends AppCompatActivity { Logger logger; + GraphView graph; private TextView tv_log; - Mikrofon Mikrofon_1= new Mikrofon(); - MicrophoneDetector Mic; private Switch TglBtn_Mic; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); - TextView textView = new TextView(this); tv_log = (TextView) findViewById(R.id.tv_log); //Set textview for showing logged content logger = new Logger(this.getClass().getSimpleName(), tv_log, ""); logger.log(this.getClass().getSimpleName() + ".onCreate"); - Mic = new MicrophoneDetector(this, logger); + graph = (GraphView) findViewById(R.id.graph); + Mic = new MicrophoneDetector(this, logger, graph); setupMic(); } @@ -38,19 +34,16 @@ public class MainActivity extends AppCompatActivity { @Override public void onResume() { super.onResume(); - Mikrofon_1.onResume(); logger.log(this.getClass().getSimpleName() + ".onResume"); } @Override public void onPause() { super.onPause(); - Mikrofon_1.onPause(); logger.log(this.getClass().getSimpleName() + ".onPause"); } private void setupMic() { - Mikrofon_1.onCreate(this, logger); TglBtn_Mic = (Switch) findViewById(R.id.TglBtn_Mic); TglBtn_Mic.setOnClickListener(new View.OnClickListener() { @Override @@ -58,9 +51,10 @@ public class MainActivity extends AppCompatActivity { if (v == TglBtn_Mic) { logger.log("onClick toggleButtonThread " + TglBtn_Mic.isChecked()); if (TglBtn_Mic.isChecked()) { - Mikrofon_1.onResume(); + Mic.startDetection(); } else { - Mikrofon_1.onPause(); + Mic.stopDetection(); + logger.clearLog(); } } } diff --git a/app/src/main/java/com/example/ueberwachungssystem/MicrophoneDetector.java b/app/src/main/java/com/example/ueberwachungssystem/MicrophoneDetector.java index d897131..8cae886 100644 --- a/app/src/main/java/com/example/ueberwachungssystem/MicrophoneDetector.java +++ b/app/src/main/java/com/example/ueberwachungssystem/MicrophoneDetector.java @@ -1,5 +1,7 @@ package com.example.ueberwachungssystem; +import static java.lang.Math.*; + import android.Manifest; import android.app.Activity; import android.content.Context; @@ -12,9 +14,14 @@ import android.os.AsyncTask; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; +import com.example.ueberwachungssystem.Signalverarbeitung.Complex; +import com.example.ueberwachungssystem.Signalverarbeitung.FFT; import com.example.ueberwachungssystem.logger.Logger; +import com.jjoe64.graphview.GraphView; +import com.jjoe64.graphview.series.DataPoint; +import com.jjoe64.graphview.series.LineGraphSeries; -public class MicrophoneDetector extends Detector{ +public class MicrophoneDetector extends Detector { /** * Constructor - takes context of current activity * @@ -24,17 +31,19 @@ public class MicrophoneDetector extends Detector{ private static final int RECHTEANFORDERUNG_MIKROFON = 1; private AufnahmeTask aufnahmeTask; - public boolean Alarm = false; - public int Schwellwert_Alarm = 500; + public boolean armed = false; + public int Schwellwert_Alarm = 100; + GraphView graph; Logger logger; - private Activity MainActivityForClass; - public MicrophoneDetector(Context context, Logger MainLogger) { + + public MicrophoneDetector(Context context, Logger MainLogger, GraphView MainGraph) { super(context); MainActivityForClass = (Activity) context; logger = MainLogger; //Class uses the same logger as the MainActivity logger.log(this.getClass().getSimpleName() + ".onCreate"); + graph = MainGraph; if (!istZugriffAufMikrofonErlaubt()) { zugriffAufMikrofonAnfordern(); @@ -44,6 +53,7 @@ public class MicrophoneDetector extends Detector{ @Override public void startDetection() { logger.log(this.getClass().getSimpleName() + ".startDetection"); + if (!istZugriffAufMikrofonErlaubt()) { zugriffAufMikrofonAnfordern(); } @@ -56,7 +66,7 @@ public class MicrophoneDetector extends Detector{ @Override public void stopDetection() { logger.log(this.getClass().getSimpleName() + ".stopDetection"); - if(aufnahmeTask!=null) { + if (aufnahmeTask != null) { aufnahmeTask.cancel(true); // aufnahmeTask = null; // if aufnahmeTask = null, break in for loop would not work (Nullpointer Exception) } @@ -71,6 +81,7 @@ public class MicrophoneDetector extends Detector{ return true; } } + private void zugriffAufMikrofonAnfordern() { ActivityCompat.requestPermissions(MainActivityForClass, new String[]{Manifest.permission.RECORD_AUDIO}, RECHTEANFORDERUNG_MIKROFON); } @@ -83,10 +94,23 @@ public class MicrophoneDetector extends Detector{ private int minPufferGroesseInBytes; private int pufferGroesseInBytes; private RingPuffer ringPuffer = new RingPuffer(10); + private float kalibierWert; + private DetectionReport detectionReport; AufnahmeTask() { minPufferGroesseInBytes = AudioRecord.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat); pufferGroesseInBytes = minPufferGroesseInBytes * 2; + if (ActivityCompat.checkSelfPermission(MainActivityForClass, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) { + // TODO: Consider calling + // ActivityCompat#requestPermissions + // here to request the missing permissions, and then overriding + // public void onRequestPermissionsResult(int requestCode, String[] permissions, + // int[] grantResults) + // to handle the case where the user grants the permission. See the documentation + // for ActivityCompat#requestPermissions for more details. + ActivityCompat.requestPermissions(MainActivityForClass, new String[]{Manifest.permission.RECORD_AUDIO}, RECHTEANFORDERUNG_MIKROFON); + } + recorder = new AudioRecord(MediaRecorder.AudioSource.MIC, sampleRateInHz, channelConfig, audioFormat, pufferGroesseInBytes); // textViewMinPufferGroesseInBytes.setText("" + minPufferGroesseInBytes); @@ -152,6 +176,40 @@ public class MicrophoneDetector extends Detector{ int anzahlVerarbeitet = 0; GleitenderMittelwert gleitenderMittelwert = new GleitenderMittelwert(0.3f); + //Kalibrierung + try { + Thread.sleep(3000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + int i = 0; + for (i = 0; i < 20; i++) { + int n = recorder.read(puffer, 0, puffer.length); + Verarbeitungsergebnis kalibrierErgebnis = verarbeiten(puffer, n); + kalibierWert += kalibrierErgebnis.maxAmp; + try { + Thread.sleep(50); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + kalibierWert = kalibierWert/i; + +// Complex[] zeitSignal = new Complex[puffer.length]; +// for (int j = 0; j < puffer.length; j++) { +// zeitSignal[j] = new Complex(puffer[j], 0); +// } +// Complex[] spektrum = FFT.fft(zeitSignal); + double[] spektrum = calculateFFT(puffer); + DataPoint AddPoint; +// LineGraphSeries series = new LineGraphSeries(new DataPoint[]{}); +// for (i = 0; i < spektrum.length; i++) { +// AddPoint = new DataPoint(i, spektrum[i]); +// series.appendData(AddPoint, true, spektrum.length); +// } +// graph.addSeries(series); + // logger.log(spektrum.toString()); + for (; ; ) { if (aufnahmeTask.isCancelled()) { break; @@ -160,6 +218,14 @@ public class MicrophoneDetector extends Detector{ Verarbeitungsergebnis ergebnis = verarbeiten(puffer, n); anzahlVerarbeitet += n; + spektrum = calculateFFT(puffer); + LineGraphSeries newseries = new LineGraphSeries(new DataPoint[]{}); + for (i = 0; i < spektrum.length; i++) { + AddPoint = new DataPoint(i, spektrum[i]); + newseries.appendData(AddPoint, true, spektrum.length); + } + graph.removeAllSeries(); + graph.addSeries(newseries); zaehlerZeitMessung++; if (zaehlerZeitMessung == maxZaehlerZeitMessung) { long time = System.currentTimeMillis(); @@ -171,10 +237,7 @@ public class MicrophoneDetector extends Detector{ lastTime = time; } - float noiseLevel = gleitenderMittelwert.MittelwertPuffer(puffer); - // logger.log("Noise Level:" + noiseLevel); - ergebnis.noiseLevel = noiseLevel; ergebnis.verarbeitungsrate = (int) verarbeitungsrate; publishProgress(ergebnis); @@ -202,14 +265,18 @@ public class MicrophoneDetector extends Detector{ for (int i = 0; i < n; i++) { if (daten[i] > max) { max = daten[i]; + //max = 20 * log10(abs(daten[i]) / 32768); } } ringPuffer.hinzufuegen(max); maxAmp = ringPuffer.maximum(); + if (maxAmp <= Schwellwert_Alarm+kalibierWert) { + armed = true; + } } - return new Verarbeitungsergebnis(status, maxAmp, 0, 0); + return new Verarbeitungsergebnis(status, maxAmp, 0); } @Override @@ -217,26 +284,71 @@ public class MicrophoneDetector extends Detector{ super.onProgressUpdate(progress); // textViewMaxAmp.setText("" + progress[0].maxAmp); // textViewVerarbeitungsrate.setText("" + progress[0].verarbeitungsrate); - logger.overwriteLastlog("VR, Max, NL:" + progress[0].verarbeitungsrate + ", " + progress[0].maxAmp - + ", " + progress[0].noiseLevel); + float maxAmpPrint = round(20*log10(abs(progress[0].maxAmp/1.0))); + float kalibierWertPrint = round(20*log10(abs(kalibierWert))); + logger.overwriteLastlog("VR, Max, Kal:" + progress[0].verarbeitungsrate + ", " + maxAmpPrint + + " dB, " + kalibierWertPrint + " dB"); - if (progress[0].maxAmp >= Schwellwert_Alarm) { - Alarm = true; + if (progress[0].maxAmp >= Schwellwert_Alarm+kalibierWert && armed == true) { + armed = false; + detectionReport = new DetectionReport("Mic1", "Audio", maxAmpPrint); + logger.log(""); + logger.log("Alarm!"); + logger.log(detectionReport.toString()); + logger.log(""); } } } + private double[] calculateFFT(short[] zeitsignal) + { + byte signal[] = new byte[zeitsignal.length]; + // loops through all the values of a Short + for (int i = 0; i < zeitsignal.length-1; i++) { + signal[i] = (byte) (zeitsignal[i]); + signal[i+1] = (byte) (zeitsignal[i] >> 8); + } + + final int mNumberOfFFTPoints =1024; + double mMaxFFTSample; + + double temp; + Complex[] y; + Complex[] complexSignal = new Complex[mNumberOfFFTPoints]; + double[] absSignal = new double[mNumberOfFFTPoints/2]; + + for(int i = 0; i < mNumberOfFFTPoints; i++){ + temp = (double)((signal[2*i] & 0xFF) | (signal[2*i+1] << 8)) / 32768.0F; + complexSignal[i] = new Complex(temp,0.0); + } + + y = FFT.fft(complexSignal); + + mMaxFFTSample = 0.0; + // mPeakPos = 0; + for(int i = 0; i < (mNumberOfFFTPoints/2); i++) + { + absSignal[i] = y[i].abs(); +// absSignal[i] = Math.sqrt(Math.pow(y[i].re(), 2) + Math.pow(y[i].im(), 2)); +// if(absSignal[i] > mMaxFFTSample) +// { +// mMaxFFTSample = absSignal[i]; +// // mPeakPos = i; +// } + } + + return absSignal; + + } + class Verarbeitungsergebnis { String status; short maxAmp; int verarbeitungsrate; - float noiseLevel; - - Verarbeitungsergebnis(String status, short maxAmp, int verarbeitungsrate, float noiseLevel) { + Verarbeitungsergebnis(String status, short maxAmp, int verarbeitungsrate) { this.status = status; this.maxAmp = maxAmp; this.verarbeitungsrate = verarbeitungsrate; - this.noiseLevel = noiseLevel; } } diff --git a/app/src/main/java/com/example/ueberwachungssystem/Mikrofon.java b/app/src/main/java/com/example/ueberwachungssystem/Mikrofon.java deleted file mode 100644 index 2d3c815..0000000 --- a/app/src/main/java/com/example/ueberwachungssystem/Mikrofon.java +++ /dev/null @@ -1,329 +0,0 @@ -package com.example.ueberwachungssystem; - -import android.Manifest; -import android.app.Activity; -import android.content.Context; -import android.content.pm.PackageManager; -import android.media.AudioFormat; -import android.media.AudioRecord; -import android.media.MediaRecorder; -import android.os.AsyncTask; -import android.os.Bundle; -import android.os.Environment; -import android.widget.TextView; - -import androidx.core.app.ActivityCompat; -import androidx.core.content.ContextCompat; - -import com.example.ueberwachungssystem.logger.Logger; - -import java.io.File; - -public class Mikrofon { - - Logger logger; - private static final int RECHTEANFORDERUNG_MIKROFON = 1; - - private Activity MainActivityForClass; - private AufnahmeTask aufnahmeTask; - public boolean Alarm = false; - public int Schwellwert_Alarm = 500; - - public void onCreate(Activity MainActivity, Logger MainLogger) { - MainActivityForClass = MainActivity; - logger = MainLogger; //Class uses the same logger as the MainActivity - logger.log(this.getClass().getSimpleName() + ".onCreate"); - - if (!istZugriffAufMikrofonErlaubt()) { - zugriffAufMikrofonAnfordern(); - } - } - - public void onResume() { - logger.log(this.getClass().getSimpleName() + ".onResume"); - if (!istZugriffAufMikrofonErlaubt()) { - zugriffAufMikrofonAnfordern(); - } - if (istZugriffAufMikrofonErlaubt()) { - aufnahmeTask = new AufnahmeTask(); - aufnahmeTask.execute(); - } - } - - public void onPause() { - logger.log(this.getClass().getSimpleName() + ".onPause"); - if(aufnahmeTask!=null) { - aufnahmeTask.cancel(true); - // aufnahmeTask = null; // if aufnahmeTask = null, break in for loop would not work (Nullpointer Exception) - } - } - - private boolean istZugriffAufMikrofonErlaubt() { - if (ContextCompat.checkSelfPermission(MainActivityForClass, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) { - logger.log("Zugriff auf Mikrofon ist verboten."); - return false; - } else { - logger.log("Zugriff auf Mikrofon ist erlaubt."); - return true; - } - } - private void zugriffAufMikrofonAnfordern() { - ActivityCompat.requestPermissions(MainActivityForClass, new String[]{Manifest.permission.RECORD_AUDIO}, RECHTEANFORDERUNG_MIKROFON); - } - - class AufnahmeTask extends AsyncTask { - private AudioRecord recorder; - private final int sampleRateInHz = 44100; - private final int channelConfig = AudioFormat.CHANNEL_IN_MONO; - private final int audioFormat = AudioFormat.ENCODING_PCM_16BIT; - private int minPufferGroesseInBytes; - private int pufferGroesseInBytes; - private RingPuffer ringPuffer = new RingPuffer(10); - - AufnahmeTask() { - minPufferGroesseInBytes = AudioRecord.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat); - pufferGroesseInBytes = minPufferGroesseInBytes * 2; - recorder = new AudioRecord(MediaRecorder.AudioSource.MIC, sampleRateInHz, channelConfig, audioFormat, pufferGroesseInBytes); - -// textViewMinPufferGroesseInBytes.setText("" + minPufferGroesseInBytes); -// textViewPufferGroesseInBytes.setText("" + pufferGroesseInBytes); -// textViewAbtastrate.setText("" + recorder.getSampleRate()); -// textViewAnzahlKanaele.setText("" + recorder.getChannelCount()); - - logger.log("Puffergroeße: "+ minPufferGroesseInBytes + " " + pufferGroesseInBytes); - logger.log("Recorder (SR, CH): "+ recorder.getSampleRate() + " " + recorder.getChannelCount()); - - int anzahlBytesProAbtastwert; - String s; - switch (recorder.getAudioFormat()) { - case AudioFormat.ENCODING_PCM_8BIT: - s = "8 Bit PCM "; - anzahlBytesProAbtastwert = 1; - break; - case AudioFormat.ENCODING_PCM_16BIT: - s = "16 Bit PCM"; - anzahlBytesProAbtastwert = 2; - break; - case AudioFormat.ENCODING_PCM_FLOAT: - s = "Float PCM"; - anzahlBytesProAbtastwert = 4; - break; - default: - throw new IllegalArgumentException(); - } -// textViewAudioFormat.setText(s); - - switch (recorder.getChannelConfiguration()) { - case AudioFormat.CHANNEL_IN_MONO: - s = "Mono"; - break; - case AudioFormat.CHANNEL_IN_STEREO: - s = "Stereo"; - anzahlBytesProAbtastwert *= 2; - break; - case AudioFormat.CHANNEL_INVALID: - s = "ungültig"; - break; - default: - throw new IllegalArgumentException(); - } -// textViewKanalKonfiguration.setText(s); - logger.log("Konfiguration: "+ s); - - int pufferGroesseInAnzahlAbtastwerten = pufferGroesseInBytes / anzahlBytesProAbtastwert; - int pufferGroesseInMillisekunden = 1000 * pufferGroesseInAnzahlAbtastwerten / recorder.getSampleRate(); - -// textViewPufferGroesseInAnzahlAbtastwerte.setText("" + pufferGroesseInAnzahlAbtastwerten); -// textViewPufferGroesseInMillisekunden.setText("" + pufferGroesseInMillisekunden); - } - - @Override - protected Void doInBackground(Long... params) { - recorder.startRecording(); - short[] puffer = new short[pufferGroesseInBytes / 2]; - long lastTime = System.currentTimeMillis(); - float verarbeitungsrate = 0; - final int maxZaehlerZeitMessung = 10; - int zaehlerZeitMessung = 0; - int anzahlVerarbeitet = 0; - GleitenderMittelwert gleitenderMittelwert = new GleitenderMittelwert(0.3f); - - for (; ; ) { - if (aufnahmeTask.isCancelled()) { - break; - } else { - int n = recorder.read(puffer, 0, puffer.length); - Verarbeitungsergebnis ergebnis = verarbeiten(puffer, n); - anzahlVerarbeitet += n; - - zaehlerZeitMessung++; - if (zaehlerZeitMessung == maxZaehlerZeitMessung) { - long time = System.currentTimeMillis(); - long deltaTime = time - lastTime; - verarbeitungsrate = 1000.0f * anzahlVerarbeitet / deltaTime; - verarbeitungsrate = gleitenderMittelwert.mittel(verarbeitungsrate); - zaehlerZeitMessung = 0; - anzahlVerarbeitet = 0; - lastTime = time; - } - - float noiseLevel = gleitenderMittelwert.MittelwertPuffer(puffer); - - // logger.log("Noise Level:" + noiseLevel); - ergebnis.noiseLevel = noiseLevel; - ergebnis.verarbeitungsrate = (int) verarbeitungsrate; - publishProgress(ergebnis); - - try { - Thread.sleep(100); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - } - recorder.release(); - return null; - } - - private Verarbeitungsergebnis verarbeiten(short[] daten, int n) { - String status; - short maxAmp = -1; - if (n == AudioRecord.ERROR_INVALID_OPERATION) { - status = "ERROR_INVALID_OPERATION"; - } else if (n == AudioRecord.ERROR_BAD_VALUE) { - status = "ERROR_BAD_VALUE"; - } else { - status = "OK"; - short max = 0; - for (int i = 0; i < n; i++) { - if (daten[i] > max) { - max = daten[i]; - } - } - - ringPuffer.hinzufuegen(max); - maxAmp = ringPuffer.maximum(); - } - - return new Verarbeitungsergebnis(status, maxAmp, 0, 0); - } - - @Override - protected void onProgressUpdate(Verarbeitungsergebnis... progress) { - super.onProgressUpdate(progress); -// textViewMaxAmp.setText("" + progress[0].maxAmp); -// textViewVerarbeitungsrate.setText("" + progress[0].verarbeitungsrate); - logger.overwriteLastlog("VR, Max, NL:" + progress[0].verarbeitungsrate + ", " + progress[0].maxAmp - + ", " + progress[0].noiseLevel); - - if (progress[0].maxAmp >= Schwellwert_Alarm) { - Alarm = true; - } - } - } - - class Verarbeitungsergebnis { - String status; - short maxAmp; - int verarbeitungsrate; - float noiseLevel; - - Verarbeitungsergebnis(String status, short maxAmp, int verarbeitungsrate, float noiseLevel) { - this.status = status; - this.maxAmp = maxAmp; - this.verarbeitungsrate = verarbeitungsrate; - this.noiseLevel = noiseLevel; - } - } - - class RingPuffer { - private short[] puffer; - private final int laenge; - private int anzahlEnthaltenerDaten; - private int position; - - public RingPuffer(int n) { - laenge = n; - anzahlEnthaltenerDaten = 0; - position = 0; - puffer = new short[laenge]; - } - - public void hinzufuegen(short wert) { - puffer[position] = wert; - position++; - if (position >= laenge) { - position = 0; - } - if (anzahlEnthaltenerDaten < laenge) { - anzahlEnthaltenerDaten++; - } - } - - public void hinzufuegen(short[] daten) { - for (short d : daten) { - puffer[position] = d; - position++; - if (position >= laenge) { - position = 0; - } - } - if (anzahlEnthaltenerDaten < laenge) { - anzahlEnthaltenerDaten += daten.length; - if (anzahlEnthaltenerDaten >= laenge) { - anzahlEnthaltenerDaten = laenge; - } - } - } - - public short maximum() { - short max = 0; - for (int i = 0; i < anzahlEnthaltenerDaten; i++) { - if (puffer[i] > max) { - max = puffer[i]; - } - } - return max; - } - - public float mittelwert() { - float summe = 0; - for (int i = 0; i < anzahlEnthaltenerDaten; i++) { - summe += puffer[i]; - } - return summe / anzahlEnthaltenerDaten; - } - } - - class GleitenderMittelwert { - private final float wichtungNeuerWert; - private final float wichtungAlterWert; - private float mittelwert = 0; - private boolean istMittelwertGesetzt = false; - - GleitenderMittelwert(float wichtungNeuerWert) { - this.wichtungNeuerWert = wichtungNeuerWert; - this.wichtungAlterWert = 1 - this.wichtungNeuerWert; - } - - float MittelwertPuffer(short[] puffer) { - - for (int i = 0; i < puffer.length; i++) { - mittelwert = Math.abs(puffer[i]); - } - mittelwert = mittelwert/puffer.length; - - return mittelwert; - } - - float mittel(float wert) { - if (istMittelwertGesetzt) { - mittelwert = wert * wichtungNeuerWert + mittelwert * wichtungAlterWert; - } else { - mittelwert = wert; - istMittelwertGesetzt = true; - } - return mittelwert; - } - } -} diff --git a/app/src/main/java/com/example/ueberwachungssystem/Signalverarbeitung/Complex.java b/app/src/main/java/com/example/ueberwachungssystem/Signalverarbeitung/Complex.java new file mode 100644 index 0000000..26a3c28 --- /dev/null +++ b/app/src/main/java/com/example/ueberwachungssystem/Signalverarbeitung/Complex.java @@ -0,0 +1,148 @@ +package com.example.ueberwachungssystem.Signalverarbeitung; + +import java.util.Objects; + +public class Complex { + private final double re; // the real part + private final double im; // the imaginary part + + // create a new object with the given real and imaginary parts + public Complex(double real, double imag) { + re = real; + im = imag; + } + + // return a string representation of the invoking com.example.ueberwachungssystem.Signalverarbeitung.Complex object + public String toString() { + if (im == 0) return re + ""; + if (re == 0) return im + "i"; + if (im < 0) return re + " - " + (-im) + "i"; + return re + " + " + im + "i"; + } + + // return abs/modulus/magnitude + public double abs() { + return Math.hypot(re, im); + } + + // return angle/phase/argument, normalized to be between -pi and pi + public double phase() { + return Math.atan2(im, re); + } + + // return a new com.example.ueberwachungssystem.Signalverarbeitung.Complex object whose value is (this + b) + public Complex plus(Complex b) { + Complex a = this; // invoking object + double real = a.re + b.re; + double imag = a.im + b.im; + return new Complex(real, imag); + } + + // return a new com.example.ueberwachungssystem.Signalverarbeitung.Complex object whose value is (this - b) + public Complex minus(Complex b) { + Complex a = this; + double real = a.re - b.re; + double imag = a.im - b.im; + return new Complex(real, imag); + } + + // return a new com.example.ueberwachungssystem.Signalverarbeitung.Complex object whose value is (this * b) + public Complex times(Complex b) { + Complex a = this; + double real = a.re * b.re - a.im * b.im; + double imag = a.re * b.im + a.im * b.re; + return new Complex(real, imag); + } + + // return a new object whose value is (this * alpha) + public Complex scale(double alpha) { + return new Complex(alpha * re, alpha * im); + } + + // return a new com.example.ueberwachungssystem.Signalverarbeitung.Complex object whose value is the conjugate of this + public Complex conjugate() { + return new Complex(re, -im); + } + + // return a new com.example.ueberwachungssystem.Signalverarbeitung.Complex object whose value is the reciprocal of this + public Complex reciprocal() { + double scale = re * re + im * im; + return new Complex(re / scale, -im / scale); + } + + // return the real or imaginary part + public double re() { + return re; + } + + public double im() { + return im; + } + + // return a / b + public Complex divides(Complex b) { + Complex a = this; + return a.times(b.reciprocal()); + } + + // return a new com.example.ueberwachungssystem.Signalverarbeitung.Complex object whose value is the complex exponential of this + public Complex exp() { + return new Complex(Math.exp(re) * Math.cos(im), Math.exp(re) * Math.sin(im)); + } + + // return a new com.example.ueberwachungssystem.Signalverarbeitung.Complex object whose value is the complex sine of this + public Complex sin() { + return new Complex(Math.sin(re) * Math.cosh(im), Math.cos(re) * Math.sinh(im)); + } + + // return a new com.example.ueberwachungssystem.Signalverarbeitung.Complex object whose value is the complex cosine of this + public Complex cos() { + return new Complex(Math.cos(re) * Math.cosh(im), -Math.sin(re) * Math.sinh(im)); + } + + // return a new com.example.ueberwachungssystem.Signalverarbeitung.Complex object whose value is the complex tangent of this + public Complex tan() { + return sin().divides(cos()); + } + + // a static version of plus + public static Complex plus(Complex a, Complex b) { + double real = a.re + b.re; + double imag = a.im + b.im; + Complex sum = new Complex(real, imag); + return sum; + } + + // See Section 3.3. + public boolean equals(Object x) { + if (x == null) return false; + if (this.getClass() != x.getClass()) return false; + Complex that = (Complex) x; + return (this.re == that.re) && (this.im == that.im); + } + + // See Section 3.3. + public int hashCode() { + return Objects.hash(re, im); + } + + // sample client for testing + public static void main(String[] args) { + Complex a = new Complex(5.0, 6.0); + Complex b = new Complex(-3.0, 4.0); + + System.out.println("a = " + a); + System.out.println("b = " + b); + System.out.println("Re(a) = " + a.re()); + System.out.println("Im(a) = " + a.im()); + System.out.println("b + a = " + b.plus(a)); + System.out.println("a - b = " + a.minus(b)); + System.out.println("a * b = " + a.times(b)); + System.out.println("b * a = " + b.times(a)); + System.out.println("a / b = " + a.divides(b)); + System.out.println("(a / b) * b = " + a.divides(b).times(b)); + System.out.println("conj(a) = " + a.conjugate()); + System.out.println("|a| = " + a.abs()); + System.out.println("tan(a) = " + a.tan()); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/ueberwachungssystem/Signalverarbeitung/FFT.java b/app/src/main/java/com/example/ueberwachungssystem/Signalverarbeitung/FFT.java new file mode 100644 index 0000000..2543310 --- /dev/null +++ b/app/src/main/java/com/example/ueberwachungssystem/Signalverarbeitung/FFT.java @@ -0,0 +1,248 @@ +package com.example.ueberwachungssystem.Signalverarbeitung; +// Source: https://introcs.cs.princeton.edu/java/97data/FFT.java.html + +import android.util.Log; + +/****************************************************************************** + * Compilation: javac FFT.java + * Execution: java FFT n + * Dependencies: com.example.ueberwachungssystem.Signalverarbeitung.Complex.java + * + * Compute the FFT and inverse FFT of a length n complex sequence + * using the radix 2 Cooley-Tukey algorithm. + * Bare bones implementation that runs in O(n log n) time and O(n) + * space. Our goal is to optimize the clarity of the code, rather + * than performance. + * + * This implementation uses the primitive root of unity w = e^(-2 pi i / n). + * Some resources use w = e^(2 pi i / n). + * + * Reference: https://www.cs.princeton.edu/~wayne/kleinberg-tardos/pdf/05DivideAndConquerII.pdf + * + * Limitations + * ----------- + * - assumes n is a power of 2 + * + * - not the most memory efficient algorithm (because it uses + * an object type for representing complex numbers and because + * it re-allocates memory for the subarray, instead of doing + * in-place or reusing a single temporary array) + * + * For an in-place radix 2 Cooley-Tukey FFT, see + * https://introcs.cs.princeton.edu/java/97data/InplaceFFT.java.html + * + ******************************************************************************/ + +public class FFT { + + // compute the FFT of x[], assuming its length n is a power of 2 + public static Complex[] fft(Complex[] x) { + int n = x.length; + + // base case + if (n == 1) return new Complex[]{x[0]}; + + // radix 2 Cooley-Tukey FFT + if (n % 2 != 0) { + throw new IllegalArgumentException("n is not a power of 2"); + } + + // compute FFT of even terms + Complex[] even = new Complex[n / 2]; + for (int k = 0; k < n / 2; k++) { + even[k] = x[2 * k]; + } + Complex[] evenFFT = fft(even); + + // compute FFT of odd terms + Complex[] odd = even; // reuse the array (to avoid n log n space) + for (int k = 0; k < n / 2; k++) { + odd[k] = x[2 * k + 1]; + } + Complex[] oddFFT = fft(odd); + + // combine + Complex[] y = new Complex[n]; + for (int k = 0; k < n / 2; k++) { + double kth = -2 * k * Math.PI / n; + Complex wk = new Complex(Math.cos(kth), Math.sin(kth)); + y[k] = evenFFT[k].plus(wk.times(oddFFT[k])); + y[k + n / 2] = evenFFT[k].minus(wk.times(oddFFT[k])); + } + return y; + } + + + // compute the inverse FFT of x[], assuming its length n is a power of 2 + public static Complex[] ifft(Complex[] x) { + int n = x.length; + Complex[] y = new Complex[n]; + + // take conjugate + for (int i = 0; i < n; i++) { + y[i] = x[i].conjugate(); + } + + // compute forward FFT + y = fft(y); + + // take conjugate again + for (int i = 0; i < n; i++) { + y[i] = y[i].conjugate(); + } + + // divide by n + for (int i = 0; i < n; i++) { + y[i] = y[i].scale(1.0 / n); + } + + return y; + + } + + // compute the circular convolution of x and y + public static Complex[] cconvolve(Complex[] x, Complex[] y) { + + // should probably pad x and y with 0s so that they have same length + // and are powers of 2 + if (x.length != y.length) { + throw new IllegalArgumentException("Dimensions don't agree"); + } + + int n = x.length; + + // compute FFT of each sequence + Complex[] a = fft(x); + Complex[] b = fft(y); + + // point-wise multiply + Complex[] c = new Complex[n]; + for (int i = 0; i < n; i++) { + c[i] = a[i].times(b[i]); + } + + // compute inverse FFT + return ifft(c); + } + + + // compute the linear convolution of x and y + public static Complex[] convolve(Complex[] x, Complex[] y) { + Complex ZERO = new Complex(0, 0); + + Complex[] a = new Complex[2 * x.length]; + for (int i = 0; i < x.length; i++) a[i] = x[i]; + for (int i = x.length; i < 2 * x.length; i++) a[i] = ZERO; + + Complex[] b = new Complex[2 * y.length]; + for (int i = 0; i < y.length; i++) b[i] = y[i]; + for (int i = y.length; i < 2 * y.length; i++) b[i] = ZERO; + + return cconvolve(a, b); + } + + // compute the DFT of x[] via brute force (n^2 time) + public static Complex[] dft(Complex[] x) { + int n = x.length; + Complex ZERO = new Complex(0, 0); + Complex[] y = new Complex[n]; + for (int k = 0; k < n; k++) { + y[k] = ZERO; + for (int j = 0; j < n; j++) { + int power = (k * j) % n; + double kth = -2 * power * Math.PI / n; + Complex wkj = new Complex(Math.cos(kth), Math.sin(kth)); + y[k] = y[k].plus(x[j].times(wkj)); + } + } + return y; + } + + // display an array of com.example.ueberwachungssystem.Signalverarbeitung.Complex numbers to standard output + public static void show(Complex[] x, String title) { + System.out.println(title); + System.out.println("-------------------"); + for (int i = 0; i < x.length; i++) { + System.out.println(x[i]); + } + System.out.println(); + } + + /*************************************************************************** + * Test client and sample execution + * + * % java FFT 4 + * x + * ------------------- + * -0.03480425839330703 + * 0.07910192950176387 + * 0.7233322451735928 + * 0.1659819820667019 + * + * y = fft(x) + * ------------------- + * 0.9336118983487516 + * -0.7581365035668999 + 0.08688005256493803i + * 0.44344407521182005 + * -0.7581365035668999 - 0.08688005256493803i + * + * z = ifft(y) + * ------------------- + * -0.03480425839330703 + * 0.07910192950176387 + 2.6599344570851287E-18i + * 0.7233322451735928 + * 0.1659819820667019 - 2.6599344570851287E-18i + * + * c = cconvolve(x, x) + * ------------------- + * 0.5506798633981853 + * 0.23461407150576394 - 4.033186818023279E-18i + * -0.016542951108772352 + * 0.10288019294318276 + 4.033186818023279E-18i + * + * d = convolve(x, x) + * ------------------- + * 0.001211336402308083 - 3.122502256758253E-17i + * -0.005506167987577068 - 5.058885073636224E-17i + * -0.044092969479563274 + 2.1934338938072244E-18i + * 0.10288019294318276 - 3.6147323062478115E-17i + * 0.5494685269958772 + 3.122502256758253E-17i + * 0.240120239493341 + 4.655566391833896E-17i + * 0.02755001837079092 - 2.1934338938072244E-18i + * 4.01805098805014E-17i + * + ***************************************************************************/ + + public static void main(String[] args) { + int n = Integer.parseInt(args[0]); + Complex[] x = new Complex[n]; + + // original data + for (int i = 0; i < n; i++) { + x[i] = new Complex(i, 0); + } + show(x, "x"); + + // FFT of original data + Complex[] y = fft(x); + show(y, "y = fft(x)"); + + // FFT of original data + Complex[] y2 = dft(x); + show(y2, "y2 = dft(x)"); + + // take inverse FFT + Complex[] z = ifft(y); + show(z, "z = ifft(y)"); + + // circular convolution of x with itself + Complex[] c = cconvolve(x, x); + show(c, "c = cconvolve(x, x)"); + + // linear convolution of x with itself + Complex[] d = convolve(x, x); + show(d, "d = convolve(x, x)"); + } +} + + diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index bea580a..edd41b2 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -11,18 +11,27 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Log" - app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent" /> + app:layout_constraintTop_toBottomOf="@+id/TglBtn_Mic" /> + + \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 3e927b1..92b9117 100644 --- a/gradle.properties +++ b/gradle.properties @@ -18,4 +18,5 @@ android.useAndroidX=true # Enables namespacing of each library's R class so that its R class includes only the # resources declared in the library itself and none from the library's dependencies, # thereby reducing the size of the R class for that library -android.nonTransitiveRClass=true \ No newline at end of file +android.nonTransitiveRClass=true +android.enableJetifier=true \ No newline at end of file From 81e04e370afb49a6c0f35cab221c083877235631 Mon Sep 17 00:00:00 2001 From: Tobias Wolz Date: Tue, 20 Jun 2023 21:04:33 +0200 Subject: [PATCH 4/6] Commit before cleaning project for merge --- app/src/main/java/com/example/ueberwachungssystem/Detector.java | 2 +- .../com/example/ueberwachungssystem/MicrophoneDetector.java | 1 + app/src/main/res/layout/activity_main.xml | 2 ++ 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/example/ueberwachungssystem/Detector.java b/app/src/main/java/com/example/ueberwachungssystem/Detector.java index bc7749b..e6ac3ea 100644 --- a/app/src/main/java/com/example/ueberwachungssystem/Detector.java +++ b/app/src/main/java/com/example/ueberwachungssystem/Detector.java @@ -22,7 +22,7 @@ abstract public class Detector { /** Triggers onDetectionListener - call this to trigger violation/alarm */ - private void reportViolation(String detectorID, String detectionType, float amplitude) { + public void reportViolation(String detectorID, String detectionType, float amplitude) { if (listener != null) { DetectionReport detectionReport = new DetectionReport(detectorID, detectionType, amplitude); listener.onDetection(detectionReport); diff --git a/app/src/main/java/com/example/ueberwachungssystem/MicrophoneDetector.java b/app/src/main/java/com/example/ueberwachungssystem/MicrophoneDetector.java index 8cae886..4a58499 100644 --- a/app/src/main/java/com/example/ueberwachungssystem/MicrophoneDetector.java +++ b/app/src/main/java/com/example/ueberwachungssystem/MicrophoneDetector.java @@ -292,6 +292,7 @@ public class MicrophoneDetector extends Detector { if (progress[0].maxAmp >= Schwellwert_Alarm+kalibierWert && armed == true) { armed = false; detectionReport = new DetectionReport("Mic1", "Audio", maxAmpPrint); + //reportViolation("Mic1", "Audio", maxAmpPrint); logger.log(""); logger.log("Alarm!"); logger.log(detectionReport.toString()); diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index edd41b2..b180d0a 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -11,6 +11,8 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Log" + app:layout_constraintBottom_toTopOf="@+id/graph" + app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/TglBtn_Mic" /> From 4381ae78f8b4a4e6563361cc0f4bab8fb2108d14 Mon Sep 17 00:00:00 2001 From: Tobias Wolz Date: Tue, 20 Jun 2023 21:16:18 +0200 Subject: [PATCH 5/6] Commit before cleaning project for merge (minor changes) --- .../{ => Detector}/DetectionReport.java | 2 +- .../{ => Detector}/Detector.java | 3 +-- .../{ => Detector}/MicrophoneDetector.java | 8 +++---- .../Signalverarbeitung/Complex.java | 22 +++++++++---------- .../Signalverarbeitung/FFT.java | 8 +++---- .../{ => Detector}/logger/Logger.java | 2 +- .../ueberwachungssystem/MainActivity.java | 13 ++++++++++- 7 files changed, 33 insertions(+), 25 deletions(-) rename app/src/main/java/com/example/ueberwachungssystem/{ => Detector}/DetectionReport.java (95%) rename app/src/main/java/com/example/ueberwachungssystem/{ => Detector}/Detector.java (94%) rename app/src/main/java/com/example/ueberwachungssystem/{ => Detector}/MicrophoneDetector.java (98%) rename app/src/main/java/com/example/ueberwachungssystem/{ => Detector}/Signalverarbeitung/Complex.java (75%) rename app/src/main/java/com/example/ueberwachungssystem/{ => Detector}/Signalverarbeitung/FFT.java (96%) rename app/src/main/java/com/example/ueberwachungssystem/{ => Detector}/logger/Logger.java (96%) diff --git a/app/src/main/java/com/example/ueberwachungssystem/DetectionReport.java b/app/src/main/java/com/example/ueberwachungssystem/Detector/DetectionReport.java similarity index 95% rename from app/src/main/java/com/example/ueberwachungssystem/DetectionReport.java rename to app/src/main/java/com/example/ueberwachungssystem/Detector/DetectionReport.java index bfcb52c..57f94d2 100644 --- a/app/src/main/java/com/example/ueberwachungssystem/DetectionReport.java +++ b/app/src/main/java/com/example/ueberwachungssystem/Detector/DetectionReport.java @@ -1,4 +1,4 @@ -package com.example.ueberwachungssystem; +package com.example.ueberwachungssystem.Detector; import android.util.Log; diff --git a/app/src/main/java/com/example/ueberwachungssystem/Detector.java b/app/src/main/java/com/example/ueberwachungssystem/Detector/Detector.java similarity index 94% rename from app/src/main/java/com/example/ueberwachungssystem/Detector.java rename to app/src/main/java/com/example/ueberwachungssystem/Detector/Detector.java index e6ac3ea..2d73b9d 100644 --- a/app/src/main/java/com/example/ueberwachungssystem/Detector.java +++ b/app/src/main/java/com/example/ueberwachungssystem/Detector/Detector.java @@ -1,6 +1,5 @@ -package com.example.ueberwachungssystem; +package com.example.ueberwachungssystem.Detector; -import android.app.Activity; import android.content.Context; import androidx.annotation.NonNull; diff --git a/app/src/main/java/com/example/ueberwachungssystem/MicrophoneDetector.java b/app/src/main/java/com/example/ueberwachungssystem/Detector/MicrophoneDetector.java similarity index 98% rename from app/src/main/java/com/example/ueberwachungssystem/MicrophoneDetector.java rename to app/src/main/java/com/example/ueberwachungssystem/Detector/MicrophoneDetector.java index 4a58499..90ed37f 100644 --- a/app/src/main/java/com/example/ueberwachungssystem/MicrophoneDetector.java +++ b/app/src/main/java/com/example/ueberwachungssystem/Detector/MicrophoneDetector.java @@ -1,4 +1,4 @@ -package com.example.ueberwachungssystem; +package com.example.ueberwachungssystem.Detector; import static java.lang.Math.*; @@ -14,9 +14,9 @@ import android.os.AsyncTask; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; -import com.example.ueberwachungssystem.Signalverarbeitung.Complex; -import com.example.ueberwachungssystem.Signalverarbeitung.FFT; -import com.example.ueberwachungssystem.logger.Logger; +import com.example.ueberwachungssystem.Detector.Signalverarbeitung.Complex; +import com.example.ueberwachungssystem.Detector.Signalverarbeitung.FFT; +import com.example.ueberwachungssystem.Detector.logger.Logger; import com.jjoe64.graphview.GraphView; import com.jjoe64.graphview.series.DataPoint; import com.jjoe64.graphview.series.LineGraphSeries; diff --git a/app/src/main/java/com/example/ueberwachungssystem/Signalverarbeitung/Complex.java b/app/src/main/java/com/example/ueberwachungssystem/Detector/Signalverarbeitung/Complex.java similarity index 75% rename from app/src/main/java/com/example/ueberwachungssystem/Signalverarbeitung/Complex.java rename to app/src/main/java/com/example/ueberwachungssystem/Detector/Signalverarbeitung/Complex.java index 26a3c28..12938a3 100644 --- a/app/src/main/java/com/example/ueberwachungssystem/Signalverarbeitung/Complex.java +++ b/app/src/main/java/com/example/ueberwachungssystem/Detector/Signalverarbeitung/Complex.java @@ -1,4 +1,4 @@ -package com.example.ueberwachungssystem.Signalverarbeitung; +package com.example.ueberwachungssystem.Detector.Signalverarbeitung; import java.util.Objects; @@ -12,7 +12,7 @@ public class Complex { im = imag; } - // return a string representation of the invoking com.example.ueberwachungssystem.Signalverarbeitung.Complex object + // return a string representation of the invoking com.example.ueberwachungssystem.Detector.Signalverarbeitung.Complex object public String toString() { if (im == 0) return re + ""; if (re == 0) return im + "i"; @@ -30,7 +30,7 @@ public class Complex { return Math.atan2(im, re); } - // return a new com.example.ueberwachungssystem.Signalverarbeitung.Complex object whose value is (this + b) + // return a new com.example.ueberwachungssystem.Detector.Signalverarbeitung.Complex object whose value is (this + b) public Complex plus(Complex b) { Complex a = this; // invoking object double real = a.re + b.re; @@ -38,7 +38,7 @@ public class Complex { return new Complex(real, imag); } - // return a new com.example.ueberwachungssystem.Signalverarbeitung.Complex object whose value is (this - b) + // return a new com.example.ueberwachungssystem.Detector.Signalverarbeitung.Complex object whose value is (this - b) public Complex minus(Complex b) { Complex a = this; double real = a.re - b.re; @@ -46,7 +46,7 @@ public class Complex { return new Complex(real, imag); } - // return a new com.example.ueberwachungssystem.Signalverarbeitung.Complex object whose value is (this * b) + // return a new com.example.ueberwachungssystem.Detector.Signalverarbeitung.Complex object whose value is (this * b) public Complex times(Complex b) { Complex a = this; double real = a.re * b.re - a.im * b.im; @@ -59,12 +59,12 @@ public class Complex { return new Complex(alpha * re, alpha * im); } - // return a new com.example.ueberwachungssystem.Signalverarbeitung.Complex object whose value is the conjugate of this + // return a new com.example.ueberwachungssystem.Detector.Signalverarbeitung.Complex object whose value is the conjugate of this public Complex conjugate() { return new Complex(re, -im); } - // return a new com.example.ueberwachungssystem.Signalverarbeitung.Complex object whose value is the reciprocal of this + // return a new com.example.ueberwachungssystem.Detector.Signalverarbeitung.Complex object whose value is the reciprocal of this public Complex reciprocal() { double scale = re * re + im * im; return new Complex(re / scale, -im / scale); @@ -85,22 +85,22 @@ public class Complex { return a.times(b.reciprocal()); } - // return a new com.example.ueberwachungssystem.Signalverarbeitung.Complex object whose value is the complex exponential of this + // return a new com.example.ueberwachungssystem.Detector.Signalverarbeitung.Complex object whose value is the complex exponential of this public Complex exp() { return new Complex(Math.exp(re) * Math.cos(im), Math.exp(re) * Math.sin(im)); } - // return a new com.example.ueberwachungssystem.Signalverarbeitung.Complex object whose value is the complex sine of this + // return a new com.example.ueberwachungssystem.Detector.Signalverarbeitung.Complex object whose value is the complex sine of this public Complex sin() { return new Complex(Math.sin(re) * Math.cosh(im), Math.cos(re) * Math.sinh(im)); } - // return a new com.example.ueberwachungssystem.Signalverarbeitung.Complex object whose value is the complex cosine of this + // return a new com.example.ueberwachungssystem.Detector.Signalverarbeitung.Complex object whose value is the complex cosine of this public Complex cos() { return new Complex(Math.cos(re) * Math.cosh(im), -Math.sin(re) * Math.sinh(im)); } - // return a new com.example.ueberwachungssystem.Signalverarbeitung.Complex object whose value is the complex tangent of this + // return a new com.example.ueberwachungssystem.Detector.Signalverarbeitung.Complex object whose value is the complex tangent of this public Complex tan() { return sin().divides(cos()); } diff --git a/app/src/main/java/com/example/ueberwachungssystem/Signalverarbeitung/FFT.java b/app/src/main/java/com/example/ueberwachungssystem/Detector/Signalverarbeitung/FFT.java similarity index 96% rename from app/src/main/java/com/example/ueberwachungssystem/Signalverarbeitung/FFT.java rename to app/src/main/java/com/example/ueberwachungssystem/Detector/Signalverarbeitung/FFT.java index 2543310..8c9426b 100644 --- a/app/src/main/java/com/example/ueberwachungssystem/Signalverarbeitung/FFT.java +++ b/app/src/main/java/com/example/ueberwachungssystem/Detector/Signalverarbeitung/FFT.java @@ -1,12 +1,10 @@ -package com.example.ueberwachungssystem.Signalverarbeitung; +package com.example.ueberwachungssystem.Detector.Signalverarbeitung; // Source: https://introcs.cs.princeton.edu/java/97data/FFT.java.html -import android.util.Log; - /****************************************************************************** * Compilation: javac FFT.java * Execution: java FFT n - * Dependencies: com.example.ueberwachungssystem.Signalverarbeitung.Complex.java + * Dependencies: com.example.ueberwachungssystem.Detector.Signalverarbeitung.Complex.java * * Compute the FFT and inverse FFT of a length n complex sequence * using the radix 2 Cooley-Tukey algorithm. @@ -158,7 +156,7 @@ public class FFT { return y; } - // display an array of com.example.ueberwachungssystem.Signalverarbeitung.Complex numbers to standard output + // display an array of com.example.ueberwachungssystem.Detector.Signalverarbeitung.Complex numbers to standard output public static void show(Complex[] x, String title) { System.out.println(title); System.out.println("-------------------"); diff --git a/app/src/main/java/com/example/ueberwachungssystem/logger/Logger.java b/app/src/main/java/com/example/ueberwachungssystem/Detector/logger/Logger.java similarity index 96% rename from app/src/main/java/com/example/ueberwachungssystem/logger/Logger.java rename to app/src/main/java/com/example/ueberwachungssystem/Detector/logger/Logger.java index 07af51a..8881762 100644 --- a/app/src/main/java/com/example/ueberwachungssystem/logger/Logger.java +++ b/app/src/main/java/com/example/ueberwachungssystem/Detector/logger/Logger.java @@ -1,4 +1,4 @@ -package com.example.ueberwachungssystem.logger; +package com.example.ueberwachungssystem.Detector.logger; import android.util.Log; import android.widget.TextView; diff --git a/app/src/main/java/com/example/ueberwachungssystem/MainActivity.java b/app/src/main/java/com/example/ueberwachungssystem/MainActivity.java index ee9c096..9acc734 100644 --- a/app/src/main/java/com/example/ueberwachungssystem/MainActivity.java +++ b/app/src/main/java/com/example/ueberwachungssystem/MainActivity.java @@ -1,13 +1,18 @@ package com.example.ueberwachungssystem; +import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; +import android.util.Log; import android.view.View; import android.widget.Switch; import android.widget.TextView; -import com.example.ueberwachungssystem.logger.Logger; +import com.example.ueberwachungssystem.Detector.DetectionReport; +import com.example.ueberwachungssystem.Detector.Detector; +import com.example.ueberwachungssystem.Detector.MicrophoneDetector; +import com.example.ueberwachungssystem.Detector.logger.Logger; import com.jjoe64.graphview.GraphView; @@ -28,6 +33,12 @@ public class MainActivity extends AppCompatActivity { graph = (GraphView) findViewById(R.id.graph); Mic = new MicrophoneDetector(this, logger, graph); setupMic(); + Mic.setOnDetectionListener(new Detector.OnDetectionListener() { + @Override + public void onDetection(@NonNull DetectionReport detectionReport) { + Log.d("Detect", "erkannt!"); + } + }); } From c60a14b6ac30db3376fbf2382d74af91eb646e4c Mon Sep 17 00:00:00 2001 From: Tobias Wolz Date: Tue, 20 Jun 2023 21:37:17 +0200 Subject: [PATCH 6/6] Clean project for merge --- .../Detector/MicrophoneDetector.java | 71 ++++++------------- .../Detector/logger/Logger.java | 61 ---------------- .../ueberwachungssystem/MainActivity.java | 19 ++--- app/src/main/res/layout/activity_main.xml | 13 ---- 4 files changed, 29 insertions(+), 135 deletions(-) delete mode 100644 app/src/main/java/com/example/ueberwachungssystem/Detector/logger/Logger.java diff --git a/app/src/main/java/com/example/ueberwachungssystem/Detector/MicrophoneDetector.java b/app/src/main/java/com/example/ueberwachungssystem/Detector/MicrophoneDetector.java index 90ed37f..4fd525e 100644 --- a/app/src/main/java/com/example/ueberwachungssystem/Detector/MicrophoneDetector.java +++ b/app/src/main/java/com/example/ueberwachungssystem/Detector/MicrophoneDetector.java @@ -10,6 +10,7 @@ import android.media.AudioFormat; import android.media.AudioRecord; import android.media.MediaRecorder; import android.os.AsyncTask; +import android.util.Log; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; @@ -33,17 +34,11 @@ public class MicrophoneDetector extends Detector { private AufnahmeTask aufnahmeTask; public boolean armed = false; public int Schwellwert_Alarm = 100; - GraphView graph; - - Logger logger; private Activity MainActivityForClass; - public MicrophoneDetector(Context context, Logger MainLogger, GraphView MainGraph) { + public MicrophoneDetector(Context context) { super(context); MainActivityForClass = (Activity) context; - logger = MainLogger; //Class uses the same logger as the MainActivity - logger.log(this.getClass().getSimpleName() + ".onCreate"); - graph = MainGraph; if (!istZugriffAufMikrofonErlaubt()) { zugriffAufMikrofonAnfordern(); @@ -52,7 +47,6 @@ public class MicrophoneDetector extends Detector { @Override public void startDetection() { - logger.log(this.getClass().getSimpleName() + ".startDetection"); if (!istZugriffAufMikrofonErlaubt()) { zugriffAufMikrofonAnfordern(); @@ -65,19 +59,17 @@ public class MicrophoneDetector extends Detector { @Override public void stopDetection() { - logger.log(this.getClass().getSimpleName() + ".stopDetection"); if (aufnahmeTask != null) { aufnahmeTask.cancel(true); - // aufnahmeTask = null; // if aufnahmeTask = null, break in for loop would not work (Nullpointer Exception) } } private boolean istZugriffAufMikrofonErlaubt() { if (ContextCompat.checkSelfPermission(MainActivityForClass, android.Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) { - logger.log("Zugriff auf Mikrofon ist verboten."); + Log.d("0","Zugriff auf Mikrofon ist verboten."); return false; } else { - logger.log("Zugriff auf Mikrofon ist erlaubt."); + Log.d("0","Zugriff auf Mikrofon ist erlaubt."); return true; } } @@ -113,13 +105,8 @@ public class MicrophoneDetector extends Detector { recorder = new AudioRecord(MediaRecorder.AudioSource.MIC, sampleRateInHz, channelConfig, audioFormat, pufferGroesseInBytes); -// textViewMinPufferGroesseInBytes.setText("" + minPufferGroesseInBytes); -// textViewPufferGroesseInBytes.setText("" + pufferGroesseInBytes); -// textViewAbtastrate.setText("" + recorder.getSampleRate()); -// textViewAnzahlKanaele.setText("" + recorder.getChannelCount()); - - logger.log("Puffergroeße: "+ minPufferGroesseInBytes + " " + pufferGroesseInBytes); - logger.log("Recorder (SR, CH): "+ recorder.getSampleRate() + " " + recorder.getChannelCount()); + Log.d("0","Puffergroeße: "+ minPufferGroesseInBytes + " " + pufferGroesseInBytes); + Log.d("0","Recorder (SR, CH): "+ recorder.getSampleRate() + " " + recorder.getChannelCount()); int anzahlBytesProAbtastwert; String s; @@ -139,7 +126,6 @@ public class MicrophoneDetector extends Detector { default: throw new IllegalArgumentException(); } -// textViewAudioFormat.setText(s); switch (recorder.getChannelConfiguration()) { case AudioFormat.CHANNEL_IN_MONO: @@ -155,14 +141,12 @@ public class MicrophoneDetector extends Detector { default: throw new IllegalArgumentException(); } -// textViewKanalKonfiguration.setText(s); - logger.log("Konfiguration: "+ s); + + Log.d("0","Konfiguration: "+ s); int pufferGroesseInAnzahlAbtastwerten = pufferGroesseInBytes / anzahlBytesProAbtastwert; int pufferGroesseInMillisekunden = 1000 * pufferGroesseInAnzahlAbtastwerten / recorder.getSampleRate(); -// textViewPufferGroesseInAnzahlAbtastwerte.setText("" + pufferGroesseInAnzahlAbtastwerten); -// textViewPufferGroesseInMillisekunden.setText("" + pufferGroesseInMillisekunden); } @Override @@ -178,7 +162,7 @@ public class MicrophoneDetector extends Detector { //Kalibrierung try { - Thread.sleep(3000); + Thread.sleep(3000); // Time to lay down the phone } catch (InterruptedException e) { e.printStackTrace(); } @@ -195,20 +179,20 @@ public class MicrophoneDetector extends Detector { } kalibierWert = kalibierWert/i; +// __Part of FFT__ // Complex[] zeitSignal = new Complex[puffer.length]; // for (int j = 0; j < puffer.length; j++) { // zeitSignal[j] = new Complex(puffer[j], 0); // } // Complex[] spektrum = FFT.fft(zeitSignal); - double[] spektrum = calculateFFT(puffer); - DataPoint AddPoint; +// double[] spektrum = calculateFFT(puffer); +// DataPoint AddPoint; // LineGraphSeries series = new LineGraphSeries(new DataPoint[]{}); // for (i = 0; i < spektrum.length; i++) { // AddPoint = new DataPoint(i, spektrum[i]); // series.appendData(AddPoint, true, spektrum.length); // } // graph.addSeries(series); - // logger.log(spektrum.toString()); for (; ; ) { if (aufnahmeTask.isCancelled()) { @@ -217,15 +201,14 @@ public class MicrophoneDetector extends Detector { int n = recorder.read(puffer, 0, puffer.length); Verarbeitungsergebnis ergebnis = verarbeiten(puffer, n); anzahlVerarbeitet += n; +// __Part of FFT__ +// spektrum = calculateFFT(puffer); +// LineGraphSeries newseries = new LineGraphSeries(new DataPoint[]{}); +// for (i = 0; i < spektrum.length; i++) { +// AddPoint = new DataPoint(i, spektrum[i]); +// newseries.appendData(AddPoint, true, spektrum.length); +// } - spektrum = calculateFFT(puffer); - LineGraphSeries newseries = new LineGraphSeries(new DataPoint[]{}); - for (i = 0; i < spektrum.length; i++) { - AddPoint = new DataPoint(i, spektrum[i]); - newseries.appendData(AddPoint, true, spektrum.length); - } - graph.removeAllSeries(); - graph.addSeries(newseries); zaehlerZeitMessung++; if (zaehlerZeitMessung == maxZaehlerZeitMessung) { long time = System.currentTimeMillis(); @@ -242,7 +225,7 @@ public class MicrophoneDetector extends Detector { publishProgress(ergebnis); try { - Thread.sleep(100); + Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } @@ -265,7 +248,6 @@ public class MicrophoneDetector extends Detector { for (int i = 0; i < n; i++) { if (daten[i] > max) { max = daten[i]; - //max = 20 * log10(abs(daten[i]) / 32768); } } @@ -282,21 +264,16 @@ public class MicrophoneDetector extends Detector { @Override protected void onProgressUpdate(Verarbeitungsergebnis... progress) { super.onProgressUpdate(progress); -// textViewMaxAmp.setText("" + progress[0].maxAmp); -// textViewVerarbeitungsrate.setText("" + progress[0].verarbeitungsrate); float maxAmpPrint = round(20*log10(abs(progress[0].maxAmp/1.0))); float kalibierWertPrint = round(20*log10(abs(kalibierWert))); - logger.overwriteLastlog("VR, Max, Kal:" + progress[0].verarbeitungsrate + ", " + maxAmpPrint + Log.d("0","VR, Max, Kal:" + progress[0].verarbeitungsrate + ", " + maxAmpPrint + " dB, " + kalibierWertPrint + " dB"); if (progress[0].maxAmp >= Schwellwert_Alarm+kalibierWert && armed == true) { armed = false; detectionReport = new DetectionReport("Mic1", "Audio", maxAmpPrint); - //reportViolation("Mic1", "Audio", maxAmpPrint); - logger.log(""); - logger.log("Alarm!"); - logger.log(detectionReport.toString()); - logger.log(""); + reportViolation("Mic1", "Audio", maxAmpPrint); + Log.d("1",detectionReport.toString()); } } } @@ -326,7 +303,6 @@ public class MicrophoneDetector extends Detector { y = FFT.fft(complexSignal); mMaxFFTSample = 0.0; - // mPeakPos = 0; for(int i = 0; i < (mNumberOfFFTPoints/2); i++) { absSignal[i] = y[i].abs(); @@ -334,7 +310,6 @@ public class MicrophoneDetector extends Detector { // if(absSignal[i] > mMaxFFTSample) // { // mMaxFFTSample = absSignal[i]; -// // mPeakPos = i; // } } diff --git a/app/src/main/java/com/example/ueberwachungssystem/Detector/logger/Logger.java b/app/src/main/java/com/example/ueberwachungssystem/Detector/logger/Logger.java deleted file mode 100644 index 8881762..0000000 --- a/app/src/main/java/com/example/ueberwachungssystem/Detector/logger/Logger.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.example.ueberwachungssystem.Detector.logger; - -import android.util.Log; -import android.widget.TextView; - -import java.io.PrintWriter; -import java.io.StringWriter; - -public class Logger { - private TextView textView; - private StringBuffer sb = new StringBuffer(); - private String tag; - private int lengthOfLastLog = 0; - private boolean overwrite = false; - - public Logger(String tag, TextView textView, String logInitText) { - this.tag = tag; - this.textView = textView; - sb.append(logInitText); - } - - public void log(String s) { - overwrite = false; - Log.d(tag, s); - sb.append(s).append("\n"); - if (textView != null) { - textView.setText(sb.toString()); - } - } - - public void overwriteLastlog(String s) { - Log.d(tag, s); - lengthOfLastLog = s.length(); - if (overwrite) - { - sb.setLength(sb.length() - (lengthOfLastLog + 1)); - } - sb.append(s).append("\n"); - overwrite = true; - if (textView != null) { - textView.setText(sb.toString()); - } - } - - public void log(Exception e) { - StringWriter sw = new StringWriter(); - e.printStackTrace(new PrintWriter(sw)); - log(sw.toString()); - } - - public void clearLog() { - sb.setLength(0); - if (textView != null) { - textView.setText(""); - } - } - - public String getLoggedText() { - return sb.toString(); - } -} diff --git a/app/src/main/java/com/example/ueberwachungssystem/MainActivity.java b/app/src/main/java/com/example/ueberwachungssystem/MainActivity.java index 9acc734..a01a2f6 100644 --- a/app/src/main/java/com/example/ueberwachungssystem/MainActivity.java +++ b/app/src/main/java/com/example/ueberwachungssystem/MainActivity.java @@ -12,14 +12,10 @@ import android.widget.TextView; import com.example.ueberwachungssystem.Detector.DetectionReport; import com.example.ueberwachungssystem.Detector.Detector; import com.example.ueberwachungssystem.Detector.MicrophoneDetector; -import com.example.ueberwachungssystem.Detector.logger.Logger; import com.jjoe64.graphview.GraphView; public class MainActivity extends AppCompatActivity { - - Logger logger; - GraphView graph; private TextView tv_log; MicrophoneDetector Mic; private Switch TglBtn_Mic; @@ -28,15 +24,13 @@ public class MainActivity extends AppCompatActivity { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tv_log = (TextView) findViewById(R.id.tv_log); //Set textview for showing logged content - logger = new Logger(this.getClass().getSimpleName(), tv_log, ""); - logger.log(this.getClass().getSimpleName() + ".onCreate"); - graph = (GraphView) findViewById(R.id.graph); - Mic = new MicrophoneDetector(this, logger, graph); + Log.d("Main",this.getClass().getSimpleName() + ".onCreate"); + Mic = new MicrophoneDetector(this); setupMic(); Mic.setOnDetectionListener(new Detector.OnDetectionListener() { @Override public void onDetection(@NonNull DetectionReport detectionReport) { - Log.d("Detect", "erkannt!"); + Log.d("Main", "Detection erkannt!"); } }); @@ -45,13 +39,13 @@ public class MainActivity extends AppCompatActivity { @Override public void onResume() { super.onResume(); - logger.log(this.getClass().getSimpleName() + ".onResume"); + Log.d("Main",this.getClass().getSimpleName() + ".onResume"); } @Override public void onPause() { super.onPause(); - logger.log(this.getClass().getSimpleName() + ".onPause"); + Log.d("Main",this.getClass().getSimpleName() + ".onPause"); } private void setupMic() { @@ -60,12 +54,11 @@ public class MainActivity extends AppCompatActivity { @Override public void onClick(View v) { if (v == TglBtn_Mic) { - logger.log("onClick toggleButtonThread " + TglBtn_Mic.isChecked()); + Log.d("Main","onClick toggleButtonThread " + TglBtn_Mic.isChecked()); if (TglBtn_Mic.isChecked()) { Mic.startDetection(); } else { Mic.stopDetection(); - logger.clearLog(); } } } diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index b180d0a..509ff73 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -11,7 +11,6 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Log" - app:layout_constraintBottom_toTopOf="@+id/graph" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/TglBtn_Mic" /> @@ -24,16 +23,4 @@ app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" /> - - \ No newline at end of file