パソコン・インターネット

2024年12月 6日 (金)

MATLAB Onlineで高周波基板設計用のRF PCB Toolboxを使ってみる。Coupled line バンドパスフィルタやratraceカプラが設計できる。モーメント法(MoM)や有限要素法(FEM)でちゃんと計算してくれているようだ。

RF Toolboxの次はRF PCB Toolbox。多層のP板の設計に使えるが、平面回路のデザインとソルバーも入っている。

まずはCoupled Line Filterをやってみる。チュートリアルから周波数だけ変えたもの。

まずはどんな構造か図示できる。

Matlabrfpcb003

そしてdesignで設計、Sパラメータに直す(結構時間かかる。ちゃんとシミュレーションしてくれているよう)。

Matlabrfpcb004

ラットレースカプラは基板をRogersに変えてみた。

Matlabrfpcb002

結果はこちら。ちゃんと3GHzで動くようになっている。

Matlabrfpcb001

ソルバーについてはこちら。

Overview of Solvers

しかしMATLABなんでもできるな。Antenna Toolboxもある。

 

 

2024年12月 5日 (木)

MATLAB Onlineで高周波用のRF Toolboxを使ってみる。Touchstoneファイルの読み込み、dB表示グラフ、スミスチャートなど簡単にできるし、フィルタ合成やIEEE P370 De-embedding(ZC-2xThru)も使える(MATLABで書かれたものがオリジナル)。

Interface誌でMATLAB Onlineのライセンスがついてきたのでいろいろ試している。

今回は高周波用途で使われるRF Toolbox。

読み込みは s = sparameters("ファイル名")で簡単にできるし、dB表示の図示はrfplot(s)でOK。ポートを指定する時は下図のようにする。

Matlabrf001

スミスチャートはsmithplot(s, i, j)とこれも簡単に描ける。

Matlabrf002

フィルタ合成もできる。

Matlabrf004

IEEE P370 De-embedding(ZC-2xThru)も使える。

Matlabrf003

もともとこれはMATLABで書かれていたので当然といえばそう。

Pythonのscikit-rfよりずっと簡単な気がする。ただTDRがどうも一回有理関数近似して、とか制限あるっぽい(自分でFFTで作ればいいだけですが)。RF pcb toolboxというのもあって、それを使えば複雑な高周波の平面回路も簡単にできそうなのでそれも試したい。

 

 

 

 

2024年12月 4日 (水)

高周波回路シミュレータQucsStudioがuSimmicsに名称変更し、バージョンも4.8.3から5.8にアップデートされた。Qucsと区別するためだそうだ。また、Pythonの高周波用ライブラリscikit-rfもv1.5.0にバージョンアップされていた

数日前に、QucsStudioの最新版が出た、と思ったらuSimmicsに名称変更されていた。

https://qucsstudio.de/download/

作者さんによると、

https://qucsstudio.de/forums/topic/version-5-8-released/」

待望の新バージョンがダウンロード セクションで入手可能です。Qucs プロジェクトと明確に区​​別するために、アプリケーション名が uSimmics に変更されたことに注意してください。これは、非常に多くの改良、新機能、バグ修正を含むメジャー リリースです。

ということでした。

画面の比較をすると、

QucsStudio

Qucsstudio

uSimmics

Usimmics

ということでアイコンがでかくなった以外はそこまで見た目は変わらない。ただ新機能が追加されているということなのでまた調べてみよう。

また、Pythonの高周波用ライブラリscikit-rfもv1.5.0にバージョンアップされていた。

https://github.com/scikit-rf/scikit-rf/releases/tag/v1.5.0

こちらもpipでアップデート済み。

 

MATLAB OnlineのSimulinkでローレンツ方程式をode8で計算してみる。Interface 2025年1月号でMATLAB Onlineの半年ライセンスがついてきたので。Simulinkを使うのは初めてだったが、わかりやすいSimulink入門コースを修了したのですぐできた。

さて昨日はInterfaceを買ってMATLAB Onlineのライセンスをゲットした話を書いた。

Interface2025年1月号はMATLABで1ニューロンから手作り 数学&図解でディープ・ラーニング。初歩からAlexNetの転移学習、CNNまで話題が豊富でなんとMatlab Onlineの半年ライセンスがついてくる。Simulinkや各種toolboxも使える。早速MATLAB入門オンラインコース修了した。

