能登地震の波形のスペクトルを求めてみた

正弦波の合成だけでは実用性はないので、今年の元旦の能登地震の公開されている波形データからスペクトルを求めてみた

公開先はこちらですが、トップにある輪島市のデータを使っています、形式はcsvなのでヘッダー情報除けばPythonやRustで簡単に処理できます

・ヘッダー情報

SITE CODE= 67016輪島市門前町走出        ,37.4962,137.2705,15.86,7.6,45292.67387,8.93,15.21
 LAT.=   37.2871,,,,,,,
 LON.=  136.7680,,,,,,,
 SAMPLING RATE= 100Hz,,,,,,,
 UNIT  = gal(cm/s/s),,,,,,,
INITIAL TIME = 2024 01 01 16 10 10,,,,,,,
 NS,EW,UD,,,,,

 

気象庁|強震観測データ|2024/1/1 石川県能登地方の地震

Pythonで全時間領域を対象にしてみたもの(画像はUD軸)

リンク先にある波形と比較するとほぼ類似の形状になってます

 

以下はRustで部分切り出し(全体で100サンプル/secで120,000フレーム、つまり120秒分のデータがありますが一番のピークの3,000~4012部分の切り出し)を、窓処理してFFTしてみたもの

<NS波形>

<NSスペクトラム>

以下はEW

以下はUD

 

NS/EWに比較すると周波数成分が高い方に出てきます、Pythonの全時間軸ともちろん傾向的には同じようなスペクトルになっています

X軸の20がほぼ10Hzに相当します

 

以下はPythonのコードになりますが、対話形式でほぼGeminiで作成させたコードがそのまま動きました、きちんと境界条件を与えてやれば人間がコードを書く手間を大幅に省力化できます、コードは読めないとダメですが

import numpy as np
import matplotlib.pyplot as plt

def fft_csv(filename, column_index, sampling_rate):
  """
  CSVファイルの特定列データをFFTする関数

  Args:
    filename: CSVファイルのパス
    column_index: FFTしたい列のインデックス (0から始まる)
    sampling_rate: サンプリングレート

  Returns:
    freqs: 周波数
    fft_result: FFTの結果
  """

  # CSVファイルを読み込む
  data = np.loadtxt(filename, delimiter=',', skiprows=1)  # ヘッダー行をスキップ

  # 指定した列のデータを取得
  target_data = data[:, column_index]

  # FFTを実行
  fft_result = np.fft.fft(target_data)

  # 周波数軸を作成
  N = len(target_data)
  freqs = np.fft.fftfreq(N, 1.0/sampling_rate)

  return freqs, fft_result

# 使用例

if __name__ == "__main__":
    filename = "noto.csv"  # CSVファイル名
    column_index = 2  # FFTしたい列 (3列目) 0 :NS/1 :EW/2 :UD
    sampling_rate = 100  # サンプリングレート

    freqs, fft_result = fft_csv(filename, column_index, sampling_rate)

# 結果をプロット (両対数プロット)
    plt.loglog(freqs, np.abs(fft_result))
    plt.xlabel("Frequency [Hz]")
    plt.ylabel("Amplitude")
    plt.title("FFT of Column {}".format(column_index+1))
    plt.grid(True)
    plt.show()

 

admin

 

micro:bitからのログの方法

Wi-Fi機能があれば簡単ですが、micro:bitには無いので普通はBLE使うのですが、BLEはいまいち使いづらい、

というわけで、もう一台micro:bitを用意してmicro:bit間は専用のradio機能で通信、受け側はUSB serialを使えば割と簡単にログが取れます。パソコンでのターミナルのシリアル動作はいまいち不安定(文字が抜ける)なのでMakeCodeのログ機能で表示させてみました、最終的にはProcessingで物体の状態を表示させるのが目的ですが、

micro:bitの送り側からはコンパス情報と加速度情報を送って、受け側にシリアル転送、MakeCodeのログ機能でビジュアル化しています。

<コンパス機能>

角度360から0は断絶してますが、micro:bitを徐々に回転させた時の数値の変化

<x軸の加速度>

micro:bitを手で左右(x軸方向)に振ってる状態

事前の処理は必要ですが、これらを使ってクラゲホバークラフトの安定化に使うつもり、

P.S. USB シリアルのデータ抜け、飛びはM1 Macで起こるけれどもIntel Macでは問題なさそうだ、MakeCodeではまともそうな理屈はつきませんが、

あと、MakeCode以外でUSBシリアル使う時にはパソコンのリブートでポート番号が変わるのは要注意

 

admin

 

 

 

 

 

 

 

シリカゲル再生

乾燥剤はものによっては再生可能らしいのでやってみた

要は熱を加えて水分追い出せば良いのだから、最初はフィラメント脱湿機でやってみたけど赤い色(吸湿状態)は数時間ではほぼ変化しない

