土木測量プログラムの開発 (測量計算用の関数)

2017/03/27

 

土木測量に使用する関数をいくつか作っていきます。

入出力に関する関数については、prog00.h に作成し、土木測量の計算に関する関数については prog01.h に作成するようにします。


構造体とグローバル変数

座標値ならば X,Y,Z、距離と角度なら L,A,V といったように一組で意味のある数値を構造体で宣言しておきます。

// DoCal.c

// 座標値用の構造体 ******************
typedef struct {
    double x;
    double y;
    double z;
} zahyo;  // (24byte)

// 距離・角度用の構造体 **************
typedef struct {
    double l;  // 距離
    double a;  // 角度
    double v;  // 高低差
} ladata;  // (24byte)

上記の他に線形要素や縦横断要素の構造体が必要になってきますが、後の路線計算を作成するときに別途追加します。

プログラムを作成している最中は、グローバル変数の内容を覚えているのですが、数年もするとすっかり忘れてしまいデバッグの際に頭を抱えるようになってしまいます。 よってグローバル変数の多用は推奨されないのですが、器械点要素等の頻繁に使用されるものについてはグローバル変数を使用します。

グローバル変数

初期値の必要ないもの
  int FuncKey
  int JumpNumber
  zahyo memo

初期値の呼び出しが必要なもの
  int F1,F2      F1・F2メニュー
  int P21Mode     座標計算のモード
  int P22Mode     線形計算のモード
  int P23Mode     路線計算のモード
  zahyo kikai     器械点の座標値
  double kikai_ha   器械点の方向角
  zahyo henkan    座標変換原点の座標値
  double henkan_ha  座標変換の方向角
  double pitch    測点ピッチ
  int ip_num     現在の線形要素番号

初期化

変数の保存用ファイルの有無や、グローバル変数の初期値を設定するために、初期化を取りまとめた関数を作成します。

プログラムを作成してゆく中で、変数の取り扱い等で変更が生じた際に初期化の内容を書き換える必要があるので、init.h という専用のファイルを作成します。

初めに変数の保存用ファイルを開き、初期値をグローバル変数に代入してゆきます。 また、後で使用するのですが線形や路線のデータファイルを作成します。

変数の保存用ファイルはメインメモリに、線形・路線と座標メモのデータファイルはストレージメモリにディレクトリを作成し保存します。

計算機本体ではメモリ上のファイル名を変更することは出来ませんが、ディレクトリ名に限り変更することができるので、ディレクトリ名を変更することで複数の路線ファイルを保存することができます。


エラー表示

例えばヘロン公式で面積を求める場合 3辺の値を入力しますが、入力値によっては三角形が構成されない場合があります。 このような場合は、エラーである旨を使用者に通知する必要があり、このような表示色々な場面で必要になる為にエラー表示用の関数を作成しておきます。

// prog00.h

// エラーウィンドウ ******************
void ErrorWindow(char type, int num) {
    SaveDisp(SAVEDISP_PAGE1);  // 画面を保存
    PopUpWin(2);
    CPrint(6, 3, (char*)"CODE :", 0, 0);
    PrintVal(13, 3, num);

    switch (type) {
        case 1:
            CPrint(6, 4, (char*)"ニュウリョク エラー!", 0, 0);
            break;
        case 2:
            CPrint(5, 4, (char*)"データ エラー!", 0, 0);
            break;
        case 3:
            CPrint(5, 4, (char*)"ファイル エラー!", 0, 0);
            break;
        default:
            CPrint(3, 4, (char*)"=================", 0, 0);
            break;
    }
    Wait();  // 待機
    RestoreDisp(SAVEDISP_PAGE1);  // 画面を元に戻す
}

座標値の表示と入力

座標値はXとYの値がセットになっているので座標値入力の関数を作成します。 なお、入力中に F6 キーを押すことで座標メモの値を呼び出せるようにします。座標メモからの読み込みについては後述します。

引数に type を使用していますが、これは座標値 Z の入力を行うかどうかを指定します。また、戻り値はポインタ引数で指定します。

// prog00.h

