之前寫的LoRaWAN GW NS代碼中提到了已經基于Twisted實現了GW/NS代碼。其中NS代碼設計起來比較簡單。因為許多Linux系統(tǒng)中并沒有Twisted,甚至CPython也是比較低的版本。所以必須基于MicroPython的多線程版本backport到低版本CPython中。
兼容性
MicroPython是基于CPython 3.4的,而舊版本CPython大多都是2.7的,甚至更低。一種做法是直接交叉編譯MicroPython到這些SBC中,另外就是乖乖滴重新寫代碼了。
主要的兼容性問題在于Timer和time兩個模塊。
Timer
MicroPython將Timer歸為machine下的模塊,因為它的定時器是物理定時器,采用定時中斷驅動。同時也正是這個原因,ISR代碼大多采用lambda來寫。
CPython中,Timer歸為threading的子類。本質上是線程,所以CPython下循環(huán)定時器代碼反而比MicroPython要繁瑣。移植后的CPython版本,成了1+3四個線程的程序:主程序+UDP接收+兩個定時器線程。
time
因為MicroPython來自CPython 3.4,所以有些高精度的計時方法,如tick_cpu()/tick_us()/sleep_ms(),其實這些也都是MicroPython特有方法,CPython 3.4里不是這些方法。目前我暫時以time.time()的浮點數來替代,理論上精確到0.1us。其實能夠實現1ms就不錯了。
LoRaWAN下發(fā)窗口其實是有時間精度要求的。在LoRaWANPktFwd協(xié)議中有三種方式計時:
- 立即下發(fā);
- 時間戳下發(fā);
- GPS時間戳下發(fā)。
如果計時有問題,會返回以下錯誤報告:
| Value | Definition |
|---|---|
| NONE | Packet has been programmed for downlink |
| TOO_LATE | Rejected because it was already too late to program this packet for downlink |
| TOO_EARLY | Rejected because downlink packet timestamp is too much in advance |
| COLLISION_PACKET | Rejected because there was already a packet programmed in requested timeframe |
| COLLISION_BEACON | Rejected because there was already a beacon planned in requested timeframe |
| TX_FREQ | Rejected because requested frequency is not supported by TX RF chain |
| TX_POWER | Rejected because requested power is not supported by gateway |
| GPS_UNLOCKED | Rejected because GPS is unlocked, so GPS timestamp cannot be used |
問題來了,在許多廣域應用中,需要借助GPS授時,這當然沒有疑問。立即下發(fā)也沒有問題。反倒是沒有嚴格要求的情況下,一般通過NTP進行授時,能夠精確到ms么?好像不能。局域網內1ms,公網一般在100ms左右。如果終端設備沒有授時裝置,則需要網關不斷地Beacon推送時間戳。加上TOF時間,誤差真的還蠻大的。
更新(20180930)
基于CPython的多線程設計已經在Ubuntu和OpenWRT中調試完畢,現在只需要對接LoRaWAN USB dongle的HCI接口即可。改HCI接口參考Bluetooth SIG HCI而來,主要做了減法,簡化了設計。
祝大家國慶快樂!