Skip to content

Commit b598ae5

Browse files
authored
Merge branch 'main' into main
2 parents 718841c + 706a3fb commit b598ae5

File tree

11 files changed

+560
-1
lines changed

11 files changed

+560
-1
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,6 @@ if (NOT CMAKE_BUILD_TYPE)
1111
endif ()
1212

1313
add_subdirectory(infinite_sense_core)
14+
1415
add_subdirectory(tools)
1516
add_subdirectory(example)

example/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ cmake_minimum_required(VERSION 3.10)
22
project(example)
33

44
add_subdirectory(NetCam)
5-
add_subdirectory(CustomCam)
5+
add_subdirectory(CustomCam)
6+
add_subdirectory(ros2_example)

example/gprmc_streaming/data.log

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
sprintf(gmrc_buffer,
2+
"GPRMC,
3+
%02d%02d%02d, <---- hhmmss
4+
A,
5+
%s, <---- 25.04776
6+
N,
7+
%s, <---- 121.53185
8+
E,
9+
022.4,
10+
084.4,
11+
%02d%02d%02d, <---- ddmmyy
12+
,
13+
A", <---- A XX
14+
15+
hh, mm, ss, "25.04776", "121.53185", day, month, year);
16+
17+
$GPRMC,hhmmss,A,25.04776,N,121.53185,E,022.4,084.4,ddmmyy,,A XX
18+
19+
void SendHex(uart_inst_t uart, uint8_t crc) {
20+
char high = (crc >> 4) & 0x0F;
21+
char low = crc & 0x0F;
22+
high = (high < 10) ? ('0' + high) : ('A' + high - 10);
23+
low = (low < 10) ? ('0' + low) : ('A' + low - 10);
24+
uart_putc(uart, high);
25+
uart_putc(uart, low);
26+
uart_puts(uart, "\n");
27+
}
28+
29+
主函数:
30+
sprintf(gmrc_buffer, "GPRMC,%02d%02d%02d,A,%s,N,%s,E,022.4,084.4,%02d%02d%02d,,A", hh, mm, ss, "25.04776", "121.53185", day, month, year);
31+
uart_puts(uart, "$");
32+
uart_puts(uart, gmrc_buffer);
33+
uart_puts(uart, "");
34+
byte crc = 0;
35+
for (byte x = 0; x < strlen(gmrc_buffer); x++) {
36+
crc = crc ^ gmrc_buffer[x];
37+
}
38+
SendHex(uart, crc);
39+
40+
65 bytes: 24 47 50 52 4d 43 2c 30 30 30 30 31 32 2c 41 2c 32 35 2e 30 34 37 37 36 2c 4e 2c 31 32 31 2e 35 33 31 38 35 2c 45 2c 30 32 32 2e 34 2c 30 38 34 2e 34 2c 30 31 30 31 2d 33 30 2c 2c 41 2a 37 30 0a
41+
65 bytes: 24 47 50 52 4d 43 2c 30 30 30 30 31 33 2c 41 2c 32 35 2e 30 34 37 37 36 2c 4e 2c 31 32 31 2e 35 33 31 38 35 2c 45 2c 30 32 32 2e 34 2c 30 38 34 2e 34 2c 30 31 30 31 2d 33 30 2c 2c 41 2a 37 31 0a
42+
65 bytes: 24 47 50 52 4d 43 2c 30 30 30 30 31 34 2c 41 2c 32 35 2e 30 34 37 37 36 2c 4e 2c 31 32 31 2e 35 33 31 38 35 2c 45 2c 30 32 32 2e 34 2c 30 38 34 2e 34 2c 30 31 30 31 2d 33 30 2c 2c 41 2a 37 36 0a
43+
65 bytes: 24 47 50 52 4d 43 2c 30 30 30 30 31 35 2c 41 2c 32 35 2e 30 34 37 37 36 2c 4e 2c 31 32 31 2e 35 33 31 38 35 2c 45 2c 30 32 32 2e 34 2c 30 38 34 2e 34 2c 30 31 30 31 2d 33 30 2c 2c 41 2a 37 37 0a
44+
65 bytes: 24 47 50 52 4d 43 2c 30 30 30 30 31 36 2c 41 2c 32 35 2e 30 34 37 37 36 2c 4e 2c 31 32 31 2e 35 33 31 38 35 2c 45 2c 30 32 32 2e 34 2c 30 38 34 2e 34 2c 30 31 30 31 2d 33 30 2c 2c 41 2a 37 34 0a
45+
65 bytes: 24 47 50 52 4d 43 2c 30 30 30 30 31 37 2c 41 2c 32 35 2e 30 34 37 37 36 2c 4e 2c 31 32 31 2e 35 33 31 38 35 2c 45 2c 30 32 32 2e 34 2c 30 38 34 2e 34 2c 30 31 30 31 2d 33 30 2c 2c 41 2a 37 35 0a
46+
65 bytes: 24 47 50 52 4d 43 2c 30 30 30 30 31 38 2c 41 2c 32 35 2e 30 34 37 37 36 2c 4e 2c 31 32 31 2e 35 33 31 38 35 2c 45 2c 30 32 32 2e 34 2c 30 38 34 2e 34 2c 30 31 30 31 2d 33 30 2c 2c 41 2a 37 41 0a
47+
65 bytes: 24 47 50 52 4d 43 2c 30 30 30 30 31 39 2c 41 2c 32 35 2e 30 34 37 37 36 2c 4e 2c 31 32 31 2e 35 33 31 38 35 2c 45 2c 30 32 32 2e 34 2c 30 38 34 2e 34 2c 30 31 30 31 2d 33 30 2c 2c 41 2a 37 42 0a
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import serial
2+
from time import sleep
3+
4+
ser = serial.Serial('/dev/ttyUSB0', # or /dev/ttyUSB0
5+
# baudrate=9600, # <-- set to the same value you used
6+
baudrate=115200, # <-- set to the same value you used
7+
bytesize=serial.EIGHTBITS,
8+
parity=serial.PARITY_NONE,
9+
stopbits=serial.STOPBITS_ONE,
10+
timeout=1)
11+
12+
def read_vendor_nmea(ser):
13+
raw = ser.readline() # blocks until LF
14+
# if raw:
15+
# print("RAW :", raw.hex(" "))
16+
17+
if not raw or raw[:1] != b'$' or len(raw) < 6:
18+
return None, False
19+
20+
raw = raw.rstrip(b'\r\n') # drop LF/CR
21+
22+
star = raw.rfind(b'*') # <─ find the asterisk
23+
if star == -1 or len(raw) - star < 3: # need at least '*XX'
24+
return None, False
25+
26+
payload = raw[1:star] # bytes between $ and *
27+
cs_hex = raw[star+1:star+3] # the two ASCII hex digits
28+
29+
try:
30+
cs_rx = int(cs_hex, 16)
31+
except ValueError:
32+
return None, False
33+
34+
cs_calc = 0
35+
for b in payload:
36+
cs_calc ^= b
37+
38+
nmea = b'$' + payload + b'*' + cs_hex + b'\r\n'
39+
return nmea.decode('ascii', errors='replace'), cs_calc == cs_rx
40+
41+
42+
while True:
43+
nmea, ok = read_vendor_nmea(ser)
44+
if nmea:
45+
print("✓" if ok else "✗", nmea)
46+
else:
47+
print("waiting …", end='\r')
48+
sleep(0.1)
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Loopback Test
2+
3+
import serial
4+
import time
5+
6+
ser = serial.Serial('/dev/ttyUSB0', # or /dev/ttyUSB0
7+
# baudrate=9600, # <-- set to the same value you used
8+
baudrate=115200, # <-- set to the same value you used
9+
bytesize=serial.EIGHTBITS,
10+
parity=serial.PARITY_NONE,
11+
stopbits=serial.STOPBITS_ONE,
12+
timeout=1)
13+
print(f"Listening on {ser.port} @ {ser.baudrate} baud – Ctrl‑C to quit")
14+
15+
# --- Read forever -----------------------------------------------------
16+
try:
17+
while True:
18+
ser.write(b'>') # send the character '>'
19+
time.sleep(1.00)
20+
print("echo:", ser.read(1))
21+
except KeyboardInterrupt:
22+
pass
23+
finally:
24+
ser.close()
25+
print("\nSerial port closed – bye.")
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
cmake_minimum_required(VERSION 3.16)
2+
3+
project(ros2_example VERSION 1.0)
4+
5+
find_package(ament_cmake REQUIRED)
6+
find_package(rclcpp REQUIRED)
7+
find_package(rclcpp_components REQUIRED)
8+
find_package(std_msgs REQUIRED)
9+
find_package(sensor_msgs REQUIRED)
10+
11+
# === InfiniteSense SDK ===
12+
set(INFINITE_SENSE_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/infinite_sense_core/include")
13+
set(INFINITE_SENSE_LIB_NAME infinite_sense_core)
14+
15+
# === Component (shared library) ===
16+
add_library(synchronizer_component SHARED
17+
synchronizer_component.cpp
18+
)
19+
20+
target_include_directories(synchronizer_component
21+
PUBLIC
22+
$<BUILD_INTERFACE:${INFINITE_SENSE_INCLUDE_DIR}>
23+
$<INSTALL_INTERFACE:include>
24+
)
25+
26+
target_link_libraries(synchronizer_component
27+
${INFINITE_SENSE_LIB_NAME}
28+
)
29+
30+
ament_target_dependencies(synchronizer_component
31+
rclcpp
32+
rclcpp_components
33+
std_msgs
34+
)
35+
36+
rclcpp_components_register_nodes(synchronizer_component SynchronizerComponent)
37+
38+
## === ROS2 GPRMC Publisher Node ===
39+
add_executable(gprmc_publisher_node gprmc_publisher_node.cpp)
40+
ament_target_dependencies(gprmc_publisher_node rclcpp std_msgs sensor_msgs)
41+
42+
# === Standalone ROS 2 Node (executable) ===
43+
add_executable(synchronizer_node
44+
synchronizer_node.cpp
45+
)
46+
target_include_directories(synchronizer_node
47+
PUBLIC
48+
${INFINITE_SENSE_INCLUDE_DIR}
49+
)
50+
target_link_libraries(synchronizer_node
51+
${INFINITE_SENSE_LIB_NAME}
52+
)
53+
ament_target_dependencies(synchronizer_node
54+
rclcpp
55+
std_msgs
56+
)
57+
58+
# === Installation ===
59+
install(TARGETS
60+
synchronizer_component
61+
synchronizer_node
62+
gprmc_publisher_node
63+
ARCHIVE DESTINATION lib
64+
LIBRARY DESTINATION lib
65+
RUNTIME DESTINATION lib/${PROJECT_NAME}
66+
)
67+
68+
# === Export ===
69+
ament_export_targets(synchronizer_component HAS_LIBRARY_TARGET)
70+
ament_export_dependencies(rclcpp rclcpp_components std_msgs)
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
#include <fstream>
2+
#include <string>
3+
#include <sstream>
4+
#include <iomanip>
5+
#include <termios.h>
6+
#include <fcntl.h>
7+
#include <unistd.h>
8+
9+
#include <rclcpp/rclcpp.hpp>
10+
#include <std_msgs/msg/string.hpp>
11+
#include <sensor_msgs/msg/time_reference.hpp>
12+
13+
class GprmcPublisher : public rclcpp::Node {
14+
public:
15+
GprmcPublisher() : Node("gprmc_publisher") {
16+
this->declare_parameter<std::string>("port", "/dev/ttyUSB0");
17+
this->declare_parameter<int>("baud", 115200);
18+
19+
std::string port;
20+
int baud;
21+
this->get_parameter("port", port);
22+
this->get_parameter("baud", baud);
23+
24+
publisher_ = this->create_publisher<std_msgs::msg::String>("/gps/gprmc", 10);
25+
time_pub_ = this->create_publisher<sensor_msgs::msg::TimeReference>("/gps/time_reference", 10);
26+
27+
fd_ = open(port.c_str(), O_RDWR | O_NOCTTY | O_SYNC);
28+
if (fd_ < 0) {
29+
RCLCPP_FATAL(this->get_logger(), "Failed to open port: %s", port.c_str());
30+
rclcpp::shutdown();
31+
return;
32+
}
33+
34+
configure_serial(fd_, baud);
35+
timer_ =
36+
this->create_wall_timer(std::chrono::milliseconds(100), std::bind(&GprmcPublisher::read_and_publish, this));
37+
38+
RCLCPP_INFO(this->get_logger(), "Opened %s @ %d baud", port.c_str(), baud);
39+
}
40+
41+
~GprmcPublisher() {
42+
if (fd_ >= 0) close(fd_);
43+
}
44+
45+
private:
46+
void configure_serial(int fd, int baudrate) {
47+
struct termios tty;
48+
tcgetattr(fd, &tty);
49+
cfsetospeed(&tty, baudrate_to_constant(baudrate));
50+
cfsetispeed(&tty, baudrate_to_constant(baudrate));
51+
tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8;
52+
tty.c_iflag = IGNBRK;
53+
tty.c_lflag = 0;
54+
tty.c_oflag = 0;
55+
tty.c_cc[VMIN] = 0;
56+
tty.c_cc[VTIME] = 10;
57+
tty.c_cflag |= (CLOCAL | CREAD);
58+
tty.c_cflag &= ~(PARENB | PARODD);
59+
tty.c_cflag &= ~CSTOPB;
60+
tty.c_cflag &= ~CRTSCTS;
61+
tcsetattr(fd, TCSANOW, &tty);
62+
}
63+
64+
speed_t baudrate_to_constant(int baud) {
65+
switch (baud) {
66+
case 9600:
67+
return B9600;
68+
case 19200:
69+
return B19200;
70+
case 38400:
71+
return B38400;
72+
case 57600:
73+
return B57600;
74+
case 115200:
75+
return B115200;
76+
default:
77+
RCLCPP_FATAL(this->get_logger(), "Unsupported baudrate: %d", baud);
78+
rclcpp::shutdown();
79+
return B9600;
80+
}
81+
}
82+
83+
void read_and_publish() {
84+
char buf[256];
85+
ssize_t n = read(fd_, buf, sizeof(buf));
86+
if (n <= 0) return;
87+
88+
for (ssize_t i = 0; i < n; ++i) {
89+
if (buf[i] == '$') {
90+
line_.clear();
91+
}
92+
line_ += buf[i];
93+
if (buf[i] == '\n') {
94+
if (is_valid_checksum(line_)) {
95+
// ---> Publish GPRMC as String
96+
std_msgs::msg::String msg;
97+
msg.data = line_;
98+
std::cout << "Data: " << line_ << std::endl;
99+
publisher_->publish(msg);
100+
101+
// ---> Publish GPRMC as TimeReference
102+
auto maybe_time = parse_gprmc_utc(line_);
103+
if (maybe_time.has_value()) {
104+
sensor_msgs::msg::TimeReference tref;
105+
tref.header.stamp = this->now();
106+
tref.header.frame_id = "infinite_sense";
107+
tref.source = "infinite_sense";
108+
tref.time_ref = maybe_time.value();
109+
time_pub_->publish(tref);
110+
}
111+
}
112+
line_.clear();
113+
}
114+
}
115+
}
116+
117+
bool is_valid_checksum(const std::string &line) {
118+
auto asterisk = line.find('*');
119+
if (asterisk == std::string::npos || asterisk + 2 >= line.size()) return false;
120+
121+
uint8_t checksum = 0;
122+
for (size_t i = 1; i < asterisk; ++i) {
123+
checksum ^= line[i];
124+
}
125+
126+
std::istringstream iss(line.substr(asterisk + 1, 2));
127+
int sent_checksum;
128+
iss >> std::hex >> sent_checksum;
129+
return checksum == sent_checksum;
130+
}
131+
132+
std::optional<builtin_interfaces::msg::Time> parse_gprmc_utc(const std::string &nmea_line) {
133+
std::vector<std::string> fields;
134+
std::istringstream ss(nmea_line);
135+
std::string token;
136+
while (std::getline(ss, token, ',')) {
137+
fields.push_back(token);
138+
}
139+
140+
if (fields.size() > 9 && fields[1].length() >= 6 && fields[9].length() >= 6) {
141+
try {
142+
std::string hh = fields[1].substr(0, 2);
143+
std::string mm = fields[1].substr(2, 2);
144+
std::string ss = fields[1].substr(4, 2);
145+
146+
std::string dd = fields[9].substr(0, 2);
147+
std::string mo = fields[9].substr(2, 2);
148+
std::string yy = fields[9].substr(4, 2);
149+
150+
std::tm timeinfo = {};
151+
timeinfo.tm_year = std::stoi(yy) + 100;
152+
timeinfo.tm_mon = std::stoi(mo) - 1;
153+
timeinfo.tm_mday = std::stoi(dd);
154+
timeinfo.tm_hour = std::stoi(hh);
155+
timeinfo.tm_min = std::stoi(mm);
156+
timeinfo.tm_sec = std::stoi(ss);
157+
timeinfo.tm_isdst = 0;
158+
159+
time_t utc_sec = timegm(&timeinfo);
160+
if (utc_sec == -1) return std::nullopt;
161+
162+
builtin_interfaces::msg::Time t;
163+
t.sec = static_cast<int32_t>(utc_sec);
164+
t.nanosec = 0;
165+
return t;
166+
} catch (...) {
167+
return std::nullopt;
168+
}
169+
}
170+
171+
return std::nullopt;
172+
}
173+
174+
int fd_;
175+
std::string line_;
176+
rclcpp::TimerBase::SharedPtr timer_;
177+
rclcpp::Publisher<std_msgs::msg::String>::SharedPtr publisher_;
178+
rclcpp::Publisher<sensor_msgs::msg::TimeReference>::SharedPtr time_pub_;
179+
};
180+
181+
int main(int argc, char **argv) {
182+
rclcpp::init(argc, argv);
183+
rclcpp::spin(std::make_shared<GprmcPublisher>());
184+
rclcpp::shutdown();
185+
return 0;
186+
}

0 commit comments

Comments
 (0)