Last active
February 19, 2023 08:01
-
-
Save nyatla/f15e6ceeefa06f0460720f44b7090f89 to your computer and use it in GitHub Desktop.
TBSKmodemをMicroPythonで動くようにしたもの。GP2から8/16kHz矩形波を出して、文字列を送信する。送信した文字列はここのライブデモで受信できるはずhttps://nyatla.jp/tbskmodem/ 簡易変調なので通信品質はご容赦ください。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" 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() | |
typingなどが使えなかったのでMicropython仕様に修正。コンパイルは通った。
L86がsleepだったので修正
5cm程度の距離で通信できた。
マジックナンバーを更新
16kHzでも動作するようになった。
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
84行目がおかしいから直した
elapsed = INTERVAL_US-time.ticks_diff(last,now)