Skip to content

Commit

Permalink
Added in SVM (non-default, but available) (schollz#92)
Browse files Browse the repository at this point in the history
* Added SVM

* It works

* Updated

* Fixed bug

* Fixed bug

* Fixed bug

* Fixed bug

* Fixed bug

* Fixed bug

* Fixed bug

* Fixed bug

* Fixed bug

* Fixed bug

* Fixed bug

* Fixed bug

* Fixed bug

* Fixed bug

* Fixed bug

* Fixed bug

* Fixed bug

* Fixed bug

* Fixed bug

* Gaussian filtering

* Gaussian filtering

* Gaussian filtering

* Gaussian filtering

* Gaussian filtering

* Gaussian filtering

* Gaussian filtering

* Gaussian filtering

* Displays NB and SVM

* Displays NB and SVM

* Displays NB and SVM

* Added information about runtimes

* Updated and fixed errors

* Fixed error

* Fixed error

* Fixed error

* Fixed error

* Fixed error

* Fixed error

* Fixed error

* Fixed error

* Fixed error

* Fixed error

* Fixed error

* Fixed error

* Fixed error

* Fixed error

* Fixed error

* Fixed error

* Fixed error

* Fixed error

* Fixed error

* Added messaging capability

* Fixed errors

* Fixed errors

* Fixed errors

* Fixed errors

* Fixed errors

* Fixed errors

* Fixed errors

* Fixed errors
  • Loading branch information
schollz committed May 7, 2016
1 parent 6ad9613 commit b0b5b8e
Show file tree
Hide file tree
Showing 8 changed files with 567 additions and 129 deletions.
15 changes: 15 additions & 0 deletions api.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ type UserPositionJSON struct {
Time interface{} `json:"time"`
Location interface{} `json:"location"`
Bayes map[string]float64 `json:"bayes"`
Svm map[string]float64 `json:"svm"`
}

func getCurrentPositionOfUser(group string, user string) UserPositionJSON {
Expand Down Expand Up @@ -74,6 +75,10 @@ func getCurrentPositionOfUser(group string, user string) UserPositionJSON {
location, bayes := calculatePosterior(fullJSON, *NewFullParameters())
userJSON.Location = location
userJSON.Bayes = bayes
// Process SVM if needed
if RuntimeArgs.Svm {
_, userJSON.Svm = classify(fullJSON)
}
}
userPositionCache[group+user] = userJSON
return userJSON
Expand All @@ -87,6 +92,16 @@ func calculate(c *gin.Context) {
return
}
optimizePriorsThreaded(strings.ToLower(group))
Debug.Println(RuntimeArgs.Svm)
if RuntimeArgs.Svm {
dumpFingerprintsSVM(strings.ToLower(group))
err := calculateSVM(strings.ToLower(group))
if err != nil {
Warning.Println("Encountered error when calculating SVM")
Warning.Println(err)
}
}
userPositionCache = make(map[string]UserPositionJSON)
c.JSON(http.StatusOK, gin.H{"message": "Parameters optimized.", "success": true})
} else {
c.JSON(http.StatusOK, gin.H{"success": false, "message": "Error parsing request"})
Expand Down
98 changes: 0 additions & 98 deletions backup.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,9 @@
package main

import (
"fmt"
"log"
"os"
"path"
"sort"
"strconv"

"github.com/boltdb/bolt"
)
Expand Down Expand Up @@ -65,98 +62,3 @@ func dumpFingerprints(group string) error {

return nil
}

// # sudo apt-get install g++
// # wget http://www.csie.ntu.edu.tw/~cjlin/cgi-bin/libsvm.cgi?+http://www.csie.ntu.edu.tw/~cjlin/libsvm+tar.gz
// # tar -xvf libsvm-3.18.tar.gz
// # cd libsvm-3.18
// # make
//
// cp ~/Documents/find/svm ./
// cat svm | shuf > svm.shuffled
// ./svm-scale -l 0 -u 1 svm.shuffled > svm.shuffled.scaled
// head -n 500 svm.shuffled.scaled > learning
// tail -n 1500 svm.shuffled.scaled > testing
// ./svm-train -s 0 -t 0 -b 1 learning > /dev/null
// ./svm-predict -b 1 testing learning.model out

func dumpFingerprintsSVM(group string) error {
err := os.MkdirAll("dump-"+group, 0664)
if err != nil {
return err
}

db, err := bolt.Open(path.Join(RuntimeArgs.SourcePath, group+".db"), 0755, nil)
if err != nil {
log.Fatal(err)
}

macs := make(map[string]int)
locations := make(map[string]int)
macI := 1
locationI := 1
// Dump the learning fingerprints
if err != nil {
return err
}
db.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte("fingerprints"))
c := b.Cursor()
for k, v := c.First(); k != nil; k, v = c.Next() {
v2 := loadFingerprint(v)
for _, fingerprint := range v2.WifiFingerprint {
if _, ok := macs[fingerprint.Mac]; !ok {
macs[fingerprint.Mac] = macI
macI++
}
}
if _, ok := locations[v2.Location]; !ok {
locations[v2.Location] = locationI
locationI++
}
}
return nil
})