今日はその中でSimulinkを使ってLorenz方程式を解いてみよう。実はMATLABは昔使っていたが、Simulinkを使うのは今回が初めて。

オンライン講座のSimulink入門がよくできていたので簡単にできた。

ローレンツ方程式は

dx/dt = σ(y - x)

dy/dt = x(ρ - z) - y

dz/dt = xy - βz

の形をしていて、それをSimulinkに落とし込むとこうなった。

Simulinklorenz02

ソルバーは指定できるのでode8にした。結果はこちら。

Simulinklorenz01

箱を線で結ぶだけでできるのは(今更ながら)新鮮。

データインスペクターを使うと重ねて描ける。

Simulinklorenz03

ついでにロジスティック写像の分岐図も描いてみた。

 

2024年12月 3日 (火)

Interface2025年1月号はMATLABで1ニューロンから手作り 数学&図解でディープ・ラーニング。初歩からAlexNetの転移学習、CNNまで話題が豊富で、なんとMatlab Onlineの半年ライセンスがついてくる。Simulinkや各種toolboxも使える。早速MATLAB入門オンラインコース修了した。

今月のInterfaceはMATLABで1ニューロンから手作り 数学&図解でディープ・ラーニング。

20241202-174726

アマゾンリンク:https://amzn.to/3OzDahr

Simulinkを使って本当に1ニューロンからモデル化してとても分かりやすい。

Matlabintroduction02

そこからAlexNetの転移学習や、CNNの実装、バックプロパゲーション、ADAM、また数学の基礎まで内容が豊富。しかしPythonで私もディープラーニングやっているけどMATLABのほうがはるかに簡単に記述できるな。

そして何と、2025年5月までのMATLAB Onlineのライセンスがついてくる!それ以外にも

・MATLAB Online ・Simulink
・Deep Learning Toolbox
・Statistics and Machine Learning Toolbox
・Image Processing Toolbox
・Computer Vision Toolbox
・Symbolic Math Toolbox

が使える。しかしMATLABは大昔使ったっきりで完全に忘れているのでオンラインコース受けてみた。

MATLAB入門のコース修了証。

Matlabintroduction01

Simulink入門の修了したのだが、うまくコース修了証が表示されない…

ディープラーニング以外にもいろいろ遊んでみよう。

知らなかったのだが、MATLAB Onlineはライセンスなしでも無償で月20時間使えるそうだし。

また付録はディープ・ラーニングの始まりと現代社会での活用、だったがこれも読みごたえがある。エミー・ネーターまで記載されていたのは驚いた。

2024年12月 2日 (月)

高周波エンジニアのためのAI・機械学習入門(18)パッチアンテナ設計モジュールをPythonで作ったので、電気的に測定できる入力インピーダンス、共振周波数を与えて指向性(Directivity)をKeras3.0を用いたディープラーニング(DNN)による回帰で予測する。

さて前回はパッチサイズ(W、L)、基板厚みh、共振周波数を与えて基板誘電率が予測できるかやってみた。

今回は電気的に測定できる入力インピーダンスRinと共振周波数f0のみを与えて指向性が予測できるかやってみる。

学習データはいつものようにモンテカルロシミュレーションで作る。


import numpy as np
import patchantenna as pa

#モンテカルロシミュレーションでは基板比誘電率、厚み、中心周波数を乱数で振るが、それの最大最小
ermin = 2.0
ermax = 10.0
hmin = 0.1
hmax = 1.0
f0min = 1
f0max = 10

#モンテカルロシミュレーションでデータ作成して保存する。
#周波数と入力インピーダンスを与えて指向性を予測する。
N = 10000
np.random.seed(1)
data = np.empty((0, 2))
label = np.empty((0, 1))
for i in range(N):
    er = ermin + (ermax - ermin) * np.random.rand()
    h = hmin + (hmax - hmin) * np.random.rand()
    f0 = f0min + (f0max - f0min) * np.random.rand()    
    ant = pa.PatchAntenna(er, h, f0)
    Rin = ant.input_impedance()
    direc = ant.directivity()
    data = np.vstack((data, np.array([Rin, f0])))
    label = np.vstack((label, direc))

#保存
np.savez_compressed("directivity.npz", data=data, label=label)

 

