2024年9月4日水曜日

小ネタ



 3分58秒頃からラジオゾンデのシーン。ゾンデをトラッキングするパラボラアンテナが写ってる。アンテナの横に風向風速計があるけど、こんな大きな皿の横じゃ正確な計測はできないだろうに。ゾンデを使っていないときはアンテナを下げるとか、あるいは本来は風向風速計はマストを伸ばすとか、そういう機材なのかな?

 ラジオゾンデって市販の機材を使っているんだろうけど、有事の際にもそれを使うのかな? ゾンデの周波数はある程度固定されているから、その周波数を見ていれば特科の運用の目安になりそう。かといってわざわざノイズフロア未満まで周波数拡散したラジオゾンデを作るって話にもならないだろうし。広く周波数拡散したラジオゾンデがあれば符号多重化できるけど、かといってラジオゾンデを複数同時に運用したいという需要もほとんど無いだろうしな。

 周波数管理の問題がなくなれば空間・時間分解能を大幅に増やして観測したりとかも可能なはずで、例えば数百m、数百秒間隔、くらいでゲリラ豪雨になりそうな積乱雲のその場観測を実施する、みたいなことも可能かもしれないけど、コスト的に厳しそうだし、大量の放球設備が必要になる欠点もあるし。こういう方向性ならドロップゾンデでたいてい観測できるだろうし。3次元の風速ベクトルは複数機の地上配備レーダで計測できるから、ゾンデならではの計測項目もほとんど無いしな。せいぜい気温とか湿度くらい? とはいえレーダでも氷粒か水滴かは判別できるから、0℃付近の高度からモデルで温度を推定したりはできるだろうし。複数のゾンデを同時に使いたい需要ってあるんだろうか。



 北米の教育現場向けの、川崎のロボットと近い構造・操作感の6軸ロボット。筐体は3Dプリンタで作っていて、トルクセンサ的なものが内蔵されているから、接触したら停止できる。繰り返し精度0.2mm。

 モノとしてはYouTubeでいろいろ転がってる多関節ロボットと似たようなものだろうし、いくら3Dプリンタ(FDM)製とはいえ、教育用ならそこそこの値段ではあるんだろう。とはいえ、本物のロボットよりは圧倒的に安いだろうし。電装系とかいくつかの機械部品をセットにして売って、自分たちで出力したフレームに組み込んでロボットの構造を理解しつつ、実際に操作して使い方を学習しつつ、みたいな感じで売るのかな。


 Chrome側なのか、ECサイト側の問題なのかわからないけど、とあるECサイトのクレカ入力画面でセキュリティコードにフォーカスをつけれないことがある。テキストボックスをクリックしてもカーソルが入らない。クレカ番号にフォーカスつけてTabを押すとセキュリティコードを飛ばしてフォーカスが移動して、そのままTabを押し続けるとブラウザのUIとか一通り一周して、クレカ番号を経由して、セキュリティコードにフォーカスがつく。一旦セキュリティコードにフォーカスが付けばGUIでも選択できるようになる。謎い。決済入力画面でクレカのセキュリティコードが入力できないって、ECサイトとしては致命的なはずなんだけど、なんで放置されてるんだろう。


 Windows版ChromeのGoogleレンズ、クソみたいに使いづらくなったな。Googleは時々本当に頭が悪い行いをする。


 麺をザルで湯切りするときに、ザルの加速度を重力加速度より大きくした場合(ジャッ、ジャッ、と音がなる状態)より、重力加速度より小さくした状態(ほとんど音がならない状態)のほうが、落ちる水の量が明らかに多い気がする。

 音が鳴る状態は麺がザルに高速で衝突している状態なわけだが、その場合は衝撃で水が等方的に飛散していると考えられる。その場合、大部分の水は麺にトラップされる。音が鳴らない状態は麺とザルが衝突せず、そのため水が飛散せず、純粋に加速度(高い比率の減速)によってのみ水が移動し、この場合は水が一方向へ移動し、最終的にザルの外へと排出される。というような考え方ができそう。あくまでもザルを振りながらつらつらと考えただけなので、仮説が正しいかとか、そもそも本当に水の排出量に差があるのかとかは確認していないが。


