Skip to content

Instantly share code, notes, and snippets.

@nyatla
Last active February 19, 2023 08:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nyatla/f15e6ceeefa06f0460720f44b7090f89 to your computer and use it in GitHub Desktop.
Save nyatla/f15e6ceeefa06f0460720f44b7090f89 to your computer and use it in GitHub Desktop.
TBSKmodemをMicroPythonで動くようにしたもの。GP2から8/16kHz矩形波を出して、文字列を送信する。送信した文字列はここのライブデモで受信できるはずhttps://nyatla.jp/tbskmodem/ 簡易変調なので通信品質はご容赦ください。
""" Copyright 2023 nyatla
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Pure MicrPythonな近接音響データ送信サンプルコードです。
RP2040の GP2にブザーかスピーカーをつないでね。
念のためダイオードでクリッピングしてください。
TBSKmodemの受信側は16000Hz(160bps)又は8000Hz(bps)を選択してください。
https://nyatla.jp/tbskmodem/
"""
from array import array
from machine import Pin
from utime import ticks_diff,ticks_us,ticks_add
import gc
import random
class BitArray:#(Iterator[int]):
""" ビットを格納するイテレータ
"""
def __init__(self,buf:array=None,size:int=None):
self.buf=buf if buf is not None else array("B",[0]*((size+7)//8))
self.size=size if size is not None else len(self.buf)*8
self._c=0
def __getitem__(self,i:int):
return (self.buf[i//8]>>(7-i%8)) &0x01
def __setitem__(self,i:int,v:int):
n=(v&0x01)<<(7-i%8)
self.buf[i//8]= self.buf[i//8]&(~n) if n==0 else self.buf[i//8]|n
def __iter__(self):
return self
@micropython.native
def __next__(self)->int:
c=self._c
if c>=self.size:
raise StopIteration()
r=self.buf[c//8]>>(7-c%8)
self._c=c+1
return r& 0x01
class PnBits(BitArray):
""" 長さがsizeのPNトーンを生成します。
"""
def __init__(self,size:int,seed:int=122)->"PnBits":
random.seed(seed)
a=array("B",[random.randint(0,255) for _ in range((size+7)//8)])
super().__init__(a,size)
class RawBits(BitArray):
""" bit配列(List[int])からBitArrayを生成します。
"""
def __init__(self,l)->"BitArray":
a=array("B",[0]*((len(l)+7)//8))
for i in range(len(l)):
a[i//8]=a[i//8]|((l[i]&0x01)<<(7-i%8))
super().__init__(array("B",a),len(l))
class DataBits(BitArray):
""" arrayからDataBitsを生成します。
"""
def __init__(self,src:array)->"BitArray":
bs=BitArray(src)
super().__init__(None,len(src)*8+1)
# [0] is initial bit
a=self.buf
lastbit=0
for i in range(bs.size):
if bs[i]==0:
lastbit=(lastbit+1)%2
a[(i+1)//8]=a[(i+1)//8]|(lastbit<<(7-(i+1)%8))
class TbskMicroModulator:
"""TBSK変調した矩形波パターンを生成します。
"""
def __init__(self,preamble_size:int=4,symbol_ticks:int=100):
self._preamble_size=preamble_size
b=[1]*preamble_size
c=[i%2 for i in range(preamble_size)]
d=[(1+c[-1])%2,(1+c[-1])%2,c[-1],]
self._preamble=RawBits([0,1]+b+[1]+c+d)
self._tone=PnBits(symbol_ticks)
@micropython.native
def modulete(self,src:array):#->Generator[int,None,None]:
tb=self._tone.buf
tl=self._tone.size
data=DataBits(src)
#@micropython.native
def G(p:array):
iter1=iter(p)
iter2=iter(data)
for i in range(tl):
yield i%3
for i in iter1:
for j in range(tl):
yield (i ^ (tb[j//8]>>(7-j%8))) #int
for i in iter2:
for j in range(tl):
yield (i ^ (tb[j//8]>>(7-j%8))) #int
for i in range(tl): #suffix
yield i%3
return G(self._preamble,)
@micropython.native
def gpioOut(bits,carrier:int,pin:Pin):
INTERVAL_US=(1000000//carrier) #8kHz period setting
INTERVAL_DC=0 if 1000000%carrier==0 else (int)(carrier/(1000000%carrier))
target :int= ticks_us()
starget:int=target
c:int=0
start_1=ticks_us()
for i in bits:
target:int=ticks_add(target,INTERVAL_US)
while True:
k:int=ticks_diff(target,ticks_us())
if k>12:
continue
elif k<-12:
target=target-k
if INTERVAL_DC!=0 and c%INTERVAL_DC==0:
target=target+1
pin.value(i) #125us毎に呼び出されるのを期待している。
break
c=c+1
print("Average symbol time:",ticks_diff(ticks_us(),start_1)/c,"us")# timing
tmm=TbskMicroModulator()
bits=tmm.modulete(array("B",b"Hello TBSKmodem from Micro Python."))
gc.collect()
gc.disable()
gpioOut(bits,16000,Pin(2, Pin.OUT, Pin.PULL_DOWN))
gc.enable()
@nyatla
Copy link
Author

nyatla commented Feb 13, 2023

84行目がおかしいから直した
elapsed = INTERVAL_US-time.ticks_diff(last,now)

@nyatla
Copy link
Author

nyatla commented Feb 13, 2023

typingなどが使えなかったのでMicropython仕様に修正。コンパイルは通った。
L86がsleepだったので修正

@nyatla
Copy link
Author

nyatla commented Feb 15, 2023

5cm程度の距離で通信できた。
マジックナンバーを更新

@nyatla
Copy link
Author

nyatla commented Feb 15, 2023

16kHzでも動作するようになった。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment