Zero Crossing of the Noisy Sine Wave

9

Saya mencoba menemukan penyilangan nol dari gelombang sinus untuk mengubah gelombang sinus menjadi gelombang persegi. Satu-satunya masalah adalah bahwa gelombang sinus berisik, jadi saya mendapatkan banyak jitter dan penyilangan nol palsu.

Adakah yang bisa merekomendasikan psuedocode sederhana atau materi yang relevan? Sejauh ini saya punya sesuatu seperti ini:

if (sample[i]>0 && sample[i+1]<0) || (sample[i]<0 && sample[i+1]>0)

Adakah yang bisa merekomendasikan metode yang lebih kuat?

Kevin Nasto
sumber
Apa tujuan Anda membuat gelombang persegi? Apakah Anda mencoba mencari tahu di mana sinyal mulai dan berakhir? Jika Anda, saya dapat merekomendasikan metode.
Spacey
if ((sample [i] * sample [i + 1]) <0) zero_crossing ++;
Marius Hrisca

Jawaban:

8

Anda dapat mencoba pemfilteran low-pass sinyal input untuk mendapatkan zero-crossing yang lebih mulus (atau bahkan pemfilteran band-pass jika Anda memiliki ide yang bagus tentang lokasi frekuensi dari gelombang sinus). Risikonya adalah bahwa jika informasi fase akurat sampel sangat penting untuk aplikasi Anda, kelambatan tambahan dari filter mungkin menjadi masalah.

Pendekatan lain: alih-alih mencoba mengubah gelombang sinus menjadi gelombang persegi, bagaimana dengan mendapatkan osilator gelombang persegi independen untuk menyelaraskan diri dalam fase / frekuensi dengan gelombang sinus? Ini dapat dilakukan dengan loop fase-terkunci .

pichenettes
sumber
6

Yang Anda tunjukkan tentu saja adalah detektor zero-crossing. Beberapa hal yang terlintas dalam pikiran yang dapat meningkatkan situasi Anda:

  • Jika Anda memiliki noise yang berada di luar pita sinyal Anda (yang hampir pasti demikian, karena input Anda adalah nada murni), maka Anda dapat meningkatkan rasio sinyal-ke-noise dengan menerapkan filter bandpass di sekitar sinyal yang menarik. . Lebar passband filter harus dipilih berdasarkan seberapa tepatnya Anda mengetahui frekuensi sinusoid a priori . Dengan mengurangi jumlah noise yang ada pada sinusoid, jumlah penyilangan nol palsu dan jitter mereka tentang waktu penyeberangan yang benar akan berkurang.

    • Sebagai catatan, jika Anda tidak memiliki informasi yang baik sebelumnya, Anda bisa menggunakan teknik yang lebih canggih yang dikenal sebagai penambah garis adaptif , yang, seperti namanya, adalah filter adaptif yang akan meningkatkan sinyal input berkala. Namun, ini adalah topik yang agak maju, dan Anda biasanya memiliki gagasan yang cukup baik tentang frekuensi sinyal Anda sehingga pendekatan semacam ini tidak diperlukan.
  • Sehubungan dengan detektor zero-crossing itu sendiri, Anda dapat menambahkan beberapa histeresis ke proses. Ini akan mencegah generasi penyeberangan terukur ekstra di sekitar persimpangan instan yang benar. Menambahkan histeresis ke detektor mungkin terlihat seperti ini:

    if ((state == POSITIVE) && (sample[i - 1] > -T) && (sample[i] < -T))
    {
        // handle negative zero-crossing
        state = NEGATIVE;
    }
    else if ((state == NEGATIVE) && (sample[i - 1] < T) && (sample[i] > T))
    {
        // handle positive zero-crossing
        state = POSITIVE;
    }
    

    Secara efektif, Anda menambahkan beberapa status ke detektor zero-crossing Anda. Jika Anda yakin sinyal input memiliki nilai positif, Anda mengharuskan sinyal turun di bawah nilai ambang batas yang dipilih -Tuntuk mendeklarasikan penyilangan nol nyata. Demikian juga, Anda mensyaratkan bahwa sinyal naik kembali di atas ambang pintu Tuntuk menyatakan bahwa sinyal telah berosilasi kembali ke positif lagi.

    Anda dapat memilih ambang batas untuk menjadi apa pun yang Anda inginkan, tetapi untuk sinyal seimbang seperti sinusoid, masuk akal untuk membuatnya menjadi simetris tentang nol. Pendekatan ini dapat membantu memberikan Anda hasil yang tampak lebih bersih, tetapi itu akan menambah waktu tunda karena Anda benar-benar mengukur batas ambang bukan nol alih-alih nol persimpangan.

Seperti yang disarankan pichenettes dalam jawabannya, loop fase-terkunci kemungkinan besar adalah cara terbaik untuk pergi, karena PLL melakukan apa yang Anda coba lakukan. Singkatnya, Anda menjalankan generator gelombang persegi yang berjalan secara paralel dengan input sinusoid. PLL melakukan pengukuran fase berkala pada sinusoid, kemudian menyaring aliran pengukuran untuk mengarahkan frekuensi sesaat dari generator gelombang persegi. Pada titik tertentu, loop akan (semoga) mengunci, pada titik mana gelombang kuadrat harus dikunci dalam frekuensi dan fase dengan sinusoid input (tentu saja dengan sejumlah kesalahan, tentu saja; tidak ada teknik yang sempurna).

