東京電機大学 工学部 情報通信工学科 ネットワークシステム研究室
指導教員 : 坂本 直志
15EC551 猪子 亮
WSN(Wireless Sensor Network)は無線通信可能なセンサを多数配置し、データを収集することのできるような無線ネットワークである。様々な環境下での利用が想定されるIoT(Internet of Things)で必要となる技術である。WSNで用いる通信規格にはWSNを主目的として策定されたIEEE802.15.4の他にBluetoothなどがあり、目的別に採用する通信規格を選択できる。
IEEE802.15.4はメッシュネットワークを構築可能である。メッシュネットワークは図 1に示すようにノード同士が相互に接続されており、宛先ノードまでの経路が複数用意されているネットワークである。メッシュネットワークではノードがトラフィックを転送するホップと呼ばれる動作を行う。必要に応じてホップを繰り返すことにより隣接ノード以外のノードに対しても通信が可能である。以降宛先に到達するまでにホップされる回数をHOP数と表現する。
図1 メッシュネットワークの例
IEEE802.15.4を採用したWSN規格にZigBeeがある。Zigbeeではメッシュネットワークのルーティングはダイナミックなので、特定のリンクに障害が発生した場合でも代替経路を用いて通信を継続できる特徴がある。さらに、ZigBeeの送信方法には非同期送信と同期送信の2種類がある。非同期送信では送信したパケットの状態(宛先に到達したか否か、ビットエラーが起きたかなど) および宛先からの応答の有無に関わらず送信処理が実行される。一方で同期送信では送信したパケットを相手が正常に受信したことを確認できるかタイムアウトまで処理がブロックされる。そのため非同期通信では損失により送信パケットの一部が不達になる可能性があるのに対し、同期送信ではタイムアウトにならない限り再送信を行うため確実に相手に到達する。
WSNでは距離やHOP数、送信方法が伝送速度へ大きく影響を与える。しかし、メッシュネットワークにおけるネットワークの特性は明らかになっていない。そこで本研究では通信規格にIEEE802.15.4を採用したZigBeeに着目し、メッシュネットワークの性能評価を行う。性能評価では2種類の送信手法についてHOP数が伝送速度へ与える影響を明らかにする。その後、先の結果をもとに効率的なファイル転送手法を提案する。
本稿では2章に実験に用いられている技術の説明を行なっている。3章では非同期送信と同期送信の実験と評価を行った後に、効率的なファイル転送手法を提案している。4章では全体のまとめと今後の展望を述べ、付録に本文には載せていないデータやプログラム掲載している。
ZigBeeはWSNを主目的とする近距離無線通信規格である。製品毎の仕様により若干の違いはあるが、ノード間の距離が数十メートルでの使用が適している。転送速度が非常に低速(数十Kbps) な代わりに低消費電力、メッシュネットワークが構築可能などの特徴がある。2004年に通信規格ZigBee (ZigBee フューチャーセット) が発表され、その後2007年にZigBeePRO (ZigBee PRO フューチャーセット)が発表され、現在の主流となっている。
図 2[1]に示すように無線規格の物理層、MAC層にはIEEE802.15.4を採用している。IEEE802.15.4では868MHz帯、915MHz帯、2.4GHz帯を使用することが出来るが日本においてはISMバンドとして使える2.4GHz仕様のみが使用可能である。実際は前述の角周波数さらに細かく区切った複数のチャネル(伝送路) が用意されている。チャネルには連番で論理番号が振られており、2.4GHz帯では11-26CHが割り当てられている。
図2 ZigBeeモデル
ZigBeeにはコーディネータ、ルータ、エンドデバイスの3つの論理的なデバイスタイプがある。コーディネータ、ルータ共に他のデバイスの接続を受け入れることができ、メッシュネットワークを構築可能である。特にコーディネータはネットワークに必ず1台だけ存在する必要であり、PAN(Personal Area Network)の形成、チャネルマネージメントなどのネットワークの管理、ゲートウェイなどの役割がある。一方エンドデバイスは他のデバイスを受け入れることができず、コーディネータまたはルータの下で動作する。エンドデバイスはセンサなどを取り付け間欠動作で用いることが多く、データの送受信には必ず親デバイスを経由する必要がある。よって、エンドデバイスはスリープ機能を用いて間欠動作させることができるが、コーディネータとルータは常時起動している必要がある。また、ZigBeeでは同一のPAN内でのみ通信が可能である。図 3はZigBee Mesh Kit User Guide[1]にある構成例である。
図3 ZigBeeネットワークの例
XBeeはDigi社が販売するZigBeeモジュールである。モジュール、開発ボード共に同シリーズから出ており、日本においても比較的入手、開発しやすいZigBeeモジュールとなっている。表 1、図 4に本研究に使用する機材を示す。図 4では開発ボードにモジュールが装着されている時の様子である。なお、XBeeモジュールのファームウェアは4060である。
表1 使用するZigBeeデバイスとその開発ボード
機材 | メーカー | モデル |
---|---|---|
モジュール | Digi | XBee ZigBee S2CTH |
開発ボード | Digi | XBee THT Grove Development Board |
図4 XBeeモジュールと開発ボード
当開発ボードは中心部に同社製XBeeモジュールを搭載することが可能である。電源はシリアルポートに接続するMicroUSBコネクタ、または2ピンのコネクタから給電することができる。開発ボードには各種プッシュボタンとインジケータが搭載されている。
XCTUはDigi社が提供するマルチプラットフォームの設定、テストツールである。各パラメータの設定やネットワーク構成をGUIで確認することが出来る。測定に本ソフトウェアを用いることはないが測定前準備として電波状況の確認やネットワークトポロジーを確認するのに有用である。また、後述するXBee Java Libraryを持ちいればJavaアプリケーションから各パラメータを設定することができる。XCTUのパラメータ設定画面を図 5に示す。
図5 XCTUパラメータ設定画面
XBeeには主にATモードとAPIモードの2つの動作モードがある。本実験ではAPIモード(escaped) を用いる。以下にそれぞれの動作モードの特徴を示す。
上記からも分かるようにATモードは1対1の通信に適しているが通信の自由度が低く、APIモードは1対nの通信に適しており通信の自由度が高くなっている。APIモードにはnon-escapedとescapedの2種類の動作モードがある。APIモードではAPIフレームを用いるために区切り文字を使用する。フレーム中に意図しない区切り文字が現れるとフレームを正しく解釈することができないため、そのような文字を特別な文字に置き換えるものがescapedモードである。APIフレームの構造を図 6に示す。
図6 API Frame Stracture
Digi社ではXBeeのJavaでの開発を容易にするためにXBee Java Libraryを公開している。導入方法については文献[2] に詳細を譲る。
基本的なメッセージのやり取りは下記の手順で行われる。
スループットの計測実験には宛先までのHOP数が異なるネットワークを用いる。さらにHOP数による影響を考察するために各ノード間でメッシュネットワークを組まないように配置する。使用するノードの情報を表 2、表 3、実験の構成図を図 7に示す。表 3におけるIDはPAN ID、JVはコーディネータが動作するチャネルを検出するか否か、CEはコーディネータで動作するか否か、BDはシリアル転送レート、APはAPIモードで動作するか否かの設定である。文献[4]よりBDは動作保証されている最大の値を用いた。図 7ではメッシュネットワークを組んでいないため、隣り合ってノード間のみ通信可能である。また、図中では最大HOP時のノード構成を表しており、ROUTER4を送信ノード、COORDを受信ノードする。これより少ないHOP数の実験では送信ノードをROUTER3、ROUTER2の順に変えていく。
表2 各ノードの概要
NI | デバイスタイプ | COORDまでのHOP数 |
---|---|---|
COORD | Coordinator | - |
ROUTER | Router | 1 |
ROUTER2 | Router | 2 |
ROUTER3 | Router | 3 |
ROUTER4 | Router | 4 |
表3 デバイスタイプごとのパラメータ
デバイスタイプ | ID | JV | CE | BD | AP |
Coordinator | 2018 | 0 | 1 | 115,200 | API with escaping |
Router | 2018 | 1 | 0 | 115,200 | API with escaping |
図7 構成図
測定場所には東京電機大学千住キャンパス1号館12階~14階を用いる。当館は全14階からなる鉄筋コンクリート製の建造物である。測定は人の往来および電子機器の使用が少ないと考えれる深夜及び早朝に行った。
データの送信はルータからコーディネータ方向に行う。送信ノードと受信ノードにはPCを接続し実験ごとに用意した測定用のJavaアプリケーションを起動する。また、図 7に示したように中間ノードにはルータ(パラメータ設定済みのXBeeモジュール) をモバイルバッテリーで稼働させる。測定に用いる送信ノードと中間ノードを図 8、図 9に示す。受信ノードは送信ノードと構成は同様である。中間ノードの接続においてモバイルバッテリーに接続する際にUSBハブを介しているのはモバイルバッテリーの漏電防止機能により給電が停止するのを防ぐためである。
図8 送信ノードの構成
図9 中間ノードの構成
ノード配置を検討するにあたりXCTUのNetwork working modeを用いた。これはネットワークトポロジ及び各ノード間のRSSI(電波信号強度)を確認できるものであり、メッシュネットワークを組まずにRSSIが最大の80%以上となるような場所に各ノードを配置した。表 4に使用するPCの環境、各ノードの配置図と実際に配置した様子を図 10から図 15に示す。なお、XCTUは送信側PCで起動し、バージョンは6.4.1である。
表4 アプリケーションの動作環境
PC | 機種 | OS | 開発環境 | Project SDK |
送信側 | MacBookAir | macOS Mojave 10.14.2 | IntelliJ IDEA 2018.2.5 | 11.0.1 |
受信側 | Let’s note CF-RZ4 | Windows10 17134 | IntelliJ IDEA 2018.2.6 | 1.8.0_191 |
図10 各ノードの配置図
図11 COORDの配置
図12 ROUTERの配置
図13 ROUTER2の配置
図14 ROUTER3の配置
図15 ROUTER4の配置
本実験を行うにあたり付録A, Bのプログラムを作成した。送信側のプログラムには予めペイロードサイズ、送信レート、計測時間を定義しておき、それらをもとに計測中に送る総パケット数と送信間隔が決まる。プログラム実行後は始めにPAN IDやCE、JVなどのパラメータをルータに再度設定し、パラメータの誤設定が起こらないようにする。その後ルータが参加するネットワークから指定した宛先デバイスを検出できた場合計測を開始する。送信時にはThread.sleepメソッドを使い、先の送信間隔でsendDataメソッドが実行されるよう調整する。送信するデータは先のペイロードサイズで指定したサイズのランダムな文字列である。この時、最初と最後のパケットの送信時刻の差を送信時間t [s]とする。また、全てのパケットを送り終えたら終了シグナルを送信して終了する。
受信側のプログラムにおいても、送信側と同様にして各パラメータの設定を行う。その後リスナーを用いることによりパケットの受信記録を取る。この時の総受信パケット数をPr とする。
本実験の送信フレームにおけるペイロードサイズは50[byte]、ヘッダを含めたフレーム長は68[byte]である。また、計測時間は100秒とし、最大送信レートはsendDataメソッドを任意の間隔で呼び出した際に設定した間隔よりsendDataメソッドの実行時間が長くなった時とする。ヘッダを含むパケットサイズをPl [byte]とするとスループットTmは次式で計算する。
本実験では非同期送信において送信レートを上げていったときの受信レートの変化を0HOPから3HOPで調べ、HOP数がスループットへ与える影響を調べる。以下に手順を示す。
本実験開始時における各リンクのRSSI値を表 5に示す。各リンクは双方向リンクであるため、リンクの状態(RSSI)は各方向2通り存在する。表 5 では各リンクを始点と終点で対応させ、RSSI値示している。また、計測結果を図 16、図 17に示す。図 17は図 16における各HOP数での最大受信レートを両対数グラフ上にプロットし、最小二乗法を用いて直線近似を行なったものである。なお、縦軸を受信レート、横軸を始点から終点までの距離(0HOPだと1) としている。
表5 非同期送信計測開始時における各リンクのRSII値
図16 非同期送信における各HOP数のスループット
図17 各HOP数での最大受信レート(両対数表示)
式(1) において Pl およびt は固定であることから本実験におけるスループットの低下はパケットのロス率の増加(Pr の減少) を意味する。パケットのロスは以下の原因が考えられる。
図18 XBee内部でのデータフロー
図 18よりXBeeには4つのバッファ領域があり、各バッファの容量はHOP数に関係なく一定である。しかし、スループットはHOP数によって変化していることからスループットの低下の主な原因はバッファ溢れではないと考える。次に電波干渉だが、送信パケットと確認応答が衝突している可能性が考えられる。これはHOP数が増える増えると衝突が起こり得るリンクの数も増えるため、本実験におけるスループット低下の原因だと考える。
両対数グラフにおける直線は冪関数であることから、HOP数が増えるにつれ、この関数にしたがってスループットが低下していくと言える。この直線の傾きが冪指数、切片が係数となり、冪指数bは次式で求められる。
本実験を行うにあたり付録C, D記載したプログラムを用いたファイル転送アプリケーションを作成した。本アプリケーションでは送信したいデータがペイロードサイズを超える場合でも分割して送受信可能なため、任意のファイルを転送することが可能である。計測には図 19に示す56,033[byte]の画像ファイルを用いた。
図19 Bird.jpg
同期送信側のプログラムでは非同期送信プログラム同様に各パラメータの再設定を初めに行う。送信するファイルはbyte配列に読み込んだ後、分割してシーケンス番号(以下Seq) を付与して送信する。同期送信であるSendDataメソッドは待ち時間なしの繰り返し分の中に記述することにより、可能な限り最大転送レートで実行される。全パケットの送信が完了すると成功シグナルを送信して送信側プログラムを終了する。
受信側プログラムも送信側同様に初めにパラメータの再設定を行う。パケットが受信されるとSeqをキーとしてMapにデータを保存する。最初のパケットを受信してから最後のパケット(成功シグナル) を受信するまでを総受信時間t[s] としており、スループットは前項同様に式(1) を用いて得られる。その後は受信データをSeqをもとに組み立て、書き出しを行う。
非同期送信と結果を比較するために本提案手法においてもフレームの各フィールド長は前項と同じである。なお、Seqはペイロード50[byte]のうち先頭4[byte]を使用しており、同期送信のフレーム構造をに示す。また、同期送信のタイムアウトはデフォルト値2000[ms]を用いている。
図20 同期送信を用いる場合のフレーム構造
本実験では同期送信において受信レートの変化を0HOPから3HOPで調べ、HOP数がスループットへ与える影響を調べる。以下に実験手順を示す。
本実験開始時のRSSI値を表 6に示す。また測定結果を図 21に示す。
表6 同期送信計測開始時における各リンクRSII値
図21 同期送信におけるスループットとHOP数の関係
同期送信ではフレーム送信後にTransmit Status Frameと呼ばれる受信応答を受け取ってから次のフレームを送信するため、非同期送信に比べてスループットは大幅に落ちていることが分かる。3.2.1項と同様にしてスループットTとHOP数xの関係は次式で与えられる。
ここで冪指数が約-1/2であることに着目するとTは次式で表せる。
式(3)、(5)の冪指数を比べると非同期送信では同期送信に比べてHOP数による影響を受けやすいことが分かる。また、図 21に示したように、どのHOP数においても同期送信に比べて非同期送信の方が高いスループットであることが分かる。
前節において非同期送信と同期送信の特性を明らかにした。実際にXBeeをWSN等で運用する際に何らかのデータをシンクノード(各センサで得られたデータを集計するノード) に対して確実に送信したい場合は同期送信を用いる必要がある。しかし、同期送信では非同期送信に比べてスループットが低いという問題がある。そこで本稿では非同期送信と同期送信を組み合わせた手法を提案し、同期送信のみの場合と比較してスループットの向上を目指す。
提案手法では初めに非同期送信を用いて分割された全てのデータにSeqを付与して送信する。最後の送信フレームは同期送信を用いることにより受信側で受け取るべきパケットの総数、すなわちSeqの最大値が分かる。また、ファイルネームはシーケンス番号を-1とすることによりデータパケットと差別している。その後非同期終了シグナルを送信することにより非同期セッションが全て終えたことを受信側に通知する。受信側が非同期終了シグナルを受信すると受信できなかったパケットのSeqを送信側に同期通信で伝え、それを受信した送信側は再送信を同期送信で行う。いずれのセッションにおいても最後にシグナルを送り相手側へセッションの終わりを通知する。なお、通常の同期送信と性能を比較するために本提案手法においてもフレームの各フィールド長は3.2.2項と同様である。提案手法における送信者と受信者のやりとりを図 22、送信側、受信側のプログラムのフローチャート それぞれに示す。
図22 提案手法における送受信間のやり取り
図23 送信側フローチャート
図24 受信側フローチャート
提案手法における実験手順は3.2.2と同様である。
実験開始時における各リンクのRSSI値を表 7、非同期セッション時に各HOP数で用いた転送レートを表 8に示す。表 8に示す転送レートは本手法が最も効率よく働く場合の値である。3.2.2項と同様のファイル転送行なった結果を図 25に示す。
表4 アプリケーションの動作環境
HOP数 | 送信レート[kbps] |
0HOP | 21.00 |
1HOP | 15.06 |
2HOP | 14.13 |
3HOP | 11.39 |
図25 提案手法におけるスループットとHOP数の関係
図 25に示したように全てのHOP数において同期送信と比べてスループットを改善することができた。最大は0HOP時に82%、最低は3HOP時に9%の改善をすることができた。これよりHOP数が少ない場合には全同期送信に比べ提案手法の方が優れているがHOP数が多くなると然程効果は得られないことが分かった。その要因として図 26に示すようにHOP数が増えると非同期送信の送信レートを上げていった際のロス率の上昇が顕著になるからであると考える。
図26 非同期送信における各HOP数のロス率
本論文ではZigBeeデバイスにおける通信特性を測定し、スループット向上のための非同期送信と同期送信を組み合わせた送信手法を提案した。当実験環境においては宛先ノードまでが0~1HOPの際に有意な効果を得られた一方、それ以上のHOP数ではあまり効果を得られなかった。
各実験においては送信パケットのペイロードサイズを50[Byte] 固定で行なっていた。また同じ周波数帯の電波を使用する他の機器(主にWi-Fi)の影響を考慮することが出来なかった。よって、今後はペイロードサイズを変化させた場合、Wi-Fi 環境下におけるZigBeeの使用の二点が通信特性にどのような影響を与えるか調査していきたいと考えている。
本研究を進めるにあたり指導してくださった東京電機大学工学部情報通信工学科 坂本直志教授、実験への理解と多数の助言をしてくださった同ネットワークシステム研究室の皆様に心より感謝致します。
本稿で使用したアプリケーションのソースコード を以下のA~Dに示す。非同期送信のスループット測定にはA(送信側)およびB(受信側)を用いた。同期送信のスループット測定および提案手法はC(送信側)およびD(受信側)を用いた。同期送信と提案手法は同じプログラムで実装されているがプログラム内のSYNC定数によって切り替えることができる。
非同期送信のスループット測定時におけるプログラムの動作は以下の通りである。
同期送信のスループット測定時におけるプログラムはsendDataAsyncメソッドの代わりにsendDataメソッドを使う点を除いて基本的には非同期と同じである。提案手法におけるプログラムの送受信双方のリスナークラスに実装されているgetSeqメソッドとgetDataメソッドはそれぞれ受信データからSeqとpayload部分を取り出すメソッドである。受信側プログラムのリスナークラスに実装されているprintEndTimeはプログラムの実行時間等を表示するものであり、writeFileは受信したファイルを出力するメソッドである。以下にプログラムの動作を示す。
A) 非同期送信スループット測定送信側プログラム
/**
* Copyright 2017, Digi International Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at http://mozilla.org/MPL/2.0/.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
import com.digi.xbee.api.RemoteXBeeDevice;
import com.digi.xbee.api.XBeeDevice;
import com.digi.xbee.api.XBeeNetwork;
import com.digi.xbee.api.exceptions.TimeoutException;
import com.digi.xbee.api.exceptions.XBeeException;
import com.digi.xbee.api.utils.ByteUtils;
import com.digi.xbee.api.utils.HexUtils;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.security.SecureRandom;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
public class ThroughputSender {
// TODO Replace with the serial port where your sender module is connected to.
private static final String PORT = "/dev/tty.usbserial-AL021QYY";
// TODO Replace with the baud rate of your sender module.
private static final int BAUD_RATE = 115200;
//Parameters to be set on XBee device
private static final String PARAM_PAN_ID = "ID";
private static final byte[] PARAM_VALUE_PAN_ID = new byte[]{0x20, 0x18};
private static final String PARAM_CHANNEL_VERIFICATION = "JV";
private static final byte[] PARAM_VALUE_CHANNEL_VERIFICATION = new byte[]{0x01};
private static final String PARAM_JOIN_NOTIFICATION = "JN";
private static final byte[] PARAM_VALUE_JOIN_NOTIFICATION = new byte[]{0x0};
private static final String PARAM_DEVICE_OPTION = "DO";
private static final byte[] PARAM_VALUE_DEVICE_OPTION = new byte[]{0x00};
private static final String PARAM_DEVICE_CONTROLS = "DC";
private static final byte[] PARAM_VALUE_DEVICE_CONTROLS = new byte[]{0x00, 0x00};
private static final String PARAM_MAXIMUM_HOPS = "NH";
private static final byte[] PARAM_VALUE_MAXIMUM_HOPS = new byte[]{0x1E};
private static final String PARAM_BROADCAST_RADIUS = "BH";
private static final byte[] PARAM_VALUE_BROADCAST_RADIUS = new byte[]{0x00};
private static final String PARAM_MANY_TO_ONE = "AR";
private static final byte[] PARAM_VALUE_MANY_TO_ONE = new byte[]{(byte) 0xFF};
// Information on packets to be transmitted
// TODO Replace with the payload size
private static final int PAYLOAD_SIZE = 50; //[Byte] max 250, do not set 0
private static final int PACKET_LENGTH = PAYLOAD_SIZE + 18;
// TODO Replace with the seconds how long you want to measurement
private static final int MEAS_TIME = 100; //seconds
// TODO Replace with the transmit rate
private static final int TRANSMIT_RATE = 10000; //bps
private static final int REPEAT_TIMES = TRANSMIT_RATE * MEAS_TIME / (PACKET_LENGTH*8);
private static final double INTERVAL_TIME = (double) PACKET_LENGTH * 8 / (double) TRANSMIT_RATE;
// TODO Replace with the node identifier where you want to transmit (Destination)
private static final String REMOTE_NODE_IDENTIFIER = "COORD";
// Configuration of output log file
private static String FILE_NAME = TRANSMIT_RATE + "bps-send.log";
// TODO Replace with the directory where you want to output the log file
private static String FILE_PATH = System.getProperty("user.home") + "/Desktop/4HOP/";
// main
public static void main(String[] args) {
XBeeDevice myDevice = new XBeeDevice(PORT, BAUD_RATE);
setParam(myDevice);
transmit(myDevice);
}
public static void setParam(XBeeDevice myDevice){
try {
myDevice.open();
// Set parameters.
myDevice.setParameter(PARAM_PAN_ID, PARAM_VALUE_PAN_ID);
myDevice.setParameter(PARAM_CHANNEL_VERIFICATION, PARAM_VALUE_CHANNEL_VERIFICATION);
myDevice.setParameter(PARAM_JOIN_NOTIFICATION, PARAM_VALUE_JOIN_NOTIFICATION);
myDevice.setParameter(PARAM_DEVICE_OPTION, PARAM_VALUE_DEVICE_OPTION);
myDevice.setParameter(PARAM_DEVICE_CONTROLS, PARAM_VALUE_DEVICE_CONTROLS);
myDevice.setParameter(PARAM_MAXIMUM_HOPS, PARAM_VALUE_MAXIMUM_HOPS);
myDevice.setParameter(PARAM_BROADCAST_RADIUS, PARAM_VALUE_BROADCAST_RADIUS);
myDevice.setParameter(PARAM_MANY_TO_ONE, PARAM_VALUE_MANY_TO_ONE);
// Get parameters
byte[] paramValueID = myDevice.getParameter(PARAM_PAN_ID);
byte[] paramValueJV = myDevice.getParameter(PARAM_CHANNEL_VERIFICATION);
byte[] paramValueJN = myDevice.getParameter(PARAM_JOIN_NOTIFICATION);
byte[] paramValueDO = myDevice.getParameter(PARAM_DEVICE_OPTION);
byte[] paramValueDC = myDevice.getParameter(PARAM_DEVICE_CONTROLS);
byte[] paramValueNH = myDevice.getParameter(PARAM_MAXIMUM_HOPS);
byte[] paramValueBH = myDevice.getParameter(PARAM_BROADCAST_RADIUS);
byte[] paramValueAR = myDevice.getParameter(PARAM_MANY_TO_ONE);
// Compare the read parameter values with the values that were set.
if (ByteUtils.byteArrayToLong(paramValueID) != ByteUtils.byteArrayToLong(PARAM_VALUE_PAN_ID)) {
System.out.println("ID parameter was not set correctly.");
myDevice.close();
System.exit(1);
}
if (ByteUtils.byteArrayToInt(paramValueJV) != ByteUtils.byteArrayToLong(PARAM_VALUE_CHANNEL_VERIFICATION)) {
System.out.println("JV parameter was not set correctly.");
myDevice.close();
System.exit(1);
}
if (ByteUtils.byteArrayToInt(paramValueJN) != ByteUtils.byteArrayToLong(PARAM_VALUE_JOIN_NOTIFICATION)) {
System.out.println("JN parameter was not set correctly.");
myDevice.close();
System.exit(1);
}
if (ByteUtils.byteArrayToInt(paramValueDO) != ByteUtils.byteArrayToLong(PARAM_VALUE_DEVICE_OPTION)) {
System.out.println("DO parameter was not set correctly.");
myDevice.close();
System.exit(1);
}
if (ByteUtils.byteArrayToInt(paramValueDC) != ByteUtils.byteArrayToLong(PARAM_VALUE_DEVICE_CONTROLS)) {
System.out.println("DC parameter was not set correctly.");
myDevice.close();
System.exit(1);
}
if (ByteUtils.byteArrayToInt(paramValueNH) != ByteUtils.byteArrayToLong(PARAM_VALUE_MAXIMUM_HOPS)) {
System.out.println("NH parameter was not set correctly.");
myDevice.close();
System.exit(1);
}
if (ByteUtils.byteArrayToInt(paramValueBH) != ByteUtils.byteArrayToLong(PARAM_VALUE_BROADCAST_RADIUS)) {
System.out.println("BH parameter was not set correctly.");
myDevice.close();
System.exit(1);
}
if (ByteUtils.byteArrayToInt(paramValueAR) != ByteUtils.byteArrayToLong(PARAM_VALUE_MANY_TO_ONE)) {
System.out.println("AR parameter was not set correctly.");
myDevice.close();
System.exit(1);
}
System.out.println("All parameters were set correctly!");
myDevice.close();
} catch (TimeoutException e) {
e.printStackTrace();
myDevice.close();
System.exit(1);
} catch (XBeeException e) {
e.printStackTrace();
myDevice.close();
System.exit(1);
}
}
public static void transmit(XBeeDevice myDevice){
// List for output log
List<String> log = new ArrayList<>();
long prev = 0, now, simStart, simEnd;
//Set date format
Calendar today, sendTime;
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm:ss.SSS");
// Generate payload
byte[] data_to_send = new byte[PAYLOAD_SIZE];
SecureRandom sr = new SecureRandom();
sr.nextBytes(data_to_send);
// send this signal when this transmitting process is done
String end_signal = "-1";
String end_error_signal = "-2";
//Add log file this program information
today = Calendar.getInstance();
log.add(dateFormat.format(today.getTime()) + " " + timeFormat.format(today.getTime()) +
" destNI=" + REMOTE_NODE_IDENTIFIER + " payload size=" + PAYLOAD_SIZE +
" total length=" + PACKET_LENGTH + " sending bps=" + TRANSMIT_RATE);
try {
myDevice.open();
XBeeNetwork xbeeNetwork = myDevice.getNetwork();
RemoteXBeeDevice remoteDevice = xbeeNetwork.discoverDevice(REMOTE_NODE_IDENTIFIER);
//Contain OP and CH parameters
byte[] paramValueOP = myDevice.getParameter("OP");
byte[] paramValueCH = myDevice.getParameter("CH");
if (remoteDevice == null) {
System.out.println("Couldn't find the remote XBee device with '" + REMOTE_NODE_IDENTIFIER + "' Node Identifier.");
System.exit(1); }
//Show, add log file operating PAN ID and channel
StringBuilder netInfo = new StringBuilder("Operating PAN ID=");
System.out.print(netInfo.toString());
for (int i=0; i<paramValueOP.length; i++){
System.out.print(Integer.toHexString(paramValueOP[i]));
netInfo.append(Integer.toHexString(paramValueOP[i])); }
System.out.println(" Operating Channel=" + Integer.toHexString(paramValueCH[0]) + "\n");
netInfo.append(" Operating Channel=" + Integer.toHexString(paramValueCH[0]));
log.add(netInfo.toString());
//Show transmitting information
System.out.format("Sending data to %s >> %s | %s... ", remoteDevice.get64BitAddress(),
HexUtils.prettyHexString(HexUtils.byteArrayToHexString(data_to_send)),
new String(data_to_send));
System.out.println();
System.out.println("\nat " + timeFormat.format(Calendar.getInstance().getTime()) +
" start to send packet. packet size=" + PACKET_LENGTH + "byte transmit rate=" +
TRANSMIT_RATE + "bps interval=" + INTERVAL_TIME+ "s time required=" + MEAS_TIME + "s");
log.add("Simulation started at, " + timeFormat.format(Calendar.getInstance().getTime()));
log.add("");
simStart = System.nanoTime();
Thread.sleep((int)(INTERVAL_TIME * 1000));
//Transmit
for (int i=0; i<REPEAT_TIMES; i++) {
sendTime = Calendar.getInstance();
log.add(timeFormat.format(sendTime.getTime()) + "," + i + ",SENT,packet size " + PACKET_LENGTH + "byte");
now = System.nanoTime();
if (i != 0) {
if ((now - prev) <= INTERVAL_TIME * 1000000000) {
Thread.sleep((int) (((INTERVAL_TIME * 1000000000) - (now - prev)) / 1000000));
} else {
Thread.sleep(5000);
System.out.print("Transmit rate error");
myDevice.sendDataAsync(remoteDevice, end_error_signal.getBytes());
System.exit(1);
}
}
prev = System.nanoTime();
myDevice.sendDataAsync(remoteDevice, data_to_send);
}
simEnd = System.nanoTime();
Thread.sleep(5000);
myDevice.sendDataAsync(remoteDevice, end_signal.getBytes());
String statistics = "\n" + "Total send packet " + REPEAT_TIMES + "\nTotal send data size " + (PACKET_LENGTH*REPEAT_TIMES +
" byte\nTotal simulation time " + ((double)(simEnd-simStart)/1000000000));
System.out.println(statistics);
log.add(statistics);
Files.write(Paths.get(FILE_PATH, FILE_NAME), log, StandardOpenOption.CREATE);
System.out.println("Success");
} catch (XBeeException e) {
System.out.println("Error");
e.printStackTrace();
System.exit(1);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
myDevice.close();
}
}
}
B) 非同期送信スループット測定受信側プログラム
/**
* Copyright 2017, Digi International Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at http://mozilla.org/MPL/2.0/.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
import com.digi.xbee.api.XBeeDevice;
import com.digi.xbee.api.exceptions.TimeoutException;
import com.digi.xbee.api.exceptions.XBeeException;
import com.digi.xbee.api.utils.ByteUtils;
public class ThroughoutReceiver {
// TODO Replace with the serial port where your receiver module is connected.
private static final String PORT = "/dev/tty.usbserial-A10586WI";
// TODO Replace with the baud rate of you receiver module.
private static final int BAUD_RATE = 115200;
// TODO Replace with the file name what you want to name the log file
private static String FILE_NAME = "5000" + "bps-recv.log";
// TODO Replace with the directory where you want to output the log file
private static String FILE_PATH = System.getProperty("user.home") + "/Desktop/Test/";
//Parameters to be set on routers
private static final String PARAM_PAN_ID = "ID";
private static final byte[] PARAM_VALUE_PAN_ID = new byte[]{0x20, 0x18};
private static final String PARAM_CHANNEL_VERIFICATION = "JV";
private static final byte[] PARAM_VALUE_CHANNEL_VERIFICATION = new byte[]{0x00};
private static final String PARAM_JOIN_NOTIFICATION = "JN";
private static final byte[] PARAM_VALUE_JOIN_NOTIFICATION = new byte[]{0x0};
private static final String PARAM_COORDINATOR_ENABLE = "CE";
private static final byte[] PARAM_VALUE_COORDINATOR_ENABLE = new byte[]{0x1};
private static final String PARAM_DEVICE_OPTION = "DO";
private static final byte[] PARAM_VALUE_DEVICE_OPTION = new byte[]{0x00};
private static final String PARAM_DEVICE_CONTROLS = "DC";
private static final byte[] PARAM_VALUE_DEVICE_CONTROLS = new byte[]{0x00, 0x00};
private static final String PARAM_MAXIMUM_HOPS = "NH";
private static final byte[] PARAM_VALUE_MAXIMUM_HOPS = new byte[]{0x1E};
private static final String PARAM_BROADCAST_RADIUS = "BH";
private static final byte[] PARAM_VALUE_BROADCAST_RADIUS = new byte[]{0x00};
private static final String PARAM_MANY_TO_ONE = "AR";
private static final byte[] PARAM_VALUE_MANY_TO_ONE = new byte[]{(byte) 0xFF};
public static void main(String[] args) {
XBeeDevice myDevice = new XBeeDevice(PORT, BAUD_RATE);
setParam(myDevice);
try {
myDevice.open();
myDevice.addDataListener(new MyDataReceiveListener(FILE_NAME, FILE_PATH));
System.out.println("\n>> Waiting for data...");
} catch (XBeeException e) {
e.printStackTrace();
System.exit(1);
}
}
public static void setParam(XBeeDevice myDevice){
try {
myDevice.open();
// Set parameters.
myDevice.setParameter(PARAM_PAN_ID, PARAM_VALUE_PAN_ID);
myDevice.setParameter(PARAM_CHANNEL_VERIFICATION, PARAM_VALUE_CHANNEL_VERIFICATION);
myDevice.setParameter(PARAM_JOIN_NOTIFICATION, PARAM_VALUE_JOIN_NOTIFICATION);
myDevice.setParameter(PARAM_DEVICE_OPTION, PARAM_VALUE_DEVICE_OPTION);
myDevice.setParameter(PARAM_DEVICE_CONTROLS, PARAM_VALUE_DEVICE_CONTROLS);
myDevice.setParameter(PARAM_MAXIMUM_HOPS, PARAM_VALUE_MAXIMUM_HOPS);
myDevice.setParameter(PARAM_BROADCAST_RADIUS, PARAM_VALUE_BROADCAST_RADIUS);
myDevice.setParameter(PARAM_MANY_TO_ONE, PARAM_VALUE_MANY_TO_ONE);
// Get parameters
byte[] paramValueID = myDevice.getParameter(PARAM_PAN_ID);
byte[] paramValueJV = myDevice.getParameter(PARAM_CHANNEL_VERIFICATION);
byte[] paramValueJN = myDevice.getParameter(PARAM_JOIN_NOTIFICATION);
byte[] paramValueCE = myDevice.getParameter(PARAM_COORDINATOR_ENABLE);
byte[] paramValueDO = myDevice.getParameter(PARAM_DEVICE_OPTION);
byte[] paramValueDC = myDevice.getParameter(PARAM_DEVICE_CONTROLS);
byte[] paramValueNH = myDevice.getParameter(PARAM_MAXIMUM_HOPS);
byte[] paramValueBH = myDevice.getParameter(PARAM_BROADCAST_RADIUS);
byte[] paramValueAR = myDevice.getParameter(PARAM_MANY_TO_ONE);
// Compare the read parameter values with the values that were set.
if (ByteUtils.byteArrayToLong(paramValueID) != ByteUtils.byteArrayToLong(PARAM_VALUE_PAN_ID)) {
System.out.println("ID parameter was not set correctly.");
myDevice.close();
System.exit(1);
}
if (ByteUtils.byteArrayToInt(paramValueJV) != ByteUtils.byteArrayToLong(PARAM_VALUE_CHANNEL_VERIFICATION)) {
System.out.println("JV parameter was not set correctly.");
myDevice.close();
System.exit(1);
}
if (ByteUtils.byteArrayToInt(paramValueJN) != ByteUtils.byteArrayToLong(PARAM_VALUE_JOIN_NOTIFICATION)) {
System.out.println("JN parameter was not set correctly.");
myDevice.close();
System.exit(1);
}
if (ByteUtils.byteArrayToInt(paramValueCE) != ByteUtils.byteArrayToLong(PARAM_VALUE_COORDINATOR_ENABLE)) {
System.out.println("CE parameter was not set correctly.");
myDevice.close();
System.exit(1);
}
if (ByteUtils.byteArrayToInt(paramValueDO) != ByteUtils.byteArrayToLong(PARAM_VALUE_DEVICE_OPTION)) {
System.out.println("DO parameter was not set correctly.");
myDevice.close();
System.exit(1);
}
if (ByteUtils.byteArrayToInt(paramValueDC) != ByteUtils.byteArrayToLong(PARAM_VALUE_DEVICE_CONTROLS)) {
System.out.println("DC parameter was not set correctly.");
myDevice.close();
System.exit(1);
}
if (ByteUtils.byteArrayToInt(paramValueNH) != ByteUtils.byteArrayToLong(PARAM_VALUE_MAXIMUM_HOPS)) {
System.out.println("NH parameter was not set correctly.");
myDevice.close();
System.exit(1);
}
if (ByteUtils.byteArrayToInt(paramValueBH) != ByteUtils.byteArrayToLong(PARAM_VALUE_BROADCAST_RADIUS)) {
System.out.println("BH parameter was not set correctly.");
myDevice.close();
System.exit(1);
}
if (ByteUtils.byteArrayToInt(paramValueAR) != ByteUtils.byteArrayToLong(PARAM_VALUE_MANY_TO_ONE)) {
System.out.println("AR parameter was not set correctly.");
myDevice.close();
System.exit(1);
}
System.out.println("All parameters were set correctly!");
myDevice.close();
} catch (TimeoutException e) {
e.printStackTrace();
myDevice.close();
System.exit(1);
} catch (XBeeException e) {
e.printStackTrace();
myDevice.close();
System.exit(1);
}
}
}
/**
* Copyright 2017, Digi International Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at http://mozilla.org/MPL/2.0/.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
import com.digi.xbee.api.listeners.IDataReceiveListener;
import com.digi.xbee.api.models.XBeeMessage;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
/**
* Class to manage the XBee received data that was sent by other modules in the
* same network.
*
* <p>Acts as a data listener by implementing the
* {@link IDataReceiveListener} interface, and is notified when new
* data for the module is received.</p>
*
* @see IDataReceiveListener
*
*/
public class MyDataReceiveListener implements IDataReceiveListener {
private String FILE_NAME;
private String FILE_PATH;
private final int HEADER_SIZE = 18;
private List<String> log;
private long totalDataLength;
private Calendar today, recvTime;
private SimpleDateFormat dateFormat;
private SimpleDateFormat timeFormat;
private int p_cnt;
MyDataReceiveListener(String file_name, String file_path){
this.FILE_NAME = file_name;
this.FILE_PATH = file_path;
log = new ArrayList<>();
dateFormat = new SimpleDateFormat("yyyy-MM-dd");
timeFormat = new SimpleDateFormat("HH:mm:ss.SSS");
today = Calendar.getInstance();
log.add(dateFormat.format(today.getTime()) + " " + timeFormat.format(today.getTime()) + "\n");
p_cnt = -1;
totalDataLength = 0;
}
@Override
public void dataReceived(XBeeMessage xbeeMessage) {
//If receiving frame is end signal
String recvDataString = xbeeMessage.getDataString();
if (recvDataString.equals("-1")){
try {
String statistics = "\n" + "Total received packet " + (p_cnt+1) + "\nTotal received data size " + totalDataLength + " byte";
System.out.println(statistics);
log.add(statistics);
Files.write(Paths.get(FILE_PATH, FILE_NAME), log, StandardOpenOption.CREATE);
System.out.println("Receiving process has just done!");
System.exit(1);
} catch (IOException e) {
e.printStackTrace();
}
//If receiving frame is transmit error
}else if (recvDataString.equals("-2")){
System.out.println("Transmit error");
System.exit(1);
//If receiving frame is data
}else {
p_cnt += 1;
totalDataLength += recvDataString.length() + HEADER_SIZE;
recvTime = Calendar.getInstance();
System.out.println("Received packet " + p_cnt + " " + timeFormat.format(recvTime.getTime()) +
" packet size " + (xbeeMessage.getData().length + HEADER_SIZE) + "byte");
log.add(timeFormat.format(recvTime.getTime()) + "," + p_cnt + "," + "RECV," + (xbeeMessage.getData().length + HEADER_SIZE) + "byte");
}
}
}
C) 同期送信スループット測定および提案手法送信側プログラム
/**
* Copyright 2017, Digi International Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at http://mozilla.org/MPL/2.0/.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
import com.digi.xbee.api.RemoteXBeeDevice;
import com.digi.xbee.api.XBeeDevice;
import com.digi.xbee.api.XBeeNetwork;
import com.digi.xbee.api.exceptions.TimeoutException;
import com.digi.xbee.api.exceptions.XBeeException;
import com.digi.xbee.api.utils.ByteUtils;
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
public class SendFile {
// TODO Replace with the serial port where your sender module is connected to.
private static final String PORT = "/dev/tty.usbserial-AL021QYY";
// TODO Replace with the baud rate of your sender module.
private static final int BAUD_RATE = 115200;
// TODO Replace with the file name which you want to transmit
private static final String SEND_FILE_NAME = "/Bird.jpg";
private static final String SEND_FILE_PATH = System.getProperty("user.dir") + SEND_FILE_NAME;
// Information on packets to be transmitted
// TODO Replace with the payload size
private static final int PAYLOAD_SIZE = 50; //[Byte] max 250, do not set 0
private static final int HEDER_SIZE = 18;
private static final int PACKET_LENGTH = PAYLOAD_SIZE + HEDER_SIZE;
private static final int SEQ_SIZE = 4; //[Byte]
private static final int DATA_SIZE = PAYLOAD_SIZE - SEQ_SIZE; //[Byte]
// TODO Replace with the transmit rate
private static final int TRANSMIT_RATE = 16000; //bps
private static final double INTERVAL_TIME = (double) PACKET_LENGTH * 8 / (double) TRANSMIT_RATE;
// TODO Replace with the node identifier where you want to transmit (Destination)
private static final String REMOTE_NODE_IDENTIFIER = "COORD";
// TODO Replace with the send option sync(=true) or async + sync(=false)
private static boolean SYNC = false;
//Have to wait some milliseconds to transmit with synchronous after asynchronous transmit.
private static int ASYS_WAIT_TIME = 250; //[ms]
//Parameters to be set on routers
private static final String PARAM_PAN_ID = "ID";
private static final byte[] PARAM_VALUE_PAN_ID = new byte[]{0x20, 0x18};
private static final String PARAM_CHANNEL_VERIFICATION = "JV";
private static final byte[] PARAM_VALUE_CHANNEL_VERIFICATION = new byte[]{0x01};
private static final String PARAM_JOIN_NOTIFICATION = "JN";
private static final byte[] PARAM_VALUE_JOIN_NOTIFICATION = new byte[]{0x0};
private static final String PARAM_DEVICE_OPTION = "DO";
private static final byte[] PARAM_VALUE_DEVICE_OPTION = new byte[]{0x00};
private static final String PARAM_DEVICE_CONTROLS = "DC";
private static final byte[] PARAM_VALUE_DEVICE_CONTROLS = new byte[]{0x00, 0x00};
private static final String PARAM_MAXIMUM_HOPS = "NH";
private static final byte[] PARAM_VALUE_MAXIMUM_HOPS = new byte[]{0x1E};
private static final String PARAM_BROADCAST_RADIUS = "BH";
private static final byte[] PARAM_VALUE_BROADCAST_RADIUS = new byte[]{0x00};
private static final String PARAM_MANY_TO_ONE = "AR";
private static final byte[] PARAM_VALUE_MANY_TO_ONE = new byte[]{(byte) 0xFF};
private static final byte[] SUCCESS_SIGNAL = "1".getBytes();
private static final byte[] ASYNC_FINISH_SIGNAL = "0".getBytes();
private static final byte[] END_ERROR_SIGNAL = "2".getBytes();
private static final int FILE_NAME_SEQUENCE = -1;
private static XBeeDevice myDevice;
private static XBeeNetwork xbeeNetwork;
private static RemoteXBeeDevice remoteDevice;
private static MyDataReceiveListener myListener;
private static byte[] sendFileArray;
private static int numFrame, finalFrameSize;
private static long simStart, simEnd;
// main
public static void main(String[] args) throws IOException {
myDevice = new XBeeDevice(PORT, BAUD_RATE);
// Read all bytes from the file which you want to send.
// Then store byte array them.
File sendFile = new File(SEND_FILE_PATH);
sendFileArray = Files.readAllBytes(sendFile.toPath());
// To start transmit process
setParam();
transmitFile();
// Add a data receive listener when transmit is asynchronous + synchronous
if (!SYNC) {
try {
myDevice.open();
myListener = new MyDataReceiveListener(PAYLOAD_SIZE, SEQ_SIZE);
myDevice.addDataListener(myListener);
} catch (XBeeException e) {
e.printStackTrace();
System.exit(1);
}
}
}
public static void setParam(){
try {
myDevice.open();
// Set parameters.
myDevice.setParameter(PARAM_PAN_ID, PARAM_VALUE_PAN_ID);
myDevice.setParameter(PARAM_CHANNEL_VERIFICATION, PARAM_VALUE_CHANNEL_VERIFICATION);
myDevice.setParameter(PARAM_JOIN_NOTIFICATION, PARAM_VALUE_JOIN_NOTIFICATION);
myDevice.setParameter(PARAM_DEVICE_OPTION, PARAM_VALUE_DEVICE_OPTION);
myDevice.setParameter(PARAM_DEVICE_CONTROLS, PARAM_VALUE_DEVICE_CONTROLS);
myDevice.setParameter(PARAM_MAXIMUM_HOPS, PARAM_VALUE_MAXIMUM_HOPS);
myDevice.setParameter(PARAM_BROADCAST_RADIUS, PARAM_VALUE_BROADCAST_RADIUS);
myDevice.setParameter(PARAM_MANY_TO_ONE, PARAM_VALUE_MANY_TO_ONE);
// Get parameters
byte[] paramValueID = myDevice.getParameter(PARAM_PAN_ID);
byte[] paramValueJV = myDevice.getParameter(PARAM_CHANNEL_VERIFICATION);
byte[] paramValueJN = myDevice.getParameter(PARAM_JOIN_NOTIFICATION);
byte[] paramValueDO = myDevice.getParameter(PARAM_DEVICE_OPTION);
byte[] paramValueDC = myDevice.getParameter(PARAM_DEVICE_CONTROLS);
byte[] paramValueNH = myDevice.getParameter(PARAM_MAXIMUM_HOPS);
byte[] paramValueBH = myDevice.getParameter(PARAM_BROADCAST_RADIUS);
byte[] paramValueAR = myDevice.getParameter(PARAM_MANY_TO_ONE);
// Compare the read parameter values with the values that were set.
if (ByteUtils.byteArrayToLong(paramValueID) != ByteUtils.byteArrayToLong(PARAM_VALUE_PAN_ID)) {
System.out.println("ID parameter was not set correctly.");
myDevice.close();
System.exit(1);
}
if (ByteUtils.byteArrayToInt(paramValueJV) != ByteUtils.byteArrayToLong(PARAM_VALUE_CHANNEL_VERIFICATION)) {
System.out.println("JV parameter was not set correctly.");
myDevice.close();
System.exit(1);
}
if (ByteUtils.byteArrayToInt(paramValueJN) != ByteUtils.byteArrayToLong(PARAM_VALUE_JOIN_NOTIFICATION)) {
System.out.println("JN parameter was not set correctly.");
myDevice.close();
System.exit(1);
}
if (ByteUtils.byteArrayToInt(paramValueDO) != ByteUtils.byteArrayToLong(PARAM_VALUE_DEVICE_OPTION)) {
System.out.println("DO parameter was not set correctly.");
myDevice.close();
System.exit(1);
}
if (ByteUtils.byteArrayToInt(paramValueDC) != ByteUtils.byteArrayToLong(PARAM_VALUE_DEVICE_CONTROLS)) {
System.out.println("DC parameter was not set correctly.");
myDevice.close();
System.exit(1);
}
if (ByteUtils.byteArrayToInt(paramValueNH) != ByteUtils.byteArrayToLong(PARAM_VALUE_MAXIMUM_HOPS)) {
System.out.println("NH parameter was not set correctly.");
myDevice.close();
System.exit(1);
}
if (ByteUtils.byteArrayToInt(paramValueBH) != ByteUtils.byteArrayToLong(PARAM_VALUE_BROADCAST_RADIUS)) {
System.out.println("BH parameter was not set correctly.");
myDevice.close();
System.exit(1);
}
if (ByteUtils.byteArrayToInt(paramValueAR) != ByteUtils.byteArrayToLong(PARAM_VALUE_MANY_TO_ONE)) {
System.out.println("AR parameter was not set correctly.");
myDevice.close();
System.exit(1);
}
System.out.println("All parameters were set correctly!");
myDevice.close();
} catch (TimeoutException e) {
e.printStackTrace();
myDevice.close();
System.exit(1);
} catch (XBeeException e) {
e.printStackTrace();
myDevice.close();
System.exit(1);
}
}
public static void transmitFile(){
try {
myDevice.open();
xbeeNetwork = myDevice.getNetwork();
remoteDevice = xbeeNetwork.discoverDevice(REMOTE_NODE_IDENTIFIER);
if (remoteDevice == null) {
System.out.println("Couldn't find the remote XBee device with '"
+ REMOTE_NODE_IDENTIFIER + "' Node Identifier.");
System.exit(1);
}
// Calculate how many frames are divided and the last frame size.
numFrame = (sendFileArray.length / DATA_SIZE) + 1;
finalFrameSize = sendFileArray.length % ((numFrame - 1) * DATA_SIZE);
long prev = 0, now, asysStart, asysEnd;
double asysSendTime;
asysStart = System.nanoTime();
simStart = System.nanoTime();
Thread.sleep((int)INTERVAL_TIME * 1000);
// Transmit process
// Sending frames are converted to ByteBuffer temporarily to add sequence bits.
// Then, the frames are reconverted to byte array.
// Repetitive statement are sending file, after that send filename and signal
// The last frame divided (numFrame th) is not necessarily PAY_LOAD_SIZE - SEQ_SIZE [byte]
// The sendData is synchronous transmit, the sendDataAsync is asynchronous transmit.
for (int i = 0; i < numFrame; i++) {
if (i % 100 == 0)
System.out.println("Sending Packet ... " + i);
//Except the final data frame
if (i != numFrame - 1) {
byte[] frameArray = genFrameWithSeq(i, new byte[PAYLOAD_SIZE]);
if (SYNC) myDevice.sendData(remoteDevice, frameArray);
else {
if (i != 0) {
now = System.nanoTime();
if ((now - prev) <= INTERVAL_TIME * 1000000000) {
Thread.sleep((int) (((INTERVAL_TIME * 1000000000) - (now - prev)) / 1000000));
} else {
Thread.sleep(5000);
System.out.print("Transmit rate error");
myDevice.sendDataAsync(remoteDevice, END_ERROR_SIGNAL);
System.exit(1);
}
} else myDevice.sendDataAsync(remoteDevice, frameArray);
prev = System.nanoTime();
myDevice.sendDataAsync(remoteDevice, frameArray);
}
//Final data frame
} else {
byte[] frameArray = genFrameWithSeq(i, new byte[finalFrameSize + SEQ_SIZE]);
Thread.sleep(ASYS_WAIT_TIME);
myDevice.sendData(remoteDevice, frameArray);
}
}
// Send receiver filename
byte[] fileName = SEND_FILE_NAME.getBytes(StandardCharsets.UTF_8);
byte[] fileNameWithSeq = new byte[fileName.length + SEQ_SIZE];
if (fileName.length <= DATA_SIZE) {
ByteBuffer frameBuf = ByteBuffer.wrap(fileNameWithSeq);
frameBuf.putInt(0, FILE_NAME_SEQUENCE);
for (int j = 0; j < fileName.length; j++)
frameBuf.put(SEQ_SIZE + j, fileName[j]);
frameBuf.get(fileNameWithSeq, 0, fileNameWithSeq.length);
myDevice.sendData(remoteDevice, fileNameWithSeq);
}else{
System.out.println("Filename is too long");
System.exit(1);
}
asysEnd = System.nanoTime();
asysSendTime = (double)(asysEnd-asysStart)/1000000000;
System.out.println("Total transmit time (asynchronous session) "
+ String.format("%1$.3f", asysSendTime) + "[s]");
System.out.println("Actual transmit rate (asynchronous session) " +
String.format("%1$.3f", (numFrame*PACKET_LENGTH*8 / asysSendTime)/1000) + "[kbps]");
// Send receiver signal
if (SYNC) {
myDevice.sendData(remoteDevice, SUCCESS_SIGNAL);
System.exit(1);
}else myDevice.sendData(remoteDevice, ASYNC_FINISH_SIGNAL);
} catch (XBeeException e) {
e.printStackTrace();
System.exit(1);
} catch (InterruptedException e) {
e.printStackTrace();
System.exit(1);
} finally {
myDevice.close();
}
}
// Generate transmit frame with sequence.
public static byte[] genFrameWithSeq(int i, byte[] frameArray){
ByteBuffer frameBuf = ByteBuffer.wrap(frameArray);
frameBuf.putInt(0, i); // Set Sequence
for (int j = 0; j < DATA_SIZE; j++) // Set data
frameBuf.put(SEQ_SIZE + j, sendFileArray[(i * DATA_SIZE) + j]);
frameBuf.get(frameArray, 0, frameArray.length);
return frameArray;
}
// Send receiver retransmit frame in this method.
// The frameReqList contains in comma delimited sequence number which receiver want sender to send.
public static void retransFrame(String frameReqList){
System.out.println("resendFrame method is called");
myDevice.removeDataListener(myListener);
try {
if (remoteDevice == null) {
System.out.println("Couldn't find the remote XBee device with '"
+ REMOTE_NODE_IDENTIFIER + "' Node Identifier.");
System.exit(1);
}
String[] splitRetrieList = frameReqList.split(",", 0);
for (int i=0; i<splitRetrieList.length; i++) {
int seq = Integer.parseInt(splitRetrieList[i]);
byte[] frameArray = genFrameWithSeq(seq, new byte[PAYLOAD_SIZE]);
myDevice.sendData(remoteDevice, frameArray);
}
// Send receiver success signal.
simEnd = System.nanoTime();
myDevice.sendData(remoteDevice, SUCCESS_SIGNAL);
System.out.println("Successful transmission of all retransmission frames!!");
double totalSendTime = (double)(simEnd-simStart)/1000000000;
System.out.println("Total transmit rate = " + String.format
("%1$.3f", ((8 * (PACKET_LENGTH * (splitRetrieList.length + numFrame - 1)
+ finalFrameSize)))
/ (totalSendTime * 1000)) + "[kbps]");
} catch (XBeeException e) {
e.printStackTrace();
System.exit(1);
}finally {
myDevice.addDataListener(myListener);
myDevice.close();
}
}
}
/**
* Copyright 2017, Digi International Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at http://mozilla.org/MPL/2.0/.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
import com.digi.xbee.api.listeners.IDataReceiveListener;
import com.digi.xbee.api.models.XBeeMessage;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.text.SimpleDateFormat;
import java.util.*;
public class MyDataReceiveListener implements IDataReceiveListener {
// TODO Replace with the payload size, enter the same numerical value as the sender
private static int PAYLOAD_SIZE; //[Byte] max 250, do not set 0
// TODO Replace with the sequence field size, enter the same numerical value as the sender
private static int SEQ_SIZE; //[Byte]
private static int DATA_SIZE; //[Byte]
private static final String SUCCESS_SIGNAL = "1";
private static final String FINISH_SIGNAL = "2";
private Map<Integer, byte[]> recvData;
int frameCnt, seq, maxSeqNum;
String receivedDataString;
MyDataReceiveListener(int payload_size, int seq_size){
PAYLOAD_SIZE = payload_size;
SEQ_SIZE = seq_size;
DATA_SIZE = PAYLOAD_SIZE - SEQ_SIZE;
recvData = new HashMap<>();
frameCnt = 0;
seq = -2;
maxSeqNum = 0;
}
// Receiving data size is not less than SEQ_SIZE[byte].
// So, it's signal when the data size is 1
@Override
public void dataReceived(XBeeMessage xbeeMessage) {
receivedDataString = "";
// If receiving frame is not any signal
if (xbeeMessage.getData().length != 1) {
if (frameCnt % 10 == 0)
System.out.println("Receiving frame ... " + frameCnt);
byte[] receivedData = xbeeMessage.getData();
seq = getSeq(receivedData);
if (maxSeqNum < seq)
maxSeqNum = seq;
frameCnt++;
recvData.put(seq, receivedData);
// Store signal to receivedDataString if receiving frame is signal
}else receivedDataString = xbeeMessage.getDataString();
// SUCCESS_SIGNAL means asynchronous transmit is finish.
if (receivedDataString.equals(SUCCESS_SIGNAL)){
System.out.println("Retransmit request was received!");
//Calculate total file size. Each frames are same capacity except the last two,
//the last frame is end signal, the other one is the last frame of data.
int fileCapa = maxSeqNum * DATA_SIZE + recvData.get(maxSeqNum).length - SEQ_SIZE;
byte[] output = new byte[fileCapa];
for (int i=0; i<=maxSeqNum; i++){
byte[] frameArray = getData(recvData.get(i));
//Assemble the frame. First 4bits of frame are sequence bits.
for (int j=0; j<frameArray.length; j++)
output[(i*DATA_SIZE)+j] = frameArray[j];
}
SendFile.retransFrame(new String(output, StandardCharsets.UTF_8));
// FINISH_SIGNAL means synchronous transmit is finish.
}else if (receivedDataString.equals(FINISH_SIGNAL)){
System.out.println("Success!!!");
System.exit(1);
}
}
// Get sequence bits from received byte array data.
public int getSeq(byte[] array){
ByteBuffer bb = ByteBuffer.wrap(array);
return bb.getInt(0);
}
// Get data from received byte array data.
public byte[] getData(byte[] array){
byte[] data = new byte[array.length - SEQ_SIZE];
for (int i=0; i<array.length - SEQ_SIZE; i++){
data[i] = array[i+SEQ_SIZE];
}
return data;
}
}
D) 同期送信スループット測定および提案手法受信側プログラム
/**
* Copyright 2017, Digi International Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at http://mozilla.org/MPL/2.0/.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
import com.digi.xbee.api.RemoteXBeeDevice;
import com.digi.xbee.api.XBeeDevice;
import com.digi.xbee.api.XBeeNetwork;
import com.digi.xbee.api.exceptions.TimeoutException;
import com.digi.xbee.api.exceptions.XBeeException;
import com.digi.xbee.api.utils.ByteUtils;
import java.nio.ByteBuffer;
public class ReceiveFile {
// TODO Replace with the serial port where your receiver module is connected.
private static final String PORT = "/dev/tty.usbserial-A10586WI";
// TODO Replace with the baud rate of you receiver module.
private static final int BAUD_RATE = 115200;
// TODO Replace with the payload size, enter the same numerical value as the sender
private static final int PAYLOAD_SIZE = 50; //[Byte] max 250, do not set 0
private static final int HEADER_SIZE = 18;
private static final int SEQ_SIZE = 4; //[Byte]
private static final int DATA_SIZE = PAYLOAD_SIZE - SEQ_SIZE; //[Byte]
// TODO Replace with the node identifier which is source node (Src)
private static final String REMOTE_NODE_IDENTIFIER = "ROUTER";
//Parameters to be set on routers
private static final String PARAM_PAN_ID = "ID";
private static final byte[] PARAM_VALUE_PAN_ID = new byte[]{0x20, 0x18};
private static final String PARAM_CHANNEL_VERIFICATION = "JV";
private static final byte[] PARAM_VALUE_CHANNEL_VERIFICATION = new byte[]{0x00};
private static final String PARAM_JOIN_NOTIFICATION = "JN";
private static final byte[] PARAM_VALUE_JOIN_NOTIFICATION = new byte[]{0x0};
private static final String PARAM_COORDINATOR_ENABLE = "CE";
private static final byte[] PARAM_VALUE_COORDINATOR_ENABLE = new byte[]{0x1};
private static final String PARAM_DEVICE_OPTION = "DO";
private static final byte[] PARAM_VALUE_DEVICE_OPTION = new byte[]{0x00};
private static final String PARAM_DEVICE_CONTROLS = "DC";
private static final byte[] PARAM_VALUE_DEVICE_CONTROLS = new byte[]{0x00, 0x00};
private static final String PARAM_MAXIMUM_HOPS = "NH";
private static final byte[] PARAM_VALUE_MAXIMUM_HOPS = new byte[]{0x1E};
private static final String PARAM_BROADCAST_RADIUS = "BH";
private static final byte[] PARAM_VALUE_BROADCAST_RADIUS = new byte[]{0x00};
private static final String PARAM_MANY_TO_ONE = "AR";
private static final byte[] PARAM_VALUE_MANY_TO_ONE = new byte[]{(byte) 0xFF};
private static final byte[] SUCCESS_SIGNAL = "1".getBytes();
private static final byte[] FINISH_SIGNAL = "2".getBytes();
private static XBeeDevice myDevice;
private static MyDataReceiveListener myListener;
public static void main(String[] args) {
myDevice = new XBeeDevice(PORT, BAUD_RATE);
setParam();
try {
myDevice.open();
myListener = new MyDataReceiveListener(HEADER_SIZE, PAYLOAD_SIZE, SEQ_SIZE);
myDevice.addDataListener(myListener);
System.out.println("\n>> Waiting for data...");
} catch (XBeeException e) {
e.printStackTrace();
System.exit(1);
}
}
public static void setParam(){
try {
myDevice.open();
// Set parameters.
myDevice.setParameter(PARAM_PAN_ID, PARAM_VALUE_PAN_ID);
myDevice.setParameter(PARAM_CHANNEL_VERIFICATION, PARAM_VALUE_CHANNEL_VERIFICATION);
myDevice.setParameter(PARAM_JOIN_NOTIFICATION, PARAM_VALUE_JOIN_NOTIFICATION);
myDevice.setParameter(PARAM_DEVICE_OPTION, PARAM_VALUE_DEVICE_OPTION);
myDevice.setParameter(PARAM_DEVICE_CONTROLS, PARAM_VALUE_DEVICE_CONTROLS);
myDevice.setParameter(PARAM_MAXIMUM_HOPS, PARAM_VALUE_MAXIMUM_HOPS);
myDevice.setParameter(PARAM_BROADCAST_RADIUS, PARAM_VALUE_BROADCAST_RADIUS);
myDevice.setParameter(PARAM_MANY_TO_ONE, PARAM_VALUE_MANY_TO_ONE);
// Get parameters
byte[] paramValueID = myDevice.getParameter(PARAM_PAN_ID);
byte[] paramValueJV = myDevice.getParameter(PARAM_CHANNEL_VERIFICATION);
byte[] paramValueJN = myDevice.getParameter(PARAM_JOIN_NOTIFICATION);
byte[] paramValueCE = myDevice.getParameter(PARAM_COORDINATOR_ENABLE);
byte[] paramValueDO = myDevice.getParameter(PARAM_DEVICE_OPTION);
byte[] paramValueDC = myDevice.getParameter(PARAM_DEVICE_CONTROLS);
byte[] paramValueNH = myDevice.getParameter(PARAM_MAXIMUM_HOPS);
byte[] paramValueBH = myDevice.getParameter(PARAM_BROADCAST_RADIUS);
byte[] paramValueAR = myDevice.getParameter(PARAM_MANY_TO_ONE);
// Compare the read parameter values with the values that were set.
if (ByteUtils.byteArrayToLong(paramValueID) != ByteUtils.byteArrayToLong(PARAM_VALUE_PAN_ID)) {
System.out.println("ID parameter was not set correctly.");
myDevice.close();
System.exit(1);
}
if (ByteUtils.byteArrayToInt(paramValueJV) != ByteUtils.byteArrayToLong(PARAM_VALUE_CHANNEL_VERIFICATION)) {
System.out.println("JV parameter was not set correctly.");
myDevice.close();
System.exit(1);
}
if (ByteUtils.byteArrayToInt(paramValueJN) != ByteUtils.byteArrayToLong(PARAM_VALUE_JOIN_NOTIFICATION)) {
System.out.println("JN parameter was not set correctly.");
myDevice.close();
System.exit(1);
}
if (ByteUtils.byteArrayToInt(paramValueCE) != ByteUtils.byteArrayToLong(PARAM_VALUE_COORDINATOR_ENABLE)) {
System.out.println("CE parameter was not set correctly.");
myDevice.close();
System.exit(1);
}
if (ByteUtils.byteArrayToInt(paramValueDO) != ByteUtils.byteArrayToLong(PARAM_VALUE_DEVICE_OPTION)) {
System.out.println("DO parameter was not set correctly.");
myDevice.close();
System.exit(1);
}
if (ByteUtils.byteArrayToInt(paramValueDC) != ByteUtils.byteArrayToLong(PARAM_VALUE_DEVICE_CONTROLS)) {
System.out.println("DC parameter was not set correctly.");
myDevice.close();
System.exit(1);
}
if (ByteUtils.byteArrayToInt(paramValueNH) != ByteUtils.byteArrayToLong(PARAM_VALUE_MAXIMUM_HOPS)) {
System.out.println("NH parameter was not set correctly.");
myDevice.close();
System.exit(1);
}
if (ByteUtils.byteArrayToInt(paramValueBH) != ByteUtils.byteArrayToLong(PARAM_VALUE_BROADCAST_RADIUS)) {
System.out.println("BH parameter was not set correctly.");
myDevice.close();
System.exit(1);
}
if (ByteUtils.byteArrayToInt(paramValueAR) != ByteUtils.byteArrayToLong(PARAM_VALUE_MANY_TO_ONE)) {
System.out.println("AR parameter was not set correctly.");
myDevice.close();
System.exit(1);
}
System.out.println("All parameters were set correctly!");
myDevice.close();
} catch (TimeoutException e) {
e.printStackTrace();
myDevice.close();
System.exit(1);
} catch (XBeeException e) {
e.printStackTrace();
myDevice.close();
System.exit(1);
}
}
public static void reqRetries(byte[] retrieReq){
myDevice.removeDataListener(myListener);
try {
System.out.println("reqRetries is called");
XBeeNetwork xbeeNetwork = myDevice.getNetwork();
RemoteXBeeDevice remoteDevice = xbeeNetwork.discoverDevice(REMOTE_NODE_IDENTIFIER);
if (remoteDevice == null) {
System.out.println("Couldn't find the remote XBee device with '"
+ REMOTE_NODE_IDENTIFIER + "' Node Identifier.");
System.exit(1);
}
// Calculate how many frames are divided and the last frame size.
int numFrame = (retrieReq.length / DATA_SIZE) + 1;
int finalFrameSize = retrieReq.length % ((numFrame - 1) * DATA_SIZE);
// Transmit process
// Sending frames are converted to ByteBuffer temporarily to add sequence bits.
// Then, the frames are reconverted to byte array.
// Repetitive statement are sending file, after that send filename and signal
// The last frame divided (numFrame th) is not necessarily PAY_LOAD_SIZE - SEQ_SIZE [byte]
// The sendData is synchronous transmit, the sendDataAsync is asynchronous transmit.
for (int i = 0; i < numFrame + 1; i++) {
if (i != numFrame) {
//Except the final request frame
if (i != numFrame - 1) {
byte[] frameArray = new byte[PAYLOAD_SIZE];
ByteBuffer frameBuf = ByteBuffer.wrap(frameArray);
frameBuf.putInt(0, i); // Set sequence
for (int j = 0; j < DATA_SIZE; j++) // Set data
frameBuf.put(SEQ_SIZE + j, retrieReq[(i * DATA_SIZE) + j]);
frameBuf.get(frameArray, 0, frameArray.length);
myDevice.sendData(remoteDevice, frameArray);
if (i % 10 == 0)
System.out.println("Sending retransmit request ... " + i);
//Final request frame
} else {
byte[] frameArray = new byte[finalFrameSize + SEQ_SIZE];
ByteBuffer frameBuf = ByteBuffer.wrap(frameArray);
frameBuf.putInt(0, i); // Set sequence
for (int j = 0; j < finalFrameSize; j++) // Set Data
frameBuf.put(SEQ_SIZE + j, retrieReq[(i * DATA_SIZE) + j]);
frameBuf.get(frameArray, 0, frameArray.length);
myDevice.sendData(remoteDevice, frameArray);
if (i % 10 == 0)
System.out.println("Sending retransmit request ... " + i);
}
//Send Sender success signal
} else {
System.out.println("Successful transmit of all retransmit request");
myDevice.sendData(remoteDevice, SUCCESS_SIGNAL);
myDevice.addDataListener(myListener);
}
}
} catch (XBeeException e) {
e.printStackTrace();
System.exit(1);
}
}
// Send sender finish signal use this method when use asynchronous transmit mode and all frame was received.
public static void sendFinishSignal() throws XBeeException {
XBeeNetwork xbeeNetwork = myDevice.getNetwork();
RemoteXBeeDevice remoteDevice = xbeeNetwork.discoverDevice(REMOTE_NODE_IDENTIFIER);
if (remoteDevice == null) {
System.out.println("Couldn't find the remote XBee device with '"
+ REMOTE_NODE_IDENTIFIER + "' Node Identifier.");
System.exit(1);
}
myDevice.sendData(remoteDevice, FINISH_SIGNAL);
myDevice.close();
}
}
/**
* Copyright 2017, Digi International Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at http://mozilla.org/MPL/2.0/.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
import com.digi.xbee.api.exceptions.XBeeException;
import com.digi.xbee.api.listeners.IDataReceiveListener;
import com.digi.xbee.api.models.XBeeMessage;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.text.SimpleDateFormat;
import java.util.*;
public class MyDataReceiveListener implements IDataReceiveListener {
private static int PAYLOAD_SIZE;
private static int PACKET_LENGTH;
private static int SEQ_SIZE;
private static int DATA_SIZE;
private static final String SUCCESS_SIGNAL = "1";
private static final String ASYNC_FINISH_SIGNAL = "0";
private static final String END_ERROR_SIGNAL = "2";
private static final int FILE_NAME_SEQUENCE = -1;
private static Map<Integer, byte[]> recvData;
int frameCnt, maxSeqNum, seq;
private Calendar startTime, endTime;
private SimpleDateFormat timeFormat;
private long startNanoTime, endNanoTime;
private String receivedDataString;
private double totalRecvTime = 0.0;
MyDataReceiveListener(int header_size, int payload_size, int seq_size){
PAYLOAD_SIZE = payload_size;
PACKET_LENGTH = PAYLOAD_SIZE + header_size;
SEQ_SIZE = seq_size;
DATA_SIZE = PAYLOAD_SIZE - SEQ_SIZE;
recvData = new HashMap<>();
frameCnt = 0;
maxSeqNum = 0;
seq = -2;
timeFormat = new SimpleDateFormat("HH:mm:ss.SSS");
}
@Override
// Receiving data size is not less than SEQ_SIZE[byte].
// So, it's signal when the data size is 1
public void dataReceived(XBeeMessage xbeeMessage) {
receivedDataString = "";
//if receiving frame is not end signal
if (xbeeMessage.getData().length != 1) {
byte[] receivedData = xbeeMessage.getData();
if (frameCnt == 0) {
startTime = Calendar.getInstance();
startNanoTime = System.nanoTime();
System.out.println("Start time " + timeFormat.format(startTime.getTime()));
}
seq = getSeq(receivedData);
if (maxSeqNum < seq)
maxSeqNum = seq;
if (frameCnt % 100 == 0)
System.out.println("Receiving Packet ... " + frameCnt);
frameCnt++;
recvData.put(seq, receivedData);
// Store signal to receivedDataString if receiving frame is signal
}else receivedDataString = xbeeMessage.getDataString();
// ASYNC_FINISH_SIGNAL means asynchronous transmit session is finish.
// So, request to retransmit to sender if all frame is not received.
if (receivedDataString.equals(ASYNC_FINISH_SIGNAL)){
StringBuilder frameReqList = new StringBuilder();
for (int i=0; i<maxSeqNum; i++) // Check missing frame
if (!recvData.containsKey(i))
frameReqList.append(Integer.toString(i) + ",");
System.out.println("RequestListSize" + frameReqList.length());
if (frameReqList.length()!=0 ) {
frameReqList.deleteCharAt(frameReqList.length() - 1);
ReceiveFile.reqRetries(frameReqList.toString().getBytes(StandardCharsets.UTF_8));
} else {
//print information about execute time
printEndTime();
writeFile();
}
// If received frame is finish signal
}else if (receivedDataString.equals(SUCCESS_SIGNAL)){
//print information about execute time
printEndTime();
writeFile();
// If received frame is error signal
}else if (receivedDataString.equals(END_ERROR_SIGNAL)){
System.out.println("Transmit error ");
System.exit(1);
}
}
// Get sequence bits from received byte array data.
public int getSeq(byte[] array){
ByteBuffer bb = ByteBuffer.wrap(array);
return bb.getInt(0);
}
// Get data from received byte array data.
public byte[] getData(byte[] array){
byte[] data = new byte[array.length - SEQ_SIZE];
for (int i=0; i<array.length - SEQ_SIZE; i++){
data[i] = array[i+SEQ_SIZE];
}
return data;
}
// Print total execute time.
public void printEndTime(){
endTime = Calendar.getInstance();
endNanoTime = System.nanoTime();
totalRecvTime = (endNanoTime-startNanoTime)/1000000000f;
System.out.println("End time " + timeFormat.format(endTime.getTime()));
System.out.println("Total receiving time " + String.format("%1$.3f", totalRecvTime) + "s");
}
// Write the received file
public void writeFile(){
System.out.println("\nwriteFile is called");
//Calculate total file size. Each frames are same capacity except the last two,
//the last frame is end signal, the other one is the last frame of data.
int fileCapa = maxSeqNum * DATA_SIZE + recvData.get(maxSeqNum).length - 4;
System.out.println("Received file size = " + String.format("%1$.1f", (double)fileCapa/1000) + "[KB]");
System.out.println("Receving rate = " + String.format("%1$.3f",
(PACKET_LENGTH * (maxSeqNum + 1) * 8 / totalRecvTime) / 1000) + "[kbps]");
byte[] output = new byte[fileCapa];
for (int i=0; i<=maxSeqNum; i++){
byte[] frameArray = getData(recvData.get(i));
//Assemble the frame. First 4bits of frame are sequence bits.
for (int j=0; j<frameArray.length; j++)
output[(i*DATA_SIZE) + j] = frameArray[j];
}
// Output the file
String filePath = System.getProperty("user.dir")+ new String(getData(recvData.get(FILE_NAME_SEQUENCE)),
StandardCharsets.UTF_8);
System.out.println(filePath);
try {
Files.write(Paths.get(filePath), output, StandardOpenOption.CREATE);
ReceiveFile.sendFinishSignal();
}catch(IOException e){
e.printStackTrace();
} catch (XBeeException e) {
e.printStackTrace();
}
System.out.println("Success!!");
System.exit(1);
}
}
E) 3.2.1項 非同期送信を用いたスループット測定データ
表9 非同期送信-0HOP時のスループット
送信レート[kbps] | 受信レート[kbps] |
---|---|
0.00 | 0.00 |
1.00 | 1.00 |
9.62 | 9.49 |
18.49 | 17.63 |
22.41 | 19.70 |
28.10 | 21.01 |
30.89 | 20.99 |
32.97 | 20.44 |
35.38 | 19.92 |
44.93 | 19.14 |
56.46 | 19.20 |
63.02 | 18.96 |
71.01 | 18.92 |
74.03 | 17.04 |
表10 非同期送信-1HOP時のスループット
送信レート[kbps] | 受信レート[kbps] |
---|---|
0.00 | 0.00 |
1.00 | 1.00 |
9.62 | 8.69 |
18.60 | 10.41 |
22.74 | 10.52 |
28.20 | 10.39 |
31.33 | 10.01 |
33.67 | 9.65 |
35.80 | 9.79 |
45.61 | 9.56 |
57.53 | 9.29 |
64.39 | 9.96 |
70.26 | 9.31 |
74.50 | 9.08 |
表11 非同期送信-2HOP時のスループット
送信レート[kbps] | 受信レート[kbps] |
---|---|
0.00 | 0.00 |
1.00 | 1.00 |
9.61 | 5.24 |
18.65 | 7.32 |
22.77 | 7.58 |
28.16 | 7.72 |
31.36 | 7.45 |
33.74 | 7.17 |
35.98 | 7.28 |
45.86 | 7.34 |
58.13 | 6.61 |
64.05 | 6.13 |
70.43 | 5.84 |
74.67 | 6.40 |
表12 非同期送信-3HOP時のスループット
送信レート[kbps] | 受信レート[kbps] |
---|---|
0.00 | 0.00 |
1.00 | 1.00 |
9.63 | 5.09 |
18.66 | 5.53 |
22.81 | 5.47 |
28.24 | 5.30 |
31.47 | 5.30 |
33.81 | 5.13 |
36.11 | 5.09 |
46.12 | 5.14 |
58.14 | 5.00 |
64.28 | 5.05 |
70.25 | 5.16 |
74.73 | 5.03 |
F) 3.2.2項 同期送信を用いたスループット測定データ
表13 同期送信を用いた場合の各HOP数におけるスループット
\ | 1回目[kbps] | 2回目[kbps] | 3回目 | 平均[kbps] |
---|---|---|---|---|
0HOP | 10.346 | 10.225 | 9.719 | 10.097 |
1HOP | 7.364 | 7.136 | 7.785 | 7.428 |
2HOP | 5.853 | 5.748 | 6.241 | 5.947 |
3HOP | 4.916 | 4.858 | 4.885 | 4.795 |
G) 3.3説 提案手法を用いたスループット測定データ
表14 提案手法を用いた場合の各HOP数におけるスループット
\ | 1回目[kbps] | 2回目[kbps] | 3回目 | 平均[kbps] |
---|---|---|---|---|
0HOP | 17.134 | 19.221 | 18.859 | 18.405 |
1HOP | 9.239 | 9.102 | 8.973 | 9.105 |
2HOP | 6.964 | 6.113 | 6.928 | 6.628 |
3HOP | 5.164 | 5.264 | 5.272 | 5.233 |