Skip to content

Commit b668c74

Browse files
authored
Improve GPS accuracy when locating videos (#109)
* use better gps measurement for videos * Improves the process of finding the correct gps location of the video. I have reviewed all my gopro videos and made all improvements to improve accuracy I have removed the GpsFix and because some videos recorded with the gopro have the correct location but the gpsfix flag was not active, the same thing also happened with the speed that there were videos with the correct location but at speed 0 Then I have added a process to select the most repeated and next locations so we discard false locations. we also control the height because it is an indicator of incorrect position. That's why in the end I added a filter by country, so that they discard the false locations, but this generates an infinite loop that makes the videos repeat over and over again. I have gone crazy and I have not been able to solve it Please help All the best * Fix async problem * When active only CountryCodes return this countries Get max country codes from config * Enable hero6+ble.mp4" for test fix * Fix test and include Fusion and karma in test * Comment fusion error and remove from test * Fix async problem * fix test karma without gps, only elevation * Fix complex nested blocks * revert android changes * remove unnecessary conversion * Fix lint * Fix lint * revert Fix lint * Update gci * Remove comment of fusion.mp4 * Reuse utils.Location * Clean errors in golangci-lint * Revert go modifications * Revert go.mod/go.sum
1 parent ee64438 commit b668c74

File tree

4 files changed

+122
-26
lines changed

4 files changed

+122
-26
lines changed

pkg/gopro/config.go

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,26 @@ import (
88

99
const parent = "gopro"
1010

11-
func gpsLockTypesFromConfig() []int {
12-
key := fmt.Sprintf("%s.gps_fix", parent)
13-
viper.SetDefault(key, []int{2, 3}) // 3d lock, 2d lock
14-
return viper.GetIntSlice(key)
15-
}
16-
1711
func gpsMinAccuracyFromConfig() uint16 {
1812
key := fmt.Sprintf("%s.gps_accuracy", parent)
1913
viper.SetDefault(key, 500)
2014
return uint16(viper.GetUint(key))
2115
}
16+
17+
func gpsMaxAltitudeFromConfig() float64 {
18+
key := fmt.Sprintf("%s.gps_max_altitude", parent)
19+
viper.SetDefault(key, 8000)
20+
return float64(viper.GetUint(key))
21+
}
22+
23+
func gpsCountryCodesFromConfig() []string {
24+
key := fmt.Sprintf("%s.gps_country_codes", parent)
25+
viper.SetDefault(key, []string{}) // 3d lock, 2d lock
26+
return viper.GetStringSlice(key)
27+
}
28+
29+
func gpsMaxCountryCodesFromConfig() int {
30+
key := fmt.Sprintf("%s.gps_max_country_codes", parent)
31+
viper.SetDefault(key, 5)
32+
return viper.GetInt(key)
33+
}

pkg/gopro/location.go

Lines changed: 79 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@ package gopro
33
import (
44
"bytes"
55
"io"
6+
"math"
67
"path/filepath"
78
"strings"
89

10+
"github.com/codingsince1985/geo-golang/openstreetmap"
911
"github.com/konradit/gopro-utils/telemetry"
1012
mErrors "github.com/konradit/mmt/pkg/errors"
1113
"github.com/konradit/mmt/pkg/utils"
@@ -37,10 +39,13 @@ func fromMP4(videoPath string) (*utils.Location, error) {
3739
return nil, err
3840
}
3941

42+
GPSNum := 0
4043
reader := bytes.NewReader(*data)
4144

4245
lastEvent := &telemetry.TELEM{}
46+
coordinates := []utils.Location{}
4347

48+
GetLocation:
4449
for {
4550
event, err := telemetry.Read(reader)
4651
if err != nil && err != io.EOF {
@@ -62,16 +67,84 @@ func fromMP4(videoPath string) (*utils.Location, error) {
6267

6368
telems := lastEvent.ShitJson()
6469
for _, telem := range telems {
65-
if telem.Speed == 0 || telem.Latitude == 0 || telem.Longitude == 0 || telem.GpsAccuracy > gpsMinAccuracyFromConfig() || !slices.Contains(gpsLockTypesFromConfig(), int(telem.GpsFix)) {
70+
if telem.Altitude > gpsMaxAltitudeFromConfig() || telem.Latitude == 0 || telem.Longitude == 0 || telem.GpsAccuracy > gpsMinAccuracyFromConfig() {
6671
continue
6772
}
68-
return &utils.Location{
69-
Latitude: telem.Latitude,
70-
Longitude: telem.Longitude,
71-
}, nil
73+
74+
CountryCodes := gpsCountryCodesFromConfig()
75+
if len(CountryCodes) != 0 {
76+
service := openstreetmap.Geocoder()
77+
78+
address, err := service.ReverseGeocode(telem.Latitude, telem.Longitude)
79+
if err != nil || !slices.Contains(CountryCodes, address.CountryCode) {
80+
continue
81+
}
82+
83+
GPSNum++
84+
}
85+
86+
coordinates = append(coordinates, utils.Location{Latitude: telem.Latitude, Longitude: telem.Longitude})
87+
88+
if GPSNum > gpsMaxCountryCodesFromConfig() {
89+
break GetLocation
90+
}
7291
}
7392
*lastEvent = *event
7493
}
7594

76-
return nil, mErrors.ErrNoGPS
95+
if len(coordinates) == 0 {
96+
return nil, mErrors.ErrNoGPS
97+
}
98+
99+
closestLocation := getClosestLocation(coordinates)
100+
101+
return &utils.Location{
102+
Latitude: closestLocation.Latitude,
103+
Longitude: closestLocation.Longitude,
104+
}, nil
105+
}
106+
107+
func getClosestLocation(locations []utils.Location) utils.Location {
108+
// Find the nearest and repeat location
109+
counts := make(map[utils.Location]int)
110+
for _, loc := range locations {
111+
counts[loc]++
112+
}
113+
114+
mostFrequentLocation := utils.Location{}
115+
maxCount := 0
116+
for loc, count := range counts {
117+
if count > maxCount {
118+
mostFrequentLocation = loc
119+
maxCount = count
120+
} else if count == maxCount {
121+
// If there are multiple locations with the same frequency, choose the closest one
122+
distanceToLoc := distance(locations[0], loc)
123+
distanceToMostFrequent := distance(locations[0], mostFrequentLocation)
124+
if distanceToLoc < distanceToMostFrequent {
125+
mostFrequentLocation = loc
126+
}
127+
}
128+
}
129+
130+
return mostFrequentLocation
131+
}
132+
133+
func distance(loc1 utils.Location, loc2 utils.Location) float64 {
134+
lat1 := degreesToRadians(loc1.Latitude)
135+
lon1 := degreesToRadians(loc1.Longitude)
136+
lat2 := degreesToRadians(loc2.Latitude)
137+
lon2 := degreesToRadians(loc2.Longitude)
138+
139+
deltaLat := lat2 - lat1
140+
deltaLon := lon2 - lon1
141+
142+
a := math.Pow(math.Sin(deltaLat/2), 2) + math.Cos(lat1)*math.Cos(lat2)*math.Pow(math.Sin(deltaLon/2), 2)
143+
c := 2 * math.Atan2(math.Sqrt(a), math.Sqrt(1-a))
144+
145+
return 6371 * c // radius of the earth in km
146+
}
147+
148+
func degreesToRadians(degrees float64) float64 {
149+
return degrees * math.Pi / 180
77150
}

pkg/gopro/location_test.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,13 @@ import (
1515

1616
var videoTests = map[string]bool{
1717
"hero5.mp4": true,
18-
"hero6+ble.mp4": false,
18+
"hero6+ble.mp4": true,
1919
"hero6.mp4": true,
2020
"hero6a.mp4": true,
2121

2222
"hero7.mp4": false,
23-
"hero8.mp4": false,
23+
"hero8.mp4": true,
24+
"karma.mp4": false,
2425
"max-360mode.mp4": true,
2526
"max-heromode.mp4": true,
2627
}

pkg/utils/ordering.go

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package utils
33
import (
44
"os"
55
"path/filepath"
6+
"sync"
67
)
78

89
type locationUtil interface {
@@ -18,6 +19,8 @@ func GetOrder(sortoptions SortOptions, GetLocation locationUtil, osPathname, out
1819
order := orderFromConfig()
1920
dayFolder := out
2021

22+
var wg sync.WaitGroup
23+
2124
for _, item := range order {
2225
switch item {
2326
case "date":
@@ -30,24 +33,31 @@ func GetOrder(sortoptions SortOptions, GetLocation locationUtil, osPathname, out
3033
if GetLocation == nil {
3134
continue
3235
}
33-
location := fallbackFromConfig()
34-
locationFromFile, locerr := GetLocation.GetLocation(osPathname)
35-
if locerr == nil {
36-
reverseLocation, reverseerr := ReverseLocation(*locationFromFile)
37-
if reverseerr == nil {
38-
location = reverseLocation
39-
if location == "" || location == " " {
40-
location = fallbackFromConfig()
36+
wg.Add(1)
37+
go func() {
38+
defer wg.Done()
39+
location := fallbackFromConfig()
40+
locationFromFile, locerr := GetLocation.GetLocation(osPathname)
41+
if locerr == nil {
42+
reverseLocation, reverseerr := ReverseLocation(*locationFromFile)
43+
if reverseerr == nil {
44+
location = reverseLocation
45+
if location == "" || location == " " {
46+
location = fallbackFromConfig()
47+
}
4148
}
4249
}
43-
}
44-
if sortoptions.ByLocation {
45-
dayFolder = filepath.Join(dayFolder, location)
46-
}
50+
if sortoptions.ByLocation {
51+
dayFolder = filepath.Join(dayFolder, location)
52+
}
53+
}()
54+
4755
default:
4856
// Not supported
4957
}
5058
}
59+
wg.Wait()
60+
5161
if _, err := os.Stat(dayFolder); os.IsNotExist(err) {
5262
_ = os.MkdirAll(dayFolder, 0o755)
5363
}

0 commit comments

Comments
 (0)