「BF」カテゴリーアーカイブ

BF論文2_効果量一覧

 統計ソフトjamoviによる効果量の算出方法がわかったため,BF論文で記述する統計処理の効果量を片っ端からスクリーンショットを貼り付けていく。ここでの効果量は,偏η²とする。出力結果のデータファイルを下記に添付した。

1. 3週間の訓練効果_3要因参加者内(訓練回×訓練方向×期間)
・反復測定分散分析で,参加者内のところのみに投入したらできた。

2. 性別と皮膚温制御成績_3要因混合(性別×訓練回×訓練方向)

3. 内受容感覚とBF_3要因混合(群×訓練方向×期間)

①気づき因子
・群×訓練方向×期間の交互作用は,惜しくも単純・単純主効果の検定(0.9と0.8)で有意でなく断念。これ出てたら,勝ちだったな…

②注意制御因子
・群×訓練方向×期間の交互作用のp値が0.100であと少しで有意傾向だった
→グラフ見たら,低群の方がうまくいってた

③感情への気づき因子

第2因子

第3因子

第6因子

第7因子

第8因子

4. 自宅での訓練回数と制御成績_3要因混合(群×訓練回×訓練方向)

===============================================================*何故かわからないが,交互作用の多重比較だけjs-starと一致しない。一致しないだけならソフト使用上仕方がないが,一番の問題は「訓練方向×期間」の下位検定で有意差が出ないため,根本的にjamovi側に問題がある気がする(js-star, HAD, Rは出てた気がする)。設定ミスもあるかもしれないが,資料が不足しているためどうすることもできない現状にある…

皮膚温バイオフィードバック装置_プログラム解説

導入
 バイオフィードバック(以下,BF)は,心身のリラクセーション法として有効な手法であり,脳波や心拍数,筋電図,血圧などの生理的指標を自己制御可能にする訓練方法です。特に,末梢皮膚温度(以下,皮膚温)は,測定が容易で反応が理解しやすく,リラクゼーション効果を得るために重要です。しかし,従来のBF装置は高価であり,専門施設でしか利用できないため,その普及が阻まれています。本研究は,オープンソースハードウェアのArduinoとデジタルファブリケーション技術を活用し,低コストで持ち運び可能な皮膚温BF装置を開発し,自宅でも手軽に訓練を行えるようにすることを目的としています。

装置の構成
 マイクロコンピュータはMakerNanoを,皮膚温センサーはLM35DZを用います。フィードバック部分は,視覚フィードバックにカラーLEDモジュールと液晶ディスプレイを,聴覚フィードバックにスピーカーを用います。

使用した電子部品の入手先
 使用した部品の入手先については,下記の部品一覧の「こちら」を選択すると,販売先のサイトに移動できます。

・MakerNano:こちら
・LM35DZ:こちら
・スピーカー:こちら
・RGBLED:こちら
・液晶ディスプレイ:こちら
・DCDCコンバータ:こちら
・ピンヘッダー:こちら
・熱収縮チューブ:こちら
・細径3心並列線:こちら
・電池ボックス:こちら
・単4電池:こちら

測定方法
 温度センサは,指サックを用いて,非利き手の第2指腹測部に直接固定します。次に,右側面(下部)にある電源スイッチを入れて装置を起動します。安静時は,装置左下部のスイッチを右にスライドさせFBを無効にし,訓練時は左にスライドしてFBを有効にします。注意点として,温度センサに風が直接当たらない場所で訓練をしてください。詳しい訓練方法については,下記の2分程度の動画をご覧ください。

プログラムの解説
 ここでは,皮膚温BF装置の開発に用いたソフトウェアの説明を行います。使用したソフトウェアは,長野(2022)を参考にArduino開発環境1.8.19と,長野(2016)を参考にProcessing開発環境3.5.4のプログラムの作成を行いました。

1. Arduino開発環境

プログラム名説明
st7032液晶ディスプレイで,皮膚温を数値化
Adpatesion安静期間の自動管理化
switch光や音のFB有無の切り替え
BFst7032_230705プログラムの全体像

2. Processing開発環境

プログラム名説明
SBF230707皮膚温のグラフ表示とCSV形式の発行
Subroutine1現在時刻と経過時間

1. Arduino開発環境

プログラム:st7032
 液晶ディスプレイ部分に該当するプログラムです。GNDと5Vで電源供給を行い,アナログピンの4,5番からI2C通信(Inter-Integrated Circuit)を行い,マイコンと液晶ディスプレイの間でデータの送受信を行っています。1行目で,液晶ディスプレイの制御に必要な「ST7032_asukiaaa.h」ライブラリをインクルードし,バージョン1.0.5を使用しました。3行目で,液晶ディスプレイの制御を行うために「lcd」とクラスをインスタンス化しています。これにより,11行目で表示領域8列2行とし,12行目でコントラスト(文字の濃さ)0~63を指定するなど液晶ディスプレイを制御する機能を呼び出しています。6行目のsetup関数でこれらの初期化を行っています。5行目のtemp1,tempdは,現在温度,1秒間で生じた温度の変化量を格納しています。15~24行目は安静期間の自動管理化を行っています。安静期間終了の条件を満たした場合,液晶ディスプレイの7列0行(画面右上)に「*」を表示し,満たされない場合,何も表示しませんでした。26~28行目は,Stringオブジェクトのbuf1,buf2を用いることで,temp1,tempdのデータを文字列に変換し,表示するための準備を行っています。30~33行目では,液晶ディスプレイの画面1行目にbuf1から現在温度,2行目にbuf2から1秒間で生じた温度の変化量をStringオブジェクトから「.c_str()」メソッドを用いて表示しました。これらを15行目のloop関数で繰り返し実行しました。

#include <ST7032_asukiaaa.h>    //液晶ディスプレイのライブラリ

ST7032_asukiaaa lcd;  //液晶ディスプレイのライブラリを使用

double temp1, tempd;   //温度データを格納する変数

double minTemp = 0.0;  // 最小温度
double maxTemp = 0.0;  // 最大温度

void setup() {
  lcd.begin(8, 2);     //液晶ディスプレイで8列2行で指定表示
  lcd.setContrast(5);  //液晶ディスプレイのコントラスト
}

void loop() {
  //3分間の皮膚温の変動が0.4℃以内の時にアスタリスク表示
  if (maxTemp - minTemp <= 0.4) {   //最大・最小温度の差が0.4℃以内の場合
    lcd.setCursor(7, 0);        //液晶ディスプレイの右上に
    lcd.print("*");             //「*」表示
    readyFlg=true;
  } else {                      //条件を満たさなかった場合
    lcd.setCursor(7, 0);        //液晶ディスプレイの右上に
    lcd.print(" ");             //何も表示しない
  }

  String buf1, buf2;
  buf1 += temp1;      //buf1は現在温度
  buf2 += tempd;      //buf2は温度変化量

  lcd.setCursor(0, 0); //液晶ディスプレイの1行目にカーソルを移動
  lcd.print(buf1.c_str()); //1行目に現在温度を表示
  lcd.setCursor(0, 1);    //液晶ディスプレイの2行目にカーソルを移動
  lcd.print(buf2.c_str());  //2行目に温度変化量を表示
}

プログラム:Adpatesion 
 このプログラムは、安静期間の自動管理化を行うため、大河内(1990)の安静期間の処理を参考に行っています。具体的には、1分ごとの皮膚温の平均値が連続する3分間で増減せず、その範囲が0.4℃以内である場合に安静期間の終了を示す「*」を液晶ディスプレイに表示します。プログラム内では、現在の温度を格納する変数temp1、平均値計算用のsum、average、cntが定義され、連続する3分間(180サンプル)の温度データを保持するための配列historyと、そのデータを格納するインデックスhistory Indexが用意されています。history Indexは180を超えると自動的に最初の位置に戻り、配列の境界をループしながらデータを保存します。初期化として、最小・最大温度を0℃で設定し、取得した温度データをhistory配列に格納、次のインデックスを更新します。履歴内のデータと比較して、現在の最小・最大温度を更新し、連続する180サンプルが増減なく0.4℃以内であるかをチェックします。条件が満たされた場合はArduinoのシリアルモニタ内に「***」を表示し、満たされない場合は「—」を表示しました。温度データの取得と処理は10msごとに行われ、安静期間の判定と表示が繰り返されることで、安静状態をモニタリングします。この処理により、安静期間の終了を正確に管理し、リアルタイムで液晶ディスプレイにフィードバックを提供します。

double temp1;   //現在温度のデータを格納する変数
double sum, average;    //温度データの合計値と平均値を格納
long cnt;      //温度データのサンプリング数

const int HISTORY_SIZE = 180; // 履歴のサイズ
double history[HISTORY_SIZE]; // 測定値の履歴
int historyIndex = 0;         // 履歴のインデックス

double minTemp = 0.0;  // 最小温度
double maxTemp = 0.0;  // 最大温度

bool readyFlg=false;  //安静期間のフラグ


