[JAVA] [Étude gratuite pour adultes] Communication acoustique avec Android

introduction

Je voulais faire du positionnement en intérieur avec des ondes sonores, mais je pensais que ce serait un problème d'acheter un haut-parleur à ultrasons, mais j'essayais de le faire, mais apparemment, j'ai trouvé que les haut-parleurs et les microphones Android peuvent couvrir même les hautes fréquences. Alors j'ai essayé.

Cependant, malheureusement, le temps était écoulé car la période des vacances d'été n'était pas suffisante et ASK (OOK) était fait avec des ondes ultrasonores.

Ce qui a été utilisé

Capture d'écran

Du haut, forme d'onde temporelle, enveloppe, forme d'onde de fréquence. Même si le nom est SoundLocater, c'est mignon qu'il ne puisse même pas se positionner.

Où vous dites "oui"

Où je dis "Wow"

ASK modulé avec ultrasons 18 kHz

――C'est vraiment beau parce que vous l'éteignez avec votre propre haut-parleur et le prenez avec votre propre microphone ―― Je pourrais capter le son d'autres appareils dans une pièce d'environ 10 m. ――Le son des autres appareils n'est pas si beau

Essayez-le

――Je ne voulais pas faire de lecture de porteuse ou de détection synchrone, j'ai donc choisi ASK. ――Mais après tout, il est impossible de distinguer l'atténuation de la distance, donc après tout c'est un niveau de jeu ――FSK utilise certaines bandes, et PSK peut être identifié sur Android, donc c'est assez difficile.

à partir de maintenant

--Je veux détecter exactement le début ―― Y a-t-il une bonne modulation autre que ASK? ――Je veux mettre dans un protocole simple de type CSMA / CA et bien m'éviter.

la mise en oeuvre