そしてKeras3.0を用いたディープラーニング(DNN)で予測する。
まずデータを読み込み、訓練データとテストデータに分ける。


import numpy as np
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
import os
os.environ["KERAS_BACKEND"] = "tensorflow"
import keras

data_label = np.load("directivity.npz")
data = data_label["data"]
label = data_label["label"]
x_train, x_test, y_train, y_test = train_test_split(data, label, test_size=0.3, random_state=0)

次にモデルを作る。セル数64の3層のモデル。


#正規化
normalizer = keras.layers.Normalization()
normalizer.adapt(x_train)

# Functional APIでDense層を3層にしたDNNを設定
hidden_dim = 64
inputs = keras.Input(shape=(2,))
x = normalizer(inputs)
x = keras.layers.Dense(hidden_dim, activation="relu")(x)
x = keras.layers.Dense(hidden_dim, activation="relu")(x)
x = keras.layers.Dense(hidden_dim, activation="relu")(x)
outputs = keras.layers.Dense(1)(x)

# モデルの設定
model = keras.Model(inputs=inputs, outputs=outputs)
model.compile(loss = 'mean_squared_error' ,optimizer=keras.optimizers.Adam())

そして学習させる。


batch_size = 32
epochs = 100

keras.utils.set_random_seed(1)
history = model.fit(
    x_train,
    y_train,
    batch_size=batch_size,
    epochs=epochs,
    validation_split=0.15,
)
y_pred = model.predict(x_test)
metric = keras.metrics.R2Score()
metric.update_state(y_test, y_pred)
result = metric.result()
print(result)
error = np.abs((y_test - y_pred)/y_test*100)
print(error.mean(axis=0))

R2score99.7%となかなかいい結果になった。

では図示して確認してみる。


legend = ["Directivity"]
fig, ax = plt.subplots(1, 2, figsize=(12,6))

maxvalue = y_pred.max()
ax[0].scatter(y_pred, y_test, c="r", s=5)
ax[0].plot([0,maxvalue], [0,maxvalue], "--", c="black")
ax[0].set_xlabel("推定した値", fontname='MS Gothic')
ax[0].set_ylabel("実際の値", fontname='MS Gothic')
ax[0].set_xlim(5, maxvalue)
ax[0].set_ylim(5, maxvalue)
ax[0].grid()
ax[0].legend([legend[0] + f" 平均誤差{error.mean():.2f}%"], prop={"family":"MS Gothic"})
ax[1].hist(error, bins = 100)
ax[1].set_xlabel("誤差[%]", fontname='MS Gothic')
ax[1].set_ylabel("頻度", fontname='MS Gothic')
ax[1].grid()
fig.tight_layout()
plt.show()

 

Dnndirectivity

これだけ合っていれば使えそう。次の題材は伝送線路かな。

 

2024年11月28日 (木)

Excelで正規表現が使えるようになっていた。REGEXTEST関数などが使える。それなら一回見てみたかった正規表現だけで素数判定をするのをやってみる。1セルに=NOT(REGEXTEST(REPT("1",A2),"^1?$|^(11+?)\1+$")))などと入れると素数判定できる。

遅ればせながら、Excelに正規表現が使える関数が追加されたことを知った。

「Excel」に正規表現の新関数が3つ導入へ ~「XLOOKUP」「XMATCH」にも対応予定

REGEXTEST 、REGEXEXTRACTREGEXREPLACEが使えるようだ。

ちょうどPython(共通テスト日本語プログラム変換)とJavaScript(見せ算)で正規表現に凝っているのでExcelでもやってみよう。

題材は前からやってみたかったこれ、

正規表現で素数判定

をやってみよう。 Excelはデリミタいらないっぽいので

"^1?$|^(11+?)\1+$"

とすればいい。1を繰り返すのはREPT("1",A2)で、結果をひっくり返すのはNOT関数。

まとめて1セルに

=NOT(REGEXTEST(REPT("1",A2),"^1?$|^(11+?)\1+$"))

等と入れる。結果はこんな感じ。TRUEに色を付けている。

Regextprime

本当だ!ちゃんと判定できている。すごいことを考える人がいるなあ。

 

続きを読む "Excelで正規表現が使えるようになっていた。REGEXTEST関数などが使える。それなら一回見てみたかった正規表現だけで素数判定をするのをやってみる。1セルに=NOT(REGEXTEST(REPT("1",A2),"^1?$|^(11+?)\1+$")))などと入れると素数判定できる。" »

