1 // -*- coding: utf-8 -*-
4 // This file is part of ruwai.
6 // If you use ruwai in any program or publication, please inform and
7 // acknowledge its author Stefan Mertl (stefan@mertl-research.at).
9 // pSysmon is free software: you can redistribute it and/or modify
10 // it under the terms of the GNU General Public License as published by
11 // the Free Software Foundation, either version 3 of the License, or
12 // (at your option) any later version.
14 // This program is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 // GNU General Public License for more details.
19 // You should have received a copy of the GNU General Public License
20 // along with this program. If not, see <http://www.gnu.org/licenses/>.
22 // Parts of this code were adapted from AP_GPS_UBLOX code from ArduPilot. The
23 // origin of the used code was by Michael Smith, Jordi Munoz and Jose Julio,
24 // DIYDrones.com and partly rewritten by Andrew Tridgell.
27 #include <gps_lea6t.h>
29 extern RuwaiSerial
& usb_serial
;
31 GpsLea6T::GpsLea6T(RuwaiSerial
&serial_stream
) :
32 serial_port(serial_stream
)
34 // Initialize the Arduino pins.
35 pinMode(GPS_ENABLE_USB
, OUTPUT
);
36 digitalWrite(GPS_ENABLE_USB
, 1);
42 GpsLea6T::init_state(void)
44 // Set the read flow state.
45 read_step
= WAITING_FOR_SYNC1
;
50 read_payload_length
= 0;
51 read_payload_counter
= 0;
53 // Initialize the state of the GPS module.
54 timepulse1_cfg
.tp_idx
= 0;
55 timepulse2_cfg
.tp_idx
= 1;
58 nav_status_changed
= false;
59 nav_timeutc_changed
= false;
60 nav_position_changed
= false;
61 cfg_timepulse1_changed
= false;
62 cfg_timepulse2_changed
= false;
63 cfg_prt_uart_changed
= false;
64 msg_ack_changed
= false;
65 timestamp_init
= false;
66 timestamp_empty
= true;
67 timestamp_buf_empty
= true;
68 timestamp_race_error
= false;
70 next_timepulse
.tow_ms
= 0;
71 next_timepulse
.tow_sub_ms
= 0;
72 next_timepulse
.q_err
= 0;
73 next_timepulse
.week
= 0;
74 next_timepulse
.flags
.time_base
= 1;
75 next_timepulse
.flags
.utc
= 0;
76 next_timepulse
.reserved1
= 0;
81 GpsLea6T::start_serial(uint32_t baud_rate
)
83 serial_port
.begin(baud_rate
);
85 // Test the connection by polling the baud rate.
86 bool is_working
= poll_baud_rate_blocking(baud_rate
);
91 GpsLea6T::stop_serial()
99 GpsLea6T::disable_timepulse2_frequency(void)
101 timepulse2_cfg
.ant_cable_delay
= 0;
102 timepulse2_cfg
.freq_period
= 0;
103 timepulse2_cfg
.freq_period_lock
= 0;
104 timepulse2_cfg
.pulse_len_ratio
= 2147483648; //Be aware of the scaling factor 0.5^-32: 50% duty cycle = 0.5 * 0.5^-32
105 timepulse2_cfg
.pulse_len_ratio_lock
= 2147483648;
106 timepulse2_cfg
.user_config_delay
= 0;
107 timepulse2_cfg
.flags
.active
= 1;
108 timepulse2_cfg
.flags
.lock_gps_freq
= 1;
109 timepulse2_cfg
.flags
.locked_other_set
= 0;
110 timepulse2_cfg
.flags
.is_freq
= 1;
111 timepulse2_cfg
.flags
.is_length
= 0;
112 timepulse2_cfg
.flags
.align_to_tow
= 1;
113 timepulse2_cfg
.flags
.polarity
= 1;
114 timepulse2_cfg
.flags
.grid_utc_gps
= 0;
116 return send_cfg_tp5(1);
120 GpsLea6T::set_timepulse2_frequency(uint32_t freq
, uint32_t freq_lock
)
122 timepulse2_cfg
.ant_cable_delay
= 0;
123 timepulse2_cfg
.freq_period
= freq
;
124 timepulse2_cfg
.freq_period_lock
= freq_lock
;
125 timepulse2_cfg
.pulse_len_ratio
= 2147483648; //Be aware of the scaling factor 0.5^-32: 50% duty cycle = 0.5 * 0.5^-32
126 timepulse2_cfg
.pulse_len_ratio_lock
= 2147483648;
127 timepulse2_cfg
.user_config_delay
= 0;
128 timepulse2_cfg
.flags
.active
= 1;
129 timepulse2_cfg
.flags
.lock_gps_freq
= 1;
130 timepulse2_cfg
.flags
.locked_other_set
= 0;
131 timepulse2_cfg
.flags
.is_freq
= 1;
132 timepulse2_cfg
.flags
.is_length
= 0;
133 timepulse2_cfg
.flags
.align_to_tow
= 1;
134 timepulse2_cfg
.flags
.polarity
= 1;
135 timepulse2_cfg
.flags
.grid_utc_gps
= 0;
137 return send_cfg_tp5(1);
142 GpsLea6T::set_timepulse1_pps()
144 timepulse1_cfg
.ant_cable_delay
= 0;
145 timepulse1_cfg
.freq_period
= 1;
146 timepulse1_cfg
.freq_period_lock
= 1;
147 timepulse1_cfg
.pulse_len_ratio
= 500000; // The scaling only applies to the frequency.
148 timepulse1_cfg
.pulse_len_ratio_lock
= 100000;
149 timepulse1_cfg
.user_config_delay
= 0;
150 timepulse1_cfg
.flags
.active
= 1;
151 timepulse1_cfg
.flags
.lock_gps_freq
= 1;
152 timepulse1_cfg
.flags
.locked_other_set
= 1;
153 timepulse1_cfg
.flags
.is_freq
= 1;
154 timepulse1_cfg
.flags
.is_length
= 1;
155 timepulse1_cfg
.flags
.align_to_tow
= 1;
156 timepulse1_cfg
.flags
.polarity
= 1;
157 timepulse1_cfg
.flags
.grid_utc_gps
= 0;
159 return send_cfg_tp5(0);
163 GpsLea6T::set_uart1_baudrate(uint32_t baudrate
)
165 ubx_cfg_prt_uart_t msg
;
169 msg
.tx_ready
.pol
= 0;
170 msg
.tx_ready
.pin
= 0;
171 msg
.tx_ready
.thres
= 0;
172 msg
.mode
.reserved1
= 1;
173 msg
.mode
.charlen
= 3;
175 msg
.mode
.nstopbits
= 0;
176 msg
.mode
.unused0
= 0;
177 msg
.mode
.unused1
= 0;
178 msg
.mode
.unused2
= 0;
179 msg
.mode
.unused3
= 0;
180 msg
.baud_rate
= baudrate
;
181 msg
.in_proto_mask
.ubx
= 1;
182 msg
.in_proto_mask
.nmea
= 1;
183 msg
.in_proto_mask
.rtcm
= 1;
184 msg
.in_proto_mask
.unused0
= 0;
185 msg
.out_proto_mask
.ubx
= 1;
186 msg
.out_proto_mask
.nmea
= 1;
187 msg
.out_proto_mask
.rtcm
= 1;
188 msg
.out_proto_mask
.unused0
= 0;
192 send_ublox_message(UBX_CLASS_CFG
, UBX_CFG_ID_PRT
, &msg
, sizeof(msg
));
193 //confirmed = wait_for_cfg_ack(UBX_CLASS_CFG, UBX_CFG_ID_PRT);
198 GpsLea6T::get_timepulse2_frequency()
200 return timepulse2_cfg
.pulse_len_ratio
;
204 GpsLea6T::get_timepulse2_frequency_locked()
206 return timepulse2_cfg
.freq_period_lock
;
210 GpsLea6T::get_gps_fix()
212 return (gpsfix_t
)navigation_status
.gps_fix
;
216 GpsLea6T::get_gps_fix_ok()
218 if (navigation_status
.flags
.gps_fix_ok
== 1)
233 uint8_t bytes_available
= 0;
236 bytes_available
= serial_port
.available();
237 for (uint16_t k
= 0; k
< bytes_available
; k
++)
239 data
= serial_port
.read();
245 //debug_print("SYNC1_FOUND");
246 if (data
== SYNC_CHAR_2
)
248 read_step
= SYNC2_FOUND
;
249 //debug_print("SYNC2_FOUND");
252 // If the second sync char is wrong, reset to initial mode and
253 // continue the processing of the current data.
254 read_step
= WAITING_FOR_SYNC1
;
255 //debug_print("SYNC2 doesn't match.");
256 case WAITING_FOR_SYNC1
:
257 if (data
== SYNC_CHAR_1
)
259 read_step
= SYNC1_FOUND
;
263 read_msg_class
= data
;
266 read_step
= CLASS_READ
;
267 //debug_print("CLASS_READ");
272 read_ckb
+= read_cka
;
273 read_step
= MSGID_READ
;
274 //debug_print("MSGID_READ");
277 read_payload_length
= data
;
279 read_ckb
+= read_cka
;
280 read_step
= LENGTH_BYTE1_READ
;
281 //debug_print("LENGTH_BYTE1_READ");
283 case LENGTH_BYTE1_READ
:
284 read_payload_length
+= (uint16_t)(data
<< 8);
286 read_ckb
+= read_cka
;
287 read_payload_counter
= 0;
288 read_step
= LENGTH_BYTE2_READ
;
289 //debug_print("LENGTH_BYTE2_READ");
290 if(read_payload_length
> 256) {
291 //debug_print("Too large payload");
292 read_payload_length
= 0;
293 read_step
= WAITING_FOR_SYNC1
;
297 case LENGTH_BYTE2_READ
:
298 //debug_print("LENGTH_BYTE2_READ");
299 if (read_payload_counter
< sizeof(read_buffer
))
301 read_buffer
.bytes
[read_payload_counter
] = data
;
303 if (++read_payload_counter
== read_payload_length
)
305 read_step
= PAYLOAD_READ
;
306 //debug_print("PAYLOAD_READ");
309 read_ckb
+= read_cka
;
312 if (read_cka
!= data
)
314 //debug_print("Bad cka.");
315 read_step
= WAITING_FOR_SYNC1
;
319 //debug_print("CKA_OK");
322 read_step
= WAITING_FOR_SYNC1
;
325 //debug_print("Bad ckb.");
328 //debug_print("CKB_OK");
329 parsed
= parse_ubx();
337 GpsLea6T::parse_ubx()
340 if (read_msg_class
== UBX_CLASS_ACK
)
346 msg_ack
= read_buffer
.ack_ack
;
347 msg_ack_changed
= true;
349 usb_serial.println("Received UBX_ACK_ID_ACK.");
350 usb_serial.println(msg_ack.cls_id);
351 usb_serial.println(msg_ack.msg_id);
352 usb_serial.println("----");
356 handle_unexpected_msg();
359 else if (read_msg_class
== UBX_CLASS_CFG
)
364 //debug_print("Parsing UBX_CFG_ID_TP5");
365 //timepulse2_cfg.tp_idx = read_buffer.cfg_tp5.tp_idx;
366 if (read_buffer
.cfg_tp5
.tp_idx
== 0)
368 timepulse_cfg
= read_buffer
.cfg_tp5
;
370 cfg_timepulse1_changed
= true;
372 else if (read_buffer
.cfg_tp5
.tp_idx
== 1)
374 timepulse2_cfg
= read_buffer
.cfg_tp5
;
376 cfg_timepulse2_changed
= true;
380 //usb_serial.println("Parsing UBX_CFG_ID_PRT");
381 cfg_prt_uart
= read_buffer
.cfg_prt_uart
;
383 cfg_prt_uart_changed
= true;
386 handle_unexpected_msg();
389 else if (read_msg_class
== UBX_CLASS_NAV
)
393 case UBX_NAV_ID_STATUS
:
394 //debug_print("Parsing UBX_NAV_STATUS");
395 navigation_status
= read_buffer
.nav_status
;
396 nav_status_changed
= true;
399 case UBX_NAV_ID_TIMEUTC
:
400 //debug_print("Parsing UBX_NAV_TIMEUTC");
401 timeutc
= read_buffer
.nav_timeutc
;
402 nav_timeutc_changed
= true;
405 case UBX_NAV_ID_POSLLH
:
406 //debug_print("Parsing UBX_NAV_TIMEUTC");
407 nav_position
= read_buffer
.nav_posllh
;
408 nav_position_changed
= true;
412 handle_unexpected_msg();
416 else if (read_msg_class
== UBX_CLASS_MON
)
420 else if (read_msg_class
== UBX_CLASS_TIM
)
425 //debug_print("Parsing UBX_TIM_TP");
426 if (!timestamp_buf_empty
)
428 timestamp_race_error
= true;
430 next_timepulse_buf
= read_buffer
.tim_tp
;
431 timestamp_buf_empty
= false;
434 timestamp_init
= true;
436 //usb_serial.println(read_buffer.tim_tp.week);
437 //usb_serial.println(read_buffer.tim_tp.tow_ms);
438 //usb_serial.println(read_buffer.tim_tp.tow_sub_ms);
442 handle_unexpected_msg();
447 handle_unexpected_msg();
455 GpsLea6T::handle_unexpected_msg()
457 debug_print("Handling an unexpected UBX message.");
462 GpsLea6T::send_ublox_message(uint8_t msg_class
, uint8_t msg_id
, void *msg
, uint8_t length
)
465 uint8_t ck_a
=0, ck_b
=0;
466 header
.sync_char1
= SYNC_CHAR_1
;
467 header
.sync_char2
= SYNC_CHAR_2
;
468 header
.msg_class
= msg_class
;
469 header
.msg_id
= msg_id
;
470 header
.payload_length
= length
;
472 // Update the checksum using the relevant header part.
473 update_ubx_checksum((uint8_t *)&header
.msg_class
, sizeof(header
) - 2, ck_a
, ck_b
);
475 // Update the checksum using the payload.
476 update_ubx_checksum((uint8_t *)msg
, length
, ck_a
, ck_b
);
478 // Send the paket to the serial port.
479 serial_port
.write((const uint8_t *)&header
, sizeof(header
));
480 serial_port
.write((const uint8_t *)msg
, length
);
481 serial_port
.write((const uint8_t *)&ck_a
, 1);
482 serial_port
.write((const uint8_t *)&ck_b
, 1);
487 GpsLea6T::disable_default_messages()
489 uint8_t ack_status
= 0;
490 // Disable all default messages on the target which is used to send this
492 if (send_cfg_msg(NMEA_CLASS_STD
, NMEA_STD_ID_GGA
, 0))
493 ack_status
|= 1UL << 0;
495 if (send_cfg_msg(NMEA_CLASS_STD
, NMEA_STD_ID_GLL
, 0))
496 ack_status
|= 1UL << 1;
498 if (send_cfg_msg(NMEA_CLASS_STD
, NMEA_STD_ID_GSA
, 0))
499 ack_status
|= 1UL << 2;
501 if (send_cfg_msg(NMEA_CLASS_STD
, NMEA_STD_ID_GSV
, 0))
502 ack_status
|= 1UL << 3;
504 if (send_cfg_msg(NMEA_CLASS_STD
, NMEA_STD_ID_RMC
, 0))
505 ack_status
|= 1UL << 4;
507 if (send_cfg_msg(NMEA_CLASS_STD
, NMEA_STD_ID_VTG
, 0))
508 ack_status
|= 1UL << 5;
510 if (send_cfg_msg(NMEA_CLASS_STD
, NMEA_STD_ID_ZDA
, 0))
511 ack_status
|= 1UL << 6;
513 // Disable the NMEA_STD_ID_TXT output using the CFG-INF message.
518 msg
.inf_msg_mask
.serial_1
.error
= 0;
519 msg
.inf_msg_mask
.serial_1
.warning
= 0;
520 msg
.inf_msg_mask
.serial_1
.notice
= 0;
521 msg
.inf_msg_mask
.serial_1
.debug
= 0;
522 msg
.inf_msg_mask
.serial_1
.test
= 0;
523 msg
.inf_msg_mask
.serial_1
.reserved
= 0;
524 send_ublox_message(UBX_CLASS_CFG
, UBX_CFG_ID_INF
, &msg
, sizeof(msg
));
525 if(wait_for_cfg_ack(UBX_CLASS_CFG
, UBX_CFG_ID_INF
))
526 ack_status
|= 1UL << 7;
531 bool GpsLea6T::enable_message(uint8_t msg_class
, uint8_t msg_id
, uint8_t rate
)
533 // Enable a message on the current target which is used to send this
535 bool confirmed
= send_cfg_msg(msg_class
, msg_id
, rate
);
540 GpsLea6T::poll_cfg_prt_uart()
542 send_ublox_message(UBX_CLASS_CFG
, UBX_CFG_ID_PRT
, NULL
, 0);
546 GpsLea6T::poll_cfg_msg(uint8_t msg_class
, uint8_t msg_id
)
548 ubx_cfg_msg_rate_poll_t msg
;
549 msg
.msg_class
= msg_class
;
551 send_ublox_message(UBX_CLASS_CFG
, UBX_CFG_ID_MSG
, &msg
, sizeof(msg
));
555 GpsLea6T::send_cfg_msg(uint8_t msg_class
, uint8_t msg_id
, uint8_t rate
)
557 bool confirmed
= false;
558 ubx_cfg_msg_rate_t msg
;
559 msg
.msg_class
= msg_class
;
562 send_ublox_message(UBX_CLASS_CFG
, UBX_CFG_ID_MSG
, &msg
, sizeof(msg
));
563 confirmed
= wait_for_cfg_ack(UBX_CLASS_CFG
, UBX_CFG_ID_MSG
);
568 GpsLea6T::poll_cfg_tp5(uint8_t timepulse_id
)
570 ubx_cfg_tp5_poll_t msg
;
571 msg
.tp_idx
= timepulse_id
;
572 send_ublox_message(UBX_CLASS_CFG
, UBX_CFG_ID_TP5
, &msg
, sizeof(msg
));
576 GpsLea6T::send_cfg_tp5(uint8_t tp_idx
)
580 send_ublox_message(UBX_CLASS_CFG
, UBX_CFG_ID_TP5
, &timepulse1_cfg
, sizeof(timepulse1_cfg
));
582 else if (tp_idx
== 1)
584 send_ublox_message(UBX_CLASS_CFG
, UBX_CFG_ID_TP5
, &timepulse2_cfg
, sizeof(timepulse2_cfg
));
587 return wait_for_cfg_ack(UBX_CLASS_CFG
, UBX_CFG_ID_TP5
);
592 GpsLea6T::poll_nav_status()
594 send_ublox_message(UBX_CLASS_NAV
, UBX_NAV_ID_STATUS
, NULL
, 0);
599 GpsLea6T::poll_nav_posllh()
601 send_ublox_message(UBX_CLASS_NAV
, UBX_NAV_ID_POSLLH
, NULL
, 0);
606 GpsLea6T::wait_for_cfg_ack(uint8_t msg_class
, uint8_t msg_id
)
608 unsigned timeout
= 1000; // The timeout in milliseconds.
609 unsigned long start
= 0;
610 unsigned long current
= 0;
611 unsigned long elapsed
= 0;
612 uint8_t gps_byte_available
= 0;
616 while (elapsed
< timeout
)
618 gps_byte_available
= serial_port
.available();
619 if (gps_byte_available
> 0)
626 if ((msg_ack
.cls_id
== msg_class
) && (msg_ack
.msg_id
== msg_id
))
628 usb_serial
.println("Message acknowledged.");
629 msg_ack_changed
= false;
634 // TODO: Handle the reception of ACK messages not fitting the
635 // required msg_class and msg_id.
636 msg_ack_changed
= false;
640 elapsed
= current
- start
;
642 usb_serial
.println("GPS ACK Timeout");
643 usb_serial
.println(msg_class
);
644 usb_serial
.println(msg_id
);
645 usb_serial
.println("#####");
650 GpsLea6T::detect_baud_rate(uint32_t baud_1
, uint32_t baud_2
)
652 uint32_t active_baud
= 0;
654 // Try the first baud rate.
655 if (test_baud_rate(baud_1
))
656 active_baud
= cfg_prt_uart
.baud_rate
;
657 else if (test_baud_rate(baud_2
))
658 active_baud
= cfg_prt_uart
.baud_rate
;
664 GpsLea6T::test_baud_rate(uint32_t baud_rate
)
666 bool confirmed
= false;
669 serial_port
.begin(baud_rate
);
671 confirmed
= poll_baud_rate_blocking(baud_rate
);
678 GpsLea6T::poll_baud_rate_blocking(uint32_t baud_rate
)
680 unsigned timeout
= 3000; // The timeout in milliseconds.
681 unsigned long start
= 0;
682 unsigned long current
= 0;
683 unsigned long elapsed
= 0;
684 uint8_t gps_byte_available
= 0;
688 while (elapsed
< timeout
)
690 gps_byte_available
= serial_port
.available();
691 if (gps_byte_available
> 0)
695 if (cfg_prt_uart_changed
)
697 cfg_prt_uart_changed
= false;
698 usb_serial
.println("Working baud rate found.");
699 usb_serial
.println(baud_rate
);
703 elapsed
= current
- start
;
705 usb_serial
.println("Baud Timeout");
706 usb_serial
.println(baud_rate
);
707 usb_serial
.println("#####");