Jason R
sumber
Apakah itu pemicu Schmitt?
Davorin
Memang, Anda bisa mengatakan bahwa itu adalah versi perangkat lunak pemicu Schmitt . Karakteristik yang menentukan dari pemicu Schmitt adalah bahwa itu adalah pembanding dengan histeresis
Jason R
Untuk menghindari tidak mendeteksi transisi, sertakan dalam salah satu dari kedua kondisi tersebut juga ambang T. Berarti bukannya && (sample[i - 1] > -T) && (sample[i] < -T)), gunakan && (sample[i - 1] >= -T) && (sample[i] < -T)). Ini perlu diterapkan untuk keduanya ifdan else ifpernyataan.
marc
2

Saya memiliki pengalaman yang baik dengan metode yang sangat sederhana untuk menemukan tanda perubahan sinyal pada waktu:

  1. a = diff (sign (signal))! = 0 # ini mendeteksi perubahan tanda
  2. kandidat = kali [a] # ini semua adalah poin kandidat, termasuk penyeberangan palsu
  3. menemukan kumpulan poin dalam kandidat
  4. rata-rata / rata-rata setiap cluster, ini adalah perubahan tanda Anda

  5. lakukan korelasi dengan fungsi langkah pada poin yang diprediksi oleh 4

  6. paskan kurva dengan hasil korelasi dan cari puncaknya

Dalam kasus saya 5 dan 6 tidak meningkatkan ketepatan metode ini. Anda dapat menggoyang-goyangkan sinyal Anda dengan noise dan melihat apakah itu membantu.

Dan
sumber
2

Saya tahu pertanyaan ini agak lama, tetapi saya harus menerapkan zero crossing baru-baru ini. Saya menerapkan cara yang disarankan Dan dan agak senang dengan hasilnya. Inilah kode python saya, jika ada yang tertarik. Saya tidak benar-benar seorang programmer yang elegan, tolong ikut saya.

import numpy as np
import matplotlib.pyplot as plt
from itertools import cycle

fig = plt.figure()
ax = fig.add_subplot(111)

sample_time = 0.01
sample_freq = 1/sample_time

# a-priori knowledge of frequency, in this case 1Hz, make target_voltage variable to use as trigger?
target_freq = 1
target_voltage = 0

time = np.arange(0.0, 5.0, 0.01)
data = np.cos(2*np.pi*time)
noise = np.random.normal(0,0.2, len(data))
data = data + noise


line, = ax.plot(time, data, lw=2)

candidates = [] #indizes of candidates (values better?)
for i in range(0, len(data)-1):
    if data[i] < target_voltage and data[i+1] > target_voltage:
        #positive crossing
        candidates.append(time[i])
    elif data[i] > target_voltage and data[i+1] < target_voltage:
        #negative crossing
        candidates.append(time[i])

ax.plot(candidates, np.ones(len(candidates)) * target_voltage, 'rx')
print('candidates: ' + str(candidates))

#group candidates by threshhold
groups = [[]]
time_thresh = target_freq / 8;
group_idx = 0;

for i in range(0, len(candidates)-1):
    if(candidates[i+1] - candidates[i] < time_thresh):
        groups[group_idx].append(candidates[i])
        if i == (len(candidates) - 2):
            # special case for last candidate
            # in this case last candidate belongs to the present group
            groups[group_idx].append(candidates[i+1])
    else:
        groups[group_idx].append(candidates[i])
        groups.append([])
        group_idx = group_idx + 1
        if i == (len(candidates) - 2):
            # special case for last candidate
            # in this case last candidate belongs to the next group
            groups[group_idx].append(candidates[i+1])



cycol = cycle('bgcmk')
for i in range(0, len(groups)):
    for j in range(0, len(groups[i])):
        print('group' + str(i) + ' candidate nr ' + str(j) + ' value: ' + str(groups[i][j]))
    ax.plot(groups[i], np.ones(len(groups[i])) * target_voltage, color=next(cycol), marker='o',  markersize=4)


#determine zero_crosses from groups
zero_crosses = []

for i in range(0, len(groups)):
    group_median = groups[i][0] + ((groups[i][-1] - groups [i][0])/2)
    print('group median: ' + str(group_median))
    #find index that best matches time-vector
    idx = np.argmin(np.abs(time - group_median))
    print('index of timestamp: ' + str(idx))
    zero_crosses.append(time[idx])


#plot zero crosses
ax.plot(zero_crosses, np.ones(len(zero_crosses)) * target_voltage, 'bx', markersize=10) 
plt.show()

Harap Catatan: kode saya tidak mendeteksi tanda dan menggunakan sedikit pengetahuan a-priori tentang frekuensi target untuk menentukan ambang waktu. Ambang ini digunakan untuk mengelompokkan penyeberangan berganda (titik-titik warna berbeda dalam gambar) dari mana median yang paling dekat dengan kelompok median dipilih (persilangan biru pada gambar).

Gelombang sinus berisik dengan tanda silang nol

Stefan Schallmeiner
sumber