Added signal check.
This commit is contained in:
		| @@ -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) | ||||
| 	} | ||||
|   | ||||
| @@ -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 { | ||||
|   | ||||
| @@ -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) | ||||
| 	} | ||||
|   | ||||
							
								
								
									
										108
									
								
								api/modem/utils/signal.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								api/modem/utils/signal.go
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||
| } | ||||
		Reference in New Issue
	
	Block a user