void loop() {

    if(readyFlg==true){
      Serial.print(",***"); //安静期間終了時に「***」表示
    }
    else{
      Serial.print(",---"); //安静期間が終了していない場合「---」表示
    }
    
    Serial.println();   //シリアルモニタに「***/---」を表示するための空行
    sum = 0;    //合計値の初期化
    cnt = 0;
    delay(10);  //10ms単おきに行う

    history[historyIndex] = temp1;  //温度データを記録するための配列
    historyIndex = (historyIndex + 1) % HISTORY_SIZE;  //新しい温度データを保存

    minTemp = history[0];   //最小温度をデータの最初の要素で初期化
    maxTemp = history[0];   //最大温度をデータの最初の要素で初期化

    for (int i = 1; i < HISTORY_SIZE; i++) {  //温度データから最小・最大温度を算出
      if (history[i] < minTemp) {   //データ内で最小温度より低い場合
        minTemp = history[i];       //最小温度を更新
      }
      if (history[i] > maxTemp) {    //データ内で最大温度より低い場合
        maxTemp = history[i];        //最大温度を更新
      }
    }

    //3分間の皮膚温の変動が0.4℃以内の時にアスタリスク表示
    if (maxTemp - minTemp <= 0.4) {   //最大・最小温度の差が0.4℃以内の場合
      lcd.setCursor(7, 0);        //液晶ディスプレイの右上に
      lcd.print("*");             //「*」表示
      //Serial.println("***");
      readyFlg=true;
    } else {                      //条件を満たさなかった場合
      lcd.setCursor(7, 0);        //液晶ディスプレイの右上に
      lcd.print(" ");             //何も表示しない
    }

プログラム:switch
 スライドスイッチを用いて,光や音のFB有無の切り替えを行うプログラムです。1行目では視覚FBに用いるLEDの制御にAdafruit_NeoPixelライブラリ(Ver1.10.7)をインクルードし,2行目では液晶ディスプレイの制御にST7032_asukiaaa.hライブラリ(Ver1.0.5)をインクルードしました。4,5行目では,LEDの制御ピン番号,使用するLEDの個数を指定し,7行目ではこれらをもとにAdafruit_NeoPixelクラスをインスタンス化しています。9,10行目で,FB有無の切り替えに用いるスイッチピン7,ブザーピン2など制御に用いるピン番号を指定しています。13行目で,スイッチの状態を入力モードにして,「INPUT_PULLUP」から内部プルアップ抵抗を有効にして,ピンの状態がHigh(オン)になっています。14行目で,ブザーのピンを出力モードに設定して,制御するための処理を行い,これらをsetup関数から初期化しています。17行目では,スイッチの状態(オン/オフ)を読み取ります。19行目で,スイッチの状態がオフの時に19~32行目で実行するFB有の状態にしました。温度が上昇した際(dir==1)に視覚FBのLEDライトが赤く点灯し,聴覚FBのブザー500Hzを10ms間出力しました(19~26行目)。温度が下降した際(dir==-1)に視覚FBのLEDライトが青く点灯し,聴覚FBのブザー1000Hzを10ms間出力しました(27~32行目)。33~35行目では,FB無しの状態を割り当て,スイッチの状態がオフの時にLEDライトが消灯し,ブザーが消音するようにFBの有無の切り替えを行いました。

#include <Adafruit_NeoPixel.h>  //LEDのライブラリ
#include <ST7032_asukiaaa.h>    //液晶ディスプレイのライブラリ
 
#define PIN        3   //LEDの制御ピン
#define NUMPIXELS 1    //LEDの数
 
Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);   //LEDのライブラリを使用

const int switchPin = 7;    //FB有無の切り替えに用いるスイッチ
int Bpin = 2;  //ブザーPIN2

