diff --git a/Notiz.txt b/Notiz.txt new file mode 100644 index 0000000..cda991c --- /dev/null +++ b/Notiz.txt @@ -0,0 +1,3 @@ +Sensor als Service einrichten, der aus Activity heraus gestartet werden kann. +Stichwort: Intent +Siehe Skript Teil 1 \ No newline at end of file diff --git a/app/src/main/java/com/example/ueberwachungssystem/Detection/Accelerometer.java b/app/src/main/java/com/example/ueberwachungssystem/Detection/Accelerometer.java new file mode 100644 index 0000000..0d4ac3a --- /dev/null +++ b/app/src/main/java/com/example/ueberwachungssystem/Detection/Accelerometer.java @@ -0,0 +1,108 @@ +package com.example.ueberwachungssystem.Detection; + +import static java.lang.Math.sqrt; + +import android.content.Context; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; + + +/** + * Accelerometer inherits some methods from abstract Detector class (more info there) + * + * + * USE FROM MAIN ACTIVITY: + * + * Accelerometer beschleunigungssensor = new Accelerometer(this); + * onCreate: + * //Accelerometer Setup + * beschleunigungssensor = new Accelerometer(this, logger, textViewLog); //logger and textview only for debugging necessary + * beschleunigungssensor.getSensor(); + * + * //Starting Detection: + * beschleunigungssensor.startDetection(); + * //Stopping Detection: also recommended at onPause to avoid unnecessary battery consumption + * beschleunigungssensor.stopDetection(); + * + * */ + +public class Accelerometer extends Detector implements SensorEventListener { + + public SensorManager sensorManager; + private static final int sensorType = Sensor.TYPE_LINEAR_ACCELERATION; + private Sensor accelerometer; + private Context context; + boolean alarm = false; + //Preallocate memory for the data of each axis of the acceleration sensor + float x; + float y; + float z; + float betrag; //Betrag aller drei Achsen sqrt(x*x + y*y + z*z) + private DetectionReport detectionReport; + + // In constructor pass Activity, Context and TextView from MainActivity in Accelerometer class + public Accelerometer(Context context){ + super(); //von Detektor + this.context = context; + } + + public void getSensor(){ + sensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE); + if(sensorManager.getSensorList(sensorType).size()==0) { + accelerometer = null; + } + else { + accelerometer = sensorManager.getSensorList(sensorType).get(0); + } + } + + @Override + public void onSensorChanged(SensorEvent event) { + try { + checkAlarm(event); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + public void checkAlarm (SensorEvent event) throws InterruptedException { + x = event.values[0]; + y = event.values[1]; + z = event.values[2]; + betrag = (float) sqrt(x*x + y*y + z*z); + float threshold = 1.5F; + + if (!alarm) { + if (betrag > threshold) { + alarm = true; + reportViolation("Bewegung", betrag); + } + } else { + if (betrag < threshold) { + alarm = false; + } else { + } + } + } + + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) { + } + + @Override + public void startDetection() { + // entspricht void start() + //getSensor(); + if (accelerometer != null) { + sensorManager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_GAME); + } + } + + @Override + public void stopDetection() { + // entspricht void stop() + sensorManager.unregisterListener(this, accelerometer); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/ueberwachungssystem/Detection/MicrophoneDetector.java b/app/src/main/java/com/example/ueberwachungssystem/Detection/MicrophoneDetector.java new file mode 100644 index 0000000..6a9e8ba --- /dev/null +++ b/app/src/main/java/com/example/ueberwachungssystem/Detection/MicrophoneDetector.java @@ -0,0 +1,382 @@ +package com.example.ueberwachungssystem.Detection; + +import static java.lang.Math.*; + +import android.Manifest; +import android.annotation.SuppressLint; +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.util.Log; + +import androidx.core.app.ActivityCompat; +import androidx.core.content.ContextCompat; + +import com.example.ueberwachungssystem.Detection.Signalverarbeitung.Complex; +import com.example.ueberwachungssystem.Detection.Signalverarbeitung.FFT; +import com.example.ueberwachungssystem.Detection.DetectionReport; +import com.example.ueberwachungssystem.Detection.Detector; + +public class MicrophoneDetector extends Detector { + /** + * Constructor - takes context of current activity + * + * @param context + */ + + private static final int RECHTEANFORDERUNG_MIKROFON = 1; + + private AufnahmeTask aufnahmeTask; + public boolean armed = false; + public int Schwellwert_Alarm = 100; + private Context context; + + public MicrophoneDetector(Context context) { + super(); + this.context = context; + } + + @Override + public void startDetection() { + aufnahmeTask = new AufnahmeTask(); + aufnahmeTask.execute(); + } + + @Override + public void stopDetection() { + if (aufnahmeTask != null) { + aufnahmeTask.cancel(true); + } + } + + 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); + private float kalibierWert; + private com.example.ueberwachungssystem.Detection.DetectionReport detectionReport; + + @SuppressLint("MissingPermission") + AufnahmeTask() { + minPufferGroesseInBytes = AudioRecord.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat); + pufferGroesseInBytes = minPufferGroesseInBytes * 2; + try { + recorder = new AudioRecord(MediaRecorder.AudioSource.MIC, sampleRateInHz, channelConfig, audioFormat, pufferGroesseInBytes); + } catch (Exception e) { + e.printStackTrace(); + } + Log.d("0","Puffergroeße: "+ minPufferGroesseInBytes + " " + pufferGroesseInBytes); + Log.d("0","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(); + } + + 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(); + } + + Log.d("0","Konfiguration: "+ s); + + int pufferGroesseInAnzahlAbtastwerten = pufferGroesseInBytes / anzahlBytesProAbtastwert; + + } + + @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); + + //Kalibrierung + try { + Thread.sleep(3000); // Time to lay down the phone + } 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; + +// __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; +// 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); + + for (; ; ) { + if (aufnahmeTask.isCancelled()) { + break; + } else { + 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); +// } + + 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; + } + + + ergebnis.verarbeitungsrate = (int) verarbeitungsrate; + publishProgress(ergebnis); + + try { + Thread.sleep(10); + } 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(); + if (maxAmp <= Schwellwert_Alarm+kalibierWert) { + armed = true; + } + } + + return new Verarbeitungsergebnis(status, maxAmp, 0); + } + + @Override + protected void onProgressUpdate(Verarbeitungsergebnis... progress) { + super.onProgressUpdate(progress); + float maxAmpPrint = round(20*log10(abs(progress[0].maxAmp/1.0))); + float kalibierWertPrint = round(20*log10(abs(kalibierWert))); + 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(true, "Audio", maxAmpPrint); + reportViolation("Audio", maxAmpPrint); + Log.d("1",detectionReport.toString()); + } + } + } + + 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 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); + + for(int i = 0; i < (mNumberOfFFTPoints/2); i++) + { + absSignal[i] = y[i].abs(); + } + + return absSignal; + + } + + class Verarbeitungsergebnis { + String status; + short maxAmp; + int verarbeitungsrate; + Verarbeitungsergebnis(String status, short maxAmp, int verarbeitungsrate) { + this.status = status; + this.maxAmp = maxAmp; + this.verarbeitungsrate = verarbeitungsrate; + } + } + + 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/Detection/Signalverarbeitung/Complex.java b/app/src/main/java/com/example/ueberwachungssystem/Detection/Signalverarbeitung/Complex.java new file mode 100644 index 0000000..07b514b --- /dev/null +++ b/app/src/main/java/com/example/ueberwachungssystem/Detection/Signalverarbeitung/Complex.java @@ -0,0 +1,148 @@ +package com.example.ueberwachungssystem.Detection.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.Detection.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.Detection.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.Detection.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.Detection.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.Detection.Signalverarbeitung.Complex object whose value is the conjugate of this + public Complex conjugate() { + return new Complex(re, -im); + } + + // return a new com.example.ueberwachungssystem.Detection.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.Detection.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.Detection.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.Detection.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.Detection.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/Detection/Signalverarbeitung/FFT.java b/app/src/main/java/com/example/ueberwachungssystem/Detection/Signalverarbeitung/FFT.java new file mode 100644 index 0000000..dc97fd1 --- /dev/null +++ b/app/src/main/java/com/example/ueberwachungssystem/Detection/Signalverarbeitung/FFT.java @@ -0,0 +1,246 @@ +package com.example.ueberwachungssystem.Detection.Signalverarbeitung; +// Source: https://introcs.cs.princeton.edu/java/97data/FFT.java.html + +/****************************************************************************** + * Compilation: javac FFT.java + * Execution: java FFT n + * Dependencies: com.example.ueberwachungssystem.Detection.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.Detection.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/build.gradle b/build.gradle index f05eacf..41532d1 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,5 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. plugins { - id 'com.android.application' version '7.4.2' apply false - id 'com.android.library' version '7.4.2' apply false + id 'com.android.application' version '8.0.0' apply false + id 'com.android.library' version '8.0.0' apply false } \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 3e927b1..3a131ca 100644 --- a/gradle.properties +++ b/gradle.properties @@ -18,4 +18,6 @@ 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.defaults.buildfeatures.buildconfig=true +android.nonFinalResIds=false \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index a5dbc0d..3a67f74 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Thu May 11 15:04:30 CEST 2023 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME