JH7UBCブログ

アマチュア無線 電子工作 家庭菜園など趣味のブログです

Raspberry Pi Pico MicroPython OZ1JHM type CW decoder

2022-02-03 21:59:50 | Raspberry Pi Pico
 昨年末から取り組んでいた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デコーダができました。スクリプトの改良で和文もデコードできるようになります。

最新の画像もっと見る

コメントを投稿