Пример #1
0
func satelliteShortName(satellite_id string) string {
	sat := db.GlobalSatelliteDB().Map[satellite_id]
	if sat == nil {
		return ""
	}
	return RenderSatelliteShortName(sat.Name)
}
Пример #2
0
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))
}
Пример #3
0
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
	}
}
Пример #4
0
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
}
Пример #5
0
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
	}
}
Пример #6
0
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
}
Пример #7
0
// 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
}
Пример #8
0
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())
	}
}
Пример #9
0
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
	}
}
Пример #10
0
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
	}
}
Пример #11
0
// 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 = &timestamp

	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
}
Пример #12
0
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)
	}
}