***


 試しにMode 3/A/Cのデコーダを作ってる

 2,758,621sps(Mode 3/A/Cのビットレートの4倍)で1090MHzをサンプリングして、Mode 3/A/Cっぽい波形を検知したら、そのパネルを明るく表示する。時々Fr24のスコークと同じ数字が見えたり、Fr24で見えない機体も見えたりする。

 コードは4096種類あるので、全部を一覧にするとかなり見づらい。4K液晶で大画面にすれば支障はないけど、FHDだと文字はほとんど読めない。4096を64x64で表示する場合、タイル1個あたりコードと高度の表示があるので、FHD全画面なら1080/64/2=8.44pxで表示しなきゃいけない。MS ゴシックはビットマップフォントも組み込んであるので小さいサイズでもそれなりに表示できるけど、とはいえすごく読みやすいというものでもない。普段遣いしたいなら、数字とマイナス記号だけあればいいから、自分でビットマップフォントを作るなり、美咲フォントあたりを読み込むなり、何らかの工夫が必要になる。まあ、こんなものを長期間使う予定もないので……

 パネルの並びはコード順と高度順が選べる。高度順で表示しておけば、連続的に変化すればMode Cであると判断できる(600fpmなら10秒で1コマ、3000fpmなら2秒で1コマの割合で変化する)。高度の割当がないパネルであればMode 3/Aと判断できる。高度割当がある場所で変化しないコードがあると、Mode 3/AとMode Cを判別できない。コード順はあまり使い道は無いかな? デバッグで全コードを網羅したダミーIQファイルを入れたときに便利くらい。使用中のコードがわかりやすいから新規に割り当てなきゃいけないときとかには便利だろうけど、そんな用途で使うわけもなく。

 パネルはSPI(ident)で左側を赤く塗って、Xビットで右側を赤く塗っている。Xビットは誰も出していないはずなんだけど、時々Xが含まれたフレームが出てくる。

 高度の表示は今のところ生値(QNE)を表示している。QNHを設定すれば補正するような機能は簡単に作れるだろうけど、今度はQNH値をどこから拾ってくるか、みたいな問題がある。アメダスとかで拾ってくればいいんだろうけど。

 Mode 3/A/Cは約0.69Mbps(1.45us間隔、0.45us幅、OOK)で、Mode Sのダウンリンクは1Mbps(2Mbaud PPM)。Mode Sにはプリアンブルが付いているけど、これも1MbaudでかつMode 3/A/Cのリプライより短いので、データ部をF2と誤認する可能性がある。Mode 3/A/CとMode Sの分離が結構面倒くさい。本物のトランスポンダ受信機なら6MHzとか10MHzとか広い帯域幅で受信してキッチリ見分けるんだろうけど、ワンセグチューナーくらいの帯域幅だとそういうわけにもいかない。ただ、古いMode 3/A/C用受信機に対してMode Sダウンリンクを与えても問題ないわけだから、なにかいい感じの解決方法があるのかも。今回はMode 3/A/Cリプライの変調レートの4倍のサンプリングレートに設定して、パルスの中央が閾値を超えていたら無視する、みたいな処理で分離している。本来は適当なPRFで問い合わせして、リプライを積分(or適当な距離ゲートを設定してカウント)してフィルタリングするのかな。

 ワンセグドングルはサンプリングレートが低いほうが処理利得が稼げるので感度が高いけど、フレームの抽出を考えると4baud(約2.76MHz)に設定せざるを得ない。10Mspsとか20Mspsとか取れるような受信機があれば色々遊べて面白いんだけどね。Mode 3/A/Cだけでなく、Mode Sも容易に復調できるようになる(Mode Sを受信したいなら2Mspsとか3Mspsで取ればいいわけだが、Mode 3/A/CとMode Sを1本のドングルで復調しようとすると面倒なことになる)。


 WAVファイルの構造、fmt␣の後にdataを置くわけだが、このdataを複数個おいたらどうなるんだろ、と思ってそういうファイルを作ってみた。結果、複数のdataチャンクがある場合、一番最後のdataチャンクを採用するらしい(Chrome、SoundEngine Free)。最後のdataチャンクの長さが0の場合、無音のWAVファイルとなる。SDR#はちょっと個性的で、最初のdataチャンクだけを読み込むらしい。

 ゲート(スケルチ)が開いたあとの数百~数千ポイントくらいの小さいサンプルを保存するのにどんなフォーマット使えばいいかなと思案中。

 ゲートが開くたびにWAVファイルを作るというのも一つの手。最近のWindowsなら小さいファイルはNTFSのMFT内に書き込まれるので、数百バイトのファイルでも4KiBとか浪費することは無い。ただ、いちいちファイルシステムに書き込みが走るし、ファイルシステムの操作は結構遅い。あと、WAVのヘッダが9%程度(@dataチャンク512バイト)あるので、それもちょっと無駄。ということで、WAVファイル内で分割できないかな、と思っていた次第。

 単純にZIPに突っ込んでも、ZIPはファイル単位でしか圧縮しないはずなので、WAVヘッダ分の損失は避けられない。が、ZIPの中に無圧縮のZIPを入れて、内側のZIPは単なるファイルアーカイブとして使えばいいんじゃないかな、と思い、方針を変更。この手の手法はtar.gzあたりがデファクトスタンダードな感じだけど、C#ではgzはともかく、tarが面倒っぽい。少なくとも標準ライブラリには用意されていなさそう。


 512バイトのdataチャンクが入ったWAVファイルは1個556バイトになる。これを128個作ると69.5KiBになる。これをWindowsでZIPに圧縮すると81.5KiBになる(dataは乱数だから圧縮ができず、ZIPの追加情報分で損をする)。

 C#でZIPを2重にしてうまいこと処理すると、556バイト*128=69.5KiBが67.4KiB(圧縮率3.12%)になる(オプション無指定。Fastestなら67.6KiB)。512個に増やすと278KiB→269KiBになる(3.35%)。ヘッダを圧縮できるので、数が増えれば効率はわずかながら改善する。

 ただ、C#でZIPを2重に作る場合、内側のZIPは一旦MemoryStream上に作成してから外側のZIPに書き込む必要がある。外側のZIPにEntryを作って内側のZIPのコンストラクタに渡すと、ZIPのEntryは書き込み専用だから、ZIPの付加情報はすべて追記で処理されるので、かなり大きなZIPファイルが出来上がる。一旦RAMに置いておかなきゃいけないのが、ちょっと面倒。まあ、1万個の生データを持ったとしても数MiBとか、ウインドウを長くとっても数十MiB程度なので、気にするほどのことはない。

