Atalay YamanGNU Radio
EN
Ekler

Ekler: kodlar, blok dizini, hatalar

Aşağıdaki kodlar test edilmiştir (GNU Radio 3.10, simüle edilmiş FM istasyonlarıyla). Sitede kod bloğu olarak, kopyalama düğmesiyle gösterilmelidir.

Ek A — oku_kayit.py

AlanDeğer
Kullanıldığı yerMP-6
AçıklamaKaydedilen IQ dosyasını okuyan bağımsız betik. radioconda Prompt'ta çalıştırılır.
oku_kayit.py
# MP-6 Bonus: Kaydedilen IQ dosyasini Python ile okuma
# Bu dosya GNU Radio'dan bagimsiz, tek basina calisan bir Python betigidir.
# Calistirma: radioconda Prompt'u ac ve su komutu yaz:
#   python "D:\Ato\Saykan-Teknofest\GNU_Radio_Test\MP-6\oku_kayit.py"

import numpy as np

DOSYA = r"D:\Ato\Saykan-Teknofest\GNU_Radio_Test\MP-6\kayit_32ksps.cfile"
SAMP_RATE = 32000  # dosyanin icinde yazmaz, bunu biz hatirlamak zorundayiz

# 1) Dosyayi oku: her ornek = 1 complex64 (4 bayt I + 4 bayt Q)
x = np.fromfile(DOSYA, dtype=np.complex64)

print("Toplam ornek sayisi :", len(x))
print("Kayit suresi (sn)   :", len(x) / SAMP_RATE)
print("Ilk 5 ornek         :", x[:5])
print("Ilk 5 ornegin genligi:", np.abs(x[:5]))

# 2) Her 0.5 saniyelik dilimin ortalama gucu (burst'u gormek icin)
dilim = SAMP_RATE // 2
print("\nZaman (sn)   Ortalama guc")
for i in range(0, len(x), dilim):
    parca = x[i:i + dilim]
    guc = np.mean(np.abs(parca) ** 2)
    print(f"{i / SAMP_RATE:5.1f}        {guc:.3f}")

Ek B — sinyal_listesi.py

AlanDeğer
Kullanıldığı yerMP-10, MP-11, MP-13
AçıklamaSinyal Listesi Python Block kodu. GRC'de Python Block → Open in Editor → tamamen değiştir.
sinyal_listesi.py
"""
Sinyal Listesi (MP-10)
Girdi : fft_len uzunlugunda dB spektrum vektoru (Log10 cikisi)
Cikti : 'sinyaller' mesaj portu -> JSON metin (frekans, bant genisligi, guc)
"""
import json
import numpy as np
import pmt
from gnuradio import gr


class blk(gr.sync_block):
    def __init__(self, fft_len=1024, samp_rate=1.92e6, lo_freq=99.75e6,
                 marj_db=10.0, bosluk_bin=3, min_bin=2, rapor_her=1000):
        gr.sync_block.__init__(
            self,
            name='Sinyal Listesi',
            in_sig=[(np.float32, fft_len)],
            out_sig=None,
        )
        # Ayni isimle saklanan parametreler GRC'den canli guncellenebilir
        self.fft_len = fft_len
        self.samp_rate = samp_rate
        self.lo_freq = lo_freq
        self.marj_db = marj_db
        self.bosluk_bin = bosluk_bin
        self.min_bin = min_bin
        self.rapor_her = rapor_her
        self.sayac = 0
        self.message_port_register_out(pmt.intern('sinyaller'))

    def _gruplar(self, maske):
        """True olan binleri ardisik gruplara ayirir; aradaki bosluk
        bosluk_bin veya daha azsa iki grubu tek sinyal sayar."""
        idx = np.flatnonzero(maske)
        if idx.size == 0:
            return []
        gruplar = []
        bas = son = idx[0]
        for i in idx[1:]:
            if i - son <= self.bosluk_bin + 1:
                son = i
            else:
                gruplar.append((bas, son))
                bas = son = i
        gruplar.append((bas, son))
        return [g for g in gruplar if g[1] - g[0] + 1 >= self.min_bin]

    def work(self, input_items, output_items):
        for spek in input_items[0]:
            self.sayac += 1
            if self.sayac % self.rapor_her:
                continue
            taban = float(np.median(spek))
            maske = spek > taban + self.marj_db
            bin_hz = self.samp_rate / self.fft_len
            baslangic = self.lo_freq - self.samp_rate / 2
            liste = []
            for bas, son in self._gruplar(maske):
                parca = spek[bas:son + 1]
                tepe = bas + int(np.argmax(parca))
                liste.append({
                    'merkez_mhz': round((baslangic + (bas + son + 1) / 2 * bin_hz) / 1e6, 4),
                    'tepe_mhz': round((baslangic + (tepe + 0.5) * bin_hz) / 1e6, 4),
                    'bant_khz': round((son - bas + 1) * bin_hz / 1e3, 1),
                    'guc_db': round(float(parca.max()), 1),
                    'snr_db': round(float(parca.max()) - taban, 1),
                })
            print(f'--- Taban {taban:.1f} dB | {len(liste)} sinyal ---')
            for s in liste:
                print(f"  {s['merkez_mhz']:9.4f} MHz  {s['bant_khz']:7.1f} kHz  "
                      f"{s['guc_db']:6.1f} dB  (SNR {s['snr_db']:.1f})")
            mesaj = json.dumps({
                'lo_mhz': self.lo_freq / 1e6,
                'fs_mhz': self.samp_rate / 1e6,
                'taban_db': round(taban, 1),
                'sinyaller': liste,
            })
            self.message_port_pub(pmt.intern('sinyaller'), pmt.intern(mesaj))
        return len(input_items[0])

