func satelliteShortName(satellite_id string) string { sat := db.GlobalSatelliteDB().Map[satellite_id] if sat == nil { return "" } return RenderSatelliteShortName(sat.Name) }
func orbitHandler(w http.ResponseWriter, r *http.Request, user userView) { id := r.URL.Query().Get("id") if id == "" { http.Error(w, "'id' param missing", http.StatusBadRequest) return } sat := db.GlobalSatelliteDB().Map[id] if sat == nil { http.NotFound(w, r) return } if sat.Tle == nil || *sat.Tle == "" { http.NotFound(w, r) return } points, err := scheduler.PassDetails( time.Now(), 5*time.Hour, 0.0, 0.0, 0.0, // Observer is irrelevant. *sat.Tle, 25.0) if err != nil { log.Printf("Error getting PassDetails: %s", err.Error()) http.Error(w, "", http.StatusInternalServerError) return } png.Encode(w, orbitMap(points)) }
func satelliteListHandler( w http.ResponseWriter, r *http.Request, user userView) { c := NewRenderContext(user, db.GlobalSatelliteDB().List) err := satelliteListTemplate.Get().ExecuteTemplate( w, "satellite_list.html", c) if err != nil { log.Printf("Error rendering satellite list: %s", err.Error()) http.Error(w, "", http.StatusInternalServerError) return } }
func PassPredictions(station *pb.Station) (PredictionList, error) { db := db.GlobalSatelliteDB() if station.Lat == nil || station.Lng == nil { return nil, nil } lat := *station.Lat lng := *station.Lng elevation := 0.0 if station.Elevation != nil { elevation = *station.Elevation } begin_time := time.Now() var predictErr error = nil var all_passes PredictionList for _, sat := range db.List { if sat.Tle == nil { continue } modes := CompatibleModes(station, sat) for _, mode := range modes { passes, err := predict( predictBinaryPath, begin_time, 18*time.Hour, lat, lng, elevation, *mode.limits.MinElevationDegrees, *mode.limits.MinAzimuthDegrees, *mode.limits.MaxAzimuthDegrees, *sat.Tle) if err != nil { predictErr = err log.Printf("Prediction error: %s", err.Error()) // This may be a temporary error so don't stop // immediately. } for i, _ := range passes { passes[i].Satellite = sat passes[i].CompatibleMode = mode } all_passes = append(all_passes, passes...) } } sort.Sort(all_passes) return all_passes, predictErr }
func renderUserProfile( cdb *db.ContactDB, stationdb *db.StationDB, w http.ResponseWriter, r *http.Request, user userView, u *pb.User) { // TODO: It would be better if we could restrict to contacts which // have telemetry. contacts, err := cdb.SearchByUserId(*u.Id, 100) if err != nil { log.Printf("cdb.SearchByUserId error: %s", err.Error()) // Continue since this isn't a critical error. } heard_satellite_ids := make(map[string]bool) for _, c := range contacts { if c.SatelliteId == nil { continue } for _, b := range c.Blob { if b.Format != nil && *b.Format == pb.Contact_Blob_DATUM { heard_satellite_ids[*c.SatelliteId] = true break } } } var pv profileView pv.User = u pv.IsOwner = (*u.Id == user.Id) pv.HeardSatellites = make([]*pb.Satellite, 0) for satellite_id, _ := range heard_satellite_ids { pv.HeardSatellites = append(pv.HeardSatellites, db.GlobalSatelliteDB().Map[satellite_id]) } stations, err := stationdb.UserStations(*u.Id) if err != nil { log.Printf("Error getting user stations: %s", err.Error()) // Continue rendering since it's not a critial error. } pv.Stations = make([]*pb.Station, 0) for _, s := range stations { if s.Lat != nil && s.Lng != nil { pv.Stations = append(pv.Stations, s) } } c := NewRenderContext(user, pv) err = userViewTemplate.Get().ExecuteTemplate(w, "user.html", c) if err != nil { log.Printf("Error rendering user view: %s", err.Error()) http.Error(w, "", http.StatusInternalServerError) return } }
func DecodeFromIQ(satellite_id, path string, sample_rate_hz float64, sample_type pb.IQParams_Type) ( blobs []pb.Contact_Blob, err error) { sat := db.GlobalSatelliteDB().Map[satellite_id] if sat == nil { e := errors.New( fmt.Sprintf("Unknown satellite_id: %s", satellite_id)) log.Print(e.Error()) return nil, e } // 1. Morse decoding var cw_params *pb.CWParams for _, c := range sat.Channels { if c.Modulation != nil && *c.Modulation == pb.Channel_CW { cw_params = c.CwParams break } } if cw_params != nil { b, err := cw.DecodeCW( path, sample_rate_hz, sample_type, cw_params) blobs = append(blobs, b...) if err != nil { log.Printf("Error during DecodeCW: %s", err.Error()) return blobs, err } } // 2. Frame decoding for _, c := range sat.Channels { if c.Modulation == nil { continue } if *c.Modulation == pb.Channel_CW { // CW is handled above. continue } b, err := packet.DecodePackets( path, sample_rate_hz, sample_type, *c) blobs = append(blobs, b...) if err != nil { log.Printf("Error during DecodePackets: %s", err.Error()) return blobs, err } } return blobs, nil }
// TODO: We really should cache the result. func fillSatOptions() (r satOptionList) { for _, sat := range db.GlobalSatelliteDB().List { var so satOption so.Id = *sat.Id so.Name = RenderSatelliteShortName(sat.Name) for _, c := range sat.Channels { if c.Downlink == nil || *c.Downlink == false { continue } so.FrequencyHz = (int64)(*c.FrequencyHz) break } r = append(r, so) } sort.Sort(r) return r }
func getLatestIQDataHandler( sdb *db.StationDB, cdb *db.ContactDB, w http.ResponseWriter, r *http.Request) { log.Printf("Request: %s", r.URL.String()) req, err := parseGetLatestIQDataRequest(r.URL.Query()) if err != nil { log.Printf("getLatestIQDataHandler: "+ "parse request error: %s", err.Error()) http.Error(w, "Error parsing request.", http.StatusBadRequest) return } sat := db.GlobalSatelliteDB().Map[req.SatelliteId] if sat == nil { http.Error(w, "Unknown satellite_id.", http.StatusBadRequest) return } station, err := sdb.Lookup(req.StationId) if err != nil { log.Printf("Error looking up station: %s", err.Error()) http.Error(w, "", http.StatusUnauthorized) return } if station == nil { log.Printf("Error looking up station.") http.Error(w, "", http.StatusUnauthorized) return } // Authenticate the station. if station.Secret == nil || *station.Secret != req.StationSecret { log.Printf("Authentication failed.") http.Error(w, "", http.StatusUnauthorized) return } // Make sure that the user is authorized for the satellite. found_good_id := false for _, station_id := range sat.AuthorizedStationId { if station_id == *station.Id { found_good_id = true } } if !found_good_id { log.Printf("Authentication failed.") http.Error(w, "", http.StatusUnauthorized) return } contacts, err := cdb.SearchBySatelliteId(req.SatelliteId, req.Limit) if err != nil { log.Printf("getLatestIQDataHandler: "+ "SearchBySatelliteId error: %s", err.Error()) http.Error(w, "", http.StatusInternalServerError) return } packets := make(GetLatestIQDataResponse, 0) for _, c := range contacts { if c.StartTimestamp == nil { continue } timestamp := *c.StartTimestamp for _, b := range c.Blob { if len(packets) >= req.Limit { continue } if b.Format == nil || *b.Format != pb.Contact_Blob_IQ { continue } var p IQLink p.Timestamp = timestamp p.URL = scheduler.GetStreamURL(*c.Id) if b.IqParams != nil { if b.IqParams.Type != nil { p.Type = b.IqParams.Type.String() } if b.IqParams.SampleRate != nil { p.SampleRate = int( *b.IqParams.SampleRate) } } packets = append(packets, p) } } json_body, err := json.Marshal(packets) if err != nil { log.Printf("json Marshal error: %s", err.Error()) http.Error(w, "", http.StatusInternalServerError) } w.Header().Add("Content-Length", fmt.Sprintf("%d", len(json_body))) w.Header().Add("Content-Type", "application/json") _, err = w.Write(json_body) if err != nil { log.Printf("Error writing response: %s", err.Error()) } }
func satellitePostContactHandler( sdb *db.StationDB, cdb *db.ContactDB, w http.ResponseWriter, r *http.Request, user userView) { if r.Method != "POST" { http.Redirect(w, r, satelliteListUrl, http.StatusFound) return } if err := r.ParseForm(); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } log.Printf("satellitePostContactHandler form: %v\n\n", r.Form) satellite_id := r.Form.Get("satellite_id") if satellite_id == "" { http.Error(w, "Missing satellite id", http.StatusBadRequest) return } sat := db.GlobalSatelliteDB().Map[satellite_id] if sat == nil { http.Error(w, "Unknown satellite id", http.StatusBadRequest) return } timestamp, err := strconv.ParseInt(r.Form.Get("timestamp"), 10, 64) if err != nil { http.Error(w, "Can't parse timestamp.", http.StatusBadRequest) return } data := r.Form.Get("data") frame := ([]byte)(data) var station *pb.Station // There are two options: logged-in or anonymous. if user.Id == "" { // Anonymous station = nil } else { // Logged-in user station_id := r.Form.Get("station_id") if station_id == "" { http.Error( w, "Missing station id", http.StatusBadRequest) return } station, err = sdb.Lookup(station_id) if err != nil { log.Printf("Error looking up station: %s", err.Error()) http.Error(w, "", http.StatusInternalServerError) return } } contact, poperr := contacts.PopulateContact( satellite_id, timestamp, "FREEFORM", frame, user.Id, "", station) if poperr != nil { poperr.HttpError(w) return } log.Printf("Contact: %s", contact) err = cdb.Store(contact) if err != nil { log.Printf("Error storing contact: %s", err.Error()) http.Error(w, "", http.StatusInternalServerError) return } var cc contactConfirmContext cc.SatelliteUrl = satelliteViewURL(*sat.Id) cc.SatelliteName = RenderSatelliteName(sat.Name) cc.Data = data if sat.Schema != nil { t := make([]pb.TelemetryDatum, 0) for _, b := range contact.Blob { if b.Format != nil && *b.Format == pb.Contact_Blob_DATUM { t = append(t, *b.Datum) } } cc.Telemetry = fe_telemetry.RenderTelemetry( *sat.Schema, t, "en") } err = contactConfirmTemplate.Get().ExecuteTemplate( w, "contact_confirm.html", NewRenderContext(user, cc)) if err != nil { log.Printf( "Error rendering contact_confirm view: %s", err.Error()) http.Error(w, "", http.StatusInternalServerError) return } }
func satelliteViewHandler( cdb *db.ContactDB, userdb *db.UserDB, stationdb *db.StationDB, commentdb *db.CommentDB, w http.ResponseWriter, r *http.Request, user userView) { if len(r.URL.Path) < len(satelliteURLPrefix) { http.Error(w, "Invalid path", http.StatusBadRequest) return } id := r.URL.Path[len(satelliteURLPrefix):] sat := db.GlobalSatelliteDB().Map[id] if sat == nil { http.NotFound(w, r) return } // TODO: It would be better if we could restrict to contacts which // have telemetry. contacts, err := cdb.SearchBySatelliteId(id, 100) if err != nil { log.Printf("cdb.SearchBySatelliteId error: %s", err.Error()) // Continue since this isn't a critical error. } t := make([]pb.TelemetryDatum, 0) var latest_contact *contactView for _, c := range contacts { for _, b := range c.Blob { if b.Format != nil && *b.Format == pb.Contact_Blob_DATUM { t = append(t, *b.Datum) if latest_contact == nil { latest_contact = fillContactView( *c, userdb) } } } } sv := satelliteViewContext{} sv.S = sat if sat.Schema != nil { t := fe_telemetry.RenderTelemetry(*sat.Schema, t, "en") if len(t) > 0 { sv.TelemetryHead = t[0] } if len(t) > 1 { sv.TelemetryTail = t[1:] } } sv.LatestContact = latest_contact sv.Comments, _ = LoadCommentsByObjectId( satelliteObjectId(id), commentdb, userdb) sv.Stations, err = stationdb.UserStations(user.Id) if err != nil { log.Printf("Error getting user stations: %s", err.Error()) // Continue rendering since it's not a critial error. } c := NewRenderContext(user, &sv) err = satelliteViewTemplate.Get().ExecuteTemplate( w, "satellite.html", c) if err != nil { log.Printf("Error rendering satellite view: %s", err.Error()) http.Error(w, "", http.StatusInternalServerError) return } }
// If station is nil, an anonymous contact will be created. // Otherwise, an authenticated contact will be created. func PopulateContact( satellite_id string, timestamp int64, format_s string, data []byte, authenticated_user_id string, station_secret string, station *pb.Station) (*pb.Contact, *PopulateContactError) { if satellite_id == "" { return nil, NewPopulateContactError( http.StatusBadRequest, "Missing id") } if len(data) == 0 { return nil, NewPopulateContactError( http.StatusBadRequest, "Empty frame") } else if len(data) > 16384 { return nil, NewPopulateContactError( http.StatusBadRequest, "Exceeded size limit") } if time.Unix(timestamp, 0).After(time.Now()) { return nil, NewPopulateContactError( http.StatusBadRequest, "Timestamp is in the future.") } intformat, ok := pb.Contact_Blob_Format_value[format_s] if !ok { return nil, NewPopulateContactError( http.StatusBadRequest, "Can't parse format.") } format := (pb.Contact_Blob_Format)(intformat) // Make sure the satellite exists. sat := db.GlobalSatelliteDB().Map[satellite_id] if sat == nil { return nil, NewPopulateContactError( http.StatusBadRequest, "Unknown satellite") } // We get a lot of invalid frames produced by noise on KISS serial // lines. Filter them out early to avoid storing too much garbage in // the database. if format == pb.Contact_Blob_FRAME && !IsValidFrame( satellite_id, data) { return nil, NewPopulateContactError( http.StatusBadRequest, "Invalid data frame.") } var user_id string if station != nil { // Make an authenticated contact. // Ensure that either the station secret is correct or the user // owns the station. if station_secret != "" { if *station.Secret != station_secret { return nil, NewPopulateContactError( http.StatusUnauthorized, "") } } else { if *station.Userid != authenticated_user_id { return nil, NewPopulateContactError( http.StatusUnauthorized, "") } } user_id = *station.Userid } else { // Make an anonymous contact. user_id = "" } contact, err := NewContact(station, user_id, &satellite_id) if err != nil { log.Printf("Error creating contact: %s", err.Error()) return nil, NewPopulateContactError( http.StatusInternalServerError, "") } contact.StartTimestamp = ×tamp blob := &pb.Contact_Blob{} blob.Format = format.Enum() blob.InlineData = data contact.Blob = []*pb.Contact_Blob{blob} blobs, err := DecodeBlobs( satellite_id, timestamp, []pb.Contact_Blob{*blob}) if err != nil { log.Printf("Error decoding blobs: %s", err.Error()) } contact.Blob = append(contact.Blob, blobs...) return contact, nil }
func consoleJSONHandler(sdb *db.StationDB, contactdb *db.ContactDB, m *rpc.Client, w http.ResponseWriter, r *http.Request, user userView) { query := r.URL.Query() id := query.Get("id") if id == "" { http.Error(w, "'id' param missing", http.StatusBadRequest) return } action := query.Get("action") if action == "" { http.Error(w, "'action' param missing", http.StatusBadRequest) return } auth, _ := canOperateStation(sdb, id, user.Id) if !auth { http.Error(w, "Not authorized", http.StatusUnauthorized) return } // FIXME: authentication and scheduling/locking satellite_id := query.Get("satellite_id") if satellite_id != "" && db.GlobalSatelliteDB().Map[satellite_id] == nil { http.Error( w, "invalid satellite_id", http.StatusBadRequest) return } switch action { case "ReceiverGetState": callStation(m, w, id, action, nil) case "ReceiverStart": log.Printf("ReceiverStart: satellite_id=%s", satellite_id) contact_id, err := contacts.StartNewConsoleContact( sdb, contactdb, id, user.Id, satellite_id) if err != nil { log.Printf("StartNewIQContact error: %s", err.Error()) http.Error(w, "", http.StatusInternalServerError) return } v := url.Values{} v.Add("stream_url", scheduler.GetStreamURL(contact_id)) callStation(m, w, id, action, v) case "ReceiverStop": callStation(m, w, id, action, nil) case "ReceiverWaterfallPNG": callStation(m, w, id, action, nil) case "ReceiverSetFrequency": hz := query.Get("hz") if hz == "" { http.Error( w, "'hz' param missing", http.StatusBadRequest) return } v := url.Values{} v.Add("hz", hz) callStation(m, w, id, action, v) case "TNCStart": log.Printf("TNCStart: satellite_id=%s", satellite_id) host, port := scheduler.GetAPIServer() if satellite_id == "" { host = "" port = "0" } v := url.Values{} v.Add("api_host", host) v.Add("api_port", port) v.Add("satellite_id", satellite_id) callStation(m, w, id, action, v) case "TNCStop": callStation(m, w, id, action, nil) case "TNCGetLatestFrames": callStation(m, w, id, action, nil) case "MotorGetState": callStation(m, w, id, action, nil) case "MotorStart": program := query.Get("program") if program == "" { http.Error(w, "'program' param missing", http.StatusBadRequest) return } v := url.Values{} v.Add("program", program) callStation(m, w, id, action, v) case "MotorStop": callStation(m, w, id, action, nil) default: http.NotFound(w, r) } }