// Find делает выборку из базы данных всех подходящих координат станций и возвращает их. func (db DB) Find(req *Request) geo.Point { if req == nil || db == nil { return geo.NaNPoint } id := fmt.Sprintf("%d:%d", req.MCC, req.MNC) // формируем первичный ключ запроса sdb, ok := db[id] // получаем вложенный раздел базы if !ok { return geo.NaNPoint // вложенный раздел не найден } var sm, slat, slon float64 // перебираем все данные о сетях for _, cell := range req.Cells { sid := fmt.Sprintf("%d:%d", cell.Area, cell.ID) points, ok := sdb[sid] if !ok { continue // игнорируем } // перебираем все доступные данные для станции for _, point := range points { m := math.Pow(10, (float64(cell.DBM)/20)) * 1000 sm += m slat += point.Lat() * m slon += point.Lon() * m } } if sm == 0 { return geo.NaNPoint } return geo.NewPoint(slat/sm, slon/sm) }
// ImportCSV импортирует данные из формата CSV. // В данный момент захардкодено игнорирование первой строки (как заголовка) и всех данных, // которые не относятся к сети GSM. func ImportCSV(filename string) (DB, error) { log.Printf("Import DB from CSV %q", filename) file, err := os.Open(filename) if err != nil { return nil, err } defer file.Close() db := make(DB) // создаем новую базу данных var counter uint32 // счетчик r := csv.NewReader(file) for { record, err := r.Read() if err == io.EOF { break } if err != nil { return nil, err } counter++ if counter == 1 { r.FieldsPerRecord = len(record) // устанавливаем количество полей continue // пропускаем первую строку с заголовком в CSV-файле } // фильруем базу данных: только GSM-станции // 250 - Россия, 255 - Украина, 257 - Беларусия if record[0] != "GSM" || !(record[1] == "250" || record[1] == "255" || record[1] == "257") { continue } id := strings.Join(record[1:3], ":") // уникальный идентификатор страны и кода оператора sdb, ok := db[id] // получаем вложенный раздел базы if !ok { sdb = make(map[string][]geo.Point) // инициализируем вложенный раздел db[id] = sdb } sid := strings.Join(record[3:5], ":") // уникальный идентификатор Cell Area и Base station number stdb, ok := sdb[sid] // получаем доступ к массиву данных для данной станции if !ok { stdb = make([]geo.Point, 0) } lng, err := strconv.ParseFloat(record[6], 64) if err != nil { log.Println("Bad longitude:", record[6]) continue } lat, err := strconv.ParseFloat(record[7], 64) if err != nil { log.Println("Bad latitude:", record[7]) continue } sdb[sid] = append(stdb, geo.NewPoint(lat, lng)) } return db, nil }
func main() { // TODO: по-хорошему, нужен, конечно, конфигурационный файл со всеми опциями log.Println("Connecting to NATS...") // подключаемся к NATS-серверу nc, err := nats.Connect("nats://" + natsServer) if err != nil { // потом убрать nc, err = nats.DefaultOptions.Connect() if err != nil { log.Println("Error NATS Connect:", err) return } } // TODO: добавить encoder сообщений (GOB?) defer nc.Close() // загружаем базу данных с гео-информацией по вышкам сотовой связи db, err := lbs.LoadDB("cells.gob") if err != nil { log.Println("Error loading GeoDB:", err) return } // добавляем подписку lbsSubs, err := nc.Subscribe(serviceNameLBS, func(msg *nats.Msg) { // пример строки с LBS: // 864078-35827-010003698-fa-2-1e50-772a-95-1e50-773c-a6-1e50-7728-a1-1e50-7725-92-1e50-772d-90-1e50-7741-90-1e50-7726-88 req, err := lbs.Parse(string(msg.Data)) // разбираем полученные данные if err != nil { log.Println("Error parse LBS:", err) return } point := db.Find(req) // получаем точку по координатам if math.IsNaN(point.Lat()) || math.IsNaN(point.Lon()) { log.Println("Error searching LBS:", err) // TODO: наверное, нужно отдавать пустой ответ return } // отправляем ответ с данными if err := nc.Publish(msg.Reply, []byte(point.String())); err != nil { log.Println("Error Publish ephemeridos:", err) } }) if err != nil { log.Println("Error NATS Subscribe:", err) return } defer lbsSubs.Unsubscribe() // инициализируем клиента для получения инициализационной информации о GPS client := ublox.NewClient("I6KKO4RU_U2DclBM9GVyrA") // добавляем профиль устройства: от этого зависит формат возвращаемых данных profile := ublox.Profile{ Datatype: []string{"eph"}, Format: "aid", FilterOnPos: true, } // инициализируем кеш для инициализационной информации о GPS cache := ublox.NewCache(client, profile, time.Minute*60, 200) // добавляем подписку ephSubs, err := nc.Subscribe(serviceNameEph, func(msg *nats.Msg) { // разбор данных var tmp MsgFromEph var point geo.Point if err = json.Unmarshal(msg.Data, &tmp); err != nil { //TODO: отправлять ошибку return } point = geo.NewPoint(tmp.Lat, tmp.Lon) // создаем координаты data, err := cache.Get(point) // получаем данные из кеша if err != nil { log.Println("Error Get ephemeridos:", err) // TODO: наверное, нужно отдавать пустой ответ return } // отправляем ответ с данными if err := nc.Publish(msg.Reply, data); err != nil { log.Println("Error Publish ephemeridos:", err) } }) if err != nil { log.Println("Error NATS Subscribe:", err) return } defer ephSubs.Unsubscribe() // ждем одного из сигналов... signal := moitorSignals(os.Interrupt, os.Kill) _ = signal log.Println("Disconnecting from NATS...") }
package ublox import ( "fmt" "testing" "github.com/mdigger/geo" ) var ( token = "ADD_YOUR_TOKEN" pointWork = geo.NewPoint(55.715084, 37.57351) // работа pointHome = geo.NewPoint(55.765944, 37.589248) // дом ) func TestClient(t *testing.T) { ubox := NewClient(token) data, err := ubox.GetOnline(pointWork, DefaultProfile) if err != nil { t.Fatal(err) } fmt.Println(data) // data, err = ubox.GetOnline(pointHome, DefaultProfile) // if err != nil { // t.Fatal(err) // } // fmt.Println(data) // data, err = ubox.GetOnline(geotools.Point{}, DefaultProfile) // if err != nil { // t.Fatal(err) // }