3
13
2019
SpresenseのGNSS(GPS)受信機能を試してみる
SpresenseでGNSS信号を受信してみる
先日入手したSpresenseの目玉機能の一つであるGNSSの受信機能がどんな物か確認するため、お手軽なArduiono開発環境を使ってどの程度GNSS衛星を捉えることができるのか確認してみた.今回は外付けの受信アンテナは用いずに、Spresenseのメインボード側のチップアンテナによる受信テストだ.
今回は、GNSSの受信機能を試すためのフレームワークとして、SONYが提供しているArduino開発環境用のライブラリ “Spresense Arduino Library” を用いることにする.
Spresenseの基本フレームワークはNuttXというリアルタイムOSで、NuttXベースで開発が行われている.ユーザは公開されているNuttXのSDKを用いてSpresenseのハードウェアをローレベルで制御可能だ.但し、一般のユーザにとってはリアルタイムOSをローレベルでいじくり回すのは敷居が高いので、NuttXをもう少し上位レベルで制御できるようにArduinoラッパーを、NuttXの上に被せたArduino互換ライブラリをユーザに提供している.
Spresense Arduino Library の構造(SONYホームページより引用)
https://static.developer.sony.com/images/image/s3/uploads/2018/11/overview_software_SDK_structure-2.png
このArduino互換ライブラリにGNSS受信サンプルスケッチ(Arduinoではプログラムのことをスケッチと呼んでいる)が2つ程組み込まれているので、これをそのまま利用することにする.
Arduinoは小型ボードコンピュータのハードウェアプラットフォームのデファクトスタンダードとなっており、Arduino対応周辺機器や関連ソフトウェア、数多くのArduino解説本が出回っているので、初心者でも取り付き易い良い手軽で身近な開発環境だ.
とは言え、私自身がArduinoでのシステム開発には不慣れなので、先ずはArduinoの開発環境の整備から始めることにする.幸い、SONYのホームページに分かり易いチュートリアルが載っているので、その説明内容を辿りながら、順に環境を整えて行く様子を紹介する.
Arduino開発環境の準備
一番最初に行う作業として、手元のPCやMac環境にArduino開発環境を導入する必要がある.Arduinoの公式ホームページから、無料でダウンロード可能なので自分の環境に合わせたIDE(統合開発環境)をインストールする.Mac OS、Linux (Intel系32/64bit、ARM系)、Windowsの環境が用意されている.尚、ARM mbed系の開発環境と同じように、クラウドサービスベースのオンライン開発環境も提供されているが、オンライン開発環境については別な機会に説明する予定だ.
Arduinoを立ち上げると、初期状態で次のような setup()、loop() という空の関数だけが書かれたスケッチ編集画面が現れる.この状態でメニューから “Tools” ⇒ “Boads:Arduino/Genuino Uno” ⇒ “Borads Manager” と辿って行く.
Arduino IDEの初期立ち上げ時のスケッチ編集画面
“Arduino”メニューの”Preferences…”の設定ダイアログ下部にある、”Additional Boards Manager URLs: ” の項目に、
https://github.com/sonydevworld/spresense-arduino-compatible/releases/download/generic/package_spresense_index.json
を設定する.
この作業によって、SONYが提供しているArduino環境用のSpresenceハードウェア定義情報をダウンロードし、Arduiono IDE環境に取り込むことが可能になる.
Spresenceのハードウェア情報をIDE環境に取り込む
検索欄に “sp” と入力すると素早くSpresenceを見つけることができる
GitHubからSpresenceのハードウェア情報のダウンロード中
次に、Arduino IDEの”Tools”メニューから 先ほどと同じように、”Tools” ⇒ “Boads:Arduino/Genuino Uno” と辿って行き、一番下にある “Spresence” を選択することで、IDEの開発ターゲットを “Spresence” に設定する.
Arduino IDEのターゲットを ”Spresence” に設定
これで、Arduino IDEの環境設定は終了であるが、まだこの状態では、手元ののPCやMacと “Spresence” ボードがUSBポートを介して通信することができないので、USB廻りのデバイスドライバーをインストールする.
Spresence用USBシリアル通信ドライバのインストール
SpresenceのUSBポートをシリアルコンソールポートとして利用できるように、ボードに内蔵されているSiLab製のUSBシリアル変換チップ用ドライバをインストールする.Mac OS用とWindows用のドライバが用意されているので、自分の環境に合わせてドライバをインストールする.
【SiLab CP210x USB to UART Bridge VCPドライバダウンロードページ】
・CP210x USB to UART Bridge VCP Drivers
CP210x USB to UART Bridge VCPドライバのインストールが完了後、SpresenceのUSBポートを手元のPC/Macにつなぎ、PC/Mac側でドライバが正しく認識されていることを確認しておく.
PC/MacとSpresenceをUSBケーブルでつなぐ
Mac OS の場合は、”Terminal”アプリのコマンドラインで、”%ls -la /dev/{tty,cu}.*” と入力すると、
iMac27:~ yasuaki$ ls -la /dev/{tty,cu}.*
crw-rw-rw- 1 root wheel 21, 5 Mar 8 07:08 /dev/cu.Bluetooth-Incoming-Port
crw-rw-rw- 1 root wheel 21, 15 Mar 13 18:53 /dev/cu.SLAB_USBtoUART
crw-rw-rw- 1 root wheel 21, 1 Mar 8 07:08 /dev/cu.usbserial-00203414
crw-rw-rw- 1 root wheel 21, 4 Mar 8 07:08 /dev/tty.Bluetooth-Incoming-Port
crw-rw-rw- 1 root wheel 21, 14 Mar 13 18:53 /dev/tty.SLAB_USBtoUART
crw-rw-rw- 1 root wheel 21, 0 Mar 8 07:08 /dev/tty.usbserial-00203414
“/dev/tty.SLAB_USBtoUART” と “/dev/cu.SLAB_USBtoUART” が存在していれば、ドライバはきちんと認識されている.
Windowsの場合は、コントロールパネルから”デバイスマネージャー”を開き、Port(COM&LPT)の項目を確認し、”Sillicon Labs CP210x USB to UART …”
が表示されていればOKだ.この際に、Windows側が割り当てているSerial Port番号を確認しておくと良いだろう.Spresenceにブートローダーを書き込む
Arduinoを使って何かのプログラムを動かそうとすると、”ブートローダー”という言葉が必ず現れる.LinuxやUNIX系のOSの扱いに慣れていれば、”ブートローダー”がOSの起動時に重要な役割を果たしている事ぐらいはご存じだろう.
Spresenceも同じようにユーザのプログラム(スケッチ)を、フラッシュメモリ等の内蔵デバイスからメモリ上に読み込んで、プログラムを実行させるためブートローダーが必要になる.
Spresenceの購入直後は、このブートローダー機能が実装されていないので、Spresenceにブートローダーを書き込む作業が必要だ.この作業を行わないとユーザが作成したプログラム(スケッチ)をSpresenceに実行させることができない.
ブートローダーを書き込む作業は、USBケーブルをSpresenceのメインボード側のMicro USB端子につないだ状態で、Arduino IDEの “Tools” メニューの “Port” を選び、サブメニューに現れる 先ほど確認したUSBシリアル変換ドライバ ”/dev/cu.SLAB_USBtoUART” (Windowsの場合は “COMx”)を選択する.これで手元のPC/MacとSpresenceとの間で、シリアル通信による情報のやりとりが可能となった.
Serial Portの設定を行う
Arduino IDEの “Tools” メニューの下から2つ目に”Programmer:”AVRISP mkII” という項目があるので、この項目を選んで表示されるサブメニューの一番下にある、”Spresence Firmware Updater” を選択する.
“Programmer” として”Spresence Firmware Updater”を選択
ここからの手順は一寸複雑で、Arduino IDEの “Tools” メニューの一番下にある “Burn Bootloader” を選択すると、”flash_writer”というArduino IDEとは別なアプリケーションプログラムが起動し、”End User License Agreement” 画面が現れる.画面下にある、”Go to download page”というボタンをクリックすると、今度はWEBブラウザ画面が開き、SONYのファームウェアダウンロードサイト(”https://developer.sony.com/file/download/download-spresense-firmware-v1-1-000″ 2019/3/13現在)が開く.
このダウンロードサイトからダウンロードした最新ファームウェアファイル “spresense-binaries-v1.1.0.zip” を、先ほどの”flash_writer”の”End User License Agreement”画面上にドラッグ&ドロップすることで最新ファームウェアがSpresenceに書き込まれる.
“flash_writer”アプリの”End User License Agreement”画面
WEBブラウザが開きFirmwareダウンロードサイトに飛ぶ
ダウンロードしたFirmwareを”End User License Agreement”画面上へドラッグ&ドロップする
ブートローダーの書き込みが終わりSpresenceが再起動する.
以上で、Spresenceのブートローダーの書き込み作業は完了である.以降は、ユーザがプログラム(スケッチ)を作成し、Spresenceに書き込んで実行させることが可能になる.
GNSSサンプルスケッチを動かしてみる
SpresenceのArduinoライブラリには、予めGNSS関連のサンプルスケッチが2種類組み込まれている.”gnss”と”gnss_tracker”という受信しているGNSSの情報をシリアルコンソール上にテキストデータとして表示する簡単なものだ.
今回は、Arduino環境でのSpresenceのGNSS受信機能を動かしてみる(慣れる)ことが目的なので、スケッチの中身については、殆ど触れないことにする.SpresenceがGNSS衛星を捉える様子を把握できれば十分だ.
GNSS受信のサンプルスケッチ “gnss” は、”File”メニューの中に、”Example” ⇒ “GNSS” ⇒ “gnss” と辿って行くと、新しいIDEウインドウが開き、”gnss” スケッチが編集画面に表示されていることだろう.
GNSS関連のサンプルスケッチを呼び出す
GNSS受信サンプルスケッチ “gnss”
この”gnss”サンプルスケッチをSpresenceで実行するには、IDEエディター画面の上部にある、左から二つめの丸で囲まれた右矢印のアイコンをクリックすると、スケッチのコンパイルを行い、実行プログラムの作成とSpresenceへのアップロードまで自動的に行ってくれる.
プログラムエラーが無ければ、Spresenceが再起動し、Spresence上でスケッチプログラムが稼働していることだろう.
GNSS受信サンプルスケッチ “gnss” がSpresence上で稼働
この状態でSpresence上で “gnss”スケッチが稼働しているのだが、IDEの編集画面上には何も表示されないので、本当にスケッチが動いているのかどうか確かめるには、”gnss”がシリアルコンソールへ吐き出す受信メッセージを確認する必要がある.
一番簡単な方法は、IDEの編集画面の上部右端にある、”Serial Monitor” アイコンをクリックして、”Serial Monitor”ウインドウを呼び出して確認する方法だろう.
“Serial Monitor”はとてもシンプルなシリアルコンソールではあるが、ユーザが出力をコピー&ペーストしたり表示を一時停止したりする機能が備わっていないので、可能で有れば他のシリアルコンソールツールを使って、”gnss”サンプルスケッチの出力を確認すると良いだろう.
“Serial Monitor”画面でコンソール出力を確認
高機能なシリアルコンソールツール “zoc7″で確認
サンプルスケッチ “gnss” の出力は、複数のGNSS衛星を捉えて位置情報の検出が成功する(Fix)すると、タイムスタンプ、捉えた衛星の数、Fix情報、緯度・経度情報を出力するだけの簡単なものだ.
GNSS衛生を捉えて位置情報が確定されるている様子
今回はマンションのベランダでGNSSの受信テストを行ったので、多くの衛星が建物の陰になってしまい、7〜9個程度の衛星しか捕捉できなかったが、見通しの開けた場所ではもっと多くの衛星を捕捉できただろう.
Spresenceの小さな内蔵チップアンテナでは、やはり役不足であることは否めないだろう.実用的なGNSS受信を行うには外部アンテナは必須だろう.
【蛇足】Spresenceのプロセッサチップは太陽光下では誤作動する模様.Spresenceのパッケージには ”遮光シール” なる物が含まれており、最初は何でこんな物が付属しているのか訝しげていたが、ひ弱なプロセッサチップは太陽光は苦手なようだ.Spresenceに限らず、Raspberry Piも間近でストロボを発光させるとフリーズしてしまうので、小型ボードコンピュータは ”もやしっ子” かも.
サンプルスケッチ “gnss” のコード
/*
* gnss.ino - GNSS example application
* Copyright 2018 Sony Semiconductor Solutions Corporation
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file gnss.ino
* @author Sony Semiconductor Solutions Corporation
* @brief GNSS example application
* @details Spresense has an built in GNSS receiver which supports GPS and other
* GNSS satellites. This skecth provides an example the GNSS operation.
* Simply upload the sketch, reset the board and check the USB serial
* output. After 3 seconds status information should start to appear.\n\n
*
* This example code is in the public domain.
*/
/* include the GNSS library */
#include
#define STRING_BUFFER_SIZE 128 /**< %Buffer size */
#define RESTART_CYCLE (60 * 5) /**< positioning test term */
static SpGnss Gnss; /**< SpGnss object */
/**
* @enum ParamSat
* @brief Satellite system
*/
enum ParamSat {
eSatGps, /**< GPS World wide coverage */
eSatGlonass, /**< GLONASS World wide coverage */
eSatGpsSbas, /**< GPS+SBAS North America */
eSatGpsGlonass, /**< GPS+Glonass World wide coverage */
eSatGpsQz1c, /**< GPS+QZSS_L1CA East Asia & Oceania */
eSatGpsGlonassQz1c, /**< GPS+Glonass+QZSS_L1CA East Asia & Oceania */
eSatGpsQz1cQz1S, /**< GPS+QZSS_L1CA+QZSS_L1S Japan */
};
/* Set this parameter depending on your current region. */
static enum ParamSat satType = eSatGps;
/**
* @brief Turn on / off the LED0 for CPU active notification.
*/
static void Led_isActive(void)
{
static int state = 1;
if (state == 1)
{
ledOn(PIN_LED0);
state = 0;
}
else
{
ledOff(PIN_LED0);
state = 1;
}
}
/**
* @brief Turn on / off the LED1 for positioning state notification.
*
* @param [in] state Positioning state
*/
static void Led_isPosfix(bool state)
{
if (state)
{
ledOn(PIN_LED1);
}
else
{
ledOff(PIN_LED1);
}
}
/**
* @brief Turn on / off the LED3 for error notification.
*
* @param [in] state Error state
*/
static void Led_isError(bool state)
{
if (state)
{
ledOn(PIN_LED3);
}
else
{
ledOff(PIN_LED3);
}
}
/**
* @brief Activate GNSS device and start positioning.
*/
void setup() {
/* put your setup code here, to run once: */
int error_flag = 0;
/* Set serial baudrate. */
Serial.begin(115200);
/* Wait HW initialization done. */
sleep(3);
/* Turn on all LED:Setup start. */
ledOn(PIN_LED0);
ledOn(PIN_LED1);
ledOn(PIN_LED2);
ledOn(PIN_LED3);
/* Set Debug mode to Info */
Gnss.setDebugMode(PrintInfo);
int result;
/* Activate GNSS device */
result = Gnss.begin();
if (result != 0)
{
Serial.println("Gnss begin error!!");
error_flag = 1;
}
else
{
/* Setup GNSS
* It is possible to setup up to two GNSS satellites systems.
* Depending on your location you can improve your accuracy by selecting different GNSS system than the GPS system.
* See: https://developer.sony.com/develop/spresense/developer-tools/get-started-using-nuttx/nuttx-developer-guide#_gnss
* for detailed information.
*/
switch (satType)
{
case eSatGps:
Gnss.select(GPS);
break;
case eSatGpsSbas:
Gnss.select(GPS);
Gnss.select(SBAS);
break;
case eSatGlonass:
Gnss.select(GLONASS);
break;
case eSatGpsGlonass:
Gnss.select(GPS);
Gnss.select(GLONASS);
break;
case eSatGpsQz1c:
Gnss.select(GPS);
Gnss.select(QZ_L1CA);
break;
case eSatGpsQz1cQz1S:
Gnss.select(GPS);
Gnss.select(QZ_L1CA);
Gnss.select(QZ_L1S);
break;
case eSatGpsGlonassQz1c:
default:
Gnss.select(GPS);
Gnss.select(GLONASS);
Gnss.select(QZ_L1CA);
break;
}
/* Start positioning */
result = Gnss.start(COLD_START);
if (result != 0)
{
Serial.println("Gnss start error!!");
error_flag = 1;
}
else
{
Serial.println("Gnss setup OK");
}
}
/* Turn off all LED:Setup done. */
ledOff(PIN_LED0);
ledOff(PIN_LED1);
ledOff(PIN_LED2);
ledOff(PIN_LED3);
/* Set error LED. */
if (error_flag == 1)
{
Led_isError(true);
exit(0);
}
}
/**
* @brief %Print position information.
*/
static void print_pos(SpNavData *pNavData)
{
char StringBuffer[STRING_BUFFER_SIZE];
/* print time */
snprintf(StringBuffer, STRING_BUFFER_SIZE, "%04d/%02d/%02d ", pNavData->time.year, pNavData->time.month, pNavData->time.day);
Serial.print(StringBuffer);
snprintf(StringBuffer, STRING_BUFFER_SIZE, "%02d:%02d:%02d.%06d, ", pNavData->time.hour, pNavData->time.minute, pNavData->time.sec, pNavData->time.usec);
Serial.print(StringBuffer);
/* print satellites count */
snprintf(StringBuffer, STRING_BUFFER_SIZE, "numSat:%2d, ", pNavData->numSatellites);
Serial.print(StringBuffer);
/* print position data */
if (pNavData->posFixMode == FixInvalid)
{
Serial.print("No-Fix, ");
}
else
{
Serial.print("Fix, ");
}
if (pNavData->posDataExist == 0)
{
Serial.print("No Position");
}
else
{
Serial.print("Lat=");
Serial.print(pNavData->latitude, 6);
Serial.print(", Lon=");
Serial.print(pNavData->longitude, 6);
}
Serial.println("");
}
/**
* @brief %Print satellite condition.
*/
static void print_condition(SpNavData *pNavData)
{
char StringBuffer[STRING_BUFFER_SIZE];
unsigned long cnt;
/* Print satellite count. */
snprintf(StringBuffer, STRING_BUFFER_SIZE, "numSatellites:%2d\n", pNavData->numSatellites);
Serial.print(StringBuffer);
for (cnt = 0; cnt < pNavData->numSatellites; cnt++)
{
const char *pType = "---";
SpSatelliteType sattype = pNavData->getSatelliteType(cnt);
/* Get satellite type. */
/* Keep it to three letters. */
switch (sattype)
{
case GPS:
pType = "GPS";
break;
case GLONASS:
pType = "GLN";
break;
case QZ_L1CA:
pType = "QCA";
break;
case SBAS:
pType = "SBA";
break;
case QZ_L1S:
pType = "Q1S";
break;
default:
pType = "UKN";
break;
}
/* Get print conditions. */
unsigned long Id = pNavData->getSatelliteId(cnt);
unsigned long Elv = pNavData->getSatelliteElevation(cnt);
unsigned long Azm = pNavData->getSatelliteAzimuth(cnt);
float sigLevel = pNavData->getSatelliteSignalLevel(cnt);
/* Print satellite condition. */
snprintf(StringBuffer, STRING_BUFFER_SIZE, "[%2d] Type:%s, Id:%2d, Elv:%2d, Azm:%3d, CN0:", cnt, pType, Id, Elv, Azm );
Serial.print(StringBuffer);
Serial.println(sigLevel, 6);
}
}
/**
* @brief %Print position information and satellite condition.
*
* @details When the loop count reaches the RESTART_CYCLE value, GNSS device is
* restarted.
*/
void loop()
{
/* put your main code here, to run repeatedly: */
static int LoopCount = 0;
static int LastPrintMin = 0;
/* Blink LED. */
Led_isActive();
/* Check update. */
if (Gnss.waitUpdate(-1))
{
/* Get NaviData. */
SpNavData NavData;
Gnss.getNavData(&NavData);
/* Set posfix LED. */
bool LedSet = (NavData.posDataExist && (NavData.posFixMode != FixInvalid));
Led_isPosfix(LedSet);
/* Print satellite information every minute. */
if (NavData.time.minute != LastPrintMin)
{
print_condition(&NavData);
LastPrintMin = NavData.time.minute;
}
/* Print position information. */
print_pos(&NavData);
}
else
{
/* Not update. */
Serial.println("data not update");
}
/* Check loop count. */
LoopCount++;
if (LoopCount >= RESTART_CYCLE)
{
int error_flag = 0;
/* Turn off LED0 */
ledOff(PIN_LED0);
/* Set posfix LED. */
Led_isPosfix(false);
/* Restart GNSS. */
if (Gnss.stop() != 0)
{
Serial.println("Gnss stop error!!");
error_flag = 1;
}
else if (Gnss.end() != 0)
{
Serial.println("Gnss end error!!");
error_flag = 1;
}
else
{
Serial.println("Gnss stop OK.");
}
if (Gnss.begin() != 0)
{
Serial.println("Gnss begin error!!");
error_flag = 1;
}
else if (Gnss.start(HOT_START) != 0)
{
Serial.println("Gnss start error!!");
error_flag = 1;
}
else
{
Serial.println("Gnss restart OK.");
}
LoopCount = 0;
/* Set error LED. */
if (error_flag == 1)
{
Led_isError(true);
exit(0);
}
}
}
このサンプルコードではGPS衛星だけしか捕捉しない設定になっているので、『みちびき』を捕捉できるようにサンプルコードの一部を修正してみる.
/* include the GNSS library */
#include
#define STRING_BUFFER_SIZE 128 /**< %Buffer size */
#define RESTART_CYCLE (60 * 5) /**< positioning test term */
static SpGnss Gnss; /**< SpGnss object */
/**
* @enum ParamSat
* @brief Satellite system
*/
enum ParamSat {
eSatGps, /**< GPS World wide coverage */
eSatGlonass, /**< GLONASS World wide coverage */
eSatGpsSbas, /**< GPS+SBAS North America */
eSatGpsGlonass, /**< GPS+Glonass World wide coverage */
eSatGpsQz1c, /**< GPS+QZSS_L1CA East Asia & Oceania */
eSatGpsGlonassQz1c, /**< GPS+Glonass+QZSS_L1CA East Asia & Oceania */
eSatGpsQz1cQz1S, /**< GPS+QZSS_L1CA+QZSS_L1S Japan */
};
/* Set this parameter depending on your current region. */
//static enum ParamSat satType = eSatGps;
static enum ParamSat satType = eSatGpsQz1cQz1S; <=== みちびきも対象にする
...
numSatellites: 9
[ 0] Type:GPS, Id:10, Elv:43, Azm:227, CN0:42.419998
[ 1] Type:GPS, Id:15, Elv: 0, Azm: 0, CN0:32.399998
[ 2] Type:GPS, Id:20, Elv: 0, Azm: 0, CN0:30.799999
[ 3] Type:GPS, Id:25, Elv:82, Azm:139, CN0:36.259998
[ 4] Type:GPS, Id:29, Elv: 0, Azm: 0, CN0:33.329998
[ 5] Type:Q1S, Id:183, Elv:53, Azm:204, CN0:18.240000
[ 6] Type:QCA, Id:193, Elv:53, Azm:204, CN0:34.059998
[ 7] Type:QCA, Id:195, Elv:21, Azm:167, CN0:36.200001
[ 8] Type:QCA, Id:199, Elv: 0, Azm: 0, CN0:29.769999
2019/03/15 01:34:00.000561, numSat: 9, No-Fix, No Position
2019/03/15 01:34:01.000538, numSat: 9, No-Fix, No Position
2019/03/15 01:34:01.837953, numSat: 9, Fix, Lat=35.xx3343, Lon=139.xx6819
2019/03/15 01:34:02.000572, numSat: 9, Fix, Lat=35.xx3343, Lon=139.xx6819
2019/03/15 01:34:03.000588, numSat: 9, Fix, Lat=35.xx3146, Lon=139.xx6951
2019/03/15 01:34:04.000567, numSat: 9, Fix, Lat=35.xx3116, Lon=139.xx6987
2019/03/15 01:34:05.000575, numSat: 9, Fix, Lat=35.xx3098, Lon=139.xx6981
2019/03/15 01:34:06.000584, numSat: 9, Fix, Lat=35.xx3088, Lon=139.xx6971
2019/03/15 01:34:07.000563, numSat: 8, Fix, Lat=35.xx3084, Lon=139.xx6964
2019/03/15 01:34:08.000572, numSat: 8, Fix, Lat=35.xx3080, Lon=139.xx6954
2019/03/15 01:34:09.000580, numSat: 8, Fix, Lat=35.xx3076, Lon=139.xx6948
2019/03/15 01:34:10.000589, numSat: 9, Fix, Lat=35.xx3065, Lon=139.xx6955
2019/03/15 01:34:11.000567, numSat: 9, Fix, Lat=35.xx3069, Lon=139.xx6949
2019/03/15 01:34:12.000576, numSat: 9, Fix, Lat=35.xx3061, Lon=139.xx6954
2019/03/15 01:34:13.000643, numSat: 9, Fix, Lat=35.xx3063, Lon=139.xx6949
2019/03/15 01:34:14.000564, numSat: 9, Fix, Lat=35.xx3071, Lon=139.xx6941
2019/03/15 01:34:15.000572, numSat: 9, Fix, Lat=35.xx3070, Lon=139.xx6937
2019/03/15 01:34:16.000581, numSat: 9, Fix, Lat=35.xx3065, Lon=139.xx6932
2019/03/15 01:34:17.000559, numSat: 9, Fix, Lat=35.xx3065, Lon=139.xx6930
2019/03/15 01:34:18.000600, numSat: 9, Fix, Lat=35.xx3074, Lon=139.xx6923
2019/03/15 01:34:19.000578, numSat: 9, Fix, Lat=35.xx3079, Lon=139.xx6918
2019/03/15 01:34:20.000587, numSat:10, Fix, Lat=35.xx3083, Lon=139.xx6913
2019/03/15 01:34:21.000564, numSat:10, Fix, Lat=35.xx3079, Lon=139.xx6910
2019/03/15 01:34:22.000575, numSat:10, Fix, Lat=35.xx3070, Lon=139.xx6916
みちびき1号機(ID=183:L1S、ID=193:L1C/A)、みちびき4号機(ID=195:L1C/A)、みちびき3号機(ID=199:L6)の情報が取得できていることが確認できる.
衛星のID番号に関する情報は、内閣府の『みちびき』情報サイト、”各国の測位衛星” に詳細な情報が載っている.
因みに、外部アクティブアンテナ(安物)をつないで見た場合のGNSS衛星の捕捉結果は下記のような結果だった.外部アクティブアンテナをつなぐことによる捕捉衛星数の増加と受信信号(CN0:Carrier-to-Noise Density)の向上が期待できる.
同軸ケーブルをランドパターンとチップアンテナの金属外皮部分に直半田付けした
numSatellites:16
[ 0] Type:GPS, Id:10, Elv:39, Azm:222, CN0:43.090000
[ 1] Type:GPS, Id:12, Elv:46, Azm: 46, CN0:17.090000
[ 2] Type:GPS, Id:14, Elv:39, Azm: 59, CN0:20.189999
[ 3] Type:GPS, Id:15, Elv: 2, Azm:132, CN0:0.000000
[ 4] Type:GPS, Id:20, Elv:21, Azm:197, CN0:35.380001
[ 5] Type:GPS, Id:22, Elv: 0, Azm: 70, CN0:0.000000
[ 6] Type:GPS, Id:24, Elv:20, Azm: 73, CN0:24.519999
[ 7] Type:GPS, Id:25, Elv:83, Azm: 99, CN0:36.090000
[ 8] Type:GPS, Id:29, Elv:20, Azm:141, CN0:37.270000
[ 9] Type:GPS, Id:31, Elv:24, Azm: 21, CN0:25.879999
[10] Type:GPS, Id:32, Elv:57, Azm: 62, CN0:16.920000
[11] Type:Q1S, Id:183, Elv:51, Azm:204, CN0:39.129997
[12] Type:QCA, Id:193, Elv:51, Azm:204, CN0:39.239998
[13] Type:QCA, Id:194, Elv:85, Azm: 45, CN0:27.699999
[14] Type:QCA, Id:195, Elv:22, Azm:167, CN0:31.939999
[15] Type:QCA, Id:199, Elv:46, Azm:201, CN0:35.279999
2019/03/16 01:39:00.000635, numSat:16, Fix, Lat=35.yy3123, Lon=139.xx6830
2019/03/16 01:39:01.000656, numSat:16, Fix, Lat=35.yy3122, Lon=139.xx6829
2019/03/16 01:39:02.000639, numSat:16, Fix, Lat=35.yy3124, Lon=139.xx6826
2019/03/16 01:39:03.000636, numSat:16, Fix, Lat=35.yy3126, Lon=139.xx6825
2019/03/16 01:39:04.000628, numSat:16, Fix, Lat=35.yy3127, Lon=139.xx6825
2019/03/16 01:39:05.000634, numSat:16, Fix, Lat=35.yy3127, Lon=139.xx6825
2019/03/16 01:39:06.000654, numSat:16, Fix, Lat=35.yy3126, Lon=139.xx6826
2019/03/16 01:39:07.000645, numSat:16, Fix, Lat=35.yy3127, Lon=139.xx6826
2019/03/16 01:39:08.000636, numSat:16, Fix, Lat=35.yy3127, Lon=139.xx6826
...
サンプルスケッチ "gnss_tracker" の出力サンプルとコード(一部抜粋)
もう一つのサンプルスケッチ"gnss_tracker" は、GNSSの受信データをNMEA形式で出力するサンプルだ.コードは1000行以上と長いのでメイン部分だけ抜粋している.
$GPGGA,005445.00,,,,,0,00,,,,,,,*48
$GPGGA,005446.00,,,,,0,00,,,,,,,*4B
$GPGGA,005447.00,,,,,0,00,,,,,,,*4A
$GPGGA,005448.00,,,,,0,00,,,,,,,*45
$GPGGA,005449.00,,,,,0,00,,,,,,,*44
$GPGGA,005449.78,35xx.9811,N,139xx.4145,E,1,05,4.7,31.7,M,,M,,*41
$GPGGA,005450.00,35xx.9811,N,139xx.4145,E,1,05,4.7,31.7,M,,M,,*46
$GPGGA,005451.00,35xx.9913,N,139xx.4005,E,1,04,4.8,28.1,M,,M,,*41
$GPGGA,005452.00,35xx.9840,N,139xx.4099,E,1,04,4.8,30.1,M,,M,,*49
$GPGGA,005453.00,35xx.9782,N,139xx.4150,E,1,04,4.8,32.9,M,,M,,*47
$GPGGA,005454.00,35xx.9797,N,139xx.4135,E,1,04,4.8,32.5,M,,M,,*4B
$GPGGA,005455.00,35xx.9808,N,139xx.4117,E,1,04,4.8,31.8,M,,M,,*4D
$GPGGA,005456.00,35xx.9866,N,139xx.4052,E,1,05,4.7,42.6,M,,M,,*42
$GPGGA,005457.00,35xx.9923,N,139xx.3976,E,1,05,4.7,59.3,M,,M,,*44
$GPGGA,005458.00,35xx.0005,N,139xx.3860,E,1,05,4.7,70.7,M,,M,,*41
$GPGGA,005459.00,35xx.0037,N,139xx.3792,E,1,05,4.7,77.9,M,,M,,*4A
$GPGGA,005500.00,35xx.9993,N,139xx.3847,E,1,05,4.7,67.9,M,,M,,*48
...
$GPGGA,005645.00,35xx.9784,N,139xx.4051,E,1,08,2.4,34.8,M,,M,,*45
$GPGGA,005646.00,35xx.9786,N,139xx.4053,E,1,08,2.4,33.0,M,,M,,*49
$GPGGA,005647.00,35xx.9792,N,139xx.4055,E,1,08,2.4,33.7,M,,M,,*4C
$GPGGA,005648.00,35xx.9801,N,139xx.4058,E,1,08,2.4,32.6,M,,M,,*4B
$GPGGA,005649.00,35xx.9804,N,139xx.4061,E,1,08,2.4,29.7,M,,M,,*4E
$GPGGA,005650.00,35xx.9808,N,139xx.4056,E,1,08,2.4,28.1,M,,M,,*49
$GPGGA,005651.00,35xx.9814,N,139xx.4049,E,1,08,2.4,26.8,M,,M,,*4C
...
因みに、左からNMEAセンテンス(GPGGA)、時刻、緯度、経度、測位モード、計算に使用した衛星数、水平精度情報(HDOP)、標高(海抜高度)(m)、チェックサムなどの情報が並んでいる.
/**
* @brief Activate GNSS device and setup positioning
*/
void setup()
{
/* put your setup code here, to run once: */
int error_flag = 0;
char KeyRead[2] = {0, 0};
/* Initialize the serial first for debug messages. */
/* Set serial baudeate. */
Serial.begin(SERIAL_BAUDRATE);
APP_PRINT("Please specify the operation mode.\n");
APP_PRINT(" 0:Run positioning. Output NMEA text.\n");
APP_PRINT("\n");
/* Wait mode select. */
sleep(3);
/* Turn on all LED:Setup start. */
ledOn(PIN_LED0);
ledOn(PIN_LED1);
ledOn(PIN_LED2);
ledOn(PIN_LED3);
/* Read key input. */
KeyRead[0] = Serial.read();
/* Convet to mode value. */
Mode = strtoul(KeyRead, NULL, 10);
APP_PRINT("set mode : ");
APP_PRINT(Mode);
APP_PRINT("\n");
switch (Mode) {
#if 0 // TBD:Not implemented
case eModeShell:
/* nop */
break;
#endif
case eModeNormal:
default:
error_flag = SetupPositioning();
break;
}
SetActiveSec = INITIAL_ACTIVE_TIME;
/* Turn off all LED:Setup done. */
ledOff(PIN_LED0);
ledOff(PIN_LED1);
ledOff(PIN_LED2);
ledOff(PIN_LED3);
/* Set error LED. */
if (error_flag == 1)
{
Led_isError(true);
}
}
/**
* @brief GNSS tracker loop
*
* @details Positioning is performed for the first 300 seconds after setup.
* After that, in each loop processing, it sleeps for SleepSec
* seconds and performs positioning ActiveSec seconds.
* The gnss_tracker use SatelliteSystem sattelites for positioning.\n\n
*
* Positioning result is notificated in every IntervalSec second.
* The result formatted to NMEA will be saved on SD card if the
* parameter NmeaOutFile is TRUE, or/and output to UART if the
* parameter NmeaOutUart is TRUE. NMEA is buffered for each
* notification. Write at once when ActiveSec completes. If SleepSec
* is set to 0, positioning is performed continuously.
*/
void loop() {
static int State = eStateActive;
static int TimeOut = IDLE_ACTIVE_TIME;
static bool PosFixflag = false;
static int skipUnstableCount = 10;
static char *pNmeaBuff = NULL;
static char *pBinaryBuffer = NULL;
/* Check state. */
if (State == eStateSleep)
{
/* Sleep. */
TimeOut--;
sleep(1);
APP_PRINT(">");
/* Counter Check. */
if (TimeOut <= 0)
{
APP_PRINT("\n");
/* Set active timeout. */
TimeOut = IDLE_ACTIVE_TIME;
SetActiveSec = Parameter.ActiveSec;
/* Set new mode. */
State = eStateActive;
/* Reset unstable counter */
skipUnstableCount = 10;
/* Go to Active mode. */
SleepOut();
}
else if ((TimeOut % 60) == 0)
{
APP_PRINT("\n");
}
}
else
{
/* Active. */
unsigned long BuffSize;
unsigned long WriteSize;
bool LedSet;
TimeOut -= Parameter.IntervalSec;
SpNavData NavData;
String NmeaString = "";
/* Blink LED. */
Led_isActive();
int WriteRequest = false;
/* Check update. */
if (Gnss.waitUpdate(Parameter.IntervalSec * 1000))
{
/* Get NavData. */
Gnss.getNavData(&NavData);
LedSet = ((NavData.posDataExist) && (NavData.posFixMode != 0));
if(PosFixflag != LedSet)
{
Led_isPosfix(LedSet);
PosFixflag = LedSet;
if(LedSet == true)
{
TimeOut = SetActiveSec;
WriteRequest = true;
}
}
/* Get Nmea Data. */
NmeaString = getNmeaGga(&NavData);
if (strlen(NmeaString.c_str()) == 0)
{
/* Error case. */
APP_PRINT_E("getNmea error");
Led_isError(true);
}
else
{
/* Output Nmea Data. */
if (Parameter.NmeaOutUart == true)
{
/* To Uart. */
APP_PRINT(NmeaString.c_str());
}
if (Parameter.NmeaOutFile == true)
{
/* To SDCard. */
BuffSize = NMEA_BUFFER_SIZE * (IDLE_ACTIVE_TIME / Parameter.IntervalSec);
if (pNmeaBuff == NULL)
{
/* Alloc buffer. */
pNmeaBuff = (char*)malloc(BuffSize);
if (pNmeaBuff != NULL)
{
/* Clear Buffer */
pNmeaBuff[0] = 0x00;
}
}
if (pNmeaBuff != NULL)
{
/* Store Nmea Data to buffer. */
strncat(pNmeaBuff, NmeaString.c_str(), BuffSize);
}
}
/* Output Binary Data. */
if (Parameter.BinaryOut == true)
{
BuffSize = Gnss.getPositionDataSize();
if (pBinaryBuffer == NULL)
{
/* Alloc buffer. */
pBinaryBuffer = (char*)malloc(Gnss.getPositionDataSize());
if (pBinaryBuffer != NULL)
{
/* Clear Buffer. */
pBinaryBuffer[0] = 0x00;
}
}
if (pBinaryBuffer != NULL)
{
if (Gnss.getPositionData(pBinaryBuffer) == BuffSize)
{
/* Write Binary Data. */
GnssPositionData *pAdr = (GnssPositionData*)pBinaryBuffer;
Led_isSdAccess(true);
WriteSize = WriteBinary((char*)&pAdr->MagicNumber, FilenameBin, sizeof(pAdr->MagicNumber), (FILE_WRITE | O_APPEND));
WriteSize += WriteBinary((char*)&pAdr->Data, FilenameBin, sizeof(pAdr->Data), (FILE_WRITE | O_APPEND));
WriteSize += WriteBinary((char*)&pAdr->CRC, FilenameBin, sizeof(pAdr->CRC), (FILE_WRITE | O_APPEND));
Led_isSdAccess(false);
/* Check result. */
if (WriteSize != BuffSize)
{
Led_isError(true);
}
}
}
}
}
}
/* Counter Check. */
if (TimeOut <= 0)
{
if (Parameter.SleepSec > 0)
{
/* Set new mode. */
State = eStateSleep;
/* Go to Sleep mode. */
SleepIn();
/* Set sleep timeout. */
TimeOut = Parameter.SleepSec;
}
else
{
/* Set sleep timeout. */
TimeOut = Parameter.SleepSec;
}
WriteRequest = true;
}
/* Check NMEA buffer. */
if(strlen(pNmeaBuff) > (BuffSize - NMEA_BUFFER_SIZE))
{
/* There is no capacity for writing in the next NMEA. */
}
/* Write NMEA data. */
if(WriteRequest == true)
{
if (pNmeaBuff != NULL)
{
/* Write Nmea Data. */
Led_isSdAccess(true);
WriteSize = WriteChar(pNmeaBuff, FilenameTxt, (FILE_WRITE | O_APPEND));
Led_isSdAccess(false);
/* Check result. */
if (WriteSize != strlen(pNmeaBuff))
{
Led_isError(true);
}
/* Clear Buffer */
pNmeaBuff[0] = 0x00;
}
}
}
}