7467ad25901cf2f88839ca39aedc07f4d368e5f1
[navit-package] / src / vehicle.c
1 #include "config.h"
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <termios.h>
6 #include <unistd.h>
7 #include <fcntl.h>
8 #include <math.h>
9 #include <sys/stat.h>
10 #include <sys/types.h>
11 #include <sys/socket.h>
12 #include <netinet/in.h>
13 #include <arpa/inet.h>
14 #include <glib.h>
15 #ifdef HAVE_LIBGPS
16 #include <gps.h>
17 #endif
18 #include "debug.h"
19 #include "coord.h"
20 #include "callback.h"
21 #include "transform.h"
22 #include "projection.h"
23 #include "statusbar.h"
24 #include "navit.h"
25 #include "item.h"
26 #include "log.h"
27 #include "vehicle.h"
28 #include "item.h"
29 #include "route.h"
30
31 static void disable_watch(struct vehicle *this_);
32 static void enable_watch(struct vehicle *this_);
33
34  /* #define INTERPOLATION_TIME 50 */
35
36 struct callback {
37         void (*func)(struct vehicle *, void *data);
38         void *data;
39 };
40
41 struct vehicle {
42         char *url;
43         GIOChannel *iochan;
44         guint watch;
45         int is_file;
46         int is_pipe;
47         int timer_count;
48         int status; // Do we have a fix? 0=no 1=yes, without DGPS 2=yes, with DGPS
49         int sats,sats_used;
50         struct coord_geo geo;
51         double height;
52         double dir,speed;
53         double time;
54         double pdop;
55         struct coord current_pos;
56         struct coord_d curr;
57         struct coord_d delta;
58
59         double speed_last;
60         int fd;
61         FILE *file;
62 #ifdef HAVE_LIBGPS
63         struct gps_data_t *gps;
64 #endif
65 #define BUFFER_SIZE 256
66         char buffer[BUFFER_SIZE];
67         int buffer_pos;
68
69         struct callback_list *cbl;
70         struct vehicle *child;
71         struct callback *child_cb;
72
73         int magic;
74         int is_udp;
75         int interval;
76         struct sockaddr_in rem;
77         struct log *nmea_log, *gpx_log, *textfile_log;
78
79         struct navit *navit;
80 };
81
82 // FIXME : this_ is an ugly hack (dixit cp15 ;) )
83 struct vehicle *vehicle_last;
84
85 #if INTERPOLATION_TIME
86 static int
87 vehicle_timer(gpointer t)
88 {
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);
98                 }
99 /*      } */
100         return TRUE; 
101 }
102 #endif
103
104 enum projection
105 vehicle_projection(struct vehicle *this_)
106 {
107         return projection_mg;
108 }
109
110 struct coord *
111 vehicle_pos_get(struct vehicle *this_)
112 {
113         return &this_->current_pos;
114 }
115
116 double *
117 vehicle_speed_get(struct vehicle *this_)
118 {
119         return &this_->speed;
120 }
121
122 double *
123 vehicle_height_get(struct vehicle *this_)
124 {
125         return &this_->height;
126 }
127 double *
128 vehicle_dir_get(struct vehicle *this_)
129 {
130         return &this_->dir;
131 }
132
133 int *
134 vehicle_status_get(struct vehicle *this_)
135 {
136         return &this_->status;
137 }
138
139 int *
140 vehicle_sats_get(struct vehicle *this_)
141 {
142         return &this_->sats;
143 }
144
145 int *
146 vehicle_sats_used_get(struct vehicle *this_)
147 {
148         return &this_->sats_used;
149 }
150
151 double *
152 vehicle_pdop_get(struct vehicle *this_)
153 {
154         return &this_->pdop;
155 }
156
157 void
158 vehicle_set_position(struct vehicle *this_, struct coord *pos)
159 {
160         this_->current_pos=*pos;
161         this_->curr.x=this_->current_pos.x;
162         this_->curr.y=this_->current_pos.y;
163         this_->delta.x=0;
164         this_->delta.y=0;
165         callback_list_call_1(this_->cbl, this_);
166 }
167
168 static int
169 enable_watch_timer(gpointer t)
170 {
171         struct vehicle *this_=t;
172         enable_watch(this_);
173         
174         return FALSE;
175 }
176
177 // FIXME Should this_ function be static ?
178 void
179 vehicle_set_navit(struct vehicle *this_,struct navit *nav) {
180         dbg(0,"vehicle_set_navit called\n");
181         this_->navit=nav;
182 }
183
184 static void
185 vehicle_parse_gps(struct vehicle *this_, char *buffer)
186 {
187         char *p,*item[16];
188         double lat,lng,scale,speed;
189         int i,bcsum;
190         int len=strlen(buffer);
191         unsigned char csum=0;
192
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);
197         }
198         for (;;) {
199                 if (len < 4) {
200                         dbg(0, "too short\n");
201                         return;
202                 }
203                 if (buffer[len-1] == '\r' || buffer[len-1] == '\n')
204                         buffer[--len]='\0';
205                 else
206                         break;
207         }
208         if (buffer[0] != '$') {
209                 dbg(0, "no leading $\n");
210                 return;
211         }
212         if (buffer[len-3] != '*') {
213                 dbg(0, "no *XX\n");
214                 return;
215         }
216         for (i = 1 ; i < len-3 ; i++) {
217                 csum ^= (unsigned char)(buffer[i]);
218         }
219         if (!sscanf(buffer+len-2, "%x", &bcsum)) {
220                 dbg(0, "no checksum\n");
221                 return;
222         }
223         if (bcsum != csum) {
224                 dbg(0, "wrong checksum\n");
225                 return;
226         }
227
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"
231                 */
232                 i=0;
233                 p=buffer;
234                 while (i < 16) {
235                         item[i++]=p;
236                         while (*p && *p != ',') 
237                                 p++;    
238                         if (! *p) break;
239                         *p++='\0';
240                 }
241
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;
246
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;
251
252                 sscanf(item[6],"%d",&this_->status);
253                 sscanf(item[7],"%d",&this_->sats);
254                 sscanf(item[9],"%lf",&this_->height);
255         
256                 if (this_->gpx_log) {
257                         char buffer[256];
258                         sprintf(buffer,"<trkpt lat=\"%f\" lon=\"%f\" />\n",this_->geo.lat,this_->geo.lng);
259                         log_write(this_->gpx_log, buffer, strlen(buffer));
260                         
261                 }
262                 if (this_->textfile_log) {
263                         char buffer[256];
264                         sprintf(buffer,"%f %f type=trackpoint\n",this_->geo.lng,this_->geo.lat);
265                         log_write(this_->textfile_log, buffer, strlen(buffer));
266                 }
267                 transform_from_geo(projection_mg, &this_->geo, &this_->current_pos);
268                         
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_);
276                 }
277         }
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 */
282                 
283                 i=0;
284                 p=buffer;
285                 while (i < 16) {
286                         item[i++]=p;
287                         while (*p && *p != ',') 
288                                 p++;    
289                         if (! *p) break;
290                                 *p++='\0';
291                 }
292                 sscanf(item[1],"%lf",&this_->dir);
293                 sscanf(item[7],"%lf",&this_->speed);
294
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;
300 #endif
301                 this_->speed_last=this_->speed;
302         }
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 */
306                 i=0;
307                 p=buffer;
308                 while (i < 16) {
309                         item[i++]=p;
310                         while (*p && *p != ',') 
311                                 p++;    
312                         if (! *p) break;
313                         *p++='\0';
314                 }
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;
323 #endif
324                 this_->speed_last=this_->speed;
325         }
326 }
327
328 #ifdef HAVE_LIBGPS
329 static void
330 vehicle_gps_callback(struct gps_data_t *data, char *buf, size_t len, int level)
331 {
332         // If data->fix.speed is NAN, then the drawing gets jumpy. 
333         if(isnan(data->fix.speed)){
334                 return;
335         }
336
337         struct vehicle *this_=vehicle_last;
338         double scale,speed;
339 #if INTERPOLATION_TIME
340         if (! (data->set & TIME_SET)) {
341                 return;
342         }
343         data->set &= ~TIME_SET;
344         if (this_->time == data->fix.time)
345                 return;
346         this_->time=data->fix.time;
347 #endif
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;
352         }
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;
360 #endif
361                 data->set &= ~TRACK_SET;
362         }
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;
372         }
373         if (data->set & ALTITUDE_SET) {
374                 this_->height=data->fix.altitude;
375                 data->set &= ~ALTITUDE_SET;
376         }
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;
382         }
383         if (data->set & STATUS_SET) {
384                 this_->status=data->status;
385                 data->set &= ~STATUS_SET;
386         }
387         if(data->set & PDOP_SET){
388                 printf("pdop : %g\n",data->pdop);
389         }
390 }
391 #endif
392
393
394 static void
395 vehicle_close(struct vehicle *this_)
396 {
397         GError *error=NULL;
398
399
400         g_io_channel_shutdown(this_->iochan,0,&error);
401 #ifdef HAVE_LIBGPS
402         if (this_->gps)
403                 gps_close(this_->gps);
404 #endif
405         if (this_->file)
406                 pclose(this_->file);
407         if (this_->fd != -1)
408                 close(this_->fd);
409 }
410
411 struct packet {
412         int magic __attribute__ ((packed));
413         unsigned char type;
414         union {
415                 struct {
416                         int x __attribute__ ((packed));
417                         int y __attribute__ ((packed));
418                         unsigned char speed;
419                         unsigned char dir;
420                 } pos;
421         } u;
422 } __attribute__ ((packed)) ;
423
424 static void
425 vehicle_udp_recv(struct vehicle *this_)
426 {
427         struct packet pkt;
428         int size;
429
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_);
439         }
440 }
441
442 static void
443 vehicle_udp_update(struct vehicle *this_, struct vehicle *child)
444 {
445         struct coord *pos=&child->current_pos;
446         struct packet pkt;
447         int speed=child->speed;
448         int dir=child->dir/2;
449         if (speed > 255)
450                 speed=255;
451         pkt.magic=this_->magic;
452         pkt.type=1;
453         pkt.u.pos.x=pos->x;
454         pkt.u.pos.y=pos->y;
455         pkt.u.pos.speed=speed;
456         pkt.u.pos.dir=dir;
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_);
462 }
463
464 static int
465 vehicle_udp_query(void *data)
466 {
467         struct vehicle *this_=(struct vehicle *)data;
468         struct packet pkt;
469         dbg(2,"enter this_=%p\n", this_);
470         pkt.magic=this_->magic;
471         pkt.type=2;
472         sendto(this_->fd, &pkt, 5, 0, (struct sockaddr *)&this_->rem, sizeof(this_->rem));
473         dbg(2,"ret=TRUE\n");
474         return TRUE;
475 }
476
477 static int
478 vehicle_udp_open(struct vehicle *this_)
479 {
480         char *host,*child,*url,*colon;
481         int port;
482         url=g_strdup(this_->url);
483         colon=index(url+6,':');
484         struct sockaddr_in lcl;
485
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);
488                 return 0;
489         }
490         host=url+6;
491         *colon='\0';
492         this_->fd=socket(PF_INET, SOCK_DGRAM, 0);
493         this_->is_udp=1;
494         memset(&lcl, 0, sizeof(lcl));
495         lcl.sin_family = AF_INET;
496
497         this_->rem.sin_family = AF_INET;
498         inet_aton(host, &this_->rem.sin_addr);
499         this_->rem.sin_port=htons(port);
500
501         bind(this_->fd, (struct sockaddr *)&lcl, sizeof(lcl));
502         child=index(colon+1,' ');
503         if (child) {
504                 child++;
505                 if (!this_->child) {
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);
510                 }
511         } else {
512                 vehicle_udp_query(this_);
513                 g_timeout_add(this_->interval*1000, vehicle_udp_query, this_);
514         }
515         g_free(url);
516         return 0;
517 }
518
519 static int
520 vehicle_demo_timer (struct vehicle *this)
521 {
522         struct route_path_coord_handle *h;
523         struct coord *c;
524         dbg(1,"###### Entering simulation loop\n");
525         if(!this->navit){
526                 dbg(1,"vehicle->navit is not set. Can't simulate\n");
527                 return 1;
528         }
529
530         // <cp15> Then check whether the route is set, if not return TRUE
531         struct route * vehicle_route=navit_get_route(this->navit);
532         if(!vehicle_route){
533                 dbg(1,"navit_get_route NOK\n");
534                 return 1;
535         }
536
537         h=route_path_coord_open(vehicle_route);
538         if (!h) {
539                 dbg(1,"navit_path_coord_open NOK\n");
540                 return 1;
541         }
542         c=route_path_coord_get(h);
543         dbg(1,"current pos=%p\n", c);
544         if (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);
548         if (c) {
549                 dbg(1,"next pos=0x%x,0x%x\n", c->x, c->y);
550                 vehicle_set_position(this,c);
551         }
552         return 1;
553 }
554
555 static int
556 vehicle_open(struct vehicle *this_)
557 {
558         struct termios tio;
559         struct stat st;
560         int fd=0;
561
562 #ifdef HAVE_LIBGPS
563         struct gps_data_t *gps=NULL;
564         char *url_,*colon;
565 #endif
566         if (! strncmp(this_->url,"file:",5)) {
567                 fd=open(this_->url+5,O_RDONLY|O_NDELAY);
568                 if (fd < 0) {
569                         g_warning("Failed to open %s", this_->url);
570                         return 0;
571                 }
572                 stat(this_->url+5, &st);
573                 if (S_ISREG (st.st_mode)) {
574                         this_->is_file=1;
575                 } else {
576                         tcgetattr(fd, &tio);
577                         cfmakeraw(&tio);
578                         cfsetispeed(&tio, B4800);
579                         cfsetospeed(&tio, B4800);
580                         tio.c_cc[VMIN]=16;
581                         tio.c_cc[VTIME]=1;
582                         tcsetattr(fd, TCSANOW, &tio);
583                 }
584                 this_->fd=fd;
585         } else if (! strncmp(this_->url,"pipe:",5)) {
586                 this_->file=popen(this_->url+5, "r");
587                 this_->is_pipe=1;
588                 if (! this_->file) {
589                         g_warning("Failed to open %s", this_->url);
590                         return 0;
591                 }
592                 fd=fileno(this_->file);
593         } else if (! strncmp(this_->url,"gpsd://",7)) {
594 #ifdef HAVE_LIBGPS
595                 url_=g_strdup(this_->url);
596                 colon=index(url_+7,':');
597                 if (colon) {
598                         *colon=0;
599                         gps=gps_open(url_+7,colon+1);
600                 } else
601                         gps=gps_open(this_->url+7,NULL);
602                 g_free(url_);
603                 if (! gps) {
604                         g_warning("Failed to connect to %s", this_->url);
605                         return 0;
606                 }
607                 gps_query(gps, "w+x\n");
608                 gps_set_raw_hook(gps, vehicle_gps_callback);
609                 fd=gps->gps_fd;
610                 this_->gps=gps;
611 #else
612                 g_warning("No support for gpsd compiled in\n");
613                 return 0;
614 #endif
615         } else if (! strncmp(this_->url,"udp://",6)) {
616                 vehicle_udp_open(this_);
617                 fd=this_->fd;
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_);
621         }
622         this_->iochan=g_io_channel_unix_new(fd);
623         enable_watch(this_);
624         return 1;
625 }
626
627
628 static gboolean
629 vehicle_track(GIOChannel *iochan, GIOCondition condition, gpointer t)
630 {
631         struct vehicle *this_=t;
632         char *str,*tok;
633         gsize size;
634
635         dbg(1,"enter condition=%d\n", condition);
636         if (condition == G_IO_IN) {
637 #ifdef HAVE_LIBGPS
638                 if (this_->gps) {
639                         vehicle_last=this_;
640                         gps_poll(this_->gps);
641                 } else {
642 #else
643                 {
644 #endif
645                         if (this_->is_udp) {
646                                 vehicle_udp_recv(this_);
647                                 return TRUE;
648                         }
649                         size=read(g_io_channel_unix_get_fd(iochan), this_->buffer+this_->buffer_pos, BUFFER_SIZE-this_->buffer_pos-1);
650                         if (size <= 0) {
651                                 vehicle_close(this_);
652                                 vehicle_open(this_);
653                                 return TRUE;
654                         }
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);
658                         str=this_->buffer;
659                         while ((tok=index(str, '\n'))) {
660                                 *tok++='\0';
661                                 dbg(1,"line='%s'\n", str);
662                                 vehicle_parse_gps(this_, str);
663                                 str=tok;
664                         }
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) {
671                                 dbg(0,"overflow\n");
672                                 this_->buffer_pos=0;
673                         }
674                         
675                 }
676
677                 return TRUE;
678         } 
679         return FALSE;
680 }
681
682 static void
683 enable_watch(struct vehicle *this_)
684 {
685         this_->watch=g_io_add_watch(this_->iochan, G_IO_IN|G_IO_ERR|G_IO_HUP, vehicle_track, this_);
686 }
687
688 static void
689 disable_watch(struct vehicle *this_)
690 {
691         g_source_remove(this_->watch);
692 }
693
694 struct vehicle *
695 vehicle_new(const char *url)
696 {
697         struct vehicle *this_;
698         this_=g_new0(struct vehicle,1);
699
700         this_->cbl=callback_list_new();
701         this_->url=g_strdup(url);
702         this_->fd=-1;
703
704         vehicle_open(this_);
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;
709         this_->delta.x=0;
710         this_->delta.y=0;
711 #if INTERPOLATION_TIME
712         g_timeout_add(INTERPOLATION_TIME, vehicle_timer, this_);
713 #endif
714         
715         return this_;
716 }
717
718 void
719 vehicle_callback_add(struct vehicle *this_, struct callback *cb)
720 {
721         callback_list_add(this_->cbl, cb);
722 }
723
724 void
725 vehicle_callback_remove(struct vehicle *this_, struct callback *cb)
726 {
727         callback_list_remove(this_->cbl, cb);
728 }
729
730
731 int
732 vehicle_add_log(struct vehicle *this_, struct log *log, struct attr **attrs)
733 {
734         struct attr *type;
735         type=attr_search(attrs, NULL, attr_type);
736         if (! type) 
737                 return 1;
738         if (!strcmp(type->u.str,"nmea")) {
739                 this_->nmea_log=log;
740                 if (this_->child) 
741                         this_->child->nmea_log=log;
742                 
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";
746                 this_->gpx_log=log;
747                 if (this_->child)
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;
754                 if (this_->child)
755                         this_->child->textfile_log=log;
756                 log_set_header(log,header,strlen(header));
757         } else
758                 return 1;
759         return 0;
760 }
761
762 void
763 vehicle_destroy(struct vehicle *this_)
764 {
765         vehicle_close(this_);
766         callback_list_destroy(this_->cbl);
767         g_free(this_->url);
768         g_free(this_);
769 }