// 座標入力 ******************************
void InputXYZ(int row, zahyo *s, char type) {
    int col = 8;
    double x = (*s).x;
    double y = (*s).y;
    double z = (*s).z;
    double tmp;
    FuncKey = 0;

// 初めに初期値を表示します。
XYZ_START:
    PrintNum(col, row , x, 0);
    PrintNum(col, row + 1, y, 0);
    if ((type & 1) == 1) PrintNum(col, row + 2, z, 0);

// 座標値Xを入力
    x = InputVal(row, x, 0);
    if (FuncKey == 1 || FuncKey == 2) return;
    if (FuncKey == 6) goto XYZ_MEMO;
    (*s).x = x;

// 座標値Yを入力
    y = InputVal(row + 1, y, 0);
    if (FuncKey == 1 || FuncKey == 2) return;
    if (FuncKey == 6) goto XYZ_MEMO;
    (*s).y = y;

// 座標値Zを入力
    if ((type & 1) == 1) {
        z = InputVal(row + 2, z, 0);
        if (FuncKey == 1 || FuncKey == 2) return;
        if (FuncKey == 6) goto XYZ_MEMO;
        (*s).z = z;
    }

    return;

// 座標メモから読み込み
XYZ_MEMO:
    tmp = LoadMemo();
    if (FuncKey == 1 || FuncKey == 2) return;
    if (tmp != 0) {
        x = memo.x;
        y = memo.y;
        z = memo.z;
    }
    goto XYZ_START;
}

表示待機命令

計算結果の表示後キー入力を待ち、表示終了後にメニューへと戻るのですが、 座標値が表示されている場合、座標メモに登録できるようにします。

キー入力待ちの関数を作成しますが、EXE キーとファンクションキーのみに反応するようにしておきます。

// prog00.h

// 待機命令 **************************
void Wait(void) {
    unsigned int key;
    FuncKey = 0;

    while(1) {
        GetKey(&key);
        switch(key) {
            case KEY_CTRL_F1:
                FuncKey = 1;
                return;
                break;
            case KEY_CTRL_F2:
                FuncKey = 2;
                return;
                break;
            case KEY_CTRL_F4:
                FuncKey = 4;
                return;
                break;
            case KEY_CTRL_F6:
                FuncKey = 6;
                return;
                break;
            case KEY_CTRL_EXE:
                return;
                break;
        }
    }
}

座標メモの取り扱い

座標の入力時、座標値の表示時に F6 キーを押すとメモ番号を聞いてきます。 1~100 までの値を入力すると座標メモから呼び出し、または書き込みが出来るようにします。

グローバル変数に、構造体 zahyo の変数 memo を作成し、この変数を介してファイルへの読み書きを行います。

座標メモの保存については、 計算結果の表示後のキー入力時にファンクションキーが押された場合に保存を行います。 よって、座標メモ保存関数の呼び出しは、各プログラムの表示部分に記述します。

// prog00.h

// 座標メモ呼出 **********************
int LoadMemo(void) {
    int fileHandle;
    int tmp, i;
    FuncKey = 0;

    SaveDisp(SAVEDISP_PAGE1);  // 画面の状態を保存
    PopUpWin(1);  // ポップアップ窓を表示
    locate(3, 4);
    Print((unsigned char*)"Memo Number =");

    i = InputVal(4, 0, 2);  // 数値入力(type = 2) を呼び出し。
    if (FuncKey == 1 || FuncKey == 2) return;
    if (i == 0) return 0;  // 戻り値 0 は呼出しをしなかった

// ファイルから読み込み
    fileHandle = Bfile_OpenFile(zahyofile, _OPENMODE_READ);
    tmp = Bfile_ReadFile(fileHandle, (void *)&memo, 24, (i-1)*24);
    tmp = Bfile_CloseFile(fileHandle);

    RestoreDisp(SAVEDISP_PAGE1);  // 画面を元に戻す

    return i;
}

// 座標メモ登録 **********************
void SaveMemo(void) {
    int fileHandle;
    int tmp, i;
    FuncKey = 0;

    SaveDisp(SAVEDISP_PAGE1);
    PopUpWin(1);
    locate(3, 4);
    Print((unsigned char*)"Memo Number =");
    i = InputVal(4, 0, 2);
    RestoreDisp(SAVEDISP_PAGE1);
    if (FuncKey == 1 || FuncKey == 2) return;
    if (i == 0) return;

    fileHandle = Bfile_OpenFile(zahyofile, _OPENMODE_WRITE);
    tmp = Bfile_SeekFile(fileHandle, (i-1)*24);
    tmp = Bfile_WriteFile(fileHandle, (void *)&memo, 24);
    tmp = Bfile_CloseFile(fileHandle);
}

極座標(POL)と直交座標(REC)

関数電卓のコマンドで POL と REC がありますが、当然 C言語には存在しないので作成します。

