// devMoved tells if a device moved based on the passed latest GPS records. // At least 2 track GPS records must be present to report moving (to return true). func devMoved(rs []*ds.GPS) bool { // Min delta distance that is considered moving const minDeltaDist = 230 // [m] var first, last *ds.GPS // First and Last track records for _, r := range rs { if !r.Track() { continue } if first == nil { first = r } if last != nil { if logic.Distance(last.GeoPoint.Lat, last.GeoPoint.Lng, r.GeoPoint.Lat, r.GeoPoint.Lng) > minDeltaDist { return true } } last = r } // Device might be moving very slow, e.g. only 100 m per minute in which case subsequent // records will not report moving. Also compare the first to the last: if first != nil && last != nil { if logic.Distance(last.GeoPoint.Lat, last.GeoPoint.Lng, first.GeoPoint.Lat, first.GeoPoint.Lng) > minDeltaDist { return true } } return false }
// checkAlert checks the specified alert. func checkAlert(c appengine.Context, a *ds.Alert, accKeyID int64) { const alertDurationMin = 5 const alertDuration = alertDurationMin * time.Minute // Get latest car GPS records carRecords, err := getDevRecords(c, a.CarDevID) if err != nil { return } // Check if car GPS records are received properly: if time.Since(carRecords[0].Created) > alertDuration { c.Warningf("No car GPS records found in the last %d minutes!", alertDurationMin) sendAlert(c, accKeyID, "Car GPS device gone dark!", carGoneDarkAlertMail) return } if a.PersMobDevID == 0 { c.Debugf("Car GPS records found in the last 5 minutes. No Personal Mobile device specified.") return } carMoved := devMoved(carRecords) if !carMoved { // Nothing more to do if car is not moving c.Infof("Car is not moving. Ok.") return } c.Infof("Car is moving!") // Get latest personal mobile GPS records persMobRecords, err := getDevRecords(c, a.PersMobDevID) if err != nil { return } // Check if personal mobile GPS records are received properly: if time.Since(persMobRecords[0].Created) > alertDuration { c.Warningf("No personal mobile GPS records found in the last %d minutes!", alertDurationMin) sendAlert(c, accKeyID, "Car is moving without you!", carMovingWithoutYouMail) return } persMobMoved := devMoved(persMobRecords) // Do not draw fast conclusion here if personal mobile is not moving, // it might be GPS tracking was just turned on and we don't have 2 track records yet // or they are not at great distance. But if we don't even have 1 track record, // that's hijacking-suspicious: if persMobMoved { c.Infof("Personal mobile is also moving!") } else { c.Infof("Personal mobile is NOT moving!") } var pg1 *ds.GPS for _, r := range persMobRecords { if r.Track() { pg1 = r break } } if pg1 == nil || time.Since(pg1.Created) > alertDuration { // Car is moving and we don't have recent track record from personal mobile! c.Warningf("No personal mobile GPS track record found in the last %d minutes!", alertDurationMin) sendAlert(c, accKeyID, "Car is moving without you!", carMovingWithoutYouMail) return } // Check distance: // We have a track record for both devices for sure (because both moved which also ensures having at least 2!). var cg1, cg2 *ds.GPS for _, r := range carRecords { if r.Track() { if cg1 == nil { cg1 = r } else if cg2 == nil { cg2 = r break } } } cg1.Dd = logic.Distance(cg2.GeoPoint.Lat, cg2.GeoPoint.Lng, cg1.GeoPoint.Lat, cg1.GeoPoint.Lng) // [m] cg1.Dt = cg1.Created.Sub(cg2.Created) // duration cv := float64(cg1.Dd) / cg1.Dt.Seconds() // Car speed [m/s] cpdt := math.Abs(cg1.Created.Sub(pg1.Created).Seconds()) // [s] dist := logic.Distance(cg1.GeoPoint.Lat, cg1.GeoPoint.Lng, pg1.GeoPoint.Lat, pg1.GeoPoint.Lng) c.Debugf("Car movement speed: %.1f km/h", cv*3.6) c.Debugf("Delta T between latest Car and PersMob GPS records: %d s", int64(cpdt)) c.Debugf("Car - PersMob distance: %d m", dist) var alertMargin int64 = 500 // [m] // Increase alert margin based on the movement speed of the car and the delta time between // the last car and personal mobile GPS records. // Also if this delta time is greater, accuracy decreases/drops. // So also increase alert margin based on delta time: 6 meters for every second. // (It is an effect like increasing car speed by 6 m/s = 21.6 km/h.) // BE RESTRICTIVE: Only do this correction if personal mobile is also moving! // If not, do not let the car get far away (if for example pers mob is not moving, // the car could get kilometers away before alert would be sent). if persMobMoved { alertMargin += int64(cv*cpdt + cpdt*6) } c.Debugf("Using alert margin distance: %d m", alertMargin) if dist > alertMargin { c.Warningf("Personal mobile is not moving together with car!") sendAlert(c, accKeyID, "Car is moving without you!", carMovingWithoutYouMail) return } c.Infof("They are moving together. Ok.") }