Ek C — es_konsol.py

AlanDeğer
Kullanıldığı yerMP-11, MP-13
AçıklamaZMQ ile GNU Radio'ya bağlanan PyQt5 + pyqtgraph arayüzü. radioconda Prompt'ta çalıştırılır.
es_konsol.py
"""
MP-11: Mini ES Konsolu (PyQt5 + pyqtgraph + ZMQ)
GNU Radio akis grafigi calisirken bu programi radioconda Prompt'tan calistirin:
    python es_konsol.py
Gerekirse once:  pip install pyqtgraph pyzmq
"""
import json
import sys

import numpy as np
import pyqtgraph as pg
import zmq
from PyQt5 import QtCore, QtWidgets

SPEKTRUM_ADRES = 'tcp://127.0.0.1:5555'   # ZMQ PUB Sink (float vektor)
LISTE_ADRES = 'tcp://127.0.0.1:5556'      # ZMQ PUB Message Sink (JSON)
FFT_LEN = 1024


def mesaji_coz(ham):
    """ZMQ PUB Message Sink, mesaji PMT formatinda gonderir. JSON metnini cikarir."""
    try:
        import pmt
        return pmt.symbol_to_string(pmt.deserialize_str(ham))
    except ImportError:
        # PMT sembolu: 1 bayt tip + 2 bayt uzunluk + metin
        return ham[3:].decode('utf-8')


