Skip to content

Commit 8a20ae5

Browse files
committed
Merge branch 'main' of https://github.com/uugear/GPSDRPP
2 parents 86d4b5d + fc9f7c6 commit 8a20ae5

File tree

11 files changed

+166
-42
lines changed

11 files changed

+166
-42
lines changed

.gitattributes

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
* text=auto eol=lf
2+
3+
*.png binary
4+
*.jpg binary
5+
*.pdf binary
6+
*.zip binary
7+
*.exe binary
8+
9+
*.bat text eol=crlf

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
sink_modules/.DS_Store
2+
.DS_Store

README.md

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,42 @@
11
# GPSDR++ (GPSDRPP)
2-
GPSDR++ is the software for [VU GPSDR](https://www.uugear.com/product/vu-gpsdr/), and it is based on the open-source [SDR++](https://github.com/AlexandreRouma/SDRPlusPlus) project, originally created by Alexandre Rouma (ON5RYZ) and enriched by many talented contributors. We deeply appreciate their dedication and hard work, which form the foundation of this software.
2+
GPSDR++ is the software for [VU GPSDR](https://www.uugear.com/product/vu-gpsdr/), and it is based the open-source [SDR++](https://github.com/AlexandreRouma/SDRPlusPlus) project, originally created by Alexandre Rouma (ON5RYZ) and enriched by many talented contributors. We deeply appreciate their dedication and hard work, which form the foundation of this software.
3+
4+
## Usage
5+
Start GPSDR++ from the Application menu, or just run "gpsdrpp" in the terminal.
6+
7+
![Run GPSDR++](https://www.uugear.com/wordpress/wp-content/uploads/2025/09/launch_gpsdrpp.jpg)
8+
9+
GPSDR++ will start as a full-screen application, and its interface looks like this:
10+
11+
![GPSDR++ Layout](https://www.uugear.com/wordpress/wp-content/uploads/2025/09/GPSDRPP_layout.jpg)
12+
13+
The toolbar at the top contains controls for showing/adjusting SDR parameters (on/off, volume, frequency, tuning mode, SNR etc.). The SDR++ menu is hidden by default - you can tap the button in the upper-left corner to toggle the visibility of the menu and/or sidebar.
14+
15+
The sidebar provides several functional pages, such as Radio, Mode S/ADS-B, GPS, and CLK OUT. You can switch between these pages using the two navigation buttons at the bottom of the sidebar.
16+
17+
The main view contains two tabs: Waterfall and Map, and you can switch between them by tapping on the tab header.
18+
19+
By default, when you are facing the screen:
20+
- The left encoder controls the zoom level of the waterfall view.
21+
- The right encoder controls the tuning frequency.
22+
23+
You can change their functionality by pressing the buttons next to each encoder.
24+
25+
On the radio page, you can turn AGC on/off, adjust the gain, and specify the demodulation mode.
26+
27+
![GPSDR++ Radio](https://www.uugear.com/wordpress/wp-content/uploads/2025/09/radio_page.jpg)
28+
29+
To enable ADS-B reception, go to the Mode S/ADS-B page and tick the checkbox at the top. When aircraft are detected, they will also appear in the Map tab within the main view.
30+
31+
![GPSDR++ ADSB](https://www.uugear.com/wordpress/wp-content/uploads/2025/09/ADSB_page.jpg)
32+
33+
To monitor GPS satellite signal strength, navigate to the GPS page. To show your surrounding geography, activate the Map tab.
34+
35+
![GPSDR++ GPS](https://www.uugear.com/wordpress/wp-content/uploads/2025/09/GPS_page.jpg)
36+
37+
To enable the clock signal output, go to the CLK OUT page and tick the checkbox at the top.
38+
39+
![GPSDR++ CLK OUT](https://www.uugear.com/wordpress/wp-content/uploads/2025/09/CLKOUT_page.jpg)
40+
341

442

core/src/core.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,8 +150,8 @@ int gpsdrpp_main(int argc, char* argv[]) {
150150
return 0;
151151
}
152152

153-
// Tell GPS to output 24MHz timepulse, align to UTC if possible
154-
core::gps.sendData((unsigned char *)Gps::UBX_CFG_TP5, Gps::UBX_CFG_TP5_SIZE);
153+
// Tell GPS to output 24MHz timepulse
154+
core::gps.outputReferenceClock(false);
155155

156156
bool serverMode = (bool)core::args["server"];
157157

@@ -205,6 +205,9 @@ int gpsdrpp_main(int argc, char* argv[]) {
205205
// Side Bar
206206
defConfig["selectedPage"] = 0;
207207

208+
// GPS Page
209+
defConfig["lockToGpsFreq"] = false;
210+
208211
// CLK OUT Page
209212
defConfig["clkOut"] = true;
210213
defConfig["reg58"] = 0;

core/src/dev_refresh_worker.cpp

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,15 @@
88
#include <linux/i2c-dev.h>
99
#include <core.h>
1010
#include <rtl_sdr_source_interface.h>
11+
#include <utils/flog.h>
1112

1213

1314
bool DeviceRefreshWorker::checkI2CDevice(int bus, int address) {
1415
char filename[20];
1516
snprintf(filename, sizeof(filename), "/dev/i2c-%d", bus);
1617
int file = open(filename, O_RDWR);
1718
if (file < 0) {
18-
std::cerr << "Failed to open I2C bus " << bus << std::endl;
19+
flog::error("Failed to open I2C bus {0}", bus);
1920
return false;
2021
}
2122
bool deviceExists = (ioctl(file, I2C_SLAVE, address) >= 0);
@@ -30,7 +31,7 @@ void DeviceRefreshWorker::delay(int milliseconds) {
3031
int DeviceRefreshWorker::executeCommand(const char* command) {
3132
int result = system(command);
3233
if (result != 0) {
33-
std::cerr << "Command failed: " << command << " (exit code: " << result << ")" << std::endl;
34+
flog::error("Command failed: {0} (exit code: {1})", command, result);
3435
}
3536
return result;
3637
}
@@ -43,32 +44,37 @@ void DeviceRefreshWorker::workerLoop() {
4344
core::modComManager.callInterface("RTL-SDR", RTL_SDR_SOURCE_IFACE_CMD_GET_DEVICE_COUNT, NULL, &count);
4445
if (count == 0) {
4546
if (checkI2CDevice(2, 0x0b)) {
46-
std::cout << "VU GPSDR device found on I2C bus 2, make a power cycle now..." << std::endl;
47+
flog::info("VU GPSDR device found on I2C bus 2, make a power cycle now...");
4748

4849
executeCommand("vgp set 4B2 1");
4950
delay(2000);
5051

5152
executeCommand("vgp set 4B2 0");
5253
delay(500);
5354

54-
// Tell GPS to output 24MHz
55-
try {
56-
core::gps.sendData((unsigned char *)Gps::UBX_CFG_TP5, Gps::UBX_CFG_TP5_SIZE);
57-
} catch (const std::exception& e) {
58-
std::cerr << "GPS sendData failed: " << e.what() << std::endl;
59-
}
60-
61-
delay(500);
55+
// Tell GPS to output 24MHz
56+
try {
57+
core::configManager.acquire();
58+
bool lockToGpsFreq = core::configManager.conf["lockToGpsFreq"];
59+
core::configManager.release(true);
60+
core::gps.outputReferenceClock(lockToGpsFreq);
61+
} catch (const std::exception& e) {
62+
flog::error("GPS sendData failed: {0}", e.what());
63+
delay(500);
64+
continue;
65+
}
66+
delay(500);
6267

6368
// Initialize Si5351
6469
try {
6570
core::si5351.initialize();
6671
} catch (const std::exception& e) {
67-
std::cerr << "Si5351 initialize failed: " << e.what() << std::endl;
72+
flog::error("Si5351 initialize failed: {0}", e.what());
73+
delay(500);
74+
continue;
6875
}
6976

70-
std::cout << "Power cycle completed." << std::endl;
71-
77+
flog::info("Power cycle completed.");
7278
delay(500);
7379

7480
// Refresh device list
@@ -77,31 +83,30 @@ void DeviceRefreshWorker::workerLoop() {
7783
}
7884
}
7985
} catch (const std::exception& e) {
80-
std::cerr << "Exception in worker thread: " << e.what() << std::endl;
86+
flog::error("Exception in DeviceRefreshWorker thread: {0}", e.what());
8187
}
8288
delay(2000);
8389
}
8490

85-
std::cout << "Worker thread stopped." << std::endl;
91+
flog::info("DeviceRefreshWorker thread stopped.");
8692
}
8793

8894
void DeviceRefreshWorker::start() {
8995
if (workerThread.joinable()) {
90-
std::cerr << "Worker thread is already running!" << std::endl;
96+
flog::error("DeviceRefreshWorker thread is already running!");
9197
return;
9298
}
93-
9499
shouldStop.store(false);
95100
workerThread = std::thread(&DeviceRefreshWorker::workerLoop, this);
96-
std::cout << "Device refresh worker thread started." << std::endl;
101+
flog::info("DeviceRefreshWorker thread started.");
97102
}
98103

99104
void DeviceRefreshWorker::stop() {
100105
shouldStop.store(true);
101106

102107
if (workerThread.joinable()) {
103108
workerThread.join();
104-
std::cout << "Device refresh worker thread joined." << std::endl;
109+
flog::info("DeviceRefreshWorker thread joined.");
105110
}
106111
}
107112

core/src/gps.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -581,4 +581,9 @@ void Gps::readerLoop() {
581581

582582
int32_t Gps::getProcessedNMEA() {
583583
return processedNMEA;
584+
}
585+
586+
void Gps::outputReferenceClock(bool lockToGpsFreq) {
587+
flog::info("Request GPS to output referernce clock, lockToGpsFreq={0}", lockToGpsFreq);
588+
sendData((unsigned char *)(lockToGpsFreq ? UBX_CFG_TP5 : UBX_CFG_TP5_NO_SYNC), UBX_CFG_TP5_SIZE);
584589
}

core/src/gps.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,14 @@ class Gps {
6868
0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
6969
0x00, 0x00, 0x6F, 0x00, 0x00, 0x00, 0x43, 0x50
7070
};
71-
71+
static constexpr unsigned char UBX_CFG_TP5_NO_SYNC[40] = {
72+
0xB5, 0x62, 0x06, 0x31, 0x20, 0x00, 0x00, 0x01,
73+
0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x36,
74+
0x6E, 0x01, 0x00, 0x36, 0x6E, 0x01, 0x00, 0x00,
75+
0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
76+
0x00, 0x00, 0x6D, 0x00, 0x00, 0x00, 0xC1, 0xC8
77+
};
78+
7279
uint8_t getUtcHour();
7380
uint8_t getUtcMinute();
7481
uint8_t getUtcSecond();
@@ -80,6 +87,8 @@ class Gps {
8087
uint8_t getFixQuality();
8188
uint8_t getUsedSatellites();
8289

90+
void outputReferenceClock(bool lockToGpsFreq);
91+
8392
int32_t processedNMEA = 0;
8493
std::vector<Satellite> getSatellites();
8594

core/src/gui/widgets/gps_page.cpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,16 +31,26 @@ const char* GPSPage::getLabel() {
3131
}
3232

3333
void GPSPage::init() {
34-
34+
core::configManager.acquire();
35+
lockToGpsFreq = core::configManager.conf["lockToGpsFreq"];
36+
core::configManager.release(true);
37+
if (lockToGpsFreq) {
38+
core::gps.outputReferenceClock(lockToGpsFreq);
39+
}
3540
}
3641

3742
void GPSPage::deinit() {
38-
43+
core::configManager.acquire();
44+
core::configManager.conf["lockToGpsFreq"] = lockToGpsFreq;
45+
core::configManager.release(true);
3946
}
4047

4148
void GPSPage::draw() {
4249
const float footer_height_to_reserve = ImGui::GetStyle().ItemSpacing.y + ImGui::GetFrameHeightWithSpacing() + 10;
4350
if (ImGui::BeginChild("GPS_Content", ImVec2(0, -footer_height_to_reserve), true)) {
51+
if (ImGui::Checkbox("Sync to 24MHz", &lockToGpsFreq)) {
52+
core::gps.outputReferenceClock(lockToGpsFreq);
53+
}
4454
ImGui::Text("Status: ");
4555
ImGui::SameLine();
4656
if (core::gps.getFixQuality() != 0) {

core/src/gui/widgets/gps_page.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ class GPSPage : public SideBar::SidePage {
2121

2222
private:
2323
void drawSatelliteTable();
24-
24+
25+
bool lockToGpsFreq = false;
2526
LoadingAnimation loader;
2627
};

core/src/version.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
#pragma once
22

3-
#define VERSION_STR "1.0.4"
3+
#define VERSION_STR "1.0.7"

0 commit comments

Comments
 (0)