From d21ad0ce009fe80c578a4c7b5b53fb1036206df2 Mon Sep 17 00:00:00 2001 From: Andrey Egorov Date: Sat, 10 Aug 2024 15:21:27 +0300 Subject: [PATCH] Added signal check. --- api/modem/at/at.go | 24 +++++++-- api/modem/modem.go | 25 +++++++-- api/modem/sms/sms.go | 5 +- api/modem/utils/signal.go | 108 ++++++++++++++++++++++++++++++++++++++ main.go | 35 ++++++------ 5 files changed, 169 insertions(+), 28 deletions(-) create mode 100644 api/modem/utils/signal.go diff --git a/api/modem/at/at.go b/api/modem/at/at.go index 3cfa85c..f79801a 100644 --- a/api/modem/at/at.go +++ b/api/modem/at/at.go @@ -37,8 +37,9 @@ type Port interface { Disconnect() error IsConnected() bool - RawSend(msg string) (string, error) + RawSend(msg string, timeout time.Duration) (string, error) Send(cmd string) (Resp, error) + SendWithTimeout(cmd string, timeout time.Duration) (Resp, error) io.Closer } @@ -106,7 +107,7 @@ func (p *atPort) IsConnected() bool { } // Low level write/read function -func (p *atPort) RawSend(msg string) (string, error) { +func (p *atPort) RawSend(msg string, timeout time.Duration) (string, error) { p.mutex.Lock() defer p.mutex.Unlock() @@ -114,7 +115,7 @@ func (p *atPort) RawSend(msg string) (string, error) { if _, err := p.port.Write([]byte(msg)); err != nil { return "", fmt.Errorf("serial port write: %w", err) } - // time.Sleep(time.Millisecond) + time.Sleep(timeout) // Read outBuf := make([]byte, 0) readLoop: @@ -131,13 +132,26 @@ readLoop: break readLoop } } - // p.logger.Println(msg, "\x1b[38;2;150;150;150mRAWREAD:", string(p.inputBuf[:readLen]), "\x1b[38;2;255;255;255m") + // p.logger.Println(msg, "\x1b[38;2;150;150;150mRAWREAD:", string(outBuf), "\x1b[38;2;255;255;255m") return string(outBuf), nil } func (p *atPort) Send(cmd string) (Resp, error) { - rawResp, err := p.RawSend(cmd + "\r\n") + rawResp, err := p.RawSend(cmd+"\r\n", time.Microsecond) + if err != nil { + return RespNil, fmt.Errorf("%s request: %w", cmd, err) + } + if len(rawResp) <= 4 { + return RespNil, fmt.Errorf("%s request: read too small msg: %d byte - %s", cmd, len(rawResp), string(rawResp)) + } + resp := rawResp[2 : len(rawResp)-2] // Cut \r\n + + return Resp(resp), nil +} + +func (p *atPort) SendWithTimeout(cmd string, timeout time.Duration) (Resp, error) { + rawResp, err := p.RawSend(cmd+"\r\n", timeout) if err != nil { return RespNil, fmt.Errorf("%s request: %w", cmd, err) } diff --git a/api/modem/modem.go b/api/modem/modem.go index 7e39586..907b818 100644 --- a/api/modem/modem.go +++ b/api/modem/modem.go @@ -15,6 +15,7 @@ import ( "gitea.unprism.ru/KRBL/sim-modem/api/modem/gps" "gitea.unprism.ru/KRBL/sim-modem/api/modem/internet" "gitea.unprism.ru/KRBL/sim-modem/api/modem/sms" + "gitea.unprism.ru/KRBL/sim-modem/api/modem/utils" ) type ModemData struct { @@ -53,6 +54,7 @@ type Modem interface { IsConnected() bool Update() error GetData() ModemData + CheckSignal() error PowerOn() error PowerOff() error @@ -170,6 +172,10 @@ func (m *modem) GetData() ModemData { } } +func (m *modem) CheckSignal() error { + return utils.CheckSignal(m.port, m.logger) +} + func (m *modem) PowerOn() error { m.onOffPin.PowerOn() // DEBUG do not want to wait 30 seconds return nil @@ -276,10 +282,10 @@ func (m *modem) saveGPS(path string) error { // Short way to send command func (m *modem) printCmd(cmd string) { if resp, err := m.port.Send(cmd); err != nil { - m.logger.Println("FAILED TO SEND REQ", cmd, ":", err.Error()) + m.logger.Println("FAILED TO SEND REQ", cmd, "===>", err.Error()) } else { _ = resp - m.logger.Println("CMD", cmd, ":", resp) + // m.logger.Println("CMD", cmd, "===>", resp) } } @@ -304,6 +310,16 @@ func (m *modem) setupPort() error { return nil } +func (m *modem) checkCurPortDead() error { + if resp, err := m.port.RawSend("AT\r\n", 20*time.Millisecond); err != nil || len(resp) == 0 { + if err != nil { + return fmt.Errorf("raw send: %w", err) + } + return fmt.Errorf("read 0") + } + return nil +} + func (m *modem) checkPort(portName string) (outErr error) { defer func() { if outErr != nil { // Clear port if there is error @@ -327,8 +343,9 @@ func (m *modem) checkPort(portName string) (outErr error) { // m.restart() // To filter dead ports - if str, err := m.port.RawSend("AT"); err != nil || len(str) == 0 { - return fmt.Errorf("ping error: %w", err) + + if err := m.checkCurPortDead(); err != nil { + return fmt.Errorf("echo: %w", err) } if err := m.setupPort(); err != nil { diff --git a/api/modem/sms/sms.go b/api/modem/sms/sms.go index 095dd68..df94c83 100644 --- a/api/modem/sms/sms.go +++ b/api/modem/sms/sms.go @@ -5,6 +5,7 @@ import ( "io" "log" "strings" + "time" "gitea.unprism.ru/KRBL/sim-modem/api/modem/at" ) @@ -40,7 +41,7 @@ func (d *dialer) Init() error { } // Setup prefered message storage if err := d.setupMsgSt(); err != nil { - return fmt.Errorf("setup msg storage: %w", err) + d.logger.Printf("ERROR setup msg storage: %s\n", err.Error()) } // Check number if resp, err := d.port.Send("AT+CNUM"); err != nil || !resp.Check() { @@ -57,7 +58,7 @@ func (d *dialer) Send(number, msg string) error { return err } d.logger.Println(sresp) - resp, err := d.port.RawSend(fmt.Sprintf("%s\x1A", msg)) // Add additional \r\n because there is not supposed to be + resp, err := d.port.RawSend(fmt.Sprintf("%s\x1A", msg), time.Millisecond) // Add additional \r\n because there is not supposed to be if err != nil { return fmt.Errorf("message request: %w", err) } diff --git a/api/modem/utils/signal.go b/api/modem/utils/signal.go new file mode 100644 index 0000000..dfe1503 --- /dev/null +++ b/api/modem/utils/signal.go @@ -0,0 +1,108 @@ +package utils + +import ( + "fmt" + "log" + "strconv" + "strings" + + "gitea.unprism.ru/KRBL/sim-modem/api/modem/at" +) + +func CheckSignal(port at.Port, logger *log.Logger) error { + rssi, ber, err := getSignalQuality(port) + if err != nil { + return fmt.Errorf("get signal quality: %w", err) + } + logger.Printf("check signal: rssi=%d ber=%d\n", rssi, ber) + if err := checkRssi(rssi); err != nil { + return fmt.Errorf("rssi: %w", err) + } + if err := checkBer(ber); err != nil { + logger.Printf("bad ber(not critical): %s", err.Error()) // Happened not to be critical + } + return nil +} + +func checkRssi(rssi int) error { + // rssi - Received signal strenght indicator + // 0 -113 dBm or less + // 1 -111 dBm + // 2...30 -109... - 53 dBm + // 31 -51 dBm or greater + // 99 not known or not detectable + // 100 -116 dBm or less + // 101 -115 dBm + // 102…191 -114... - 26dBm + // 191 -25 dBm or greater + // 199 not known or not detectable + // 100…199 expand to TDSCDMA, indicate RSCPreceived + + if rssi <= 2 { // Too poor + return fmt.Errorf("too poor <= -109dBm") + } + if rssi > 2 && rssi <= 31 { + return nil // Can live + } + if rssi == 99 { + return fmt.Errorf("not known or not detectable") + } + if rssi >= 100 && rssi <= 102 { + return fmt.Errorf("too poor <= -114dBm") + } + if rssi > 102 && rssi <= 191 { + return nil // Can live + } + if rssi == 199 { + return fmt.Errorf("not known or not detectable") + } + return fmt.Errorf("invalid code %d", rssi) +} + +func checkBer(ber int) error { + // ber - Bit error rate + // 0 <0.01% + // 1 0.01% --- 0.1% + // 2 0.1% --- 0.5% + // 3 0.5% --- 1.0% + // 4 1.0% --- 2.0% + // 5 2.0% --- 4.0% + // 6 4.0% --- 8.0% + // 7 >=8.0% + // 99 not known or not detectable + if ber >= 0 && ber <= 3 { + // ber -> [0%;1%) + // Ok, can live + return nil + } + if ber >= 4 && ber <= 7 { + return fmt.Errorf("too high: %d code", ber) + } + if ber == 99 { + return fmt.Errorf("not known or not detectable") + } + return fmt.Errorf("invalid code %d", ber) +} + +func getSignalQuality(port at.Port) (int, int, error) { + resp, err := port.Send("AT+CSQ") + if err != nil { + return 99, 99, err + } + if !resp.Check() || !resp.CheckFront("+CSQ: ") { + return 99, 99, fmt.Errorf("error response: %s", resp) + } + values := strings.Split(strings.ReplaceAll(strings.Split(resp.RmFront("+CSQ: ").String(), "\n")[0], "\r", ""), ",") + if len(values) != 2 { + return 99, 99, fmt.Errorf("invalid values(len): [% s]", values) + } + rssi, err := strconv.Atoi(values[0]) + if err != nil { + return 99, 99, fmt.Errorf("parse rssi: %w", err) + } + ber, err := strconv.Atoi(values[1]) + if err != nil { + return 99, 99, fmt.Errorf("parse ber: %w", err) + } + return rssi, ber, nil +} diff --git a/main.go b/main.go index b4742d1..f1f02d7 100644 --- a/main.go +++ b/main.go @@ -3,6 +3,7 @@ package main import ( "log" "os" + "time" "gitea.unprism.ru/KRBL/sim-modem/api/modem" "gitea.unprism.ru/KRBL/sim-modem/api/modem/at" @@ -42,19 +43,8 @@ func mainE() error { // logger.Printf("DATA: %+v\n", m.GetData()) logger.Println("||||||||||||||||| SMS |||||||||||||||||") - // resp, err := m.At().Send("AT+CNUM") - // logger.Println("CNUM:", resp, err) - // // if err := m.Sms().Send("+79218937173", "CGSG forever"); err != nil { - // // return err - // // } - // if ms, err := m.Sms().ReadNew(); err != nil { - // return err - // } else { - // logger.Println("NEW:", ms) - // } - Cmd := func(cmd string) { - resp, err := m.At().Send(cmd) + resp, err := m.At().SendWithTimeout(cmd, 50*time.Millisecond) logger.Println(cmd, "===>", resp, err) } _ = Cmd @@ -67,16 +57,27 @@ func mainE() error { _ = buf // Select ME PMS - // resp, err = m.At().Send("AT+CPMS=?") - // logger.Println("Possible mem storages:", resp, err) - // resp, err = m.At().Send("AT+CPMS?") - // logger.Println("Prefered mem storage:", resp, err) - // Cmd("AT") // logger.Println("SEND SMS") // logger.Println(m.Sms().Send("+79218937173", "CGSG forever!!!")) // m.At().RawSend("\r\n\x1A\r\n") + Cmd("AT+CREG?") + Cmd("AT+CNMI?") + Cmd("AT+CSQ") + Cmd("AT+CSCA?") + Cmd("AT+CPOL?") + Cmd("AT+COPS?") + // Cmd("AT+COPS=?") + Cmd("AT+CPSI?") + resp, err = m.At().Send("AT+CNMI=2,2") for { + + // if err := m.CheckSignal(); err != nil { + // logger.Println(err) + // } else { + // logger.Println("AAAAAAAAAAA THERE IS SIGNAL") + // } + // time.Sleep(250 * time.Millisecond) readLen, err := m.At().SerialPort().Read(buf) if err != nil { return err