昨年末から取り組んでいたArduinoで動作するOZ1JHM CW decoderのプログラムをRaspberry Pi Picoに移植する作業が一応完了しました。
このCW解読器の心臓部であるGeortzel アルゴリズムによるCWトーン検出部は、前の記事のテストでうまく動作させることができましたが、Raspberry Pi PicoのADCの分解能は16bitですので、ArduinoのADCの分解能10bitに合わせてADC0の読み取り値を右に6bitシフトしています。CW信号の短点と長点を判定するプログラムはOZ1JHMのまま使いましたが、モールス符号のデコードは、以前micro:bitで試作したモールス符号解読器と同じアルゴリズムを使いました。
モールス信号は、スタートビットを1、短点を0、長点を1としてコード化しています。
例えば、Aは、・ーですから、スタートビットを先頭にして101となります。Bは、ー・・・ですから11000、Cはー・ー・ですから11010となります。各文字、記号をコード化し、小さい順にならべて、Oubunというリストを作り、受信したモールス符号をコード化しリストから対応した文字を取り出します。今回は、欧文のみのデコードとしました。
解読した文字は、I2Cインターフェース付きのLCD2004に表示します。最初ライブラリを利用してみたのですが、表示速度が遅いので、以前にテストしたI2C LCD1602用のスクリプトをLCD2004に対応させて使いました。
回路図です。CW信号は、GPIO26に加え、ADC0を使います。LCD2004は今回はレベル変換しないで、直接PicoにI2C接続しています。
スクリプトです。ちょっと長いのですが、掲載します。
-------------------------------------------------------------------------------------------
from machine import Pin,I2C,ADC
import utime
import math
i2c=I2C(1,sda=Pin(14),scl=Pin(15),freq=400000)
led=Pin(25,Pin.OUT)
a=ADC(0)
#character list
Oubun = ['*','*','E','T','I','A','N','M','S','U','R','W','D',
'K','G','O','H','V','F','*','L','*','P','J','B','X',
'C','Y','Z','Q','*','*','5','4','*','3','*','*','*',
'2','*','*','*','*','*','*','*','1','6','*','/','*',
'*','*','(','*','7','*','*','*','8','*','9','0']
Mcode=1
#LCD2004関係定義
LCD_addr=0x27
LCD_EN=0x04 #LCD Enable
LCD_BL=0x08 #Back Light
CMD=0x00 #command mode
CHR=0x01 #character mode
LINE1=0x80 #Line1 top address
LINE2=0xC0 #Line2 top address
LINE3=0x94 #Line3 top address
LINE4=0xD4 #Line4 top address
buf=bytearray(2)
colums=20
rows=4
lcdindex=0
line1=['','','','','','','','','','','','','','','','','','','','']
line2=['','','','','','','','','','','','','','','','','','','','']
#LCD2004表示関係関数
def LCD_write(bits,mode):
#High 4bits
data=(bits & 0xF0)|mode
buf[0]=data|LCD_EN|LCD_BL
buf[1]=data|LCD_BL
i2c.writeto(LCD_addr,buf)
utime.sleep_us(100)#wait 100us
#Low 4bits
data=((bits<<4)&0xF0)|mode
buf[0]=data|LCD_EN|LCD_BL
buf[1]=data|LCD_BL
i2c.writeto(LCD_addr,buf)
utime.sleep_us(100)#wait 100us
def LCD_init():
LCD_write(0x33,CMD)#8bit mode 0x03を2回送る
LCD_write(0x32,CMD)#8bit mode,4bit mode 0x02を送る
LCD_write(0x06,CMD)#Entry modeセット
LCD_write(0x0C,CMD)#表示ON,カーソルOFF,カーソル点滅OFF
LCD_write(0x28,CMD)#2桁表示,7ドットモード
LCD_write(0x01,CMD)#Display clear
utime.sleep_ms(2)#waite 2ms
def LCD_cursor(x,y):
if y==0:
LCD_write(LINE1+x,CMD)
if y==1:
LCD_write(LINE2+x,CMD)
if y==2:
LCD_write(LINE3+x,CMD)
if y==3:
LCD_write(LINE4+x,CMD)
def LCD_print(str):
for c in str:
LCD_write(ord(c),CHR)
def printchar(c):
global lcdindex
if lcdindex > colums-1:
lcdindex=0
if rows==4:
for i in range(colums):
LCD_cursor(i,rows-3)
LCD_print(line2[i])
line2[i]=line1[i]
for i in range(colums):
LCD_cursor(i,rows-2)
LCD_print(line1[i])
LCD_cursor(i,rows-1)
LCD_print(" ")
line1[lcdindex]=c
LCD_cursor(lcdindex,rows-1)
LCD_print(c)
lcdindex = lcdindex+1
#CW速度表示更新関数
def updateinfolinelcd():
global wpm
wpmtxt=str(wpm)
if wpm<10:
LCD_cursor(8,0)
LCD_print("0")
LCD_cursor(9,0)
LCD_print(wpmtxt)
LCD_cursor(10,0)
LCD_print(" WPM ")
else:
LCD_cursor(8,0)
LCD_print(wpmtxt)
LCD_cursor(10,0)
LCD_print(" WPM ")
#モールス符号解読と表示関数
def decode():
global Mcode,Oubun
if Mcode >1 and Mcode < 64:
c=Oubun[Mcode]
printchar(c)
elif Mcode==76:
printchar("?")
elif Mcode==85:
printchar(".")
elif Mcode==90:
printchar("@")
elif Mcode==97:
printchar("-")
elif Mcode==109:
printchar(")")
elif Mcode==115:
printchar(",")
elif Mcode==120:
printchar(":")
Mcode=1
#setup
LCD_init()
#Goertzel algprithm関係定義
Q1=0.0
Q2=0.0
sampling_freq=8928.0
target_freq=558.0
n=48.0
testData=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
Pi=3.14159265
magnitude=100.0
magnitudelimit=100
magnitudelimit_low=100
realstate=0#LOW
realstatebefore=0#LOW
#Basic goertzel Calcuration
k=int(0.5+(n*target_freq)/sampling_freq)
omega=(2.0*Pi*k)/n
sine=math.sin(omega)
cosine=math.cos(omega)
coeff=2.0*cosine
#noise blanker関係定義
nbtime=6 #ms
starttimehigh=0
highduration=0
lasthighduration=0
hightimesavg=0
lowtimesavg=0
starttimelow=0
lowduration=0
laststarttime=0
stop=0
wpm=0
filteredstate=0#LOW
filteredstatebefore=0#LOW
#main loop
while True:
for i in range(n):
testData[i]=a.read_u16()>>6
utime.sleep_us(60)
for i in range(n):
Q0=coeff*Q1-Q2+testData[i]
Q2=Q1
Q1=Q0
magnitudeSquared=(Q1*Q1)+(Q2*Q2)-Q1*Q2*coeff
magnitude=math.sqrt(magnitudeSquared)
Q1=0.0
Q2=0.0
#set the magnitudelimit automatic
if magnitude > magnitudelimit_low:
magnitudelimit = magnitudelimit + ((magnitude - magnitudelimit)/6)
if magnitudelimit < magnitudelimit_low:
magnitudelimit = magnitudelimit_low
#check for the magnitude
if magnitude > magnitudelimit*0.6:
realstate=1#HIGH
else:
realstate=0#LOW
#noise blanker
if realstate != realstatebefore:
laststarttime = utime.ticks_ms()
if (utime.ticks_ms()-laststarttime) > nbtime:
if realstate != filteredstate:
filteredstate = realstate
#duration high and low
if filteredstate != filteredstatebefore:
if filteredstate == 1:#HIGH
starttimehigh = utime.ticks_ms()
lowduration = utime.ticks_ms() - starttimelow
if filteredstate ==0:#LOW
starttimelow = utime.ticks_ms()
highduration = utime.ticks_ms() - starttimehigh
if highduration < (2*hightimesavg) or hightimesavg == 0:
hightimesavg = (highduration + hightimesavg + hightimesavg)/3
if highduration > (5*hightimesavg):
hightimesavg = highduration + hightimesavg
#check baud dot dash
if filteredstate != filteredstatebefore:
stop = 0#LOW
if filteredstate == 0:#LOW
if highduration < (hightimesavg*2) and highduration > (hightimesavg*0.6):
Mcode =Mcode<< 1
if highduration > (hightimesavg*2) and highduration < (hightimesavg*6):
Mcode =(Mcode<<1) + 1
wpm = int((wpm + (1200/((highduration)/3)))/2 + 0.5)
if filteredstate == 1:#HIGH
lacktime = 1.0
if wpm > 25:
lacktime = 1.0
if wpm > 30:
lacktime = 1.2
if wpm > 35:
lacktime = 1.5
if lowduration > hightimesavg*(2*lacktime): #and lowduration < hightimesavg*(5*lacktime):#letter space
decode()
if lowduration >= hightimesavg*(5*lacktime):#word space
# decode()
printchar(" ")
#no more letters
if utime.ticks_ms() - starttimelow > (highduration*6) and stop == 0:
decode()
stop = 1
#LED表示
if filteredstate == 1:
led.value(1)
else:
led.value(0)
#the end of main loop clean up
updateinfolinelcd()
realstatebefore = realstate
tasthighduration = highduration
filteredstatebefore = filteredstate
-------------------------------------------------------------------------------------------
動作テストをCW練習ソフトCWTW-Proを使って行いました。
CWTW-Proの設定画面です。周波数は、587HzとOZ1JHM CW decoderのCWトーン検出の中心周波数に近い値としました。
CWTW-Proから速度50字/分=10WPMのモールス符号を送出して、デコードしてみます。アルファベットに数字と記号を含めて送出してみました。
ブレッドボードとデコード結果です。
速度は、10WPMと妥当な値を表示しました。デコードは、最初の一文字はうまくデコードできませんでしたが、2文字目からは、OKです。ただし、BTや'(アポストロフィ)、"(ダブルクォーテーション)はデコードできません。
一応Raspberry Pi PicoでCWデコーダができました。スクリプトの改良で和文もデコードできるようになります。
※コメント投稿者のブログIDはブログ作成者のみに通知されます