2024年11月27日 (水)

M1決勝でのさや香のネタと新ネタを含んだ見せ算、8見せ1や3見せ2見せ8にも対応した新・見せ算計算機をJavaScriptの正規表現を使って作った。

カシオの高精度計算サイトに見せ算を計算できるものを作っている。見せ算(さや香のネタ)

ただこれは応用編や大学院レベルはできないし、コメントで8見せ1や3見せ2見せ8というのが新たにできたと知った。
ちょうどこの前から正規表現に凝っている(Pythonで共通テストの日本語プログラムを実行するのに使った)ので、JavaScriptを使ってhtmlでさや香の見せ算を計算できるものを作った。

こちらが計算機。

misezan_calculator.html

こんな感じで計算できます。

Misezan001

説明:

使い方:「見せ」はここでは👀の記号で表します。ボタンを押すと入力されます。例えば1👀1を入力して計算、と書かれたボタンを押すと眼(答え)と説明がでます。

👩(優しい女性)のボタンを押すと👩が入力されます。100👩のように使うと100人の中に優しい女性がいることを表します。

1👀2, 6👀9, 2👀5, 1👀100, 1👀100👩, 8👀1, 3👀2👀8などを試してください。

 

ソースコードはhtmlの中に直接埋め込んでいる(<script></script>の間)のでそちらを参照ですが、こちらにも書いておきます。

適当に作り始めたのでとても冗長になった…


<div>
    <p><h1>新・見せ算計算機</h1></p>
    <label for = "input">入力:</label>
    <input type = "text" required id = "input">
    <p></p>
    <label for = "answer">眼:</label>
    <input type = "text" id="answer" disabled>
    <p id = "reason">解説</p>
    <table>
        <tr>
        <td><button><input type = "button" id = "gan" value = "👀(見せ)"></td>
        <td><button><input type = "button" id = "beauty" value = "👩(優しい女性)"></button></td>
        <td><button><input type = "button" id = "calc" value = "計算"></button></td>
        <td><button><input type = "button" id = "clear" value = "クリア"></button></td>        
        </tr>
    </table>
    <p>使い方:「見せ」はここでは👀の記号で表します。ボタンを押すと入力されます。例えば1👀1を入力して計算、と書かれたボタンを押すと眼(答え)と説明がでます。</p>
    <p>👩(優しい女性)のボタンを押すと👩が入力されます。100👩のように使うと100人の中に優しい女性がいることを表します。</p>
    <p>1👀2, 6👀9, 2👀5, 1👀100, 1👀100👩, 8👀1, 3👀2👀8などを試してください。</p>
</div>
<script>
"use strict";
const input = document.getElementById("input");
const answer = document.getElementById("answer");
const calc = document.getElementById("calc");
const gan = document.getElementById("gan");
const clear = document.getElementById("clear");
const beauty = document.getElementById("beauty");
const reason = document.getElementById("reason");

gan.addEventListener('click', ()=>{input.value += "👀"; input.focus();});
beauty.addEventListener('click', ()=>{input.value += "👩"; input.focus();});
clear.addEventListener('click', ()=>{input.value =""; input.focus();});
calc.addEventListener('click', mise);

function mise() {
    const re = /(\d+)(👩*)👀(\d+)(👩*)👀*(\d*)/u;
    const str = input.value;
    const num = str.match(re);
    let ans;
    if (num[2] ==='' && num[4] ==='' && num[5] === '') {
        ans = two_items(num[1],num[3]);
    } else if ((num[2] ==='👩' || num[4] ==='👩') && num[5] === '') {
        ans = extended_two_items(num[1],num[3], num[2], num[4]);
    } else if (num[1] === "3" && num[2] ==='' && num[3] === '2' && num[4] ==='' && num[5] === '8') {
        ans = three_items();
    } else {
        ans =  [undefined, "さや香のネタにないから"];
    }

    answer.value = ans[0];
    reason.textContent = ans[1];
}