/* WindowsのエクスプローラーでZIPを開くとCRC-32が見れるの、なんでこんな機能があるんだ…… */


 波形の生データをWAVで保存しておけば、SoundEngine Free等で波形を見れる

 IQファイルなので、脳内で振幅復調しなきゃいけないのが面倒だけど。あと、SoundEngine Freeはサンプリングレートの取り扱いがあまり良くないので、SDRのIQファイルとかを突っ込むと時間マーカーが正しくないのに注意する必要がある。

 波形を2重のZIPの中に入れて保存するようなものを作ってみたはいいけど、いまいち使いづらい。いちいちZIPからファイルを取り出さなきゃいけないし、複数の波形をまとめて見れない。

 結局、1個のファイル(かつ1個のdataチャンク)に大量に埋め込むのが楽、みたいなことになりそう。WAVでチャプター的なフォーマットがあればいいんだけど、たぶん無いはず。dataチャンクの前に適当な大きさのJUNKチャンクを作って、その中に位置と時刻を追記していく、みたいな方法は考えられそうだけど。あるいは、長さとか時刻もdataに埋め込んでやるか。例えばFFh 00h FFh 00hみたいな同期マーカーを埋めて、そこから波形長さ(次の同期マーカーまでの距離)やタイムスタンプを埋め込む、みたいなフォーマットも考えられる。


 ということで、単一のWAVに書き込むように方針転換

 WAVファイルを1個開くだけで時系列に並んでるので便利。結局一番シンプルな方法が一番楽なのだ。

 同期マーカーに0xFF0000FFFF0000FFを使っているので、I/Q軸ともに一旦フルスイングするので、区切りが目で見ても見やすい。音有りで再生するとめちゃくちゃなノイズになるけど、2.7MspsのIQなんて耳で聞くようなものでもないし。

 左右はMode 3/A/Cっぽい感じ。中央の3個はMode Sっぽい。ゲート時間をMode 3/A/Cに合わせているので、Mode Sみたいな長い信号は複数のウインドウに分割してキャプチャされる。コヒーレントで周波数オフセットが見える。


 簡易的なログビューアも作成

 dataの中に同期マーカーやタイムスタンプ、デコード結果等が埋め込まれているから、それを分割して読み込んで、一覧に表示できる。リストから選択すれば生波形が表示される(タイムスタンプはあくまでもデコーダにサンプルが入った時刻を記録しているだけなので、IQファイルをTCP経由で注入した場合は実際とは異なる時刻が記録される)。

 信号によってはパルス間でコヒーレントな信号が出てくる。まあ、最近のトランスポンダなら水晶/PLLで1090MHzのCWを作ってPAの前でゲートするみたいな信号処理のはずなので、わざわざインコヒーレントで作る必要もなく。とはいえ、コヒーレント復調で処理利得を稼げるほど普及しているわけでもないはず(実際、インコヒーレントな信号も入ってるし)。/* コヒーレントなMode 3/A/Cリプライってドップラ周波数の推定に使える程度の精度あるんかしら? */

 スケルチは開いたが正常にデコードできない波形とかも全部記録している。デコードできている場合はコードまたは高度に応じた高さにドットを、デコードできていない場合は線として表示している。高度で表示すれば上昇や降下に応じてドットが並ぶ。

 ざっと見た感じデコードできていない信号はMode Sか弱すぎる信号(トリガレベルを低く設定しすぎた)みたいな感じだから、デコーダとしてはある程度正常に動いているかな。24時間くらい受信してみると400MBちょっと。うちのあたりはあまりトラフィックが多くないので、特に夕方から早朝はほとんど信号は無い。400MBで約150万件くらいの波形になるけど、ビューアも結構サクサク動作する。

 ただ、波形を肉眼で見れば十分に綺麗で非常に強い信号なのに、デコードには失敗している信号とかも結構な数あるので、デコーダはもう改善の余地ありだな。信号が強いとパルス間で落ちきらず、閾値を超えている(=Mode Sっぽい)と判断しているらしい。現在はパルス間の閾値はトリガレベルで判定しているから、例えばF1/F2の4分の1とかも合わせて判断に使えば、強い信号もデコードできるはず。

 今のところ、ログビューワーは単純にログを見る機能しかない。サーバー機能があればログの波形を再生してデコーダソフトにもう一度注入できるので、デコードに失敗した信号を再度復調処理できる。デコード処理を変えたときの動作確認とかに便利そう。


 Xビットがアサートされている信号が時々入るけど、実際に波形を見ても、波形が綺麗(全ビットで振幅が安定でしかもコヒーレント)かつXビットがアサートされているから、ノイズとかではなさそう。うーん…… Xビットを使っている機体が存在しているのか?

 複数の応答が重なると、XとかSPIが誤認される波形になる

 この場合、F2やSPIの後ろにもビットが続いて、振幅がガタガタだったり、インコヒーレントだったり、目で見ればフレームが重なった場合はそれらしい波形になる。簡単な処理でもある程度は除去できそう。例えばビットパルスがフレーミングパルスより数割以上高いとか低い状態ならパルスが重なっている、とか、そういうロジックが使える。このあたりはENRIが詳しく研究していたはずなので、探せば色々出てくるはず。

 信号が重なるかどうかはインテロゲータと受信機を焦点に置いて、2個以上のトランスポンダが一定の双曲線に乗ったときに発生するので、位置関係が絶えず変化する航空機のトランスポンダでは発生しやすいのかもしれない。


 スコーク1200でQNE1300ftくらいのやつが受信できることがある。Fr24にはそれっぽい機体は見当たらない。アメダスで1006.2hPaなので換算して29.71inHg、QNHに換算すると1410ft/430mくらい? うちの周りは海抜200m前後くらいなので、AGL200m程度。少し離れたら急に標高が高くなるから、場合によってはもっと低い。だいぶ低い高度で飛んでるな。なんの機体だろう?

 日没後に1200でMode 3/Aが取れることがある(1200はMode C割当なし)。同時に2000ftくらいのMode Cらしいコードも入ることがある。旭川空港あたりで夜間飛行の訓練とかやってるのかな? 1090MHzが受信できるならVHFも受信できるはずだから、上からの一方通行とはいえ、なにか聞こえるかな。

 あるMode 3/Cコードが2.8時間くらい安定して受信できていた。なんの機体なんだろう? 測量とかに使う機体なのかな。でもそういう機体ってADS-Bも出してそう(Fr24で見えそう)だが。

 Mode 3/A/Cだけ受信すると「トランスポンダが存在していること」だけしかわからないのであまりおもしろくないのよなぁ。その機体がどこにいるのか(どの方向にいるのか)がわからない。飛行場まで視線が通るなら同時に1030MHzを受信して質問を受信すれば双曲線が求まるから、Mode 3/AとMode Cの分離ができれば双曲線と平面の交点で飛行機の位置が推定できるのだが、家から旭川空港まではさすがに遠すぎる。確か三菱電機かどこかがだいぶ前にこういうコンセプトの開発をやっていた気がする。あれどうなったのかな? 運用の制限(インテロゲータから可視の範囲でしか使えない)が厳しいので使い勝手悪そうだが。あと、問い合わせのタイミングはP2(SLSパルス)から推定するけど、P2だけを見てもMode 3/AとMode Cを識別できないので、コードと高度の分離が難しそう。高利得のアンテナを使えばP1/P3もサイドローブから受信できるのかもしれないけど。

 複数のドングルを使えば、時差を見るなり、アンテナの指向性で分離するなりで、方位も推定できそう。2.7Msps程度だと距離分解能は100m程度しかない。30Mspsでもようやく10m程度になる。SDRでRDFを作るような例もあるけど、どうやって処理してるんだろう? CW的な電波であれば位相差を見ればいいってことなんだろうか。せっかく同じモデル(ロット違いではあるけど)のSDRドングルを3本も買っちゃったし、RDF的なやつも試してみたいけど、とはいえコヒーレント(ビームフォーミング)をやるにはクロック周りを改造しなきゃいけないのでちょっと手間がかかるんだよな。。。

 Fr24のMLATって2Msps程度でサンプリングしてるんだろうけど、距離分解能150m程度からどうやって処理してるんだろうか。あと、ADS-Bを出していない機体を追尾するってことはMode 3/A/Cをデコードして推定してるんだろうけど、2MspsでもMode 3/A/Cをデコードできるんかな。


 飛行機の気圧高度ってどうやって計算しているものなんだろう? en.wikipediaに書いてあるNOAAの計算式を使っているんだろうか? この計算では単に現在の気圧だけを考慮して、それ以外(海面気圧や海面気温)は一切取り込んでいない。そのあたりの誤差はQNHで補正するってことなんだろうか。とはいえ、QNHの計算にしたって気温は使ってないし。

 そもそも、飛行機の高度で必要なのはあくまでも交差した航路で衝突しないように、あるいは離着陸時に滑走路との高さを正しく認識できるように、程度のものであって、厳密な高度計算は必要なく、むしろ簡易的な処理で小型機を含めて多くの飛行機に普及させるのが目的であって、すべての機体で同一の計算式を使用していれば実際の高度と違ったところで特に問題はない(標高が高い山や建物に関しては航空管制で接近しないようにさせる)、みたいな方針なんだろうか。

 気温が高くなると空気密度は低くなるので、ある体積に含まれる質量が減るから、高度あたりの気圧変化は小さくなるはず。例えば同じセパレーションでも、気温の低い場所では近い高度になったり、気温の高い場所では離れたりするんだろうか。ATCとかATISで得られるQNHは単位はinHgとかmbarだけど、実際のところは飛行場の海面気圧とは別物で、気温等の誤差要素をまとめて補正するような別の単位系と考えるべきなのかな。

 飛行機の気圧高度の計算がwikipediaの計算式でいいなら、QNHの計算は結構簡単な気がする。GPSなりDEMなりで標高がわかっている場所で気圧を計測して、それから求めた気圧高度と実際の高度の差を求めて、1000ftあたり1inで換算すればいい。

 wikipedia曰く、QNHにinHgを使っているのは「カナダを除く北米と日本」だそう。2カ国だけでしか使ってないのか? 日本もアメリカのヤードポンド法を馬鹿にできないな。まあ、LiveATCでトロントのATISを聴いてみると明らかにinHgだし、成田はhPaとinHgの両方を使ってるっぽいけど。羽田はinHgだけかな。飛行機がたくさん飛んでいる場所では古い気圧高度計のリプレイスも考えなきゃいけないだろうし、インペリアルからメトリックへ切り替えるのはそう簡単ではなさそう。

 inHgは1"=1000'換算だけど、hPa/mbarは1mbar=30'換算なので、1.6%くらい差がある。単に下駄の高さが変わるだけなので、日本国内の空港ならそう大きな違いにはならないだろうけど。海外の標高が高い空港だとinHgとhPa/mbarの併用は難しいかも。たぶん100ftとか200ftくらいの誤差になるはず。まあ、上下の分離を大きめに取るとか、あるいは標高の高い空港は能力に余裕のある大型機しか使わないだろうから、併用せず一気に移行するとか、そんな感じで運用はできそう。


 C#のFileStream、あるスレッドでゴリゴリ書き込みつつ他のスレッドで数秒~1時間に1回程度の適当な頻度でFlush(true)する、みたいな処理ってどうやって書くのがいいんだろうか。

 スレッドセーフにしたいならStream.Synchronizedでprivate class SyncStreamのインスタンスが得られて、これを経由するとスレッドセーフになるらしい。ただ、SyncStream.FlushはあくまでもStream.Flushを呼ぶだけだから、FileStream.Flush(true)を呼ぶことはできない。

 SyncStreamのメソッド呼び出しはStream.Synchronizedに渡したStreamをlockに使って再呼び出しするだけだから、FileStreamを渡してFlush(true)したいのであれば、同じインスタンスでlock(fs){fs.Flush(true);}的なコードを書いてもいいはず。内部処理に依存するのできれいなやり方じゃないけど。


 C#、安定ソートが無いらしいのが地味に不便。

 List<T>で安定ソートを手軽に(パフォーマンスはある程度犠牲にしても)実行したい場合、var a=list.ToArray();list.Clear();list.AddRange(a.OrderBy(a=>a));的な感じが一番簡単かな? ToArrayで配列を作って、OrderByでも配列を作るので、2重にヒープの確保が行われる。List<T>でなくT[]なら直接OrderByすれば1回で済む。

 Microsoftのドキュメント、Array.SortやList<T>.Sortには丁寧に「不安定ソートだよ」と書いてあるのに、MemoryExtension.Sort(Span<T>には何も書いてないのが不親切。


 このあいだMAUIで遊ぶためにVisual Studioを更新したからか、int[] a = [ 1, 2, 3, ];みたいなコードがフォーマットしたときに意図しない形にフォーマットされるのはなくなってある意味では便利になったんだけど、[ ... ]の構文の中のフォーマットが一律で無効になったらしく、Task.WaitAll([Task.Run(()=>{...}),Task.Run(()=>{})]);的なコードのラムダ式で書いたコードもフォーマットされなくなったのがなんとも不便。/* 設定で変えられるのかもしれないけど */

0 件のコメント:

コメントを投稿