nextzlog / todo

ToDo lists for ATS-4, CW4ISR, QxSL, ZyLO.
https://nextzlog.dev
1 stars 0 forks source link

確定した文字のみ表示 #191

Open jucky154 opened 1 year ago

jucky154 commented 1 year ago

問題意識

OBの方から、確定していない文字の表示に関して、「出てくる文字が確定していないところはコロコロ変わる。それも無線家の頭の中に浮かぶ文字ではないものが表示される。よって、混乱を招くので確定していない文字、つまりは文字間の空白が来る前の文字は表示しないようにするべき」という要望が出た。 (現状、コロコロ変わる文字はhttps://blog.goo.ne.jp/ykimata/e/b5dee1229c784c05b39ae74456260010 のように聞き取れた部分までの分岐点の場所で、結構混乱するかもしれない)

確定していない文字を表示しない手法が求められる

解決方法

以前の確定していない文字を表示しない手法は前回と同じ文字のみ表示するというものだった (https://github.com/jucky154/cwListener/blob/b24f4925e8132212f0b2920dec0917762bb08aa8/cwListener.go#L218 ) がかなりコードが複雑化しているので、あまり採用するべき手法ではないと考えられる。

よって、OBの言うとおり、「文字間の空白が来る前の文字は表示しない」という対応が良いと思われる。 対応としては https://github.com/nextzlog/cw4i/blob/5dae69de1cc035755d3241c8c3db3a123de44e0a/core/symbols.go#L27C1-L36C2 の部分に関して、

func CodeToText(code string) (result string) {
    for _, s := range strings.Split(code, " ") {
        if val, ok := reverse[s]; ok {
            result += string(val)
        } else {
            result += "?"
        }
    }
    if code[len(code)-2:len(code)-1] != " " {
        result = result[len(result)-2:len(result)-1]
    }               
    return
}

のように対処すれば最後の部分が文字間の空白であれば確定なので表示し、そうでないならば最後の文字は確定していないので表示しないといった対応が可能であると考えられる。

jucky154 commented 1 year ago

https://github.com/nextzlog/cw4i/blob/5dae69de1cc035755d3241c8c3db3a123de44e0a/core/decoder.go#L63-L82 をみると、next.Code == prev.Codeという前と今が同じ「Code」ならばmissが増える。 つまり、Scan関数が文字間の空白が最後に来ることが許されるのであれば

最後の文字が解読される
↓
文字間の空白が出るのでmissは増えない
↓
文字間の空白でそのままなのでmissが増える
↓
解読終了

という流れになっているはずなので、上のコードでいいはず(多分)

jucky154 commented 1 year ago

実際に実装すると panic: runtime error: slice bounds out of range [:-1] というエラーが出たlen(code)の長さを考える必要があった

jucky154 commented 1 year ago

上のコードではよくなかったので、下のように修正

func CodeToText(code string) (result string) {
    for _, s := range strings.Split(code, " ") {
        if val, ok := reverse[s]; ok {
            result += string(val)
        } else {
            result += "?"
        }
    }
    if len(code) > 0 {
        if code[len(code)-1:] != " " {
            result = result[:len(result)-1]
        } 
    }
    return
}

実際の実働の動画が以下 https://github.com/nextzlog/todo/assets/58735989/36185382-b5ed-4055-a127-c894b3f79e93 確かに、ころころ変わるのは無くなったが、最後の文字が消えただけの状態

最後の文字が解読される
↓
文字間の空白が出るのでmissは増えない
↓
文字間の空白でそのままなのでmissが増える
↓
解読終了

という自分の仮定が誤りで、最後に空白がやってくると言うことがあり得ないと言うことだと思われるので、別の対処が求められる。

jucky154 commented 1 year ago

基本的には、現在の解析手法では最後の文字が正しいかどうかがわからない Miss==0の時には、前回と比較するとcodeが変わっている(最後の文字が変化したか、新しい文字が増えたかはわからないが、どちらにしろ最後の文字が正しくない) Miss > 0のときには、前回と比較するとcodeが同じであり、文字列として確定していると考えられる。 それを実装したのが以下のcode

func (d *Decoder) Read(signal []float64) (result []Message) {
    for _, next := range d.Next(signal) {
        for _, prev := range d.History {
            if prev.Freq == next.Freq {
                next = d.Scanner.Scan(prev.Merge(next))
                if next.Code == prev.Code {
                    next.Miss = prev.Miss + 1
                }
            }
        }
        if d.Program != nil {
            next = d.Program(next)
        }
        if next.Miss == 0 {
            next.Text = next.Text[:len(next.Text)-1]
        }
        if next.Miss <= d.MaxMiss {
            result = append(result, next)
        }
    }
    d.History = result
    return
}

https://github.com/nextzlog/todo/assets/58735989/5d403141-9a29-4ce7-9a63-d957538aea85

ただし、この手法だと無音から1周期必須であるので、最悪0.4秒遅れてしまう。

そのため、「文字間の空白」を正しく検知する方が正しい実装な気もする。

jucky154 commented 1 year ago

https://github.com/nextzlog/cw4i/blob/5dae69de1cc035755d3241c8c3db3a123de44e0a/core/segment.go#L17

    for until, value := range m.X {
        if class := m.Class(value); first != class {
            result = append(result, Segment{
                Class: first == 1,
                Since: since,
                Until: until,
                Width: float64(until - since),
                Level: med64(m.X[since:until]),
            })
            since = until
            first = class
        }
}

となっていることから、最後のところが空白にならないので

    func CodeToText(code string) (result string) {
    for _, s := range strings.Split(code, " ") {
        if val, ok := reverse[s]; ok {
            result += string(val)
        } else {
            result += "?"
        }
    }
    if len(code) > 0 {
        if code[len(code)-1:] != " " {
            result = result[:len(result)-1]
        } 
    }
    return
}

では正しく表示されなかった。 現状のコードの良さは今聴いている音が短音なのか長音を区別してから解析されるので、新語調法的な流れで表示がすすむ。 しかし、最後の文字を消すのであれば関係ないので、

    for until, value := range m.X {
        if class := m.Class(value); first != class {
            result = append(result, Segment{
                Class: first == 1,
                Since: since,
                Until: until,
                Width: float64(until - since),
                Level: med64(m.X[since:until]),
            })
            since = until
            first = class
        }
    }
    result = append(result, Segment{
            Class: first == 1,
            Since: since,
            Until: until,
            Width: float64(until - since),
            Level: med64(m.X[since:until]),
    })

と最後につけておいて、最後の空白が出るようにする?

jucky154 commented 1 year ago
func (m *Classes) Segments(first int) (result []Segment) {
    since := 0
    for until, value := range m.X {
        if class := m.Class(value); first != class {
            result = append(result, Segment{
                Class: first == 1,
                Since: since,
                Until: until,
                Width: float64(until - since),
                Level: med64(m.X[since:until]),
            })
            since = until
            first = class
        }

        class := m.Class(value)
        if until == len(m.X) -1 && since != until && class == 0{
            result = append(result, Segment{
                Class: first == 1,
                Since: since,
                Until: until,
                Width: float64(until - since),
                Level: med64(m.X[since:until]),
            })
        }
    }

    if len(result) > 2 {
        return result[1:]
    } else {
        return nil
    }
}

とすると、うまくいっているように見えるが、最後の文字が?になってしまう…

jucky154 commented 1 year ago

たぶん、最後の文字が?になってしまうのは、

    for _, s := range strings.Split(code, " ") {
        if val, ok := reverse[s]; ok {
            result += string(val)
        } else {
            result += "?"
        }
    }

のところで、最後の文字が""か" "になっていることが原因だと思われる。

    for key, value := range forward {
        reverse[value] = key
    }

    for key, value := range forward {
        reverse[value] = key
    }
    reverse[" "] = ""
    reverse[""] = ""

を追加すれば解決すると思われる

jucky154 commented 1 year ago

上の実装で試験したのが下の動画です

https://github.com/nextzlog/todo/assets/58735989/2673613d-dbab-412c-baa4-742a2bd9b1c3

jucky154 commented 1 year ago

最新の実装でPRを出しておきました

JG1VPP commented 1 year ago

素晴らしいです。ただ、CodeToText関数は素直にモールス符号を文字列に変換する想定なので、今回のPRではテストが落ちます。何点か修正があるので、待ってください。