10 #include <sys/types.h>
11 #include <sys/socket.h>
12 #include <netinet/in.h>
13 #include <arpa/inet.h>
21 #include "transform.h"
22 #include "projection.h"
23 #include "statusbar.h"
31 static void disable_watch(struct vehicle *this_);
32 static void enable_watch(struct vehicle *this_);
34 /* #define INTERPOLATION_TIME 50 */
37 void (*func)(struct vehicle *, void *data);
48 int status; // Do we have a fix? 0=no 1=yes, without DGPS 2=yes, with DGPS
55 struct coord current_pos;
63 struct gps_data_t *gps;
65 #define BUFFER_SIZE 256
66 char buffer[BUFFER_SIZE];
69 struct callback_list *cbl;
70 struct vehicle *child;
71 struct callback *child_cb;
76 struct sockaddr_in rem;
77 struct log *nmea_log, *gpx_log, *textfile_log;
82 // FIXME : this_ is an ugly hack (dixit cp15 ;) )
83 struct vehicle *vehicle_last;
85 #if INTERPOLATION_TIME
87 vehicle_timer(gpointer t)
89 struct vehicle *this_=t;
90 /* if (this_->timer_count++ < 1000/INTERPOLATION_TIME) { */
91 if (this_->delta.x || this_->delta.y) {
92 this_->curr.x+=this_->delta.x;
93 this_->curr.y+=this_->delta.y;
94 this_->current_pos.x=this_->curr.x;
95 this_->current_pos.y=this_->curr.y;
96 if (this_->callback_func)
97 (*this_->callback_func)(this_, this_->callback_data);
105 vehicle_projection(struct vehicle *this_)
107 return projection_mg;
111 vehicle_pos_get(struct vehicle *this_)
113 return &this_->current_pos;
117 vehicle_speed_get(struct vehicle *this_)
119 return &this_->speed;
123 vehicle_height_get(struct vehicle *this_)
125 return &this_->height;
128 vehicle_dir_get(struct vehicle *this_)
134 vehicle_status_get(struct vehicle *this_)
136 return &this_->status;
140 vehicle_sats_get(struct vehicle *this_)
146 vehicle_sats_used_get(struct vehicle *this_)
148 return &this_->sats_used;
152 vehicle_pdop_get(struct vehicle *this_)
158 vehicle_set_position(struct vehicle *this_, struct coord *pos)
160 this_->current_pos=*pos;
161 this_->curr.x=this_->current_pos.x;
162 this_->curr.y=this_->current_pos.y;
165 callback_list_call_1(this_->cbl, this_);
169 enable_watch_timer(gpointer t)
171 struct vehicle *this_=t;
177 // FIXME Should this_ function be static ?
179 vehicle_set_navit(struct vehicle *this_,struct navit *nav) {
180 dbg(0,"vehicle_set_navit called\n");
185 vehicle_parse_gps(struct vehicle *this_, char *buffer)
188 double lat,lng,scale,speed;
190 int len=strlen(buffer);
191 unsigned char csum=0;
193 dbg(1, "buffer='%s' ", buffer);
194 if (this_->nmea_log) {
195 log_write(this_->nmea_log, buffer, len);
196 log_write(this_->nmea_log, "\n", 1);
200 dbg(0, "too short\n");
203 if (buffer[len-1] == '\r' || buffer[len-1] == '\n')
208 if (buffer[0] != '$') {
209 dbg(0, "no leading $\n");
212 if (buffer[len-3] != '*') {
216 for (i = 1 ; i < len-3 ; i++) {
217 csum ^= (unsigned char)(buffer[i]);
219 if (!sscanf(buffer+len-2, "%x", &bcsum)) {
220 dbg(0, "no checksum\n");
224 dbg(0, "wrong checksum\n");
228 if (!strncmp(buffer,"$GPGGA",6)) {
229 /* $GPGGA,184424.505,4924.2811,N,01107.8846,E,1,05,2.5,408.6,M,,,,0000*0C
230 UTC of Fix,Latitude,N/S,Longitude,E/W,Quality,Satelites,HDOP,Altitude,"M"
236 while (*p && *p != ',')
242 sscanf(item[2],"%lf",&lat);
243 this_->geo.lat=floor(lat/100);
244 lat-=this_->geo.lat*100;
245 this_->geo.lat+=lat/60;
247 sscanf(item[4],"%lf",&lng);
248 this_->geo.lng=floor(lng/100);
249 lng-=this_->geo.lng*100;
250 this_->geo.lng+=lng/60;
252 sscanf(item[6],"%d",&this_->status);
253 sscanf(item[7],"%d",&this_->sats);
254 sscanf(item[9],"%lf",&this_->height);
256 if (this_->gpx_log) {
258 sprintf(buffer,"<trkpt lat=\"%f\" lon=\"%f\" />\n",this_->geo.lat,this_->geo.lng);
259 log_write(this_->gpx_log, buffer, strlen(buffer));
262 if (this_->textfile_log) {
264 sprintf(buffer,"%f %f type=trackpoint\n",this_->geo.lng,this_->geo.lat);
265 log_write(this_->textfile_log, buffer, strlen(buffer));
267 transform_from_geo(projection_mg, &this_->geo, &this_->current_pos);
269 this_->curr.x=this_->current_pos.x;
270 this_->curr.y=this_->current_pos.y;
271 this_->timer_count=0;
272 callback_list_call_1(this_->cbl, this_);
273 if (this_->is_file) {
274 disable_watch(this_);
275 g_timeout_add(1000, enable_watch_timer, this_);
278 if (!strncmp(buffer,"$GPVTG",6)) {
279 /* $GPVTG,143.58,T,,M,0.26,N,0.5,K*6A
280 Course Over Ground Degrees True,"T",Course Over Ground Degrees Magnetic,"M",
281 Speed in Knots,"N","Speed in KM/H","K",*CHECKSUM */
287 while (*p && *p != ',')
292 sscanf(item[1],"%lf",&this_->dir);
293 sscanf(item[7],"%lf",&this_->speed);
295 scale=transform_scale(this_->current_pos.y);
296 speed=this_->speed+(this_->speed-this_->speed_last)/2;
297 #ifdef INTERPOLATION_TIME
298 this_->delta.x=sin(M_PI*this_->dir/180)*speed*scale/3600*INTERPOLATION_TIME;
299 this_->delta.y=cos(M_PI*this_->dir/180)*speed*scale/3600*INTERPOLATION_TIME;
301 this_->speed_last=this_->speed;
303 if (!strncmp(buffer,"$GPRMC",6)) {
304 /* $GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A */
305 /* Time,Active/Void,lat,N/S,long,W/E,speed in knots,track angle,date,magnetic variation */
310 while (*p && *p != ',')
315 sscanf(item[8],"%lf",&this_->dir);
316 sscanf(item[7],"%lf",&this_->speed);
317 this_->speed *= 1.852;
318 scale=transform_scale(this_->current_pos.y);
319 speed=this_->speed+(this_->speed-this_->speed_last)/2;
320 #ifdef INTERPOLATION_TIME
321 this_->delta.x=sin(M_PI*this_->dir/180)*speed*scale/3600*INTERPOLATION_TIME;
322 this_->delta.y=cos(M_PI*this_->dir/180)*speed*scale/3600*INTERPOLATION_TIME;
324 this_->speed_last=this_->speed;
330 vehicle_gps_callback(struct gps_data_t *data, char *buf, size_t len, int level)
332 // If data->fix.speed is NAN, then the drawing gets jumpy.
333 if(isnan(data->fix.speed)){
337 struct vehicle *this_=vehicle_last;
339 #if INTERPOLATION_TIME
340 if (! (data->set & TIME_SET)) {
343 data->set &= ~TIME_SET;
344 if (this_->time == data->fix.time)
346 this_->time=data->fix.time;
348 if (data->set & SPEED_SET) {
349 this_->speed_last=this_->speed;
350 this_->speed=data->fix.speed*3.6;
351 data->set &= ~SPEED_SET;
353 if (data->set & TRACK_SET) {
354 speed=this_->speed+(this_->speed-this_->speed_last)/2;
355 this_->dir=data->fix.track;
356 scale=transform_scale(this_->current_pos.y);
357 #ifdef INTERPOLATION_TIME
358 this_->delta.x=sin(M_PI*this_->dir/180)*speed*scale/3600*INTERPOLATION_TIME;
359 this_->delta.y=cos(M_PI*this_->dir/180)*speed*scale/3600*INTERPOLATION_TIME;
361 data->set &= ~TRACK_SET;
363 if (data->set & LATLON_SET) {
364 this_->geo.lat=data->fix.latitude;
365 this_->geo.lng=data->fix.longitude;
366 transform_from_geo(projection_mg, &this_->geo, &this_->current_pos);
367 this_->curr.x=this_->current_pos.x;
368 this_->curr.y=this_->current_pos.y;
369 this_->timer_count=0;
370 callback_list_call_1(this_->cbl, this_);
371 data->set &= ~LATLON_SET;
373 if (data->set & ALTITUDE_SET) {
374 this_->height=data->fix.altitude;
375 data->set &= ~ALTITUDE_SET;
377 if (data->set & SATELLITE_SET) {
378 this_->sats=data->satellites;
379 data->set &= ~SATELLITE_SET;
380 /* FIXME : the USED_SET check does not work yet. */
381 this_->sats_used=data->satellites_used;
383 if (data->set & STATUS_SET) {
384 this_->status=data->status;
385 data->set &= ~STATUS_SET;
387 if(data->set & PDOP_SET){
388 printf("pdop : %g\n",data->pdop);
395 vehicle_close(struct vehicle *this_)
400 g_io_channel_shutdown(this_->iochan,0,&error);
403 gps_close(this_->gps);
412 int magic __attribute__ ((packed));
416 int x __attribute__ ((packed));
417 int y __attribute__ ((packed));
422 } __attribute__ ((packed)) ;
425 vehicle_udp_recv(struct vehicle *this_)
430 dbg(2,"enter this_=%p\n",this_);
431 size=recv(this_->fd, &pkt, 15, 0);
432 if (pkt.magic == this_->magic) {
433 dbg(3,"magic 0x%x size=%d\n", pkt.magic, size);
434 this_->current_pos.x=pkt.u.pos.x;
435 this_->current_pos.y=pkt.u.pos.y;
436 this_->speed=pkt.u.pos.speed;
437 this_->dir=pkt.u.pos.dir*2;
438 callback_list_call_1(this_->cbl, this_);
443 vehicle_udp_update(struct vehicle *this_, struct vehicle *child)
445 struct coord *pos=&child->current_pos;
447 int speed=child->speed;
448 int dir=child->dir/2;
451 pkt.magic=this_->magic;
455 pkt.u.pos.speed=speed;
457 sendto(this_->fd, &pkt, 15, 0, (struct sockaddr *)&this_->rem, sizeof(this_->rem));
458 this_->current_pos=child->current_pos;
459 this_->speed=child->speed;
460 this_->dir=child->dir;
461 callback_list_call_1(this_->cbl, this_);
465 vehicle_udp_query(void *data)
467 struct vehicle *this_=(struct vehicle *)data;
469 dbg(2,"enter this_=%p\n", this_);
470 pkt.magic=this_->magic;
472 sendto(this_->fd, &pkt, 5, 0, (struct sockaddr *)&this_->rem, sizeof(this_->rem));
478 vehicle_udp_open(struct vehicle *this_)
480 char *host,*child,*url,*colon;
482 url=g_strdup(this_->url);
483 colon=index(url+6,':');
484 struct sockaddr_in lcl;
486 if (! colon || sscanf(colon+1,"%d/%i/%d", &port, &this_->magic, &this_->interval) != 3) {
487 g_warning("Wrong syntax in %s\n", this_->url);
492 this_->fd=socket(PF_INET, SOCK_DGRAM, 0);
494 memset(&lcl, 0, sizeof(lcl));
495 lcl.sin_family = AF_INET;
497 this_->rem.sin_family = AF_INET;
498 inet_aton(host, &this_->rem.sin_addr);
499 this_->rem.sin_port=htons(port);
501 bind(this_->fd, (struct sockaddr *)&lcl, sizeof(lcl));
502 child=index(colon+1,' ');
506 dbg(3,"child=%s\n", child);
507 this_->child=vehicle_new(child);
508 this_->child_cb=callback_new_1(callback_cast(vehicle_udp_update), this_);
509 vehicle_callback_add(this_->child, this_->child_cb);
512 vehicle_udp_query(this_);
513 g_timeout_add(this_->interval*1000, vehicle_udp_query, this_);
520 vehicle_demo_timer (struct vehicle *this)
522 struct route_path_coord_handle *h;
524 dbg(1,"###### Entering simulation loop\n");
526 dbg(1,"vehicle->navit is not set. Can't simulate\n");
530 // <cp15> Then check whether the route is set, if not return TRUE
531 struct route * vehicle_route=navit_get_route(this->navit);
533 dbg(1,"navit_get_route NOK\n");
537 h=route_path_coord_open(vehicle_route);
539 dbg(1,"navit_path_coord_open NOK\n");
542 c=route_path_coord_get(h);
543 dbg(1,"current pos=%p\n", c);
545 dbg(1,"current pos=0x%x,0x%x\n", c->x, c->y);
546 c=route_path_coord_get(h);
547 dbg(1,"next pos=%p\n", c);
549 dbg(1,"next pos=0x%x,0x%x\n", c->x, c->y);
550 vehicle_set_position(this,c);
556 vehicle_open(struct vehicle *this_)
563 struct gps_data_t *gps=NULL;
566 if (! strncmp(this_->url,"file:",5)) {
567 fd=open(this_->url+5,O_RDONLY|O_NDELAY);
569 g_warning("Failed to open %s", this_->url);
572 stat(this_->url+5, &st);
573 if (S_ISREG (st.st_mode)) {
578 cfsetispeed(&tio, B4800);
579 cfsetospeed(&tio, B4800);
582 tcsetattr(fd, TCSANOW, &tio);
585 } else if (! strncmp(this_->url,"pipe:",5)) {
586 this_->file=popen(this_->url+5, "r");
589 g_warning("Failed to open %s", this_->url);
592 fd=fileno(this_->file);
593 } else if (! strncmp(this_->url,"gpsd://",7)) {
595 url_=g_strdup(this_->url);
596 colon=index(url_+7,':');
599 gps=gps_open(url_+7,colon+1);
601 gps=gps_open(this_->url+7,NULL);
604 g_warning("Failed to connect to %s", this_->url);
607 gps_query(gps, "w+x\n");
608 gps_set_raw_hook(gps, vehicle_gps_callback);
612 g_warning("No support for gpsd compiled in\n");
615 } else if (! strncmp(this_->url,"udp://",6)) {
616 vehicle_udp_open(this_);
618 } else if (! strncmp(this_->url,"demo://",7)) {
619 dbg(0,"Creating a demo vehicle\n");
620 g_timeout_add(1000, (GSourceFunc) vehicle_demo_timer, this_);
622 this_->iochan=g_io_channel_unix_new(fd);
629 vehicle_track(GIOChannel *iochan, GIOCondition condition, gpointer t)
631 struct vehicle *this_=t;
635 dbg(1,"enter condition=%d\n", condition);
636 if (condition == G_IO_IN) {
640 gps_poll(this_->gps);
646 vehicle_udp_recv(this_);
649 size=read(g_io_channel_unix_get_fd(iochan), this_->buffer+this_->buffer_pos, BUFFER_SIZE-this_->buffer_pos-1);
651 vehicle_close(this_);
655 this_->buffer_pos+=size;
656 this_->buffer[this_->buffer_pos]='\0';
657 dbg(1,"size=%d pos=%d buffer='%s'\n", size, this_->buffer_pos, this_->buffer);
659 while ((tok=index(str, '\n'))) {
661 dbg(1,"line='%s'\n", str);
662 vehicle_parse_gps(this_, str);
665 if (str != this_->buffer) {
666 size=this_->buffer+this_->buffer_pos-str;
667 memmove(this_->buffer, str, size+1);
668 this_->buffer_pos=size;
669 dbg(1,"now pos=%d buffer='%s'\n", this_->buffer_pos, this_->buffer);
670 } else if (this_->buffer_pos == BUFFER_SIZE-1) {
683 enable_watch(struct vehicle *this_)
685 this_->watch=g_io_add_watch(this_->iochan, G_IO_IN|G_IO_ERR|G_IO_HUP, vehicle_track, this_);
689 disable_watch(struct vehicle *this_)
691 g_source_remove(this_->watch);
695 vehicle_new(const char *url)
697 struct vehicle *this_;
698 this_=g_new0(struct vehicle,1);
700 this_->cbl=callback_list_new();
701 this_->url=g_strdup(url);
705 this_->current_pos.x=0x130000;
706 this_->current_pos.y=0x600000;
707 this_->curr.x=this_->current_pos.x;
708 this_->curr.y=this_->current_pos.y;
711 #if INTERPOLATION_TIME
712 g_timeout_add(INTERPOLATION_TIME, vehicle_timer, this_);
719 vehicle_callback_add(struct vehicle *this_, struct callback *cb)
721 callback_list_add(this_->cbl, cb);
725 vehicle_callback_remove(struct vehicle *this_, struct callback *cb)
727 callback_list_remove(this_->cbl, cb);
732 vehicle_add_log(struct vehicle *this_, struct log *log, struct attr **attrs)
735 type=attr_search(attrs, NULL, attr_type);
738 if (!strcmp(type->u.str,"nmea")) {
741 this_->child->nmea_log=log;
743 } else if (!strcmp(type->u.str,"gpx")) {
744 char *header="<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<gpx version=\"1.0\" creator=\"Navit http://navit.sourceforge.net\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"http://www.topografix.com/GPX/1/0\" xsi:schemaLocation=\"http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd\">\n<trk>\n<trkseg>\n";
745 char *trailer="</trkseg>\n</trk>\n</gpx>\n";
748 this_->child->gpx_log=log;
749 log_set_header(log,header,strlen(header));
750 log_set_trailer(log,trailer,strlen(trailer));
751 } else if (!strcmp(type->u.str,"textfile")) {
752 char *header="type=track\n";
753 this_->textfile_log=log;
755 this_->child->textfile_log=log;
756 log_set_header(log,header,strlen(header));
763 vehicle_destroy(struct vehicle *this_)
765 vehicle_close(this_);
766 callback_list_destroy(this_->cbl);