Avec quelques extraits. Je n'ai pas assez d'énergie pour tout mettre \ _ (: 3 "∠) \ _

Receveur

Les deux suivants sont importants.

--Utiliser la classe AudioRecord --Fourier transformation (FFT)

La transformée de Fourier inverse n'a pas fonctionné et j'étais vraiment dedans, mais à la fin il semble que fft.rdft (-1, données FFT) était bon.

De plus, les deux éléments suivants ont été ajoutés qui n'étaient pas inclus dans la source de référence.

--Réglez la bande inutile à 0 dans la gamme de fréquences pour ne laisser passer que la bande souhaitée

        //Créer un enregistrement audio
        audioRec = new AudioRecord(MediaRecorder.AudioSource.MIC, SAMPLING_RATE, CHANNEL_CONFIG, AUDIO_FORMAT, 2*bufSize);
        audioRec.startRecording();
        isRecording = true;

        //Fil d'analyse de Fourier
        fft = new Thread(new Runnable() {
            @Override
            public void run() {
                byte buf[] = new byte[bufSize * 2];
                while (isRecording) {
                    audioRec.read(buf, 0, buf.length);

                    //Conversion endian
                    ByteBuffer bf = ByteBuffer.wrap(buf);
                    bf.order(ByteOrder.LITTLE_ENDIAN);
                    short[] s = new short[bufSize];
                    for (int i = bf.position(); i < bf.capacity() / 2; i++) {
                        s[i] = bf.getShort();
                    }

                    //Créer une classe FFT et transmettre des valeurs
                    FFT4g fft = new FFT4g(FFT_SIZE);
                    double[] FFTdata = new double[FFT_SIZE];
                    for (int i = 0; i < bufSize; i++) {
                        FFTdata[i] = (double) s[i];
                    }
                    fft.rdft(1, FFTdata);

                    //Calcul des décibels
                    short[] dbfs = new short[FFT_SIZE / 2];
                    for (int i = 0; i < FFT_SIZE; i += 2) {
                        dbfs[i / 2] = (short) (
                                20 * Math.log10(
                                        Math.sqrt( Math.pow(FFTdata[i], 2) + Math.pow(FFTdata[i + 1], 2) )
                                        /dB_baseline
                                )
                        );

                        //★★ Réglez la bande inutile sur 0 dans la gamme de fréquences pour passer une bande spécifique
                        if ( width/2 < Math.abs(rxFreq-resol*i/2) ) {
                            FFTdata[i] = 0;
                            FFTdata[i+1] = 0;
                        }
                    }

                    //FFT inversé
                    fft.rdft(-1, FFTdata);

                    //★★ Moyenne mobile avec valeur absolue pour la détection d'enveloppe
                    short[] s2 = new short[bufSize];
                    for (int i=16; i<bufSize; i++) {
                        for (int j=0; j<16; j++) {
                            s2[i-16] += (Math.abs(FFTdata[i-j]) * 2.0 / FFT_SIZE) /16;
                        }
                    }

                    updateChart(mChartTime, s);
                    updateChart(mChartTime2, s2);
                    updateChart(mChartFreq, dbfs);

                }
                //Arrête d'enregistrer
                audioRec.stop();
                audioRec.release();
            }
        });
        //Début du fil
        fft.start();

référence

C'est à peu près comme suit.

C'est FFT.

Émetteur

Par rapport à la partie réceptrice, la partie émettrice était considérablement plus difficile.

Les choses importantes sont les suivantes.

--Utiliser la classe AudioTrack

Ingéniosité etc.

--Pour la détection de tête, 16 bits de 01 sont ajoutés à plusieurs reprises au début en tant que préambule.

        //Réglage de la fréquence
        int durationPerSymbol = 40;
        int samplesPerT = 4;
        int samplesPerSymbol = (int)( (double)( freq * durationPerSymbol) / 1000.0 );
        samplesPerSymbol = samplesPerT * (int) Math.ceil(samplesPerSymbol / samplesPerT);
        int samplingRate = samplesPerT * freq;

        //Génération de données
        Random rnd = new Random();
        byte[] preamble = new byte[]{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0};
        byte[] data = new byte[64-preamble.length];
        for (int i=0; i<data.length; i++) {
            data[i] = (byte)rnd.nextInt(2);
        }
        int length = preamble.length + data.length;

        //Génération de transporteurs
        byte[] samples = new byte[samplesPerSymbol*length];
        for (int i=0; i<samplesPerSymbol/samplesPerT*length; i++) {
            samples[samplesPerT * i + 0] = (byte) 0x70;
            samples[samplesPerT * i + 1] = (byte) 0x70;
            samples[samplesPerT * i + 2] = (byte) 0x00;
            samples[samplesPerT * i + 3] = (byte) 0x00;
        }

        //Modulation AM (préambule)
        int rolloffSamples = samplesPerSymbol/8;
        for (int i=0; i<preamble.length; i++) {
            for (int j=0; j<samplesPerSymbol; j++) {
                double factor = 1.0;

                //Filtre cosinus surélevé
                if (j<rolloffSamples) {
                    factor = (1 - Math.cos( Math.PI/(double)rolloffSamples * (double)j )) /2;
                } else if (samplesPerSymbol-rolloffSamples<j) {
                    factor = (1 - Math.cos( Math.PI/(double)rolloffSamples * (double)(samplesPerSymbol-j) )) /2;
                }

                samples[samplesPerSymbol * i + j] = (byte)( (double)(samples[samplesPerSymbol * i + j] * preamble[i]) * factor );
            }
        }

        //Modulation AM (partie données)
        for (int i=0; i<data.length; i++) {
            for (int j=0; j<samplesPerSymbol; j++) {
                double factor = 1.0;

                //Filtre cosinus surélevé
                if (j<rolloffSamples) {
                    factor = (1 - Math.cos( Math.PI/(double)rolloffSamples * (double)j )) /2;
                } else if (samplesPerSymbol-rolloffSamples<j) {
                    factor = (1 - Math.cos( Math.PI/(double)rolloffSamples * (double)(samplesPerSymbol-j) )) /2;
                }

                samples[samplesPerSymbol * (preamble.length+i) + j] = (byte)( (double)(samples[samplesPerSymbol * (preamble.length+i) + j] * data[i]) * factor );
            }
        }

        //Répéter 10 fois
        final byte[] txsamples = new byte[10*samples.length];
        for (int i=0; i<10; i++) {
            for (int j=0; j<samples.length; j++) {
                txsamples[samples.length*i+j] = samples[j];
            }
        }

        //Constructeur AudioTrack
        final AudioTrack mTrack = new AudioTrack(
                AudioManager.STREAM_MUSIC,
                samplingRate,
                AudioFormat.CHANNEL_OUT_MONO,
                AudioFormat.ENCODING_PCM_8BIT,
                txsamples.length,
                AudioTrack.MODE_STATIC
        );

        //Lecture des paramètres d'écoute complets
        mTrack.setNotificationMarkerPosition(txsamples.length);
        mTrack.setPlaybackPositionUpdateListener(
            new AudioTrack.OnPlaybackPositionUpdateListener() {
                public void onPeriodicNotification(AudioTrack track) {}
                public void onMarkerReached(AudioTrack track) {
                    if (track.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) {
                        track.stop();
                        track.release();
                        track = null;
                    }
                }
            }
        );

        //Écriture et lecture de données de forme d'onde
        track = new Thread(new Runnable() {
            @Override
            public void run() {
                mTrack.reloadStaticData();
                mTrack.write(txsamples, 0, txsamples.length);
                mTrack.play();
            }
        });
        //Début du fil
        track.start();

référence

J'ai fait référence à ce domaine autour d'AudioTrack. Presque comme ça.

Tous ceux qui ont pris soin de moi

Etudier la communication numérique

Relations de lecture

Lié à l'enregistrement

Liés à la FFT

--Version portée par Java de la bibliothèque FFT du Dr Oura - Ooura-FFT-Library-by-Other-Language/fft4g.java at master · YSRKEN/Ooura-FFT-Library-by-Other-Language - FFT4g.java

Lié au dessin graphique

Recommended Posts

[Étude gratuite pour adultes] Communication acoustique avec Android
Utiliser la communication série sur Android
Essayez la communication en utilisant gRPC sur un serveur Android + Java
[Android] Notes sur xml