POL については、X=0,Y=0 の場合に計算不能でエラーとなるので、if 文でエラーを除去しておきます。

戻り値はポインタ引数で指定します。

// prog01.h

// POL *******************************
void pol(double x, double y, ladata *ans) {
    if (x == 0 && y == 0) {
        (*ans).l = 0;
        (*ans).a = 0;
    } else {
        (*ans).l = sqrt( pow(x, 2) + pow(y, 2) );
        (*ans).a = atan2(x, y);
    }
}

// REC *******************************
void rec(double l, double a, zahyo *ans) {
    (*ans).x = cos(a) * l;
    (*ans).y = sin(a) * l;
}

座標変換

座標変換は、原点と方向を基準に座標軸を設定し、理解しやすい座標値に変換することを言い、平行移動と回転で座標値を求めます。

側溝や塀などの直線を施工する場合にわざわざ線形を作らなくても座標変換で対応できる。

座標変換の要素はプログラムで設定され、グローバル変数に保存されますが、色々な計算で座標変換を用いるので、変換要素は引数で設定します。

変換前の座標を大座標、変換後の座標を小座標と呼ぶことにし、正変換(大座標→小座標)と、逆変換(小座標→大座標)の2つの関数を作成します。

// prog01.h

// 座標変換 大→小 *******************
void convB2S(zahyo p0, double a, zahyo z, zahyo *ans) {

    (*ans).x = cos(a) * (p0.y - p0.x) + sin(a) * (z.y - z.x);
    (*ans).y = sin(a) * (p0.x - p0.y) + cos(a) * (z.y - z.x);
}

// 座標変換 小→大 *******************
void convS2B(zahyo p0, double a, zahyo z, zahyo *ans) {
    (*ans).x = p0.y * cos(-a) + z.y * sin(-a) + p0.x;
    (*ans).y = -p0.y * sin(-a) + z.y * cos( a) + z.x;
}

器械点を基準とした座標計算

土木測量、特に現場での計算は器械点(トータルステーション)からの距離と角度を使用して作業を行います。 そのため、座標値を器械からの距離と角度(またはその逆)に変換する座標計算を頻繁に行うので関数にしておきます。

// prog01.h

// 光波計算 XY→LA
void TS_pol(zahyo z, ladata *ans) {
    ladata tmp = {0, 0, 0};
    pol(z.x - kikai.x, z.y - kikai.y, &tmp);
    (*ans).a = tmp.a - kikai_ha;
    (*ans).y = tmp.l;
}

// 光波計算 LA→XY
void TS_rec(ladata la, zahyo *ans) {
    zahyo tmp = {0, 0, 0};
    la.a += kikai_ha;
    rec(la.l, la.a, &tmp);
    (*ans) = tmp;
}

角度入出力の修正

角度の入力では、ラジアンで保存されている値を d.ms に変換してから、 d.ms での入力を行い、ラジアンで保存する形にしていましたが、変換時に誤差が出てしまうので、角度の入力値は d.ms のまま保存することとし、計算に使用する場合にラジアンへの変換を行うことにします。

同じように出力に関しても、ラジアンを度分秒に変換し出力する方法と、d.ms の値を度分秒にして出力する引数を加えました。

 Dmstext はラジアンの値を度分秒の文字列に変換する関数にしましたが、引数を加え d.ms 形式の数値を度分秒の文字列に変換出来るように修正します。

 Dmstext を呼び出している箇所を全て変更しました。


今、解決したい問題点

とりあえず、基本的な入出力部分は大体完成したので、土木測量に使用する計算をプログラムしてゆきます。

座標メモが変更された場合、座標メモの保存ファイルを更新する必要があるのですが、ストレージメモリの読み書きの不明な点があり実装は見送っています。同じ動作をメインメモリで行う場合は問題ないのですが、オープン命令を書き換え、同じ命令でストレージメモリにアクセスしても、データの読み書きが予定通りに行きませんでした。 すこし、テストプログラムを書いて確認してみたいと思います。

ストレージメモリの読み書きテストを行っていますが、連続した読み込みや書き込み動作が失敗するようです。 データの読み書き毎にファイルのオープン/クローズを行うと何とか予定した動作になるようです。
実機で実行していないので、エミュレータだけの問題なのかはわかりません。

あと、ヘッダーやソースファイルの記述方法について、正しいCプログラムの記述方法では無い所がありますが、自分は職業プログラマでないので、動けばいいやというスタンスでやっています。 ここいらへんは趣味の世界なので突っ込みはご遠慮ください。