void setup() {
  pinMode(switchPin, INPUT_PULLUP);  // 内部プルアップ抵抗を有効にする
  pinMode(Bpin, OUTPUT);

void loop() {
  int switchState = digitalRead(switchPin);
 
  if (switchState == LOW) {   //FBスイッチがオフの時に
    pixels.clear();
    if (dir == 1) {
        pixels.setPixelColor(0, pixels.Color(mag, 0, 0)); //温度上昇を赤で表示
      if (mag > 0) {
        tone(Bpin, 500, 10);    //温度上昇時は500Hz
      }
    }
    if (dir == -1) {
      pixels.setPixelColor(0, pixels.Color(0, 0, mag)); //温度下降を青で表示
      if (mag > 0) {
        tone(Bpin, 1000, 10);   //温度下降時は1000Hz
      }
    }
  } else if (switchState == HIGH) {         //左下のスイッチを入れるとFBなし
    pixels.setPixelColor(0, pixels.Color(0, 0, 0));    //LEDを消す
    noTone(Bpin);         //ブザー音を消す
  }

プログラム:BFst7032_230705
 長野(2022)が作成した皮膚温BFプログラムと,上記で開発したプログラムを統合した全体像です。温度センサーから入力された値をもとに,「現在温度,温度変化量,変化の強さ,サンプリング数,安静期間終了の判定」が算出されます。温度が上昇したときに,LEDが赤く点灯し,ブザーから500HzでFBが行われます。温度が下降したときにLEDが青く点灯し,ブザーから1000HzでFBが行われました。温度変化が顕著に行われない場合LEDの色は変化せず,温度の変化量が大きい場合はLEDがより強く点灯しました。1秒毎に平均値の算出および各種FBなどが行われました。これらの機能を用いて,BF装置のプログラムを作成しました。

#include <Adafruit_NeoPixel.h>  //LEDのライブラリ
#include <ST7032_asukiaaa.h>    //液晶ディスプレイのライブラリ

#define PIN        3   //LEDの制御ピン
#define NUMPIXELS 1    //LEDの数

Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);   //LEDのライブラリを使用
ST7032_asukiaaa lcd;        //液晶ディスプレイのライブラリを使用

const int switchPin = 7;    //FB有無の切り替えに用いるスイッチ

double mv, temp0, temp1, tempd;   //温度データを格納する変数
double sum, average;    //温度データの合計値と平均値を格納
long t, t0, cnt;      //現在の時刻・温度データをサンプリングした時の時刻・温度データのサンプリング数
int Bpin = 2;  //ブザーPIN2

const int HISTORY_SIZE = 180; // 履歴のサイズ
double history[HISTORY_SIZE]; // 測定値の履歴
int historyIndex = 0;         // 履歴のインデックス

double minTemp = 0.0;  // 最小温度
double maxTemp = 0.0;  // 最大温度

bool readyFlg=false;  //安静準備フラグ

void setup() {
  pinMode(switchPin, INPUT_PULLUP);  // 内部プルアップ抵抗を有効にする

  pinMode(Bpin, OUTPUT);
  pinMode(13, OUTPUT);
  Serial.begin(115200);  //シリアル通信115200
  analogReference(INTERNAL); //参照電圧を1.1Vに設定
  cnt = sum = 0;
  pixels.begin();      //LEDを初期化

  lcd.begin(8, 2);     //液晶ディスプレイで8列2行で指定表示
  lcd.setContrast(5);  //液晶ディスプレイのコントラスト
}

void loop() {
  t0 = t;
  t = millis();        //現在時刻をms単位で取得
  mv = (double)1100 / (double)1024 * analogRead(0);  //デジタル値を電圧に変換
  sum = sum + mv;
  cnt++;        //サンプリング数のカウント
  if (t0 / 1000 != t / 1000) {    //1秒(100ms)毎に平均算出
    average = sum / (double)cnt;  //平均を計算

    temp0 = temp1;                //前回の温度を保存
    temp1 = average * 0.1;        //電圧を温度に変換
    tempd = temp1 - temp0;        //温度変化量を算出

    int mag, dir;                 //変化の程度(mag)と方向(dir)
    if (tempd > 0) {
      mag = abs(tempd * 100);
      dir = 1;                    //温度上昇時
    } else if (tempd <= 0) {
      mag = abs(tempd * 100);
      dir = -1;                   //温度下降時
    }

    int switchState = digitalRead(switchPin);

    if (switchState == LOW) {   //FBスイッチがオフの時に
      pixels.clear();
      if (dir == 1) {
        pixels.setPixelColor(0, pixels.Color(mag, 0, 0)); //温度上昇を赤で表示
        if (mag > 0) {
          tone(Bpin, 500, 10);    //温度上昇時は500Hz
        }
      }
      if (dir == -1) {
        pixels.setPixelColor(0, pixels.Color(0, 0, mag)); //温度下降を青で表示
        if (mag > 0) {
          tone(Bpin, 1000, 10);   //温度下降時は1000Hz
        }
      }
    } else if (switchState == HIGH) {         //左下のスイッチを入れるとFBなし
      pixels.setPixelColor(0, pixels.Color(0, 0, 0));    //LEDを消す
      noTone(Bpin);         //ブザー音を消す
    }

    pixels.show();

    Serial.print(temp1); //現在温度
    Serial.print(",");   //カンマ区切り
    Serial.print(tempd); //温度変化量
    Serial.print(",");
    Serial.print(mag);   //変化の強さ
    Serial.print(",");
    Serial.print(cnt);   //サンプリング数

    if(readyFlg==true){
      Serial.print(",***"); //安静期間終了時に「***」表示
    }
    else{
      Serial.print(",---"); //安静期間が終了していない場合「---」表示
    }
    
    Serial.println();   //シリアルモニタに「***/---」を表示するための空行
    sum = 0;    //合計値の初期化
    cnt = 0;
    delay(10);  //10ms単おきに行う

    history[historyIndex] = temp1;  //温度データを記録するための配列
    historyIndex = (historyIndex + 1) % HISTORY_SIZE;  //新しい温度データを保存

    minTemp = history[0];   //最小温度をデータの最初の要素で初期化
    maxTemp = history[0];   //最大温度をデータの最初の要素で初期化

    for (int i = 1; i < HISTORY_SIZE; i++) {  //温度データから最小・最大温度を算出
      if (history[i] < minTemp) {   //データ内で最小温度より低い場合
        minTemp = history[i];       //最小温度を更新
      }
      if (history[i] > maxTemp) {    //データ内で最大温度より低い場合
        maxTemp = history[i];        //最大温度を更新
      }
    }

    //3分間の皮膚温の変動が0.4℃以内の時にアスタリスク表示
    if (maxTemp - minTemp <= 0.4) {   //最大・最小温度の差が0.4℃以内の場合
      lcd.setCursor(7, 0);        //液晶ディスプレイの右上に
      lcd.print("*");             //「*」表示
      //Serial.println("***");
      readyFlg=true;
    } else {                      //条件を満たさなかった場合
      lcd.setCursor(7, 0);        //液晶ディスプレイの右上に
      lcd.print(" ");             //何も表示しない
    }

    String buf1, buf2;
    buf1 += temp1;      //buf1は現在温度
    buf2 += tempd;      //buf2は温度変化量

    lcd.setCursor(0, 0); //液晶ディスプレイの1行目にカーソルを移動
    lcd.print(buf1.c_str()); //1行目に現在温度を表示
    lcd.setCursor(0, 1);    //液晶ディスプレイの2行目にカーソルを移動
    lcd.print(buf2.c_str());  //2行目に温度変化量を表示
  }
}

2. Processing開発環境

プログラム:SBF230707
 1つ目は、シリアル通信を介して受信した皮膚温データを処理し、リアルタイムでグラフ表示しながら、そのデータをCSVファイルに保存するプログラムです。1行目では、Processing開発環境のシリアル通信ライブラリをインポートして、3行目ではシリアル通信を行うための準備として、Serialオブジェクトを宣言しています。4、5行目では、BF装置とシリアル通信を行うため、Arduino開発環境で表示されたシリアルポートの指定および通信速度115200ポートレートを使用しています。6、7行目では、ST波形などを表示するためのエリアとして、幅640、高さ200と指定しています。8~10行目では、受信したデータ「現在温度、温度変化量、変化の強さ、サンプリング数、安静期間終了の判定」の5つのデータについて、float型の配列の宣言からデータ格納を行い、String関数からデータ文字列を格納するための宣言を行っています。また、現在データと前のデータを線でつなぐため、prevDate配列を使用しています。11~15行目は、グラフを描画するために、赤・青・透明色の指定を行っています。17行目は、8~10行目のデータの初期値を0と指定し、18行目はCSVファイルの出力に使用するファイル名の格納を行っています。19行目は、一時的に取得したデータを格納するため、buf2という変数を指定。20行目では、CSVファイルにデータを書き込むためにオブジェクトの指定を行っています。21行目では、プログラムの開始時刻をミリ秒単位で格納するため、startMills変数を指定しています。27行目では、プログラムの実行開始時に表示されるウィンドウサイズを幅600ピクセル、高さ500ピクセルに設定しています。28行目では、myPortオブジェクトを作成し、4、5行目で指定したポート名とポートレートで初期化を行っています。30、31行目は、現在時刻および経過時間を描画するプログラム(図3-6)内で用いたgetTimestamp関数から取得した現在日時のタイムスタンプに基づいて、CSVファイル名を指定しています。32行目は、新しいデータが一定数蓄積するたびにファイル内に書き込むため初期化を行っています。

 36~92行目では、draw関数を用いて取得したデータをグラフに描画するための処理を行っています。39~43行目は、データの表示設定を行っており、変数datecountを用いてデータの受信回数が600の倍数の時に(39行目)次の条件を実行しました。background(255)からウィンドウ全体を白く塗りつぶし、fill(0, 0, 0)からテキスト描画を黒色で塗りつぶしました。さらにテキストサイズを12として、センサーから取得された温度データの単位を示すため、画面左上に摂氏40℃を表す40degを表示しました。46行目では、myPortオブジェクトから指定したシリアル通信が可能な場合、次の処理を行いました。48行目のmyPort.readStringUntiでデータを読み込み、取得したデータがnull表記ではない場合(50行目)、trim(inString)からデータ前後の空白を削除し、データ処理を行いました(52行目)。シリアルポートから読み込まれた1行のテキストデータを格納するためのInstring関数をCSV形式のデータ入力のため、カンマで分割して、得られた文字列をparts配列に格納しており(53行目)、取得中のデータに不足はないか確認しています(55、56行目)。57行目では、現在のSTをグラフに描画する際に、前回のデータが紛失しないようにデータの保存が行われています。60行目では、グラフの縦軸の範囲を可視化するため、元の範囲20~40の値を0~500に変換しています。これらデータをCSVファイルに記録するため、dataMV配列を使用しています(61行目)。64、65行目では、getTimestamp2から現在時刻、getElapsedTimeから経過時間を取得しています。67、68行目では、Processing開発環境のコンソール画面に表示するために、bufという変数を用いて「タイムスタンプ、経過時間」とdataMV配列に格納された温度データがカンマ区切りで表示されました。これにより、Arduino開発環境のシリアルモニタなどから温度データを確認する手間を省きました。69行目では、連続して受信したデータbufをbuf2に追加し、一度に大量のデータをファイルに書き込む回数を減らしました。70行目では、取得したデータ数が10の倍数ごとにCSVファイルに、writer.flushからリアルタイムで書き込む処理を行っています(71行目)。その後、ファイル内の残りのデータを書き込み(72行目)、73行目でbuf2を空にしました。これにより、ファイルのサイズを一定に保ちつつ、連続してデータを記録することができました。76~82行目では、グラフを描画するための処理を行っています。受信したデータ数を600で割った値からグラフのx座標を計算して(76行目)、描画する線の太さを1に設定(77行目)。78行目で実際に描画を行い、11~16行目で設定した色を用いて、描画する線の輪郭線の色を設定しています。81行目では、直前のデータから現在のデータまでを、グラフ上での位置を計算して線で結ぶ描画処理を行っています。「xp-1」から線の始点のx座標、「500-prevData[i]」から線の始点のy座標、「xp」から線の終点のx座標、「500 – data[i]」から線の終点のy座標をそれぞれ表しています。84、85行目では、現在時刻と経過時間を描画し、描画された文字が潰れないようにデータのカウントの更新を行っています(88行目)。94~98行目では、プログラムの終了時にファイルに書き込めてないデータをCSVファイルに強制的に書き込み(95行目)、ファイルを閉じるための処理を行っています(96、97行目)。

import processing.serial.*;

Serial myPort;
String portName = "COM6"; //BT serial port
int baudRate = 115200;
int dataPlotWidth = 640;
int dataPlotHeight = 200;
float[] data = new float[5];
String[] dataMV = new String[5];
float[] prevData = new float[5];
color[] colors = {
  color(255, 0, 0), 
  color(0, 0, 255), 
  color(0, 0, 0, 0), 
  color(0, 0, 0, 0)
};
int datacount=0;
String OutFileName;
String buf2;
PrintWriter writer;
int startMillis;


void setup() 
{
  //size(dataPlotWidth, dataPlotHeight);
  size(600,500);
  myPort = new Serial(this, portName, baudRate);
  
  OutFileName=getTimestamp()+".csv";
  writer = createWriter("./data/"+OutFileName); // Create a new file in the sketch directory
  buf2="";

}

void draw()
{
  
  if((datacount % 600) == 0) {
    background(255);  // clear screen    
    fill(0, 0, 0); // set the fill color to black
    textSize(12);
    text("40 deg", 10, 20); // 
  }
  
  while (myPort.available() > 0) 
  {
    String inString = myPort.readStringUntil('\n');
    
    if (inString != null) 
    {
      inString = trim(inString);
      String[] parts = split(inString, ',');
      
      if(parts.length >3) 
      {
        for (int i = 0; i < 5; i++) 
        {
          prevData[i] = data[i];
          data[i] = map(float(parts[i]), 20, 40, 0, 500);  // Assuming your data is in the range 0-200
          dataMV[i] = parts[i];
          
        }
        String stamp=getTimestamp2();
        String etime= getElapsedTime();
        String buf=stamp+","+etime+","+dataMV[0] + "," + dataMV[1] + "," + dataMV[2] + "," + dataMV[3] + "," + dataMV[4];
        println(buf);
        buf2=buf2+buf+"\n";
        
        if(datacount%10==0){
          writer.print(buf2);
          writer.flush();
          buf2="";
        }

        int xp = datacount%600;
        strokeWeight(1);
        for (int i = 0; i < 1; i++) 
        {
          stroke(colors[i]);
          line(xp-1, 500 - prevData[i], xp, 500 - data[i]);
        }
        
        drawCurrentTime();
        drawElapsedTime();

    
        datacount++;
      }
    }
  }
}

void stop() {
  writer.flush(); // Writes the remaining data to the file
  writer.close(); // Finishes the file
  super.stop();
}

プログラム:Subroutine1
 2つ目は、現在時刻および経過時間を描画するプログラムです(図3-6)。1行目は、現在の日時情報をフォーマットして文字列で返すために、getTimestampというString型の関数の宣言を行っています。2~7行目では、現在の日時を「年、月、日、時、分、秒」から取得して、それぞれ変数に代入しています。10行目では、2~7行目で取得した現在日時の情報を連結した文字列のタイムスタンプを作成しています。12行目では、作成した文字列のタイムスタンプは、getTimestamp関数内のtimestampに格納されているため、その関数内でしか使用できません。そのため、return timestampを使用し、timestampを関数の外に返すことでgetTimestampの呼び出し元に、作成した文字列のタイムスタンプが出力できます。15行目では、getTimestam2というString型の関数の宣言を行っています。16~18行目では、現在時刻「時、分、秒」を取得し、それぞれ変数に代入しています。21行目では、現在時刻の情報を連結した文字列のタイムスタンプを作成しています。23行目では、12行目と同様にreturn timestampを使用し、getTimestam2の呼び出し元に、作成した文字列のタイムスタンプを出力する処理を行っています。

26~42行目は、Processing開発環境の実行ボタンを選択した際、ウィンドウ内に表示する現在時刻および経過時間の描画設定を行っています。26行目では、現在時刻を描画するため、drawCurrentTimeという関数の宣言を行っています。28行目では、getTimestam2から現在時刻をstamp2に格納しています。29行目のfill(255)は描画される形状の塗りつぶしを白色に設定し、noStroke()は形状の輪郭線を非表示に設定しています。これにより、白色の背景が表示されます。30行目では、rect関数を用いて、位置(450, 5)に幅130、高さ50の現在時刻を表示するための長方形の背景を描画しています。31行目では、描画の属性を設定しており、fill(0)から黒色のテキストに設定し、Stroke(0)は形状の輪郭線を黒色に設定しています。また、textSize(24)からテキストサイズを24としました。32行目では、text関数を使用して、28行目のstamp2から画面右上(x:450, y:30)に現在時刻を表示しました。35~42行目も同様の処理を行い、経過時間を描画するための関数をdrawElapsedTimeと定義し(35行目)、経過時刻を取得するための変数をelapsedTimeに格納しました(37行目)。経過時刻を描画するための背景色やテキストサイズは同様です(38~40行目)。描画位置に関しては、画面右上の現在時刻の下の段(x:450, y:60)に表示しました(41行目)。

44~55行目は、経過時間の計算を行い、「時、分、秒」のフォーマットに整形するString型の関数getElapsedTime(44行目)を用いたプログラムです。45行目では、millis()関数を使用してプログラムの実行開始からの経過時間をミリ秒数単位で取得し、elapsedMillis変数に格納しています。47~49行目は、取得したミリ秒数を元に、経過時間を「時間、分、秒」に変換しています。経過ミリ秒数を1000で割った値を「秒」、秒を60で割った値を「分」、分を60で割った値を時間に変換しています。51、52行目では、秒数や分数で60を超えた値を再計算できるように、60未満の範囲に収めています。54行目では、nf()関数を使用して、表示される経過時間の間にコロンで区切り「時間:分:秒」の形式で表示する文字列を形成しています。以上から、プログラムの実行開始と同時に設定したウィンドウ内に現在時刻および経過時間を表示することが可能となりました。

String getTimestamp() {
  int y = year();
  int m = month();
  int d = day();
  int h = hour();
  int min = minute();
  int s = second();

  // Use nf() to add leading zeroes to each time component if needed
  String timestamp = nf(y, 4) + nf(m, 2) + nf(d, 2) + "_" + nf(h, 2) + nf(min, 2) + nf(s, 2);
  
  return timestamp;
}

String getTimestamp2() {
  int h = hour();
  int min = minute();
  int s = second();

  // Use nf() to add leading zeroes to each time component if needed
  String timestamp =  nf(h, 2) +":"+ nf(min, 2) +":"+ nf(s, 2);
  
  return timestamp;
}

void drawCurrentTime()
{
  String stamp2=getTimestamp2();
  fill(255); noStroke();
  rect(450, 5,130,50); 
  fill(0); stroke(0);textSize(24); 
  text(stamp2, 450, 30); //   
}

void drawElapsedTime()
{
  String elapsedTime = getElapsedTime();
  fill(255); noStroke();
  rect(450, 55,130,50); 
  fill(0); stroke(0);textSize(24); 
  text(elapsedTime, 450, 60);
}

String getElapsedTime() {
  int elapsedMillis = millis() - startMillis;

  int s = elapsedMillis / 1000;
  int m = s / 60;
  int h = m / 60;

  s = s % 60;
  m = m % 60;

  return nf(h, 2) + ":" + nf(m, 2) + ":" + nf(s, 2);
}

====================================================================

引用文献
長野祐一郎(2016). 自作測定装置で学ぶ皮膚温バイオフィードバック バイオフィードバック研究,43,49-51.
長野祐一郎(2022). 自作測定機器を用いたバイオフィードバック バイオフィードバック研究,49,77-81.
大河内浩人(1990). 皮膚温制御におよぼす訓練課題とフィードバックの効果 バイオフィードバック研究,17,8-14.

MAIA_3因子の考察

北原(2019)
・MAIA「気づき・注意制御・感情への気づき因子」を用いて,①「脱中心化,視点取得,ひいては認知的再評価を説明するという仮説モデルの構築」②「身体感覚の知覚そのものは精神的問題を生じやすい一方で,内受容感覚への気づきによって適応的な感情調整が促された場合には,精神的問題が軽減されるか」について検討。

・結果:身体感覚の単純な知覚は,「抑うつ・不安」を高めるが,身体感覚への「気づき」が「感情への気づき」や「注意制御」につながり,脱中心化に基づいた適応的な感情調整が促されると,「抑うつ・不安」が軽減されるという関連性が示唆された

先行研究
①「内受容感覚MAIA×(マインドフルネス)FFMQ」正の相関(栗野,2022)
 →互いに身体意識に注意を向ける尺度である(Mehling, 2012)
 →マインドフルネスによって,内受容感覚の敏感さ(IS)が向上
 →MAIAと生理指標(HR, SCL):注意制御が高いとHR下降,SCL増大
  SCLの増大は,注意や認知と関連がある。HRは注意を向けると下降
 →「内受容感覚MAIA×FMS ver.a.j(フォーカシング的態度)」負の相関
 →特性不安と有意な相関は認められない

②内受容感覚3種測定(櫻井・清水,2018)
・手続き:MAIA測定後,心拍カウント・心拍弁別課題を実施
・相関:「注意制御因子×気づき・心配しない因子」正の相関,「感情への気づき因子×気づき・注意制御因子」正の相関
・心拍~課題の相関:「心拍カウント成績×自己制御因子」正の相関のみ
→課題間の有意な相関は認められなかった。異なる側面を反映している
→質問紙とも有意な相関は示されなかった。〃
・心拍カウント課題で,「1回も心拍を感じなかった」が多い

③庄司(2017)のMAIAのまとめ
・内受容感覚の低下を修正する試みで,介入群に3か月の瞑想を行った結果,コントロール群と比較して,「attention regulation、emotinal awareness、self regulation、body listening、body trust-ing 」の各因子で改善がみられた。

④田中(2019)
・感情や情動と内受容感覚(IAc)
→脳指標を用いて内受容感覚の正確性を測定することが盛んとなっている。反映されるのが「島皮質(前部)」とされ,正確性の島皮質の灰白質体積が相関することから,島皮質の活動量から正確性が判断されている。「島皮質(前部)」が感情の主観的経験に中心的な働きをしている(心拍数が上がると興奮状態にあるなど)。
・瞑想によって,内受容感覚の正確性っが上昇するという報告も

⑤中島(2021)
・内受容感覚のまとめ

⑥寺澤・梅田(2014)
・BFにおいて,自分の身体内部の変化をどの程度分間に知覚できているかは重要
・内受容感覚の敏感さとBFについて,質問紙(IS)で測定する
*測定された客観的な身体反応と質問紙に反映される身体反応の気づきの関係性は一致せず,敏感さを評定するのに十分な方法ではないという指摘がある

==================================================================

MAIA3因子の考察

質問紙の気づきとBFの制御成績は必ずしも関連しているとは言えないのでは(http://protolab.sakura.ne.jp/LAB01/?p=31183)

・MAIAの相関が高い物が本研究でみられた。先行研究でも相関やパスで同じ
・今回用いたものでも相関がみられなかったのが,内受容感覚と関連しているのではないのでは
・質問紙の気づきとBFの制御成績は必ずしも関連しているとは言えないのでは(http://protolab.sakura.ne.jp/LAB01/?p=31183)

気づきと感情は,身体について述べている
→PAの項目多め・ニュートラルの身体状態の気づき,

不快さや痛み,
→3,4因子は不快さ,NAな身体上にフォーカスあててる

皮膚温制御は,身体はリラックスしていないとできないと,本研究で見出した

ooguriへ (Processing等)コメント文の注意点

はじめに
 卒論のProcessing, Arduino開発環境のプログラムに,コメント文をつける際の注意点および,GPTをフル活用した時のメモを残しておく。

1. 図の添付(subroutine1,SBF230707)

 一番の問題が画質が荒くなる問題。対抗策は,「4Kモニタでスクショ,文字サイズの変更,コメント文を改行」。コメント文に関しては,横か真上が基本。

 「ファイル→設定」からウィンドウを開き,フォントサイズの変更しとけば4Kモニタなくても形になる。んでもって,言語とフォント指定しないと,日本語コメントがつけられないから注意な。

2. プログラム解説_付録化について

↑長野(2022)を参考にプログラム解説を行った。

 勝手なイメージだが,プログラム内のコメント文は簡略というかメモ書き。そんでもって,本文には詳細を記述する。というのもプログラム内で長文をたらたら書いてたら邪魔になるから。複数行でこの機能を果たすって解説に必要だしな。

3. プログラム解説のプロンプト

【コメント文】

プロンプトは,1行ずつコメント文つけて…だったような。
図のように数行コメント文がないものは,個別で再度GPTに聞く。

【本文】

プロンプトは,1行ずつ詳細を説明して…だったような。
同様に個別で再度GPTに聞く。たまに訳のわからねえことを言い出すから,その時はリセット(新しいチャットを出す)。スクリプトガチャ臭で非常に香ばしい。

=====================================================================

一通り完成したら本文・抄録と一緒に添削するから連絡して(Teams経由)。
質問あったらプログラム問わずくれ。

Green, 1977 温度BFTモデル

はじめに
STBFと訓練回の制御成績は, 濱田・下川(2002)の論文で説明されている通りグリーンモデルで説明できることに気づいた。内受容感覚もこっから引用できる。

BFの最終段階(大隈, 1986)

①外的FB依存段階:イメージ方略や装置のFB機能をもとに,制御訓練を行う
②内的制御段階:「気づき+注意」で,体内にFB環が成立し,装置いらずになる。

訓練回

・計測1, モデル1
 STBFの事前説明により, 自発性低下の阻止。心身の回路は閉じているため, 制御成績は低い状態であり, ST変化は非随意的である。STコントロールできない。

・計測2, モデル2
 各種FBと身体内部の感覚の違いに注意を向け(ぼんやりとしたレベル), 回路5から身体内部の感覚の違いに気づき「何となく重感・温感がわかる」とST上昇が感覚的に知覚される。受動的注意・イメージ方略の葛藤。

・計測3, モデル3
 身体内部の変化・心的状態の変化の気づきを学習し, 心身の関係のスキーマを構築する。各種FBから心身の状態に気づき, 自己制御の回路に結び付く。

・計測4, モデル4
 上下方向のコントロールが自発的に行える。BF装置がなくても制御が成立し, リラクゼーション効果や薬物のように感受性を低下させることもなくなる。

*今回の場合, モデル3止まりになる。FBなしで計測していないため。

231110 卒論進捗

進捗

1. 卒論の添削のお願い

・研究Ⅰ:方法, 考察, 付録, (結果)
・研究Ⅱ:方法, 結果

2. 特性尺度MAIA_分析結果の表を作成

分析方法
 計測1回目, 計測4回目について, 2(群:高群, 低群)×2(訓練方向:上昇, 下降)×5(期間:1~5分)の3要因混合計画による分散分析を行った。分析結果は, 以前先生から頂いた表 (IoT皮膚コンダクタンス測定器を用いた授業評価)を参考に作成しました。

計測1回目

【下位検定の結果】

因子3
・群の効果は, 高群より低群の皮膚温変化量が有意に高い傾向にあった。
・群×期間の交互作用は, 群および期間の単純主効果が有意でなかった。

因子5
・群×訓練方向の交互作用は, 群の単純主効果が下降訓練で有意であった(p<.10)。つまり, 下降訓練において高群より低群の皮膚温変化量が有意に低かった
 また, 訓練方向の単純主効果が高群で有意であった(p<.10)。つまり, 高群において下降訓練より上昇訓練の皮膚温変化量が有意に低かった

因子8
・群×訓練方向の交互作用は, 群および期間の単純主効果が有意でなかった。

計測4回目

因子4の下位検定
・群×訓練方向の交互作用において, 訓練方向の単純主効果が低群で有意であった(p<.05)。つまり, 低群において下降訓練より上昇訓練の皮膚温変化量が有意に高かったと言えた。なお, 2次の交互作用について, 下記に図を添付した。

=======================================================================

質問

質問1
 上記表について, 本分析結果では, いずれも「群×訓練方向の交互作用」といった2水準同士の交互作用が有意であったため, 多重比較を行っていない。そのため, 下位検定の分析結果は, 文章でのみ記述するか, もしくは表に示すのでしょうか。表に示す場合, 群の効果にのみ焦点を充てるのでしょうか。

質問2
 表のサイズが大きく, 数値が読めない。本文には, 横向きで添付でしょうか。

========================================================================

やること

・研究Ⅱの考察書く
・抄録書く
・Processingのプログラム解説にコメント文を書く

=======================================================================

コメント

・MAIAの分析は, 以前の「群×訓練方向×期間」の分析に戻す(解釈がしやすい)
・表の横長問題に関しては, 分割して添付する
 →表1:平均値・標準偏差
 →表2:主効果・交互作用
 →表3:単純主効果・多重比較(表にすべて情報を記載する, 時間があれば下位検定)
・卒論の添削が終わったため, 修正を行うこと
・時間がないから考察を書く

卒論進捗_231027

進捗

1. 二次の交互作用の下位検定を「R」で分析できた (分析方法は152ブログにて)
2. 特性尺度MAIAの分析を行った (下記画像は各因子の説明)

=======================================================================

分析1
 MAIAの8因子をそれぞれ高群/低群に分け, 皮膚温変化量を従属変数として, 「2(群:高群, 低群)×4(訓練回:1~4回目)×2(訓練方向:上昇, 下降)」の3要因混合計画の分散分析を行った結果, いずれの因子においても群の効果は有意でなかった

質問1
 卒論の本文内に上記の結果を記述する場合も同様に, 統計量を記載せず, 「いずれの因子においても群の効果は有意でなかった」と記述するのでしょうか。

分析2→因子1, 4, 5で群の効果・交互作用が有意であった。
 MAIAの8因子をそれぞれ高群/低群に分け, 皮膚温変化量を従属変数として, 「2(群:高群, 低群)×2(訓練方向:上昇, 下降)×5(期間:1~5分)」の3要因混合計画の分散分析を行った。なお, 二次の交互作用の下位検定の分析の流れについて確認を行うため, 因子1のみ全文載せました。

因子1:気づき(身体感覚)
 3要因混合計画の分散分析を行った結果, 群×訓練方向×期間の交互作用のみ有意であった(F(4, 48)=2.32, p<.10)。
 群×訓練方向×期間の交互作用が有意であったため, 群の水準別に訓練方向×期間の単純交互作用の検定を行ったところ, 高群における訓練方向×期間の単純交互作用は有意であった(F(4,48)=6.17, p<.01)。低群における訓練方向×期間の単純交互作用は有意でなかった(F(4,48)=0.53, n.s.)。高群における訓練方向×期間の単純交互作用は有意であったため, 単純・単純主効果の検定(α=0.35)を行ったところ, 高群と上昇訓練における期間の単純・単純主効果が有意であった(F(4,48)=4.16, MSE=0.022, adjusted p=0.135)。
 そこで, Holm法による多重比較を行ったところ, 高群と上昇訓練において, 1分と4分, 5分の間, 2分と3分, 4分, 5分の間, 3分と4分, 5分の間で有意であった(ps<.10)。つまり, 高群と上昇訓練において, 訓練時間が経つにつれて, 皮膚温変化量が有意に高くなったと言えた。

質問2(図の作成方法)
 上記, 分析結果を図に表す場合, 左図のように両群か, もしくは右図の「高群における訓練方向×期間の単純交互作用」のどちらでしょうか。

質問3(有意水準)
 単純・単純主効果の有意水準について質問があります。Rで単純・単純主効果検定を行った際, 「高群と上昇訓練における期間の単純・単純主効果が有意であった(F(4,48)=4.16, MSE=0.022, adjusted p=0.135)」で有意であったと出力されましたが, 有意水準は0.05ではなく, α=0.35を基に判断するのでしょうか。
 また, 単純・単純主効果が有意でない場合, 「単純交互作用→多重比較」の順で分析を行うのでしょうか。

①Rの出力結果_単純・単純主効果の検定

> tx7 # 単純・単純主効果の検定 (α=0.35推奨)
               SS df1 df2    MSE     F adjust_p
B at_A1_C1 0.0007   1  12 0.1659 0.0040   0.9742
B at_A1_C2 0.0612   1  12 0.1659 0.3690   0.9135
B at_A1_C3 0.1495   1  12 0.1659 0.9007   0.9135
B at_A1_C4 0.3464   1  12 0.1659 2.0879   0.9135
B at_A1_C5 0.4369   1  12 0.1659 2.6331   0.9135
C at_A1_B1 0.3608   4  48 0.0217 4.1621   0.1358 ←A1-B1におけるCが有意
C at_A1_B2 0.0357   4  48 0.0217 0.4117   0.9135

②二次の交互作用の下位検定の記述例(一丸, 2021)

因子4:注意制御(身体感覚への注意)
群×期間の交互作用のみ有意であった(F(4, 48)=3.37, p<.05)。
 →期間の単純主効果が高群において有意であった(p<.01)。
 →Holm法による多重比較を行ったところ, 1分と5分, 2分と5分, 3分と5分で有意であった(ps<.05)。つまり, 高群において, 訓練時間が経つにつれて, 皮膚温変化量が有意に高くなったと言えた。

因子5:感情への気づき (身体と感情の関連性)
・群の効果のみ有意であった(F(1, 12)=3.57, p<.10)。
 →つまり, 低群より高群の皮膚温変化量が有意に高かったと言えた。

因子2, 3, 6~8
・群の効果および交互作用は有意でなかった。

=======================================================================

質問

・上記, 質問1~3について =======================================================================

やること

・実験Ⅱの考察を執筆
・実験Ⅰのプログラム解説を付録化
・ブログに添付したものに加え, 引用文献を探す

=======================================================================

回答

・村井先生に確認したところ, 5%水準で判断でいいとのこと。α=0.35は, パッケージのなんらかのルールかもしれないみたいだ。
・例)群×期間の交互作用で有意であった場合, 訓練方向の図は本文に載せない。ただし, 結果の解釈としてゼミの進捗などには添付する。
・有意傾向の場合, 総括として「高い/低い傾向にあった」と記述する。1%, 5%の場合, 「高かった」とする。
・MAIAの分析に関しては, 8因子あるため, 分析結果を表に表し

231006 卒論進捗

進捗

生理指標

1. 計測回数ごとの制御方略別の皮膚温制御成績

① 計測1
 「2(制御方略:温冷感, 情動)×5(課題期間:1分~5分)×2(訓練方向:上昇, 下降)」の3要因5水準混合計画の分散分析を行った結果, 主効果・交互作用の効果は認められなかった。

② 計測2
 「2(制御方略:温冷感, 情動)×5(課題期間:1分~5分)×2(訓練方向:上昇, 下降)」の3要因5水準混合計画の分散分析を行った結果, 制御方略の主効果が有意傾向(F(1, 11)=3.36, p<.10)であった。交互作用を行った結果, 「制御方略×課題期間」の効果が5%水準で有意だった(F(4, 44)=2.85, p<.05)。
 制御方略の主効果が有意傾向であったため, Holm法による多重比較を行った結果, 1分目より4分目, 5分目が高く, 2分目より5分目が高かった(ps<.05)。また, 「制御方略×課題期間」の交互作用の効果が有意であったため, 単純主効果の検定を行ったところ, 4分目および5分目で制御方略の単純主効果が有意傾向であった(ps<.10)。加えて, 課題期間と温冷感イメージの単純主効果が有意であった(p<.01)。

図1 計測1, 2の「制御方略×課題期間」

③計測3
 「2(制御方略:イメージなし, その他)×5(課題期間:1分~5分)×2(訓練方向:上昇, 下降)」の3要因5水準混合計画の分散分析を行った結果, 制御方略の主効果(F(1, 10)=3.85, p<.10), 課題期間の主効果(F(4, 40)=2.88, p<.05)が認められた。交互作用を行った結果, 「制御方略×課題期間」の効果(F(4, 40)=3.41, p<.05), 「訓練方向×課題期間」の効果(F(4, 40)=3.41, p<.05)が有意だった。
 制御方略の主効果が有意傾向であったため, Holm法による多重比較を行った結果, 1分目より4分目, 5分目が高く, 2分目より5分目が高かった(ps<.05)。また, 「制御方略×課題期間」の交互作用の効果が有意であったため, 単純主効果の検定を行ったところ, 3分目および4分目で制御方略の単純主効果が有意傾向であり(ps<.10), 5分目で有意であった(p<.05)。加えて, 課題期間とその他(イメージ)の単純主効果が有意であった(p<.01)。

「訓練方向×課題期間」の交互作用の効果が有意であったため, 単純主効果の検定を行ったところ, 上昇訓練課題期間の単純主効果が有意であった(p<.01)。

図2 計測3の「制御方略×課題期間」「訓練方向×課題期間」

④ 計測4
 「3(制御方略:温冷感, イメージなし, その他)×5(課題期間:1分~5分)×2(訓練方向:上昇, 下降)」の3要因5水準混合計画の分散分析を行った結果, 訓練方向の主効果(F(1, 11)=3.99, p<.10), 課題期間の主効果(F(4, 44)=2.34, p<.10)が有意傾向であった。「制御方略×訓練方向」の交互作用の効果が有意傾向(F(2, 11)=3.94, p<.10), 「訓練方向×課題期間」の交互作用の効果が5%水準で有意(F(4, 44)=3.13, p<.05), 「制御方略×課題期間×訓練方向」の交互作用の効果が1%水準で有意(F(4, 44)=3.13, p<.05)であった。

図3 計測4「制御方略×訓練方向「訓練方向×課題期間」「制御方略×課題期間×訓練方向」

質問1
・計測4回目の結果「A×B×C」の解釈と図のプロット方法。
・下画像の生理指標は, 論文内に添付するか

2. 独自項目の分析

① BF経験の有無
 「2(BF経験の有無:有り, 無し)×4(計測回数:1~4回目)×2(訓練方向:上昇, 下降)」の3要因4水準混合計画の分散分析を行った結果, 訓練方向の主効果が認められた(F(1, 12)=4.73, p<.10)。交互作用の効果および多重比較では, 有意な差が認められなかった。

② 主観的な制御レベル
 「2(以前より皮膚温制御能力が身についたか:高群, 低群)×4(計測回数:1~4回目)×2(訓練方向:上昇, 下降)」の3要因4水準混合計画の分散分析を行った結果, 群の主効果(F(1, 12)=4.29, p<.10), 計測回数の主効果(F(3, 36)=2.56, p<.10), 訓練方向の主効果が認められた(F(1, 12)=3.85, p<.10)。交互作用の効果および多重比較では, 有意な差が認められなかった。

③ 内受容感覚(身体の温度変化に注意を向けているか)を計測回数ずつ

・計測1
 「2(内受容の注意:高群, 低群)×2(訓練方向:上昇, 下降)×5(課題期間:1~5分)」の3要因5水準混合計画の分散分析を行った結果, 群の主効果が認められた(F(1, 12)=4.33, p<.10)。「群×訓練方向」の交互作用の効果に有意傾向が認められた(F(1, 12)=4.12, p<.10)。「群×訓練方向」の交互作用の効果が有意傾向であったため, 単純主効果の検定を行ったところ, 群と下降訓練の単純主効果が有意であった(p<.05)。

・計測2
 「2(内受容の注意:高群, 低群)×2(訓練方向:上昇, 下降)×5(課題期間:1~5分)」の3要因5水準混合計画の分散分析を行った結果, 課題期間の主効果が認められた(F(4, 48)=4.19, p<.01)。交互作用の効果および多重比較では, 有意な差が認められなかった。

・計測3
 「2(内受容の注意:高群, 低群)×2(訓練方向:上昇, 下降)×5(課題期間:1~5分)」の3要因5水準混合計画の分散分析を行った結果, 「訓練方向×課題期間」の交互作用の効果が有意だった(F(4, 48)=3.43, p<.05)。「訓練方向×課題期間」の交互作用の効果が有意であったため, 単純主効果の検定を行ったところ, 課題期間と上昇訓練の単純主効果が有意であった(p<.01)。

・計測4
 「2(内受容の注意:高群, 低群)×2(訓練方向:上昇, 下降)×5(課題期間:1~5分)」の3要因5水準混合計画の分散分析を行った結果, 訓練方向の主効果(F(1, 12)=3.99, p<.10), 課題期間の主効果(F(3, 36)=2.56, p<.10)認められた。「群×訓練方向」の交互作用の効果が有意傾向(F(1, 12)=4.64, p<.10), 「訓練方向×課題期間」の交互作用の効果が有意傾向(F(4, 48)=2.49, p<.10), 「群×訓練方向×課題期間」の交互作用の効果(F(4, 48)=3.62, p<.05)が有意であった。

④ 内受容感覚(身体の温度変化に気づいているか)

・計測1
 「2(内受容の気づき:高群, 低群)×2(訓練方向:上昇, 下降)×5(課題期間:1~5分)」の3要因5水準混合計画の分散分析を行った結果, 「群×訓練方向」の交互作用の効果が有意傾向(F(1, 12)=3.00, p<.10)であったため, 単純主効果の検定を行ったところ, 単純主効果が認められなかった。

・計測2
 「2(内受容の気づき:高群, 低群)×2(訓練方向:上昇, 下降)×5(課題期間:1~5分)」の3要因5水準混合計画の分散分析を行った結果, 課題期間の主効果が認められた(F(4, 48)=3.20, p<.05)。交互作用の効果および多重比較では, 有意な差が認められなかった。

・計測3
 「2(内受容の気づき:高群, 低群)×2(訓練方向:上昇, 下降)×5(課題期間:1~5分)」の3要因5水準混合計画の分散分析を行った結果, 課題期間の主効果認められた(F(4, 48)=2.41, p<.10)。「群×課題期間」の交互作用の効果が有意(F(4, 48)=3.10, p<.05), 「訓練方向×課題期間」の交互作用の効果(F(4, 48)=3.53, p<.05)が有意であった。「群×課題期間」の交互作用の効果が有意であったため, 単純主効果の検定を行ったところ, 群と5分目の単純主効果が有意であった(p<.05)。また, 課題期間と低群が有意であった(p<.01)。

・計測4
 「2(内受容の気づき:高群, 低群)×2(訓練方向:上昇, 下降)×5(課題期間:1~5分)」の3要因5水準混合計画の分散分析を行った結果, 訓練方向の主効果が認められた(F(1, 12)=4.61, p<.10)。「群×訓練方向」の交互作用の効果が有意(F(1, 12)=5.54, p<.05), 「訓練方向×課題期間」の交互作用の効果が有意(F(4, 48)=3.13, p<.05), 「群×訓練方向×課題期間」の交互作用の効果(F(4, 48)=2.14, p<.10)が有意であった。

=======================================================================

心理指標_MAIA(特性尺度)

3. 内受容感覚と計測回数の検討
 下位尺度8つについて
, 「2(群(内受容感覚):高群, 低群)×4(計測回数:1~4回目)×2(訓練方向:上昇, 下降)」の3要因4水準混合計画の分散分析を行った結果, 群の主効果および交互作用の効果はいずれの因子も認められなかった

4. 内受容感覚と課題期間の検討

因子1
 「2(群(内受容感覚):高群, 低群)×5(課題期間:1~5分目)×2(訓練方向:上昇, 下降)」の3要因5水準混合計画の分散分析を行った結果, 訓練方向の主効果(F(1, 12)=3.65, p<.10), 課題期間の主効果(F(4, 48)=2.64, p<.05)が認められた。「訓練方向×課題期間」の交互作用の効果(F(4, 48)=4.38, p<.01), 「群×訓練方向×課題期間」の交互作用の効果が有意(F(4, 48)=2.32, p<.10), が有意であった。
 課題期間の主効果が有意であったため, Holm法による多重比較を行った結果, 1分目より4分目, 5分目が高く, 3分目より5分目が高かった(ps<.05)。「訓練方向×課題期間」の交互作用の効果が有意であったため, 単純主効果の検定を行ったところ, 訓練方向と3, 4, 5分目の単純主効果が有意であった(ps<.10)。また, 課題期間と上昇訓練が有意であった(p<.01)。

因子2, 3, 6~8
 群の主効果・交互作用は認められなかった。

因子4
 「2(群(内受容感覚):高群, 低群)×5(課題期間:1~5分目)×2(訓練方向:上昇, 下降)」の3要因5水準混合計画の分散分析を行った結果, 訓練方向の主効果(F(1, 12)=3.90, p<.10), 課題期間の主効果(F(4, 48)=4.04, p<.01)が認められた。「群×課題期間」の交互作用の効果(F(4, 48)=3.37, p<.05), 「訓練方向×課題期間」の交互作用の効果が有意(F(4, 48)=4.30, p<.01), が有意であった。
 課題期間の主効果が有意であったため, Holm法による多重比較を行った結果, 1分目より5分目が高く, 2分目より5分目が高く, 3分目より5分目が高かった(ps<.05)。「群×課題期間」の交互作用の効果が有意であったため, 単純主効果の検定を行ったところ, 課題期間と高群の単純主効果が有意であった(p<.01)。

因子5
 「2(群(内受容感覚):高群, 低群)×5(課題期間:1~5分目)×2(訓練方向:上昇, 下降)」の3要因5水準混合計画の分散分析を行った結果, 群の主効果(F(1, 12)=3.57, p<.10), 課題期間の主効果(F(4, 48)=2.74, p<.05)が認められた。「訓練方向×課題期間」の交互作用の効果が有意(F(4, 48)=4.06, p<.01)であった。

質問2
 内受容感覚は特性尺度のため, 他の内受容感覚の尺度を用いて調査した方がよろしいでしょうか。しかし, 他の内受容感覚尺度を見ても, ストレス反応の評価や対人面が多く, 本研究に合う尺度が見つかりません。先行研究を読んでも, BFに内受容感覚は重要としか記載されておらず, 具体的な使用例が見つかりません(マインドフルネスなど)。
 そもそも, 心理指標を使わず「心拍カウント課題 , 心拍弁別課題」から内受容感覚を評価している研究もあった(櫻井, 2017)。

=======================================================================

やること

・内受容感覚MAIAの分析聞く
・メンストラムサイクルの論文を調べる(STBFと絡めて)
・生理指標をある程度分析し終わったら, 結果の記入を始める
・卒論のコメントをいただいたところの修正作業を始める

・分析が一通り終了したため, 執筆作業に移行

230922 卒論進捗

実験Ⅰ

1. プログラム解説
 ソフトウェアArduino, Processing開発環境双方のプログラム解説が終わりました。また, ハードウェアの解説も終了したので, 卒論本文の方法が完成しました。

Arduino開発環境
Processing開発環境

2. 卒論本文
 ソフトウェアの解説に加え結果・考察を記入し, 本文完成。
 お時間があるときに, 添削をお願いします。

========================================================================

*訓練期間の表記に関して、漆師さんから「訓練~週間後」より「計測~回目」の方がわかりやすいとアドバイスをいただいて、表記を変えましたが、画像内で修正できていない部分がありますのでご承知おきください。
①修正前:「訓練期間:訓練初日, 1週間後, 2週間後, 3週間後」
②修正後:「訓練期間:計測1回目~計測4回目」

実験Ⅱ

1. データ収集
 一昨日, 実験参加者15名分のデータ収集が完了し, 実験終了とした。

2. 生理指標

① 訓練期間別_大学での計測

計測回数が増えるごとに, 訓練方向の差が開いていき、分別ができてきているように見受けられた。

③ 訓練期間および制御方略別_皮膚温変化量

計測4回目の制御方略「その他」が訓練方向の差が鮮明に見受けられる。

④ 訓練期間および性別ごとの皮膚温変化量

計測2回目以降の男性の皮膚温変化量は, 訓練方向の差がはっきりしてる。

⑤ 「性別×期間×方向」3要因4水準混合計画の分散分析

 「2(性別:男性, 女性)×4(訓練期間:計測1回目~計測4回目)×2(訓練方向:上昇/下降)」の3要因4水準混合計画の分散分析を行った結果, 性別および訓練期間の主効果は認められなかった。訓練方向の主効果が5%水準で認められた(F(1, 13)=7.01, p<.05)。また, 性別×訓練方向の交互作用は有意傾向であった(F(1, 13)=3.90, p<.10)。交互作用の効果が有意傾向であったため, 単純主効果の検討を行ったところ, 訓練方向と男性において1%水準で有意な差が認められた(p<.01)。

⑥ 「訓練期間×訓練方向×課題期間(1分ずつ)」3要因5水準混合計画の分散分析

 「4(訓練期間:計測1回目~計測4回目)×2(訓練方向:上昇/下降)×5(課題期間:1分目~5分目)」の3要因5水準混合計画の分散分析を行った結果, 訓練期間の主効果が有意傾向であった(F(3, 42)=2.41, p<.10)。訓練方向の主効果が有意傾向であった(F(1, 14)=3.96, p<.10)。課題期間の主効果は5%水準で有意な差が認めれた(F(4, 56)=3.56, p<.05)。また, 訓練方向×課題期間の交互作用の効果が1%水準で有意な差が認められた(F(4, 56)=4.85, p<.01)。訓練期間×課題期間の交互作用の効果が5%水準で有意な差が認められた(F(12, 168)=1.87, p<.05)。訓練期間×訓練方向の交互作用は有意な差が認められなかった(F(3, 42)=0.92, n.s.)。
 訓練期間の主効果が有意傾向であったため, Holm法による多重比較を行った結果, 有意な差は認められなかった。また, 課題期間について, 多重比較を行ったところ, 1分目より5分目の方が高かった(p<.05)

質問1
・他にも有意差がみられましたが、3要因の結果の見方がわかりません。
・グラフに表す際、1つにまとめるのではなく, 訓練期間(計測1回目~4回目)に分けて、計4つのグラフを作成方法でよろしいでしょうか。

3. 心理指標_自尊感情尺度(RSES-J)→分析やり直し

① 訓練期間ごとのRSES-J平均得点の推移

 「2(性別:男性, 女性)×4(訓練期間:計測1回目~計測4回目)」の2要因4水準混合計画の分散分析を実施したところ,性別の主効果は認められなかった(F(1, 13)=0.17, n.s.)。また, 訓練期間の主効果は有意傾向が認められた(F(3, 39)=2.32, p<.10)。Holm法による多重比較を行った結果, 有意な差は認められなかった。また, 訓練期間×性別の交互作用は有意な効果が認められなかった(F(3, 39)=2.05, n.s.)。

「群×計測回数×訓練方向」

 独立変数をRSES-Jの群(高群, 低群),計測回数, 訓練方向,  従属変数を皮膚温変化量として, 「2(群:高群, 低群)×4(計測回数:計測1回目~計測4回目)×2(訓練方向:上昇, 下降)」の3要因4水準混合計画の分散分析を実施したところ, 群の主効果に有意傾向(F(1, 12)=3.29, p<.10), 計測回数の主効果に有意傾向(F(3, 36)=2.34, p<.10), 訓練方向の主効果の有意傾向(F(1, 12)=4.09, p<.10)が認められた。また, 群×計測回数の交互作用に有意傾向な効果が認められた(F(3, 36)=2.85, p<.10)。
 計測回数の主効果が有意傾向であったため, Holm法による多重比較を行った結果, 計測1回目より2回目が高い(p<.05)。計測1回目より4回目が高い(p<.05)。
 群×計測回数の交互作用の効果が5%水準で有意であったため, 単純主効果の検討を行ったところ, 群と計測1回目に有意傾向(p<.10), 群と計測2回目に5%水準で有意(p<.05), 計測回数と高群に1%水準で有意な差が認められた(p<.01)。

「変動性×計測回数×訓練方向」

 独立変数をRSES-Jの変動性(安定, 不安定), 従属変数を皮膚温変化量として, 「2(変動性:安定, 不安定)×4(計測回数:計測1回目~計測4回目)×2(訓練方向:上昇, 下降)」の3要因4水準混合計画の分散分析を実施したところ, 変動性の主効果は認められなかった(F(1, 12)=1.98, n.s.)。計測回数の主効果に有意傾向が認められた(F(3, 36)=2.44, p<.10)。訓練方向の主効果に有意傾向が認められた(F(1, 12)=9.06,  p<.10)。また, 変動性×計測回数の交互作用の効果に有意傾向が認められた(F(3, 36)=2.43,  p<.10)。計測回数×訓練方向の交互作用では, 5%水準で有意な効果が認められた(F(3, 36)=3.17,  p<.05)。
 計測回数の主効果が有意傾向であったため, Holm法による多重比較を行った結果, 計測1回目より2回目が高い(p<.05)。計測1回目より3回目が高い(p<.05)。
 変動性×計測回数の交互作用の効果が有意傾向であったため, 単純主効果の検討を行ったところ, 変動性と計測2回目に有意傾向(p<.10), 計測回数と安定群に有意差が認められた(p<.01)。
 訓練方向×計測回数の交互作用の効果が5%水準で有意であったため, 単純主効果の検討を行ったところ, 計測回数と上昇訓練に有意(p<.05), 訓練方向と計測2回目に有意傾向(p<.10), 訓練方向と計測3回目に有意(p<.05), 訓練方向と計測4回目に有意差が認められた(p<.01)。

先行研究
 自尊感情の質問紙研究を調べたところ, 自尊感情の「高群/低群」「安定/不安定」の2パターンから見るのが主流のようだ。自尊感情が高群だからといって, 変動が激しかったり, 攻撃性を持つこともあるからだ(櫻井, 2014)。そのため, 有意差がでなくても「自尊感情の揺れ幅が小さく, 安定的であることが精神的健康や適応の指標となる」

⑤ 変動性・高群/低群の算出方法の確認

質問4

①高群/低群に分類したいのですが, 平均得点から割り当てでしょうか。
②変動性(安定, 不安定)に分類したいのですが, 標準偏差から割り当てでしょうか。
③上記2点について, 期間で推移をみたいのですが, 平均化して一つにまとめた後に折れ線をプロットすればいいのでしょうか。

4. 内省報告等

①実験中の制御方略別イメージ内容

*本実験で使用した制御方略は、「温冷感イメージ, 情動イメージ, イメージなし, その他」の4種類です。「その他」に関しては, 「温冷感, 情動イメージ」の併用。もしくは, 液晶ディスプレイの温度が変化するイメージなど, 文字通り制御方略1~3に該当しない方略です。

② 訓練期間別_実験後の内省報告

 計測最終日の実験参加者の体調不良率が, 1/3という状態。また, 眠いと回答した人の睡眠時間を確認したところ, 「1~3時間」のため, 実験による眠気ではなかった。

5. 卒論本文
方法まで記入済み。これから, 取得したデータを元に結果・考察を執筆。

===========================================================================

質問5

・実験Ⅰ:プログラム解説部分で, 卒論本文にプログラムの画像を添付いたしましたが, 画像内にコメント文を付けた方がいいでしょうか。
・実験Ⅲ(仮):8月の上旬に先生に発注依頼した「XIAO ESP32S3」の状況はいかがでしょうか。もし到着していて, 先生のお時間をいただければ日曜日などを利用して, 装置開発を行いたいです。

========================================================================

残りの分析
・「制御方略×期間×方向」
・カウンターバランス:「訓練順序×期間×方向」
・内受容感覚
・自宅での訓練
・訓練回数×計測回数×RSES-J

========================================================================

コメント

・抄録の余白幅を狭める。
・STBFでのメンストラムサイクルの論文を読む。

実験Ⅰ
・プログラムは資料として、別枠で添付する。引用文献のあと。
 →本文を読んでほしいから資料として添付。本文とは?
・本文はBF装置の機能面のみを記載する。
・プログラム内の説明で、「~」ではなく「から」と表記する。
・プログラム画像が見えにくい。高解像度の倍率100%でスクショ。
 →先駆者にならって,プログラムを4Kモニターでスクショして記載
・Processingもコメント文つける

実験Ⅱ
【生理指標】
・交互作用の表記「有意な効果が…」に訂正
・制御方略:最初は「温冷, 情動」だったが、回数を重ねていくうえで自分に合ったものを参加者自身が能動的に選んでいき、その他を選んだ人は訓練方向の区別ができていた。
・性差の考察:生理周期(メンストラムサイクル)から男性は環境が安定しているが、女性は体調不良や体温が上昇するなどから、性差がでたのではないか。海外の論文等を調べる。→今回得られた結果だと、「男性は徐々にうまくなる結果だった」。
・2要因の分析(訓練期間×訓練方向)は、性別や課題期間で3要因の分析をしているため、かさまし疑惑が生じるため消す。
・交互作用の図をプロットする。
・「訓練期間×課題期間」の交互作用をプロット
・交互作用の解釈に内省報告を用いて、考察を記載する。
・総括としては、従来の方法では装置が限られているうえ、クリニックに通わない限り訓練を行うことができない。しかし、装置10台作成し、クリニックだけではなく、装置を持ち帰ってもらい、日常的に訓練を行うことで、よりクリアな結果を得ることができる。

・皮膚温の変動として概日(24時間)・概月(1か月)リズムによる変動。
・概月リズム:女性の概月リズムに体温の変動がある。月経後の増殖機で低体温が続き、排卵日後・分泌期(黄体期)の前半にプロゲステロンの作用で「0.2~0.4℃」急上昇し、以後その水準を維持する。月経開始とともに急激に下降する(P224)。
・女性は生理周期により基礎体温は排卵期後に上昇し, 月経開始とともに急激に下降し, 卵胞期を通じて低体温にとどまる(P230)

【心理指標】
・縦軸を「0~」ではなく、「15~30」など範囲を狭めて見やすくする。
・とりあえず、群(高群/低群)の分析を優先して、変動性は忘れていい。
 →群分けしたら、生理指標と絡めて分析する

Processing_プログラム解説

はじめに
前回のBFプログラム解説に続き, processingのプログラムについて要点を絞って解説していく。 processingのプログラム解説が終われば, 卒論Ⅰは残りまとめだけ。

要点
・.csv発行
・時刻表示, カウント
・皮膚温の波形をプロット
・Arduino開発環境と連携し, シリアルモニタの値を確認(安静期間の確認も)

SBF230707(ST波形表示, シリアル通信)

import processing.serial.*;

Serial myPort;
String portName = "COM6"; //BT serial port
int baudRate = 115200;
int dataPlotWidth = 640;
int dataPlotHeight = 200;
float[] data = new float[5];
String[] dataMV = new String[5];
float[] prevData = new float[5];
color[] colors = {
  color(255, 0, 0), 
  color(0, 0, 255), 
  color(0, 0, 0, 0), 
  color(0, 0, 0, 0)
};
int datacount=0;
String OutFileName;
String buf2;
PrintWriter writer;
int startMillis;


void setup() 
{
  //size(dataPlotWidth, dataPlotHeight);
  size(600,500);
  myPort = new Serial(this, portName, baudRate);
  
  OutFileName=getTimestamp()+".csv";
  writer = createWriter("./data/"+OutFileName); // Create a new file in the sketch directory
  buf2="";

}

void draw()
{
  
  if((datacount % 600) == 0) {
    background(255);  // clear screen    
    fill(0, 0, 0); // set the fill color to black
    textSize(12);
    text("40 deg", 10, 20); // 
  }
  
  while (myPort.available() > 0) 
  {
    String inString = myPort.readStringUntil('\n');
    
    if (inString != null) 
    {
      inString = trim(inString);
      String[] parts = split(inString, ',');
      
      if(parts.length >3) 
      {
        for (int i = 0; i < 5; i++) 
        {
          prevData[i] = data[i];
          data[i] = map(float(parts[i]), 20, 40, 0, 500);  // Assuming your data is in the range 0-200
          dataMV[i] = parts[i];
          
        }
        String stamp=getTimestamp2();
        String etime= getElapsedTime();
        String buf=stamp+","+etime+","+dataMV[0] + "," + dataMV[1] + "," + dataMV[2] + "," + dataMV[3] + "," + dataMV[4];
        println(buf);
        buf2=buf2+buf+"\n";
        
        if(datacount%10==0){
          writer.print(buf2);
          writer.flush();
          buf2="";
        }

        int xp = datacount%600;
        strokeWeight(1);
        for (int i = 0; i < 1; i++) 
        {
          stroke(colors[i]);
          line(xp-1, 500 - prevData[i], xp, 500 - data[i]);
        }
        
        drawCurrentTime();
        drawElapsedTime();

    
        datacount++;
      }
    }
  }
}

void stop() {
  writer.flush(); // Writes the remaining data to the file
  writer.close(); // Finishes the file
  super.stop();
}

Subroutine1(現在時刻, 経過時間)

String getTimestamp() {
  int y = year();
  int m = month();
  int d = day();
  int h = hour();
  int min = minute();
  int s = second();

  // Use nf() to add leading zeroes to each time component if needed
  String timestamp = nf(y, 4) + nf(m, 2) + nf(d, 2) + "_" + nf(h, 2) + nf(min, 2) + nf(s, 2);
  
  return timestamp;
}

String getTimestamp2() {
  int h = hour();
  int min = minute();
  int s = second();

  // Use nf() to add leading zeroes to each time component if needed
  String timestamp =  nf(h, 2) +":"+ nf(min, 2) +":"+ nf(s, 2);
  
  return timestamp;
}

void drawCurrentTime()
{
  String stamp2=getTimestamp2();
  fill(255); noStroke();
  rect(450, 5,130,50); 
  fill(0); stroke(0);textSize(24); 
  text(stamp2, 450, 30); //   
}

void drawElapsedTime()
{
  String elapsedTime = getElapsedTime();
  fill(255); noStroke();
  rect(450, 55,130,50); 
  fill(0); stroke(0);textSize(24); 
  text(elapsedTime, 450, 60);
}

String getElapsedTime() {
  int elapsedMillis = millis() - startMillis;

  int s = elapsedMillis / 1000;
  int m = s / 60;
  int h = m / 60;

  s = s % 60;
  m = m % 60;

  return nf(h, 2) + ":" + nf(m, 2) + ":" + nf(s, 2);
}