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
| Alan | Değer |
|---|---|
| Kullanıldığı yer | MP-6 |
| Açıklama | Kaydedilen IQ dosyasını okuyan bağımsız betik. radioconda Prompt'ta çalıştırılır. |
# 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
| Alan | Değer |
|---|---|
| Kullanıldığı yer | MP-10, MP-11, MP-13 |
| Açıklama | Sinyal Listesi Python Block kodu. GRC'de Python Block → Open in Editor → tamamen değiştir. |
"""
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
| Alan | Değer |
|---|---|
| Kullanıldığı yer | MP-11, MP-13 |
| Açıklama | ZMQ ile GNU Radio'ya bağlanan PyQt5 + pyqtgraph arayüzü. radioconda Prompt'ta çalıştırılır. |
"""
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
| Alan | Değer |
|---|---|
| Kullanıldığı yer | MP-13 |
| Açıklama | Tetikli Kayıt Python Block kodu. |
"""
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.
| Blok | Ne yapar? | Kategori | Projeler |
|---|---|---|---|
| Options | Projenin başlığı, arayüz türü (QT GUI / No GUI) ve çalışma modu | Ayar | Tümü |
| Variable | Birden çok blokta kullanılan sabit değer | Ayar | Tümü |
| Import | Python modülü ekler (import math) | Ayar | 4, 7, 9 |
| QT GUI Range | Kaydırma çubuğu olan değişken | Arayüz | 4, 5, 7, 8, 9, 12 |
| QT GUI Chooser | Açılır listeli değişken | Arayüz | 7, 12 |
| QT GUI Check Box | Aç/kapa değişkeni | Arayüz | 13 |
| Signal Source | Sinüs, kosinüs, kare vb. dalga üretir | Kaynak | 1, 2, 3, 4, 5, 6, 7, 12 |
| Noise Source | Gaussian gürültü üretir | Kaynak | 3, 4, 5, 6, 7, 12 |
| Random Source | Rastgele tam sayılar üretir | Kaynak | 12 |
| File Source | Dosyadan örnek okur | Kaynak | 6 |
| PlutoSDR Source | ADALM-Pluto'dan IQ alır | Kaynak / donanım | 8, 9, 10, 11, 12, 13 |
| Throttle | Donanımsız akışta hızı gerçek zamana sabitler | Akış kontrolü | 1, 2, 3, 4, 5, 6, 12 |
| Head | İlk N örnekten sonra akışı durdurur | Akış kontrolü | 6 |
| Add | Sinyalleri toplar | Matematik | 3, 4, 5, 6, 7, 12 |
| Subtract | İki akışı çıkarır | Matematik | 9 |
| Multiply | Sinyalleri çarpar | Matematik | 6, 7, 12 |
| Multiply Const | Sabitle çarpar (ses seviyesi vb.) | Matematik | 7, 8, 12 |
| Add Const | Sabit ekler | Matematik | 7, 9 |
| Log10 | n·log10(x) + k; dB'ye çevirir | Matematik | 4, 9, 10, 11, 12, 13 |
| Complex to Mag | Genlik |x| (zarf) | Tip dönüşümü | 7, 12 |
| Complex to Mag^2 | Güç |x|² | Tip dönüşümü | 4, 9, 10, 11, 12, 13 |
| Float To Complex | İki float'tan complex | Tip dönüşümü | 6, 7 |
| Stream to Vector | Akışı N'lik paketlere toplar | Tip dönüşümü | 4, 9, 10, 11, 12, 13 |
| Vector to Stream | Paketi tek tek değerlere açar | Tip dönüşümü | 4, 9 |
| Keep 1 in N | Her N öğeden birini tutar | Hız değişimi | 9, 11, 13 |
| Repeat | Her öğeyi N kez tekrarlar | Hız değişimi | 9 |
| Chunks to Symbols | Sayıyı constellation noktasına çevirir | Dijital | 12 |
| FFT | Zaman → frekans | DSP | 4, 9, 10, 11, 12, 13 |
| Low Pass Filter | Kesim frekansının üstünü siler | Filtre | 5 |
| Frequency Xlating FIR Filter | Kaydırma + filtre + decimation | Filtre | 5, 7, 8, 12, 13 |
| Single Pole IIR Filter | Üstel ortalama | Filtre | 9, 10, 11, 12, 13 |
| Moving Average | Kayan ortalama | Filtre | 9, 12 |
| Threshold | Histerezisli eşik (0/1) | Karar | 4, 9, 12 |
| Selector | Girişlerden birini seçip çıkışa verir | Yönlendirme | 7 |
| Frequency Mod | FM modülatör | Modülasyon | 7 |
| Quadrature Demod | FM demodülatör | Modülasyon | 7 |
| WBFM Receive | Ticari FM radyo alıcısı (demod + de-emphasis + decimation) | Modülasyon | 8, 9, 13 |
| File Sink | Örnekleri dosyaya yazar | Hedef | 6 |
| Audio Sink | Ses kartına gönderir | Hedef / donanım | 7, 8, 9, 13 |
| QT GUI Time Sink | Zaman ekranı (tetiklemeli) | Hedef / arayüz | 1, 2, 3, 7, 12 |
| QT GUI Frequency Sink | Hazır spektrum ekranı | Hedef / arayüz | 1, 2, 3, 4, 5, 6, 7, 8, 9, 12 |
| QT GUI Waterfall Sink | Zamanla spektrum (renkli) | Hedef / arayüz | 6, 7, 8, 9, 12 |
| QT GUI Vector Sink | Hazır vektörü (spektrum, tespit) çizer | Hedef / arayüz | 4, 9 |
| QT GUI Number Sink | Tek bir sayıyı gösterir | Hedef / arayüz | 9 |
| QT GUI Constellation Sink | I/Q noktalarını çizer | Hedef / arayüz | 12 |
| ZMQ PUB Sink | Akışı ağa yayınlar | Hedef / ağ | 11, 13 |
| ZMQ PUB Message Sink | Mesajları ağa yayınlar | Hedef / ağ | 11, 13 |
| Python Block | Kendi Python kodunla blok | Özel | 10, 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.
| MP | Hata | Belirti | Çözüm |
|---|---|---|---|
| 4 | Num Items ile Vec Length'i karıştırmak | Tip uyuşmazlığı | Num Items = fft_len, Vec Length = 1 |
| 4 | Bloğun çıkışını kendi girişine bağlamak | Hata | Bağlantıyı sil |
| 4 | Throttle eksik | İşlemci %100 | Throttle ekle |
| 4 | Eşiği normalize edilmemiş genliğe göre vermek | Çıkış 1'de takılı | dB + normalizasyon |
| 4 | FFT sonrası Frequency Sink kullanmak | Anlamsız grafik | Vector Sink |
| 5 | Çıkış portu boşta | Kırmızı başlık | Bağla |
| 5 | Taps'te geçiş genişliği yazım hatası (5000) | Aliasing | 500 |
| 6 | GRC'nin ürettiği .py'ye kod eklemek | Kod silinir | Ayrı betik |
| 7 | Taşıyıcı frekansı varsayılan bırakmak | Alıcı gürültü duyar | 10000 |
| 8 | Pluto int alanlarına float değişken | Kırmızı alan | int(...) |
| 8 | RF Bandwidth boş | Assertion hatası | int(samp_rate) |
| 8 | samp_rate = 1.96e6 | Ses takılır | 1.92e6 |
| 8 | Kanal ekranını yanlış kaynağa bağlamak | Yanlış etiketli grafik | Xlating çıkışı |