で、世の中でよくやられているようにフライパン(弱火)で炒ってみる、これは顕著に変化して、徐々に赤玉が青玉に変化していく

写真は赤玉ほとんど見えなくなった状態なのでこの程度で完了、二、三回はこの方法で再生できそうだ、再生後は適度に空気を通す袋に入れて使用

 

admin

地震計を作ってみる(その4)— コードと回路からの考察

震度計算で気象庁から提示されているのは以下のリンクになります。

https://www.data.jma.go.jp/eqev/data/kyoshin/kaisetsu/calc_sindo.html

じゃ、近似計算でどの程度近似なのかをちょっと考察、

計算方法は① 加速度波形を周波数軸に変換(実質FFT)して、② 重み付けを行ない、さらに③ それを周波数に変換(実質IFFT)して、④ 0.3秒以上継続する値の最小値を求めて⑤ 震度計算式(対数目盛)で求めた値を四捨五入と切り捨てで震度とする、というステップになりますが、

近似計算と気象庁提示の差分のポイントは、②の周波数重み付けに差があります、このフィルターの特性は建物や構造物への影響度を考慮したものだと思われます

filter.png

近似計算方式では、ハイカット側はデジタル処理ではなくて、以下の回路図で加速度計に付加されているキャパシタ(0.1μF)で一次のカットオフ周波数30Hzぐらい(デフォルトでは内蔵の3300pFでカットオフ800Hzを変化させている、一方ロー側は格段の処理はされていない模様

コード(以下のリンク)でフィルター処理のコメントありますが、実質は平滑処理になってます

https://github.com/p2pquake/rpi-seismometer/blob/master/seismic_scale.py

とはいえ、FFTを使って計算するのとそれほどの差があるわけでもなさそうだから、実用的には十分じゃないかというところで、正確に震度を求めようとすると計測器を設置する工事だけでも大変なのだから

 

P.S. pythonでnumpyの機能にあるFFTを実行してみると、

M1 Macとラズパイzeroでの速度比較

<用意したデータ>
sampling_rate = 2000  # サンプリング周波数(Hz)
T = 1 / sampling_rate  # サンプリング間隔
t = np.arange(0, 1.0, T)  # 時間ベクトル

# 信号生成(50Hzと120Hzのサイン波を重ねたもの)
f1 = 100  # Hz
f2 = 300  # Hz
signal = np.sin(2*np.pi*f1*t) + 0.5*np.sin(2*np.pi*f2*t)

<実行速度>
M1 Mac:		np.fft.fft:     0.000027 [sec] 
ラズパイゼロ:	np.fft.fft:	 0.001830 [sec] 

ふーむ、およそ60倍ぐらい違うか、想定範囲だけど

ラズパイzeroでもフレーム周期5msでできなくはなさそうなレベル

 

admin

FromとInto変換できる浮動小数と整数の精度条件(@Rust)

まあどちらもトレイトで定義されているわけで、対応する型が定義されてなければ変換できないわけですが、Begginig Rustのコードで浮動小数点に整数から変換するコードが出てきますが、テキストではi32をfloat変換するには相手先はf64でなければできないとあります。

https://doc.rust-lang.org/std/convert/trait.From.html

で該当部分を抜き出すと、

impl From<i16> for f32
impl From<i16> for f64
~
impl From<i32> for f64

i32からfloatに変換するときにはf32ではダメで、f64になるよということで無理に変換しようとするとエラーメッセージは、

the trait `From<i32>` is not implemented for `f32`

なので、そういうことです。i32の精度はf32では保持できないから変換に意味がないから用意されてないということです。

 

admin

所有権と借用について(@Rust)

Rustのメモリ管理の特徴の最大の機能と言えるものが所有権ですが、所有権(以下ではほぼ借用についての記述)と参照借用の特徴的なところを簡単なコードで説明できるようにしてみました。まあ、オリジナルのドキュメント読めばわかることですが。

以下のコメントでcase1~4がそれに該当するところです。

case1 : mutableな変数(ここではString型)をmutableで参照借用すると、元の変数は変更ができなくなるし、一度借用したらその後の複数借用もできない。変更を伴うと制限が出てくるのは、所有権に類似してます。

case2 : mutableにできる変数をimmutableで扱う場合は変更される可能性がないので、参照処理はcopyになり尚且つ複数のcopyも許容されます。

case3 : mutableにできない静的領域に格納される変数の場合には参照(&)を付加しなくとも暗黙で参照(copy)できます。文字型(char)はcopy traitを実装しているから。(ヒープを利用するString型のcopyにはclone()を使います)

case4 : 参照と非参照の比較はできないので、参照戻し処理(*の付加)が必要になります。case1でも参照型で使える関数の制限で参照戻しを使っていますが、

fn main() {
    // case1 : borrow in "String":mutable case
    //
    let mut aa = String::from("abc");       // variable length "String"
    let bb = &mut aa;
    bb.push_str("gh");
    let cc = format!("{}{}", "ko", bb);    // Linking "Strings"
    println!("mutable case (bb, cc) : {}, {}", bb, cc);

    //aa += "refa";     // error ; second mutable borrow occurs here, after first borrow you can't change the original content
    *bb += "refb";      // you can't use `+=` on type `&mut String`, use reference return
    println!("updated bb : {}", bb);

    // case2 : immutable case
    //
    let aa_i = String::from("kile");
    let bb_i1 = &aa_i;
    let bb_i2 = &aa_i;
    println!("immutable case (bb_i1, bb_i2) : {}, {}", bb_i1, bb_i2);

    // case3 :  literal(immutable) case in "str"
    //
    let aa_c = "abc";
    let bb_c = aa_c;    // refrence in immutable case, you can eliminate "&"
    //bb_c.push_str("gh");  // "push_str" is not found in `&&str` since bb_c is immutable
    let cc_c = format!("{}{}", "ko", bb_c); // linked output is a "String"
    println!("bb_c, cc_c : {}, {}", bb_c, cc_c);

    // case4 : reference return
    //
    let original = String::from("def");
    let reference = &original;
    if original == *reference {        // comparing "String == &String" causes compile error, if "*" is not used
        println!("equal");
    } else {
        println!("not equal");
    }
}

所有権の本質は、ある変数(ヒープ上の変数、静的領域上の変数に対しては所有権の概念は無い)を変更できるのは所有権を持っている唯一の変数だけだよといっていますが、借用は所有権は移動せずに参照/変更する権利は有すると言うことになります。変更できる借用(可変借用)は実質的には所有権を移動することと同等なので、掛かる制限が出てくると言うことになります。

 

admin

 

Maker Faire Tokyo 2023

本日初めて行ってきました。オレイリージャパンの主催でスポンサーも多数、出展は個人レベルから趣味のグループ、大学が主体でメーカーは少ない印象です。

そのうちのいくつかの作品から、全部の写真は多すぎなのでおいおい整理、

1 静電容量方式でキータッチすると音階を指定できるキーボード。

2 自作のMRI、液体窒素冷却とかしなくとも解像度が低くなるけれども核磁気共鳴はセンスできるそうです。画像に落とし込むソフトも全部自作だそうです。おそらく標準のライブラリはあるでしょうが。

3 両手で持って、リングのように見えるのが弦相当で演奏できる電子楽器。

会場の作品を見ると、多くはラズパイ、M5Stackを使っています。これはM5Stackのブース。

全部を細かく見だすと、到底一日ではカバーできそうもないので最後は結構駆け足になってしまいました。

個人的に会場の作品制作などを見て、あれば良さそう(欲しい)と思ったのは、レーザーカッターと多軸関節ロボットアーム。どちらも10万円以下で個人でなら使えそうなものが手にはいる。

 

admin

 

 

 

 

クロージャー(Golang and Rust)

現代の言語ではクロージャー機能を多くの言語で持っていますが、同じ機能(単純な1の加算)をGolangとRustで実装の比較。Rustの説明で出てくる、クロージャーは環境をキャプチャーできる匿名関数という定義はわかりやすい。

<Golang>

package main

import "fmt"

func add(i int) func() int {
	n := i

	clo := func() int {
		n++
		return n
	}

	return clo		// equivalent to specify anonymous function here and maybe it's more popular
}

func main() {
	a := add(1)
	for i := 1; i < 10; i++ {
		fmt.Println(a())
	}
}

<Rust>

fn f1(i: i32) -> Box<dyn FnMut -> i32> {	// FnMut recieves &mut self
	let mut i = i;						// dyn keyword sepcifies a trait pointer in the heap area
	Box::new(move || {				// force to allocate values in the heap area
		i += 1;
		i
	})
 }
 
 fn main() {
	 let mut cup = f1(0);
	 for _ in 1..10 {
		 println!("{}", cup());
	 }
 }

コードの参考は、

https://scrapbox.io/takker/Rustで関数を返す関数を作

FnMutの解説は、

https://qiita.com/hiratasa/items/c1735dc4c7c78b0b55e9

Fnでは変更できないし、FnOnceでは一度しか呼べないのでFnMutの指定になります。

比較してみると、Rustの方が細かな指定が必要ですが、これはメモリ管理に関する指定を明示的に行わなければいけない言語だからでしょう。したがってこれぐらいの例では変わらないけれども、コードは長めになります。比較してGolangは細かな操作はしなくとも使うだけなら簡単ということになるでしょうか。

Box<dyn … >については、

Boxは変数をスタック領域ではなくヒープ領域への割り当て、dynについては以下の通り、

https://doc.rust-lang.org/stable/rust-by-example/trait/dyn.html

“Rust tries to be as explicit as possible whenever it allocates memory on the heap. So if your function returns a pointer-to-trait-on-heap in this way, you need to write the return type with the dynkeyword

 

admin

 

Rustで特徴的と思ったところ(その1)

Rustのオンラインブック、

https://doc.rust-jp.rs/book-ja/title-page.html

を読み始めて、第5章までの分です。自分の他言語経験から見てユニークと思えるところを、並びは時系列です。特にC/C++などで起こるメモリ管理上のバグの作り込みを防ぐためのメモリ管理機能(所有権)がいちばん特徴的だろうと思う。

・crate : クレートはRustソースコードを集めたものである、バイナリ形式でもそういうけども

・Cargo.toml : Tom’s Obvious, Minimal Language、パッケージのリストと版数を指定するテキストファイル

・Cargo : Rustのビルドシステム兼パッケージマネージャ

 cargo checkでコンパイルエラーがチェックできる

・println! : !はマクロの意味、簡易に結果出力で使用で構造体ではコンパイルエラー、回避方法(20行ぐらい下)はありますが

・関連関数

String::new() :newは関連関数で、Stringインスタンス作成する

・参照変数のmutable化

io::stdin().read_line(&mut guess) : &mutは参照変数をミュータブルにする、記法はmut &guessではない

・Cargo.lockファイル : ビルドの再現性確保のためにクレートのバージョンを保存しておく、自動生成されユーザがいじるファイルでは無い

・traitはデータ型を分類する仕組み、crateの要素(任意の型となりうるSelfに対して定義されたメソッドの集合のこと)、類型的にはJavaのinterfaceのようなもの

・Shadowing : 前の値を新しい値で覆い隠す(shadowする)ことが許されている、型は違っていても同じでも良い

・タプルの要素は型が違っても大丈夫

let x: (i32, f64, u8) = (500, 6.4, 1);

    let five_hundred = x.0; // タプル要素へのダイレクトアクセス

・Rustの配列は固定長、ベクター型は可変長

    let a: [i32: 5] = [1, 2, 3, 4, 5];

・戻り値の指定方法は;をつけてはいけない、-> は戻り値の型を指定

fn five() -> i32 {
        5 // it’s formula not statement
}         // return value

・条件式を右辺に記述できる

let number = if condition { 5 } else { 6 }; // possible if both type is same

・所有権:これはRustのコア機能(メモリ管理がRustの一大機能)、本質はヒープ領域の管理になりますが

・println!に指示する: 直接エンドユーザ向けの出力で、構造体はこれではダメで

#[derive(Debug)]行を追加必要

・メソッド記法は構造体の文脈で定義(impl Rectangle)される、Golangの構造体との関連付けに書式は違うが似てると思う

struct Rectangle {
    width: u32,
    height: u32,
}

impl Rectangle {
    fn area(&self) -> u32 { // implしてるから&self、構造体インスタンスへの参照
        self.width * self.height
    }
}

・関連関数:implブロック内で定義される、selfを引数に取らない関数。構造体と関連づけられていないからメソッドではない、String::new()はその例。よくある使い方は構造体のインスタンスを返却する関数。

 

admin

型定義しない気持ち悪さ(@TypeScript)

をTSとTSからtscで生成されるJSを比較してみます。

以下はTSのコード、

// safety
type WeekDay = 'Mon' | 'Tue' | 'Wed' | 'Thu' | 'Fri'
type Day = WeekDay | 'Sat' | 'Sun'

function getNextDay(w: Day): Day | undefined{
    switch (w){
        case 'Mon': return 'Tue'
        case 'Thu': return 'Wed'
        case 'Wed': return 'Thu'
        case 'Thu': return 'Fri'
        case 'Fri': return 'Sat'
        case 'Sat': return 'Sun'
        case 'Sun': return "Mon"
    }
    return undefined
}

console.log(getNextDay('Mon'))

tscでコンパイルされて生成されるコード、

function getNextDay(w) {
    switch (w) {
        case 'Mon': return 'Tue';
        case 'Thu': return 'Wed';
        case 'Wed': return 'Thu';
        case 'Thu': return 'Fri';
        case 'Fri': return 'Sat';
        case 'Sat': return 'Sun';
        case 'Sun': return "Mon";
    }
    return undefined;
}
console.log(getNextDay('Mon'));

もちろんどちらも結果は同じですが、素のJSでは裸で外を歩いているように感じます。TSの場合には最初に取り得る型を定義することでその型以外を指定すればコンパイラ(VScode)がチェックしてくれる(例えばgetNextDay(w: Day)のDayをWeekDayにすればcase 'Sat'とcase 'Sun'はエラー表示表示されます)訳だから、数百行程度のプロジェクトでもTypeScriptを使うメリットはあると思います。

 

admin