function two_items(a, b) {
    let ans = "";
    // 同じ数字の場合は0
    if (a === b) {
        return  ["0","同じ数字だと恥ずかしくて立ち去るため"];
    }
    // 6と9, 2と5の処理
    let first = "";
    let last = "";
    let delim = "";
    if (a.length === b.length && /^[69]+$/.test(a) && /^[69]+$/.test(b) ) {
        let matched = false;
        for (let i = 0; i < a.length; i++) {
            if ((a[i] === "6" && b[i] === "9") ||(a[i] === "9" && b[i] === "6")){
                first += "1";
                last += "1";
                matched = true;
            } else {
                matched = false;
                break;
            }
        }
        if (matched) {
            return [first + last, "6と9が「俺か?」と近づいて行って1人と1人になるから"];
        }
    }
    if (a.length === b.length && /^[25]+$/.test(a) && /^[25]+$/.test(b) ) {
        let matched = false;
        for (let i = 0; i < a.length; i++) {
            if ((a[i] === "2" && b[i] === "5") ||(a[i] === "5" && b[i] === "2")){
                first += "1";
                delim += "."
                last += "1";
                matched = true;
            } else {
                matched = false;
                break;
            }
        }
        if (matched) {
            return [first + delim + last, "2と5が「俺か?」と近づいて行って1人と1人になるがあまりに違うので5が携帯(.)を落とすから"];
        }
    }
    if (a === "1" && Number(b) >=100) {
        return [String(Number(b)-17), "1は大群で逃げられないと思って腹くくって、うおーって叫びながらバーって100に突っ込んでいって、頑張って17人倒すから"]
    }
    if (b === "1" && Number(a) >=100) {
        return [String(Number(a)-17), "1は大群で逃げられないと思って腹くくって、うおーって叫びながらバーって100に突っ込んでいって、頑張って17人倒すから"]
    }
    if ((a === "8" && b ==="1") ||(a === "1" && b ==="8")  ) {
        return ["%", "1は自分が串で8が団子と思って刺しに行き、8は嫌がって避けるがつなぎ目に刺さって分裂するから"]
    }
   
    if (Number(a) > Number(b)) {
        return [a, "小さいものが大きいものを見ると怖いから小さい方が立ち去る"];
    } else {
        return [b, "小さいものが大きいものを見ると怖いから小さい方が立ち去る"];
    }

    return [undefined, "さや香のネタにないから"];

}

function extended_two_items(a, b, ex1, ex2) {
    let ans = ""
    if (a === "1" && Number(b) >=100 && ex2 ==="👩") {
        return [String(Number(b)-17+1), "1は大群で逃げられないと思って腹くくって、うおーって叫びながらバーって大群に突っ込んでいって、頑張って17人倒すが自分も倒れ中に優しい女性が1人いて介抱され、その2人が恋に落ちて後々結婚して子供が1人生まれてさらにまた1増えるから"]
    }
    if (b === "1" && Number(a) >=100 && ex1 ==="👩") {
        return [String(Number(a)-17+1), "1は大群で逃げられないと思って腹くくって、うおーって叫びながらバーって大群に突っ込んでいって、頑張って17人倒すが自分も倒れ中に優しい女性が1人いて介抱され、その2人が恋に落ちて後々結婚して子供が1人生まれてさらにまた1増えるから"]
    }
    return [undefined, "さや香のネタにないから"];

}

function three_items() {
    return ["140", "3と2と8はとても仲が良く肩を組み合い、俺ら(328)三ツ矢サイダーやな、という。三ツ矢サイダーは140周年だから。"]
}

2024年11月21日 (木)

令和7年度大学入学共通テスト「情報」に出てくる日本語プログラム言語をPythonに直して実行するDNCL2Pythonを作った(3) 試験対策として非線形方程式の数値解法である二分法とはさみうち法を書いてみる。

今回は非線形方程式の数値解法。二分法とはさみうち法が簡単なのでやってみよう。

この辺りを参考に。

https://www1.doshisha.ac.jp/~bukka/lecture/computer/resume/chap08.pdf

解くのはy=x^2-2。なので範囲を正にすれば答えは√2になるはず。

まずは二分法から。

日本語プログラム


関数 func(x) を定義する:
⎿ x**2 - 2を返す

a = 0.0
b = 2.0

b-a > 0.0001の間繰り返す:
| c = (a + b) / 2
| もしfunc(a) * func(c) < 0 ならば:
| ⎿ b = c
| そうでなければ:
⎿ ⎿ a = c
表示する(c)