class Konsol(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle('Mini ES Konsolu')
        self.resize(1100, 650)

        ctx = zmq.Context()
        self.sok_spek = ctx.socket(zmq.SUB)
        self.sok_spek.connect(SPEKTRUM_ADRES)
        self.sok_spek.setsockopt(zmq.SUBSCRIBE, b'')
        self.sok_liste = ctx.socket(zmq.SUB)
        self.sok_liste.connect(LISTE_ADRES)
        self.sok_liste.setsockopt(zmq.SUBSCRIBE, b'')

        self.lo_mhz, self.fs_mhz = 0.0, 1.0

        # Spektrum grafigi
        self.grafik = pg.PlotWidget(title='Spektrum')
        self.grafik.setLabel('bottom', 'Frekans', units='MHz')
        self.grafik.setLabel('left', 'Guc', units='dB')
        self.grafik.setYRange(-100, 0)
        self.grafik.showGrid(x=True, y=True, alpha=0.3)
        self.egri = self.grafik.plot(pen='y')
        self.taban_cizgi = pg.InfiniteLine(angle=0, pen=pg.mkPen('r', style=QtCore.Qt.DashLine))
        self.grafik.addItem(self.taban_cizgi)
        self.isaretler = []

        # Sinyal listesi tablosu
        self.tablo = QtWidgets.QTableWidget(0, 4)
        self.tablo.setHorizontalHeaderLabels(['Merkez (MHz)', 'Bant (kHz)', 'Guc (dB)', 'SNR (dB)'])
        self.tablo.horizontalHeader().setStretchLastSection(True)
        self.durum = QtWidgets.QLabel('Veri bekleniyor...')

        sag = QtWidgets.QVBoxLayout()
        sag.addWidget(QtWidgets.QLabel('Tespit edilen sinyaller'))
        sag.addWidget(self.tablo)
        sag.addWidget(self.durum)
        duzen = QtWidgets.QHBoxLayout(self)
        duzen.addWidget(self.grafik, 3)
        duzen.addLayout(sag, 2)

        self.zamanlayici = QtCore.QTimer(self)
        self.zamanlayici.timeout.connect(self.guncelle)
        self.zamanlayici.start(50)   # saniyede 20 kez kontrol

    def guncelle(self):
        # 1) Spektrum: kuyrukta ne varsa oku, en sonuncuyu ciz
        son = None
        while True:
            try:
                son = self.sok_spek.recv(zmq.NOBLOCK)
            except zmq.Again:
                break
        if son is not None:
            v = np.frombuffer(son, dtype=np.float32)
            if v.size >= FFT_LEN:
                spek = v[-FFT_LEN:]  # bir pakette birden fazla vektor olabilir
                x = self.lo_mhz - self.fs_mhz / 2 + np.arange(FFT_LEN) * self.fs_mhz / FFT_LEN
                self.egri.setData(x, spek)

        # 2) Sinyal listesi
        while True:
            try:
                ham = self.sok_liste.recv(zmq.NOBLOCK)
            except zmq.Again:
                break
            veri = json.loads(mesaji_coz(ham))
            self.lo_mhz, self.fs_mhz = veri['lo_mhz'], veri['fs_mhz']
            self.liste_goster(veri)

    def liste_goster(self, veri):
        sinyaller = veri['sinyaller']
        self.tablo.setRowCount(len(sinyaller))
        for satir, s in enumerate(sinyaller):
            for sutun, anahtar in enumerate(['merkez_mhz', 'bant_khz', 'guc_db', 'snr_db']):
                self.tablo.setItem(satir, sutun, QtWidgets.QTableWidgetItem(str(s[anahtar])))
        for i in self.isaretler:
            self.grafik.removeItem(i)
        self.isaretler = []
        for s in sinyaller:
            cizgi = pg.InfiniteLine(pos=s['merkez_mhz'], angle=90, pen=pg.mkPen('g', width=1))
            self.grafik.addItem(cizgi)
            self.isaretler.append(cizgi)
        self.taban_cizgi.setValue(veri['taban_db'])
        self.durum.setText(f"Taban: {veri['taban_db']} dB  |  {len(sinyaller)} sinyal  |  "
                           f"LO {self.lo_mhz:.3f} MHz")


if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    pencere = Konsol()
    pencere.show()
    sys.exit(app.exec_())

Ek D — tetikli_kayit.py

AlanDeğer
Kullanıldığı yerMP-13
AçıklamaTetikli Kayıt Python Block kodu.
tetikli_kayit.py
"""
Tetikli Kayit (MP-13)
Girdi 1: Pluto'dan gelen ham IQ akisi (complex)
Girdi 2: 'sinyaller' mesaj portu (Sinyal Listesi blogundan JSON)
Yeni bir sinyal 'onay' kez ust uste gorulunce, genis bant IQ'yu 'kayit_sn'
saniye boyunca .cfile olarak kaydeder ve yanina .json bilgi dosyasi yazar.
"""
import json
import os
import threading
import time

import numpy as np
import pmt
from gnuradio import gr


class blk(gr.sync_block):
    def __init__(self, samp_rate=1.92e6, lo_freq=99.75e6, klasor='kayitlar',
                 kayit_sn=0.5, onay=3, cozunurluk_mhz=0.05, kayit_acik=True):
        gr.sync_block.__init__(
            self,
            name='Tetikli Kayit',
            in_sig=[np.complex64],
            out_sig=None,
        )
        self.samp_rate = samp_rate
        self.lo_freq = lo_freq
        self.klasor = klasor
        self.kayit_sn = kayit_sn
        self.onay = onay
        self.cozunurluk_mhz = cozunurluk_mhz
        self.kayit_acik = kayit_acik

        self.sayaclar = {}      # frekans anahtari -> ust uste gorulme sayisi
        self.kaydedilenler = set()
        self.kuyruk = []        # kaydedilmeyi bekleyen sinyaller
        self.aktif = None       # o an yazilan kayit
        self.kilit = threading.Lock()

        self.message_port_register_in(pmt.intern('sinyaller'))
        self.set_msg_handler(pmt.intern('sinyaller'), self.mesaj_geldi)

    def anahtar(self, mhz):
        return round(round(mhz / self.cozunurluk_mhz) * self.cozunurluk_mhz, 4)

    def mesaj_geldi(self, msg):
        veri = json.loads(pmt.symbol_to_string(msg))
        simdi = {self.anahtar(s['merkez_mhz']): s for s in veri['sinyaller']}
        with self.kilit:
            # Gorunmeyenlerin sayaci sifirlanir, gorunenler artar
            self.sayaclar = {k: self.sayaclar.get(k, 0) + 1 for k in simdi}
            for k, s in simdi.items():
                if self.sayaclar[k] == self.onay and k not in self.kaydedilenler:
                    self.kaydedilenler.add(k)
                    self.kuyruk.append(s)

    def kayit_baslat(self, s):
        os.makedirs(self.klasor, exist_ok=True)
        zaman = time.strftime('%Y%m%d_%H%M%S')
        ad = f"{s['merkez_mhz']:.3f}MHz_{int(self.samp_rate)}sps_{zaman}"
        yol = os.path.join(self.klasor, ad)
        bilgi = dict(s, lo_mhz=self.lo_freq / 1e6, samp_rate=self.samp_rate,
                     tip='complex64 (cfile)', zaman=zaman)
        with open(yol + '.json', 'w', encoding='utf-8') as f:
            json.dump(bilgi, f, indent=2, ensure_ascii=False)
        self.aktif = {'dosya': open(yol + '.cfile', 'wb'),
                      'kalan': int(self.kayit_sn * self.samp_rate), 'ad': ad}
        print(f'[KAYIT] basladi: {ad}')

    def work(self, input_items, output_items):
        x = input_items[0]
        if self.aktif is None and self.kayit_acik:
            with self.kilit:
                s = self.kuyruk.pop(0) if self.kuyruk else None
            if s is not None:
                self.kayit_baslat(s)
        if self.aktif is not None:
            n = min(len(x), self.aktif['kalan'])
            x[:n].astype(np.complex64).tofile(self.aktif['dosya'])
            self.aktif['kalan'] -= n
            if self.aktif['kalan'] <= 0:
                self.aktif['dosya'].close()
                print(f"[KAYIT] bitti  : {self.aktif['ad']}")
                self.aktif = None
        return len(x)

Ek E — Blok dizini

Seride kullanılan tüm GRC blokları, tek cümlelik açıklamaları ve geçtikleri projeler. Sitede blok adına tıklayınca ilgili MP'ye gidilebilir.

BlokNe yapar?KategoriProjeler
OptionsProjenin başlığı, arayüz türü (QT GUI / No GUI) ve çalışma moduAyarTümü
VariableBirden çok blokta kullanılan sabit değerAyarTümü
ImportPython modülü ekler (import math)Ayar4, 7, 9
QT GUI RangeKaydırma çubuğu olan değişkenArayüz4, 5, 7, 8, 9, 12
QT GUI ChooserAçılır listeli değişkenArayüz7, 12
QT GUI Check BoxAç/kapa değişkeniArayüz13
Signal SourceSinüs, kosinüs, kare vb. dalga üretirKaynak1, 2, 3, 4, 5, 6, 7, 12
Noise SourceGaussian gürültü üretirKaynak3, 4, 5, 6, 7, 12
Random SourceRastgele tam sayılar üretirKaynak12
File SourceDosyadan örnek okurKaynak6
PlutoSDR SourceADALM-Pluto'dan IQ alırKaynak / donanım8, 9, 10, 11, 12, 13
ThrottleDonanımsız akışta hızı gerçek zamana sabitlerAkış kontrolü1, 2, 3, 4, 5, 6, 12
Headİlk N örnekten sonra akışı durdururAkış kontrolü6
AddSinyalleri toplarMatematik3, 4, 5, 6, 7, 12
Subtractİki akışı çıkarırMatematik9
MultiplySinyalleri çarparMatematik6, 7, 12
Multiply ConstSabitle çarpar (ses seviyesi vb.)Matematik7, 8, 12
Add ConstSabit eklerMatematik7, 9
Log10n·log10(x) + k; dB'ye çevirirMatematik4, 9, 10, 11, 12, 13
Complex to MagGenlik |x| (zarf)Tip dönüşümü7, 12
Complex to Mag^2Güç |x|²Tip dönüşümü4, 9, 10, 11, 12, 13
Float To Complexİki float'tan complexTip dönüşümü6, 7
Stream to VectorAkışı N'lik paketlere toplarTip dönüşümü4, 9, 10, 11, 12, 13
Vector to StreamPaketi tek tek değerlere açarTip dönüşümü4, 9
Keep 1 in NHer N öğeden birini tutarHız değişimi9, 11, 13
RepeatHer öğeyi N kez tekrarlarHız değişimi9
Chunks to SymbolsSayıyı constellation noktasına çevirirDijital12
FFTZaman → frekansDSP4, 9, 10, 11, 12, 13
Low Pass FilterKesim frekansının üstünü silerFiltre5
Frequency Xlating FIR FilterKaydırma + filtre + decimationFiltre5, 7, 8, 12, 13
Single Pole IIR FilterÜstel ortalamaFiltre9, 10, 11, 12, 13
Moving AverageKayan ortalamaFiltre9, 12
ThresholdHisterezisli eşik (0/1)Karar4, 9, 12
SelectorGirişlerden birini seçip çıkışa verirYönlendirme7
Frequency ModFM modülatörModülasyon7
Quadrature DemodFM demodülatörModülasyon7
WBFM ReceiveTicari FM radyo alıcısı (demod + de-emphasis + decimation)Modülasyon8, 9, 13
File SinkÖrnekleri dosyaya yazarHedef6
Audio SinkSes kartına gönderirHedef / donanım7, 8, 9, 13
QT GUI Time SinkZaman ekranı (tetiklemeli)Hedef / arayüz1, 2, 3, 7, 12
QT GUI Frequency SinkHazır spektrum ekranıHedef / arayüz1, 2, 3, 4, 5, 6, 7, 8, 9, 12
QT GUI Waterfall SinkZamanla spektrum (renkli)Hedef / arayüz6, 7, 8, 9, 12
QT GUI Vector SinkHazır vektörü (spektrum, tespit) çizerHedef / arayüz4, 9
QT GUI Number SinkTek bir sayıyı gösterirHedef / arayüz9
QT GUI Constellation SinkI/Q noktalarını çizerHedef / arayüz12
ZMQ PUB SinkAkışı ağa yayınlarHedef / ağ11, 13
ZMQ PUB Message SinkMesajları ağa yayınlarHedef / ağ11, 13
Python BlockKendi Python kodunla blokÖzel10, 13

Ek F — Seri boyunca yaşanan hatalar (özet)

Bu tablo, serinin gerçek kurulumu sırasında yapılan hataların derlemesidir. Sitede "Sık hatalar" sayfası olarak kullanılabilir.

MPHataBelirtiÇözüm
4Num Items ile Vec Length'i karıştırmakTip uyuşmazlığıNum Items = fft_len, Vec Length = 1
4Bloğun çıkışını kendi girişine bağlamakHataBağlantıyı sil
4Throttle eksikİşlemci %100Throttle ekle
4Eşiği normalize edilmemiş genliğe göre vermekÇıkış 1'de takılıdB + normalizasyon
4FFT sonrası Frequency Sink kullanmakAnlamsız grafikVector Sink
5Çıkış portu boştaKırmızı başlıkBağla
5Taps'te geçiş genişliği yazım hatası (5000)Aliasing500
6GRC'nin ürettiği .py'ye kod eklemekKod silinirAyrı betik
7Taşıyıcı frekansı varsayılan bırakmakAlıcı gürültü duyar10000
8Pluto int alanlarına float değişkenKırmızı alanint(...)
8RF Bandwidth boşAssertion hatasıint(samp_rate)
8samp_rate = 1.96e6Ses takılır1.92e6
8Kanal ekranını yanlış kaynağa bağlamakYanlış etiketli grafikXlating çıkışı