fmt.Println(locations)
fmt.Println(macs)
// Dump the tracking fingerprints
f, err := os.OpenFile("svm", os.O_WRONLY|os.O_CREATE, 0755)
if err != nil {
panic(err)
}
err = db.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte("fingerprints"))
c := b.Cursor()
for k, v := c.First(); k != nil; k, v = c.Next() {
v2 := loadFingerprint(v)
_, err := f.WriteString(strconv.Itoa(locations[v2.Location]) + " ")
if err != nil {
panic(err)
}

// To create a map as input
m := make(map[int]int)
for _, fingerprint := range v2.WifiFingerprint {
m[macs[fingerprint.Mac]] = fingerprint.Rssi
}
var keys []int
for k := range m {
keys = append(keys, k)
}
sort.Ints(keys)
for _, k := range keys {
f.WriteString(strconv.Itoa(k) + ":" + strconv.Itoa(m[k]) + " ")
}

f.WriteString("\n")
}
return nil
})
if err != nil {
panic(err)
}
f.Close()
db.Close()
return nil
}
75 changes: 53 additions & 22 deletions fingerprint.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"encoding/json"
"fmt"
"log"
"math"
"strconv"

"net/http"
Expand Down Expand Up @@ -117,9 +118,9 @@ func putFingerprintIntoDatabase(res Fingerprint, database string) error {
func trackFingerprintPOST(c *gin.Context) {
var jsonFingerprint Fingerprint
if c.BindJSON(&jsonFingerprint) == nil {
message, success, locationGuess, bayes := trackFingerprint(jsonFingerprint)
message, success, locationGuess, bayes, svm := trackFingerprint(jsonFingerprint)
if success {
c.JSON(http.StatusOK, gin.H{"message": message, "success": true, "location": locationGuess, "bayes": bayes})
c.JSON(http.StatusOK, gin.H{"message": message, "success": true, "location": locationGuess, "bayes": bayes, "svm": svm})
} else {
c.JSON(http.StatusOK, gin.H{"message": message, "success": false})
}
Expand Down Expand Up @@ -158,57 +159,87 @@ func learnFingerprint(jsonFingerprint Fingerprint) (string, bool) {
return message, true
}

func trackFingerprint(jsonFingerprint Fingerprint) (string, bool, string, map[string]float64) {
func trackFingerprint(jsonFingerprint Fingerprint) (string, bool, string, map[string]float64, map[string]float64) {
bayes := make(map[string]float64)
svmData := make(map[string]float64)
cleanFingerprint(&jsonFingerprint)
if !groupExists(jsonFingerprint.Group) || len(jsonFingerprint.Group) == 0 {
return "You should insert fingerprints before tracking", false, "", bayes
return "You should insert fingerprints before tracking", false, "", bayes, make(map[string]float64)
}
if len(jsonFingerprint.WifiFingerprint) == 0 {
return "No fingerprints found to track, see API", false, "", bayes
return "No fingerprints found to track, see API", false, "", bayes, make(map[string]float64)
}
if len(jsonFingerprint.Username) == 0 {
return "No username defined, see API", false, "", bayes
return "No username defined, see API", false, "", bayes, make(map[string]float64)
}
if wasLearning, ok := isLearning[strings.ToLower(jsonFingerprint.Group)]; ok {
if wasLearning {
Debug.Println("Was learning, calculating priors")
group := strings.ToLower(jsonFingerprint.Group)
isLearning[group] = false
optimizePriorsThreaded(group)
if RuntimeArgs.Svm {
dumpFingerprintsSVM(group)
calculateSVM(group)
}
if _, ok := usersCache[group]; ok {
if len(usersCache[group]) == 0 {
usersCache[group] = append([]string{}, strings.ToLower(jsonFingerprint.Username))
}
}
}
}
locationGuess, bayes := calculatePosterior(jsonFingerprint, *NewFullParameters())
jsonFingerprint.Location = locationGuess
locationGuess1, bayes := calculatePosterior(jsonFingerprint, *NewFullParameters())
percentGuess1 := float64(0)
total := float64(0)
for _, locBayes := range bayes {
total += math.Exp(locBayes)
if locBayes > percentGuess1 {
percentGuess1 = locBayes
}
}
percentGuess1 = math.Exp(bayes[locationGuess1]) / total * 100.0

jsonFingerprint.Location = locationGuess1
putFingerprintIntoDatabase(jsonFingerprint, "fingerprints-track")
positions := [][]string{}
positions1 := []string{}
positions2 := []string{}
positions1 = append(positions1, locationGuess)
positions2 = append(positions2, " ")
positions = append(positions, positions1)
positions = append(positions, positions2)
var userJSON UserPositionJSON
userJSON.Location = locationGuess
userJSON.Bayes = bayes
userJSON.Time = time.Now().String()
userPositionCache[strings.ToLower(jsonFingerprint.Group)+strings.ToLower(jsonFingerprint.Username)] = userJSON

Debug.Println("Tracking fingerprint containing " + strconv.Itoa(len(jsonFingerprint.WifiFingerprint)) + " APs for " + jsonFingerprint.Username + " (" + jsonFingerprint.Group + ") at " + jsonFingerprint.Location + " (guess)")
message := "NB: " + locationGuess1 + " (" + strconv.Itoa(int(percentGuess1)) + "%)"

// Process SVM if needed
if RuntimeArgs.Svm {
locationGuess2, svmData2 := classify(jsonFingerprint)
percentGuess2 := int(100 * math.Exp(svmData2[locationGuess2]))
if percentGuess2 > 100 {
percentGuess2 = percentGuess2 / 10
}
message = "NB: " + locationGuess1 + " (" + strconv.Itoa(int(percentGuess1)) + "%)" + ", SVM: " + locationGuess2 + " (" + strconv.Itoa(int(percentGuess2)) + "%)"
svmData = svmData2
}

// Send MQTT if needed
if RuntimeArgs.Mqtt {
type FingerprintResponse struct {
LocationGuess string `json:"location"`
Bayes map[string]float64 `json:"bayes"`
Svm map[string]float64 `json:"svm"`
}
mqttMessage, _ := json.Marshal(FingerprintResponse{
LocationGuess: locationGuess,
LocationGuess: locationGuess1,
Bayes: bayes,
Svm: svmData,
})
go sendMQTTLocation(string(mqttMessage), jsonFingerprint.Group, jsonFingerprint.Username)
}
return "Calculated location: " + locationGuess, true, locationGuess, bayes

// Send out the final responses
var userJSON UserPositionJSON
userJSON.Location = locationGuess1
userJSON.Bayes = bayes
userJSON.Svm = svmData
userJSON.Time = time.Now().String()
userPositionCache[strings.ToLower(jsonFingerprint.Group)+strings.ToLower(jsonFingerprint.Username)] = userJSON

return message, true, locationGuess1, bayes, svmData

}
7 changes: 4 additions & 3 deletions routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,9 +123,10 @@ func slashDashboard(c *gin.Context) {
}
}
c.HTML(http.StatusOK, "dashboard.tmpl", gin.H{
"Group": group,
"Dash": dash,
"Users": people,
"Message": RuntimeArgs.Message,
"Group": group,
"Dash": dash,
"Users": people,
})
}

Expand Down
37 changes: 37 additions & 0 deletions server.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"log"
"net/http"
"os"
"os/exec"
"path"
"strings"

Expand All @@ -35,7 +36,9 @@ var RuntimeArgs struct {
MosquittoPID string
MqttAdminPassword string
Dump string
Message string
Mqtt bool
Svm bool
}

// VersionNum keeps track of the version
Expand All @@ -46,6 +49,7 @@ func init() {
cwd, _ := os.Getwd()
RuntimeArgs.SourcePath = path.Join(cwd, "data")
RuntimeArgs.Cwd = cwd
RuntimeArgs.Message = ""
}

func main() {
Expand All @@ -62,6 +66,7 @@ func main() {
flag.StringVar(&RuntimeArgs.MqttAdminPassword, "mqttadminpass", "", "admin to read all messages")
flag.StringVar(&RuntimeArgs.MosquittoPID, "mosquitto", "", "mosquitto PID")
flag.StringVar(&RuntimeArgs.Dump, "dump", "", "group to dump to folder")
flag.StringVar(&RuntimeArgs.Message, "message", "", "message to display to all userse")
flag.CommandLine.Usage = func() {
fmt.Println(`find (version ` + VersionNum + `)
run this to start the server and then visit localhost at the port you specify
Expand All @@ -88,6 +93,7 @@ Options:`)
RuntimeArgs.Mqtt = false
}

// Check whether we are just dumping the database
if len(RuntimeArgs.Dump) > 0 {
err := dumpFingerprints(strings.ToLower(RuntimeArgs.Dump))
if err == nil {
Expand All @@ -98,6 +104,24 @@ Options:`)
os.Exit(1)
}

// Check whether SVM libraries are available
cmdOut, _ := exec.Command("svm-scale", "").CombinedOutput()
if len(cmdOut) == 0 {
RuntimeArgs.Svm = false
fmt.Println("SVM is not detected.")
fmt.Println(`To install:
sudo apt-get install g++
wget http://www.csie.ntu.edu.tw/~cjlin/cgi-bin/libsvm.cgi?+http://www.csie.ntu.edu.tw/~cjlin/libsvm+tar.gz
tar -xvf libsvm-3.18.tar.gz
cd libsvm-3.18
make
cp svm-scale ../
cp svm-predict ../
cp svm-train ../`)
} else {
RuntimeArgs.Svm = true
}

// Setup Gin-Gonic
gin.SetMode(gin.ReleaseMode)
r := gin.Default()
Expand All @@ -119,6 +143,8 @@ Options:`)
})
})

// r.PUT("/message", putMessage)

// Routes for logging in and viewing dashboards (routes.go)
r.GET("/", slash)
r.GET("/login", slashLogin)
Expand Down Expand Up @@ -166,3 +192,14 @@ Options:`)
r.Run(RuntimeArgs.Port)
}
}

// // putMessage usage: curl -G -X PUT "http://localhost:8003/message" --data-urlencode "text=hello world"
// func putMessage(c *gin.Context) {
// newText := c.DefaultQuery("text", "none")
// if newText != "none" {
// RuntimeArgs.Message = newText
// c.JSON(http.StatusOK, gin.H{"success": true, "message": "Message set as '" + newText + "'"})
// } else {
// c.JSON(http.StatusOK, gin.H{"success": false, "message": "Error parsing request"})
// }
// }
Loading

0 comments on commit b0b5b8e

Please sign in to comment.