Python。


def func(x) :
    return x**2 - 2

a = 0.0
b = 2.0

while b-a > 0.0001:
    c = (a + b) / 2
    if func(a) * func(c) < 0 :
        b = c
    else :
        a = c
print(c)

実行するとこうなった。だいたい√2。

1.41424560546875

次ははさみうち法。

日本語プログラム。


関数 func(x) を定義する:
⎿ x**2 - 2を返す

a = 0.0
b = 2.0

b-a > 0.0001の間繰り返す:
| c = (a*func(b) - b*func(a)) / (func(b) - func(a))
| もしfunc(a) * func(c) < 0 ならば:
| ⎿ b = c
| そうでなければ:
⎿ ⎿ a = c
表示する(c)

Python。


def func(x) :
    return x**2 - 2

a = 0.0
b = 2.0

while b-a > 0.0001:
    c = (a*func(b) - b*func(a)) / (func(b) - func(a))
    if func(a) * func(c) < 0 :
        b = c
    else :
        a = c
print(c)

実行すると 

1.41424560546875

が得られた。

さて次は何をやるかな…

 

 

 

 

2024年11月19日 (火)

令和7年度大学入学共通テスト「情報」に出てくる日本語プログラム言語をPythonに直して実行するDNCL2Pythonを作った(2) 試験対策として試作問題にあった最小交換硬貨枚数の問題と、一番簡単なバブルソートを書いてみる。

さて前回は変換コードを作ったので、次は試作問題にあった最小交換硬貨枚数の問題。
https://www.dnc.ac.jp/kyotsu/shiken_jouhou/r7/r7_kentoujoukyou/r7mondai.html

これを実行するには仕様にはない関数定義が必要で、それは変換コードに追加している。

日本語プログラムは


関数 枚数(kingaku) を定義する:
| Kouka = [1,5,10,50,100]
| maisu = 0
| nokori = kingaku
| iを4から0まで1ずつ減らしながら繰り返す:
| | maisu = maisu + nokori ÷ Kouka[i]
| ⎿ nokori = nokori % Kouka[i]
⎿ maisuを返す

kakaku = 46
min_maisu = 100
tsuri を0から99まで1ずつ増やしながら繰り返す:
|shiharai = kakaku + tsuri
|maisu = 枚数(shiharai) + 枚数(tsuri)
|もしmaisu < min_maisu ならば:
⎿⎿ min_maisu = maisu
表示する(min_maisu)

となってこれをPythonに直すと


def 枚数(kingaku) :
    Kouka = [1,5,10,50,100]
    maisu = 0
    nokori = kingaku
    for i in reversed(range(0, 4+1 , 1)):
        maisu =   maisu   +   nokori // Kouka[i]  
        nokori =   nokori % Kouka[i]
    return maisu

kakaku = 46
min_maisu = 100
for tsuri  in range(0, 99+1 , 1):
    shiharai = kakaku + tsuri
    maisu = 枚数(shiharai) + 枚数(tsuri)  
    if maisu < min_maisu :
        min_maisu = maisu    
print(min_maisu)

となり、答えも3と出た。

次は試験に出そうなアルゴリズムを書いてみる。

まあバブルソートとかかな…

これを参考に。

https://www.programiz.com/dsa/bubble-sort

では日本語プログラムは

Data = [-2, 45, 0, 11, -9]
kazu=要素数(Data)
iを0からkazu-1まで1ずつ増やしながら繰り返す:
| jを0からkazu-i-2まで1ずつ増やしながら繰り返す:
| | もしData[j] > Data[j + 1]ならば:
| | | temp = Data[j]
| | | Data[j] = Data[j + 1]
⎿ ⎿ ⎿ Data[j + 1] = temp
表示する(Data)

これをPythonに変換して実行すると、


Data = [-2, 45, 0, 11, -9]
kazu=len(Data)
for i in range(0, kazu-1+1 , 1):
    for j in range(0, kazu-i-2+1 , 1):
        if Data[j] > Data[j + 1]:
            temp = Data[j]
            Data[j] = Data[j + 1]
            Data[j + 1] = temp
print(Data)

[-9, -2, 0, 11, 45]

となった。次は何かな。

より以前の記事一覧

最近の記事

最近のコメント

2024年12月
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31        
フォト
無料ブログはココログ