純然たるネタ投稿です
1. 概要
もう昨年のことになりますが、政府がパスワードの後送プロトコル、いわゆるPPAPの廃止を発表しました。その代替手段として提示されたものの中に「電話」があったことで、某SNS上で喧々囂々の議論が交わされたのを覚えております。
実際に電話でのパスワード伝送を行う上で、以下のような困難が考えられます。
そこで、今回はパスワードのビットストリームを周波数偏移変調(FSK: Frequency Shift Keying)した音声信号を送受信するアプリをApp Designerで開発しました。
電話によるパスワード伝送、「先ほどのパスワードはピィイイイヒョロロロロロディウンディウンディウンガーーーーーーーーーーーーーーです。」とか最高にクールでは。
— 🦓幸福🦓 (@HppyCtrlEngnrng) November 24, 2020
2. システム構成
2.1 ハードウェア
ハードウェアの構成は上図の通り、スマホのオーディオI/Oを一度PCに取り込み、MATLABで処理できるようにしています。パスワードを送信する場合はスマホのマイクにFSK変調した音声を再生し、受信する場合はスマホのスピーカーを録音することで相手側から送信される変調信号を取得します。また、PC接続のヘッドセットなどを利用して通常の会話も可能です。
2.2 ソフトウェア
上半分は搬送波周波数や変調レート、送信音声のサンプリング周期、使用するオーディオデバイスの設定などです。
下半分がパスワードの送受信機能で、Sendテキストボックスに送信パスワードを入力してXMITボタンを押すとFSK変調されたパスワードが通話相手に送信されます。またRCV RDYボタンを押下している間、通話先の音声を録音し、録音完了後にFSK復調を行ってReceiveテキストボックスに受信したパスワードを表示します。
2.2.1 送信処理
前述のとおり、送信パスワードはFSKで変調します。FSKは周波数偏移変調の名の通り、デジタルデータのビット0, 1に応じて搬送波の周波数を切り替える変調方式です。
またFSKはさらに多くの周波数を用いることで、複数のビットを同時に送信することができます。ここでは2ビットを4周波数に割り当てる4FSKを採用しています。理由としては伝送速度よりも、携帯電話で正弦波状の音声を送出したとき、(おそらくノイズキャンセラにより)音が消えてしまうので帯域を広げたかったためです。
App Designer内での4FSKの実装は以下の通りです。
%%% 4FSK変調
% msg: 送信メッセージ(char array)
% y: 変調波形(double array)
function y = make4FskSignal(app, msg)
% 1文字ごとの変調波形をセルで割り当て
y = cell(length(msg), 1);
% ASCIIコードに変換
bits = double(reshape(dec2bin(msg, 8)', 1, [])) - '0';
% 偶数ビット、奇数ビットに分割
bits1 = bits(1:2:end);
bits2 = bits(2:2:end);
% 1文字ぶんの長さの時間配列
tc = (0:1/app.f_sample:1/app.bitrate-1/app.f_sample)';
% 1文字ぶんの変調波形を順次代入
for idx_c = 1:length(bits1)
y{idx_c} = sin(2*pi*app.f_mod*(1+2*bits1(idx_c)+bits2(idx_c))*tc);
end
% arrayに戻す
y = 0.5*cell2mat(y);
end
%% 送信(XMIT)ボタン押下コールバック
function XMITButtonPushed(app, event)
% Sendパスワードテキストボックスの値を取得
msg = cell2mat(app.SendTextArea.Value);
% 多重送信禁止のためボタンをDistable
app.XMITButton.Enable = false;
if ~isempty(msg)
pause(1.0);
% パスワードから変調信号を取得
y = app.make4FskSignal(msg);
% デバッグ用にセーブ
save("sound_sent", 'y');
% audioplayerオブジェクトの作成
player = audioplayer(y, app.f_sample, 16, app.OutputDeviceDropDown.Value);
% 再生
playblocking(player);
end
app.XMITButton.Enable = true;
end
音声の送信にはいつものsound
関数ではなく、audioplayer
を用いてデバイスを指定して送信します。
2.2.2 受信処理
録音したパスワードは直交検波により復調します。直交検波は搬送波周波数$f_c$が既知な信号
y(t)=A \sin(2\pi f_c t+\phi)
に対し、同じ周波数の正弦波・余弦波との内積(の絶対値)
\mathrm{abs}\left(\frac{1}{T}\int_{0}^{T}\mathrm{e}^{j 2\pi f_c t} y(t)\mathrm{d}t\right)= A
を評価して信号振幅を復元するものです。($T$は符号長で変調レートの逆数)
%% 直交検波
% y: 録音した音声信号(double array)
% f: 搬送波周波数(double array)
function x = qdem(app, y, f)
% 1符号(2ビット)あたりの信号長(サンプリングレート/ビットレート)
symbol_len = round(app.f_sample/app.bitrate);
% 1符号ぶんの時間ベクトル
t = ((0:length(y)-1)/app.f_sample)';
if size(f,1) > size(f,2)
f = f';
end
% 複素正弦波との内積
x = abs(movsum(exp(1j*2*pi*t*f) .* y, symbol_len/2));
% 信号が出ている時間のみを抽出
% 任意の搬送波の振幅が、最大振幅の5 %以上である時間
t_fskon = sum(x, 2) > max(sum(x,2)) * 0.05;
x = x(t_fskon, :);
% パルスの中心のみピックアップ
x = x(0.5*symbol_len:symbol_len:end, :);
% 1文字を8 bits/4符号で変調しているため、4の倍数でカット
x = x(1:floor(size(x,1)/4)*4,:);
end
%% 復調処理
% y: 録音した音声信号(double array)
function pass_demod = demodFskOrth(app, y)
% 直交検波により搬送波ごとの振幅を取得
x = app.qdem(y, app.f_mod*(1:4));
% 各時刻で振幅最大の搬送波を選択
[~, bits] = max(x, [], 2);
% 搬送波周波数に対応した2ビットを重みづけ加算しデコード
pass_demod = char(reshape(bits-1, [], 4)'*[64;16;4;1])';
end
%% 受信(RCV RDY)ボタン押下コールバック
function RCVRDYButtonValueChanged(app, event)
% ボタン状態取得
value = app.RCVRDYButton.Value;
if value == 1 % 受信開始
app.ReceiveTextArea.Value = "Receiving...";
% audiorecorderオブジェクトの作成
app.recorder = audiorecorder(app.f_sample, 16, 1, app.InputDeviceDropDown.Value);
% 録音開始
record(app.recorder);
else % 受信終了
% 録音停止
stop(app.recorder);
% オーディオデバイスから音声信号を取得
y = getaudiodata(app.recorder);
% デバッグ用に保存
save("sound_rcvd", 'y');
% 復調実施・表示
pass_demod = app.demodFskOrth(y);
app.ReceiveTextArea.Value = pass_demod;
end
end
録音にはaudiorecorder
関数を使用しています。
3. 動作例
以下の設定で動作テストを実施しました。
- 搬送波周波数:600/1200/1800/2400 Hz
- 変調レート:100 Hz
- 音声サンプル周波数:32000 Hz
- パスワード:"Fear the old blood."
3.1 変調
パスワード"Fear the old blood."をASCIIコードに変換し、2ビットずつ4周波数に割り当てます。信号の先頭部分を下図に示します。
この音声信号が受信側のスマホに送られます。
3.2 復調
MATLABに繋がったスマホを2台用意できなかったため、受信側で音声を録音してもらい、その信号に対して復調処理を実施しました。受信側の波形は以下のようになりました。伝送により振幅に変動が出てしまっていますが、直交検波したパルス信号は綺麗に各符号を捉えています。
受信信号の先頭部分を下図に示します。
デコード結果も正しく送信したパスワードが得られました。
実際の音声は以下の通りです。
変調・復調した音声 pic.twitter.com/S1Kkg8lkTk
— 🦓幸福🦓 (@HppyCtrlEngnrng) December 25, 2021
4. まとめ
以上、お粗末様でした。
まぁ実用するにはパリティとか諸々加味する必いやもうチャットでいいよな!(本末転倒)