
前回、遅ればせながら入手のソフトウエア無線受信機RTL-SDRをPythonで読み取りできました。順当な線ではPythonで後段処理、ということになりますが、このところやっていたScilab様をお見限り、というのは寂しい限り。どうせリアルタイム処理は狙わないのでRTL-SDRのデータをScilabへもお裾分けっと。
※「手習ひデジタル信号処理」投稿順 Indexはこちら
案ずるより産むがやすしということで、長年逡巡していたRTL-SDRも使ってみたらば今のところ順調です。pyrtlsdrモジュールを使えば、PythonでIQ信号を取り出せるので、Pythonには各種の実績あるモジュールあり、それらを駆使しての後段処理に夢が広がるというわけです。
しかし度々お世話になってきたScilabを止めてしまうのも寂しいということで、今回はPythonからScilabへRTL-SDRのIQ信号をファイルで送る(勿論リアルタイム性はまったく無。バッチです。)ルートを開拓。
python側のコード
python側のコードは以下のようです。RTL-SDRを駆動して所定の分量の信号を取得し、それをScilab可読のフォーマットのテキストファイルに落とすもの。
$ python pyrtlsdr2sci.py -h
上記のように -h オプションつければ使用方法が表示されます。といっても引数に値をセットして呼び出すだけ。全てにデフォルト値が与えられているので、何も引数にセットしなければ、Tokyo FM(80MHz)を中心とした2MHz幅くらいの領域のIQ信号を262144点取得し、toScilab.csv というファイル名にてカレントフォルダに書き出します。
なお、以下のコードは暫定版っす。当然バグあり、エラーチェックなど無のもの。
#!/usr/bin/python
# coding: utf-8
### @file pyrtlsdr2sci.py
### @brief save RTL-SDR IQ signal for scilab
###
### @author: Joseph Halfmoon
### @date: June 6, 2023
"""
Save RTL-SDR IQ signal for scilab v0.1
input
=====
--FS sample rate (default=2.048[MHz])
--FC center_freq (defalut=80[MHz]
--N number of samples (defalut=256*1024)
--FNAME File name to save (defalut=toScilab.csv)
-V VERSION
"""
import argparse
import csv
import numpy as np
import sys
from rtlsdr import RtlSdr
versionSTR = "pyrtlsdr2sci.py v0.1"
def parse2float(st):
"""parse string to float
Returns at Fail: None
"""
try:
work = float(st)
except:
return None
return work
def chkVal(arg, dval):
vl = parse2float(arg)
if vl is None:
nLen = len(arg)
if nLen > 1:
bdy = arg[0:nLen-1]
lastChar = arg[nLen-1]
vl = parse2float(bdy)
if vl is None:
vl = dval
else:
lastChar = "0"
return (vl, lastChar)
def freqIN(arg, dval):
prefixes = 'mkMG'
vl, lastChar = chkVal(arg, dval)
pfx = prefixes.find(lastChar)
if pfx == 0:
ex = 1e-3
elif pfx == 1:
ex = 1e3
elif pfx == 2:
ex = 1e6
elif pfx == 3:
ex = 1e9
else:
ex = 1
return vl * ex
def parse2int(st):
"""parse string to int
Returns at Fail: None
"""
try:
work = int(st)
except:
return None
return work
class rtlsdrBuffer(object):
"""rtlsdr buffer class
read & save RTL-SDR IQ signal
Fs : sampling Freq[Hz]
Fc : center Freq[Hz]
"""
def __init__(self, Fs, Fc, N):
self.sdr = RtlSdr()
self.sdr.sample_rate = Fs
self.sdr.center_freq = Fc
self.sdr.freq_correction = 60
self.sdr.gain = 'auto'
self.N = N
self.buffer = np.zeros(self.N, dtype=np.complex128)
def read(self):
self.buffer = self.sdr.read_samples(self.N)
def writeSCI(self, fnam):
self.fnam = fnam
np.savetxt(self.fnam, self.buffer, fmt='%.18e %+.18ej')
def main():
"""main program.
process options
"""
parser = argparse.ArgumentParser(description=versionSTR)
parser.add_argument('--FS', nargs=1, help='sample rate (default=2.048[MHz])')
parser.add_argument('--FC', nargs=1, help='center_freq (defalut=80[MHz])')
parser.add_argument('--N', nargs=1, help='number of samples (defalut=256*1024)')
parser.add_argument('--FNAME', nargs=1, help='File name to save (defalut=toScilab.csv)')
parser.add_argument('-V', dest='VERSION', help='Show Version, then exit', action='store_true', default=False)
args = parser.parse_args()
if args.VERSION:
print( versionSTR )
sys.exit(0)
Fs = 2.048e6
if args.FS is not None:
Fs = freqIN(args.FS[0], Fs)
Fc = 80e6
if args.FC is not None:
Fc = freqIN(args.FC[0], Fc)
N = 256*1024
if args.N is not None:
tmpN = parse2int(args.N[0])
if tmpN is not None:
N = tmpN
FNAME = "toScilab.csv"
if args.FNAME is not None:
FNAME = args.FNAME[0]
sdr = rtlsdrBuffer(Fs, Fc, N)
sdr.read()
sdr.writeSCI(FNAME)
sys.exit(0)
if __name__ == "__main__":
main()
上記コードを書くにあたって、numpyのndarray(複素数が詰まっている)のファイル出力の方法が分かってなかったので、以下のページにお世話になりました。
以下のようなコマンドラインで信号データが、–FNAMEで指定のファイルに出力されます。
出力ファイルとScilabでの読み込み
出力されたcsvファイルをテキストエディタで開くとこんな感じです。1行毎に複素数ひとつが書き出されてます。ポイントは虚数単位が “j” と表現されているところですかね。Python(numpy)側のデフォルトは 電気電子風に j みたいです。
一方、Scilabではキホンは i を使っている(コンソールからの手入力は%iだがファイル上では i で良い)のですが、試したところファイル上では j でも虚数単位と解釈して読み込んでくれました。読み込んだ後は勿論 i に統一されてます。心が広いなScilab。
Scilba上で、Python出力のファイルを読み込む処理シーケンスが以下に。
よめとるみたいやね。
