add framedist
[aversive.git] / projects / microb2010 / mainboard / strat_scan.c
1 /*  
2  *  Copyright Droids, Microb Technology (2009)
3  * 
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  *
18  *  Revision : $Id: strat_scan.c,v 1.2 2009-11-08 17:24:33 zer0 Exp $
19  *
20  *  Olivier MATZ <zer0@droids-corp.org> 
21  */
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <math.h>
27
28 #include <aversive/pgmspace.h>
29 #include <aversive/queue.h>
30 #include <aversive/wait.h>
31 #include <aversive/error.h>
32
33 #include <ax12.h>
34 #include <uart.h>
35 #include <pwm_ng.h>
36 #include <time.h>
37 #include <spi.h>
38
39 #include <pid.h>
40 #include <quadramp.h>
41 #include <control_system_manager.h>
42 #include <trajectory_manager.h>
43 #include <vect_base.h>
44 #include <lines.h>
45 #include <polygon.h>
46 #include <obstacle_avoidance.h>
47 #include <blocking_detection_manager.h>
48 #include <robot_system.h>
49 #include <position_manager.h>
50
51 #include <rdline.h>
52 #include <parse.h>
53
54
55 #include "../common/i2c_commands.h"
56 #include "main.h"
57 #include "cmdline.h"
58 #include "i2c_protocol.h"
59 #include "strat.h"
60 #include "strat_base.h"
61 #include "strat_utils.h"
62 #include "strat_avoid.h"
63 #include "sensor.h"
64
65 #define ERROUT(e) do {                          \
66                 err = e;                        \
67                 goto end;                       \
68         } while(0)
69
70
71 void scanner_dump_state(void)
72 {
73         uint8_t status;
74
75         printf_P(PSTR("scanner state:\r\n"));
76         status = sensorboard.scan_status;
77
78         printf_P(PSTR("  status=%x: "), sensorboard.scan_status);
79
80         if (status & I2C_SCAN_DONE)
81                 printf_P(PSTR("DONE "));
82         else
83                 printf_P(PSTR("RUNNING "));
84         if (status & I2C_SCAN_MAX_COLUMN)
85                 printf_P(PSTR("OBSTACLE "));
86
87         printf_P(PSTR("\r\n"));
88
89         if (sensorboard.dropzone_h == -1) {
90                 printf_P(PSTR("No zone found\r\n"));
91                 return;
92         }
93         
94         printf_P(PSTR("  column_h=%d\r\n"), sensorboard.dropzone_h);
95         printf_P(PSTR("  column_x=%d\r\n"), sensorboard.dropzone_x);
96         printf_P(PSTR("  column_y=%d\r\n"), sensorboard.dropzone_y);
97 }
98
99 /* must be larger than the disc poly */
100 #define CHECKPOINT_DIST 600
101
102 /* go to a specific angle on disc, if level == -1, don't move arms */
103 uint8_t strat_goto_disc_angle(int16_t a_deg, int8_t level)
104 {
105         uint8_t err;
106         uint16_t old_spdd, old_spda;
107         double x, y;
108         uint8_t need_clear = 0;
109
110         DEBUG(E_USER_STRAT, "%s(a_deg=%d, level=%d)", __FUNCTION__,
111               a_deg, level);
112
113         strat_get_speed(&old_spdd, &old_spda);
114         strat_set_speed(SPEED_DIST_FAST, SPEED_ANGLE_FAST);
115
116         /* workaround for some static cols configurations */
117         if ((strat_infos.conf.flags & STRAT_CONF_EARLY_SCAN) == 0) {
118                 if (time_get_s() > 15)
119                         i2c_mechboard_mode_loaded();
120         }
121         /* another workaround for offensive configuration */
122         else {
123                 if (strat_infos.i2c_loaded_skipped == 0) {
124                         DEBUG(E_USER_STRAT, "%s() need clear");
125                         strat_infos.i2c_loaded_skipped = 1;
126                         i2c_mechboard_mode_prepare_pickup_next(I2C_AUTO_SIDE,
127                                                                I2C_MECHBOARD_MODE_CLEAR);
128                         need_clear = 1;
129                 }
130                 else
131                         i2c_mechboard_mode_loaded();
132         }
133
134
135         /* calculate the checkpoint */
136         x = CHECKPOINT_DIST;
137         y = 0;
138         rotate(&x, &y, RAD(a_deg));
139         x += CENTER_X;
140         y += CENTER_Y;
141
142         err = goto_and_avoid(x, y, TRAJ_FLAGS_STD,
143                              TRAJ_FLAGS_NO_NEAR);
144         if (!TRAJ_SUCCESS(err))
145                 ERROUT(err);
146         
147         /* early offensive conf only */
148         if (need_clear) {
149                 err = WAIT_COND_OR_TIMEOUT(get_column_count() == 2,
150                                            3000L);
151                 DEBUG(E_USER_STRAT, "%s() offensive: err=%d", err);
152                 if (err == 0) /* timeout */
153                         return END_ERROR;
154         }
155         err = strat_goto_disc(level);
156
157  end:
158         strat_set_speed(old_spdd, old_spda);
159         return err;
160         
161 }
162
163 /* only valid for temple on disc */
164 int16_t strat_get_temple_angle(struct temple *temple)
165 {
166         int16_t x, y;
167         double a;
168
169         x = temple->x;
170         y = temple->y;
171         x -= CENTER_X;
172         y -= CENTER_Y;
173         a = atan2(y, x);
174         return DEG(a);
175 }
176
177 #define SCAN_ANGLE_OFFSET (-40)
178 int16_t strat_temple_angle_to_scan_angle(int16_t temple_angle)
179 {
180         return temple_angle + SCAN_ANGLE_OFFSET;
181 }
182
183 /* start to scan after this distance */
184 #define DIST_START_SCAN 50
185
186 /* scan during this distance (includes DIST_START_SCAN) */
187 #define DIST_SCAN 430
188
189 /* speed of the scan */
190 #define SPEED_SCAN 450
191
192 /* from scanner point of view */
193 #define DISC_CENTER_X 15
194 #define DISC_CENTER_Y 13
195
196 /* distance of the checkpoint */
197 #define CKPT_DST 550.
198
199 /* to convert in robot coordinates */
200 #define SIDE_OFFSET (ROBOT_WIDTH/2)
201 #define DIST_OFFSET (DIST_SCAN - DIST_START_SCAN)
202
203 /* center of the disc in robot coordinates */
204 #define CENTER_X_SCANNER 166
205 #define CENTER_Y_SCANNER 174
206
207 /* center of the disc in scanner millimeters coordinates */
208 #define CENTER_X_SCANNER2 120
209 #define CENTER_Y_SCANNER2 155
210
211 /* structure filled by strat_scan_disc() */
212 struct scan_disc_result {       
213 #define SCAN_FAILED              0
214 #define SCAN_VALID               1
215         uint8_t status;
216
217 #define SCAN_ACTION_BUILD_TEMPLE 0
218 #define SCAN_ACTION_BUILD_COL    1
219         uint8_t action;
220
221         uint8_t level;
222 };
223
224 #define SCAN_MODE_CHECK_TEMPLE 0
225 #define SCAN_MODE_SCAN_COL     1
226 #define SCAN_MODE_SCAN_TEMPLE  2
227
228 int8_t strat_scan_get_checkpoint(uint8_t mode, int16_t *ckpt_rel_x,
229                                  int16_t *ckpt_rel_y, int16_t *back_mm)
230 {
231         int16_t center_rel_x, center_rel_y;
232         int16_t col_rel_x, col_rel_y;
233         int16_t col_vect_x, col_vect_y;
234         double col_vect_norm;
235         int16_t ckpt_vect_x, ckpt_vect_y;
236
237         /* do some filtering */
238         if (mode == SCAN_MODE_SCAN_TEMPLE &&
239             sensorboard.dropzone_x > CENTER_X_SCANNER) {
240                 DEBUG(E_USER_STRAT, "x too big");
241                 return -1;
242         }
243                 
244         /* process relative pos from robot point of view */
245         center_rel_x = DIST_OFFSET - CENTER_Y_SCANNER;
246         center_rel_y = -(SIDE_OFFSET + CENTER_X_SCANNER);
247         
248         col_rel_x = DIST_OFFSET - sensorboard.dropzone_y;
249         col_rel_y = -(SIDE_OFFSET + sensorboard.dropzone_x);
250         DEBUG(E_USER_STRAT, "col_rel = %d,%d", col_rel_x, col_rel_y);
251         
252         /* vector from center to column */
253         col_vect_x = col_rel_x - center_rel_x;
254         col_vect_y = col_rel_y - center_rel_y;
255         col_vect_norm = norm(col_vect_x, col_vect_y);
256         
257         /* vector from center to ckpt */
258         ckpt_vect_x = (double)(col_vect_x) * CKPT_DST / col_vect_norm;
259         ckpt_vect_y = (double)(col_vect_y) * CKPT_DST / col_vect_norm;
260         
261         /* rel pos of ckpt */
262         *ckpt_rel_x = center_rel_x + ckpt_vect_x;
263         *ckpt_rel_y = center_rel_y + ckpt_vect_y;
264
265         /* do some filtering */
266         if (col_vect_norm > 150 || col_vect_norm < 30) {
267                 DEBUG(E_USER_STRAT, "bad norm");
268                 return -1;
269         }
270                 
271         if (mode == SCAN_MODE_SCAN_TEMPLE) {
272                 if (col_vect_norm > 50) {
273                         *back_mm = ABS(col_vect_norm-50);
274                 }
275         }
276         return 0;
277 }
278
279 /* 
280  * scan the disc: return END_TRAJ on success (and status in result is
281  * set to SCAN_VALID). In this case, all the scan_disc_result
282  * structure is filled with appropriate parameters.  mode can be
283  * 'check' or 'scan_col'. Note that if we do a check_temple, the level
284  * field in structure must be filled first by the caller.
285  */
286 uint8_t strat_scan_disc(int16_t angle, uint8_t mode,
287                         struct scan_disc_result *result)
288 {
289         uint16_t old_spdd, old_spda;
290         uint8_t err, stop_scanner = 0;
291         uint8_t original_mode = mode;
292         int16_t pos1x, pos1y, dist;
293         int16_t back_mm = 0;
294
295         int16_t ckpt_rel_x = 0, ckpt_rel_y = 0;
296
297         double center_abs_x, center_abs_y;
298         double ckpt_rel_d, ckpt_rel_a;
299         double ckpt_abs_x, ckpt_abs_y;
300
301         /* mark status as failed for now */
302         result->status = SCAN_FAILED;
303
304         DEBUG(E_USER_STRAT, "%s(angle=%d)", __FUNCTION__, angle);
305
306         strat_get_speed(&old_spdd, &old_spda);
307
308         /* go on disc */
309         err = strat_goto_disc_angle(angle, -1);
310         if (!TRAJ_SUCCESS(err))
311                 ERROUT(err);
312         
313         /* wait opponent before scanning */
314         if (strat_infos.conf.wait_opponent > 0) {
315                 int16_t opp_x, opp_y, opp_d, opp_a;
316                 int8_t err;
317                 microseconds us;
318
319                 us = time_get_us2();
320                 while ((err = get_opponent_xyda(&opp_x, &opp_y,
321                                                 &opp_d, &opp_a)) == 0) {
322                         if (opp_d > 600)
323                                 break;
324                         if (opp_a < 180)
325                                 break;
326
327                         if (time_get_us2() - us >= (uint32_t)strat_infos.conf.wait_opponent * 1000000L)
328                                 return END_ERROR;
329                 }
330         }
331         
332         /* save absolute position of disc */
333         rel_da_to_abs_xy(265, 0, &center_abs_x, &center_abs_y);
334
335         strat_limit_speed_disable();
336
337         /* go back and prepare to scan */
338         strat_set_speed(1000, 1000);
339         trajectory_d_a_rel(&mainboard.traj, -140, 130);
340         err = wait_traj_end(TRAJ_FLAGS_SMALL_DIST);
341         if (!TRAJ_SUCCESS(err))
342                 ERROUT(err);
343
344         /* XXX check that opp is not behind us */
345
346         /* prepare scanner */
347
348         stop_scanner = 1;
349         i2c_sensorboard_scanner_prepare();
350         time_wait_ms(250); /* XXX to remove ? */
351
352         strat_set_speed(SPEED_SCAN, 1000);
353
354         pos1x = position_get_x_s16(&mainboard.pos);
355         pos1y = position_get_y_s16(&mainboard.pos);
356         trajectory_d_rel(&mainboard.traj, -DIST_SCAN);
357         
358         while (1) {
359                 err = test_traj_end(TRAJ_FLAGS_SMALL_DIST);
360                 if (err != 0)
361                         break;
362                 
363                 dist = distance_from_robot(pos1x, pos1y);
364
365                 if (dist > DIST_START_SCAN)
366                         break;
367
368                 if (get_scanner_status() & I2C_SCAN_MAX_COLUMN) {
369                         err = END_ERROR;
370                         break;
371                 }
372         }
373         
374         if (err) {
375                 if (TRAJ_SUCCESS(err))
376                         err = END_ERROR; /* should not reach end */
377                 strat_hardstop();
378                 trajectory_goto_xy_abs(&mainboard.traj, pos1x, pos1y);
379                 wait_traj_end(TRAJ_FLAGS_SMALL_DIST);
380                 ERROUT(err);
381         }
382
383         /* start the scanner */
384
385         i2c_sensorboard_scanner_start();
386
387         err = WAIT_COND_OR_TRAJ_END(get_scanner_status() & I2C_SCAN_MAX_COLUMN,
388                                     TRAJ_FLAGS_NO_NEAR);
389         if (err == 0)
390                 err = END_ERROR;
391         if (!TRAJ_SUCCESS(err)) {
392                 strat_hardstop();
393                 trajectory_goto_xy_abs(&mainboard.traj, pos1x, pos1y);
394                 wait_traj_end(TRAJ_FLAGS_NO_NEAR);
395                 ERROUT(err);
396         }
397
398         wait_scan_done(1000);
399
400         i2c_sensorboard_scanner_stop();
401         stop_scanner = 0;
402
403         if (mode == SCAN_MODE_CHECK_TEMPLE) {
404                 i2c_sensorboard_scanner_algo_check(result->level,
405                                                    CENTER_X_SCANNER2,
406                                                    CENTER_Y_SCANNER2);
407                 i2cproto_wait_update();
408                 wait_scan_done(1000);
409                 scanner_dump_state();
410
411                 if (sensorboard.dropzone_h == -1 && 
412                     !(strat_infos.conf.flags & STRAT_CONF_SKIP_WHEN_CHECK_FAILS)) {
413                         DEBUG(E_USER_STRAT, "-- try to build a temple");
414                         mode = SCAN_MODE_SCAN_TEMPLE;
415                 }
416                 else {
417                         result->action = SCAN_ACTION_BUILD_TEMPLE;
418                         /* level is already set by caller */
419                 }
420         }
421
422         if (mode == SCAN_MODE_SCAN_TEMPLE) {
423                 i2c_sensorboard_scanner_algo_temple(I2C_SCANNER_ZONE_DISC,
424                                                     DISC_CENTER_X,
425                                                     DISC_CENTER_Y);
426                 i2cproto_wait_update();
427                 wait_scan_done(1000);
428                 scanner_dump_state();
429
430                 if (sensorboard.dropzone_h == -1 ||
431                     strat_scan_get_checkpoint(mode, &ckpt_rel_x,
432                                               &ckpt_rel_y, &back_mm)) {
433                         if (original_mode != SCAN_MODE_CHECK_TEMPLE) {
434                                 DEBUG(E_USER_STRAT, "-- try to build a column");
435                                 mode = SCAN_MODE_SCAN_COL;
436                         }
437                         else {
438                                 DEBUG(E_USER_STRAT, "-- check failed");
439                         }
440                 }
441                 else {
442                         result->action = SCAN_ACTION_BUILD_TEMPLE;
443                         result->level = sensorboard.dropzone_h;
444                 }
445         }
446
447         if (mode == SCAN_MODE_SCAN_COL) {
448                 i2c_sensorboard_scanner_algo_column(I2C_SCANNER_ZONE_DISC,
449                                                     DISC_CENTER_X,
450                                                     DISC_CENTER_Y);
451                 i2cproto_wait_update();
452                 wait_scan_done(1000);
453                 scanner_dump_state();
454                 
455                 if (sensorboard.dropzone_h == -1 ||
456                     strat_scan_get_checkpoint(mode, &ckpt_rel_x,
457                                               &ckpt_rel_y, &back_mm)) {
458                         ERROUT(END_ERROR);
459                 }
460                 else {
461                         result->action = SCAN_ACTION_BUILD_COL;
462                         result->level = sensorboard.dropzone_h;
463                 }
464         }
465
466         if (sensorboard.dropzone_h == -1) {
467                 ERROUT(END_ERROR);
468         }
469
470         if (mode == SCAN_MODE_CHECK_TEMPLE) {
471                 ckpt_rel_x = 220;
472                 ckpt_rel_y = 100;
473         }
474
475         DEBUG(E_USER_STRAT, "rel xy for ckpt is %d,%d", ckpt_rel_x, ckpt_rel_y);
476
477         rel_xy_to_abs_xy(ckpt_rel_x, ckpt_rel_y, &ckpt_abs_x, &ckpt_abs_y);
478         abs_xy_to_rel_da(ckpt_abs_x, ckpt_abs_y, &ckpt_rel_d, &ckpt_rel_a);
479
480         DEBUG(E_USER_STRAT, "abs ckpt is %2.2f,%2.2f", ckpt_abs_x, ckpt_abs_y);
481
482         strat_set_speed(SPEED_DIST_FAST, SPEED_ANGLE_FAST);
483
484         /* intermediate checkpoint for some positions */
485         if ( (DEG(ckpt_rel_a) < 0 && DEG(ckpt_rel_a) > -90) ) {
486                 trajectory_goto_xy_rel(&mainboard.traj, 200, 100);
487                 err = wait_traj_end(TRAJ_FLAGS_NO_NEAR);
488                 if (!TRAJ_SUCCESS(err))
489                         ERROUT(err);
490         }
491
492         trajectory_goto_xy_abs(&mainboard.traj, ckpt_abs_x, ckpt_abs_y);
493         err = wait_traj_end(TRAJ_FLAGS_NO_NEAR);
494         if (!TRAJ_SUCCESS(err))
495                 ERROUT(err);
496
497         if (result->action == SCAN_ACTION_BUILD_TEMPLE) {
498                 i2c_mechboard_mode_prepare_build_both(result->level);
499         }
500
501         trajectory_turnto_xy(&mainboard.traj, center_abs_x, center_abs_y);
502         err = wait_traj_end(TRAJ_FLAGS_NO_NEAR);
503         if (!TRAJ_SUCCESS(err))
504                 ERROUT(err);
505
506         pos1x = position_get_x_s16(&mainboard.pos);
507         pos1y = position_get_y_s16(&mainboard.pos);
508
509         strat_set_speed(SPEED_DIST_SLOW, SPEED_ANGLE_SLOW);
510         trajectory_d_rel(&mainboard.traj, 400);
511         err = WAIT_COND_OR_TRAJ_END(distance_from_robot(pos1x, pos1y) > 200,
512                                     TRAJ_FLAGS_SMALL_DIST);
513         if (err == 0) {
514                 strat_set_speed(SPEED_DIST_VERY_SLOW, SPEED_ANGLE_VERY_SLOW);
515                 trajectory_d_rel(&mainboard.traj, 400);
516                 err = wait_traj_end(TRAJ_FLAGS_SMALL_DIST);
517         }
518         if (TRAJ_SUCCESS(err))
519                 err = END_ERROR; /* should not reach end */
520         if (err != END_BLOCKING && !TRAJ_SUCCESS(err)) 
521                 ERROUT(err);
522
523         if (back_mm) {
524                 trajectory_d_rel(&mainboard.traj, -back_mm);
525                 wait_traj_end(TRAJ_FLAGS_SMALL_DIST);
526         }
527
528         result->status = SCAN_VALID;
529
530         strat_limit_speed_enable();
531         return END_TRAJ;
532
533  end:
534         if (stop_scanner)
535                 i2c_sensorboard_scanner_stop();
536         strat_limit_speed_enable();
537         strat_set_speed(old_spdd, old_spda);
538         return err;
539         
540 }
541
542 /* do action according to scanner result. temple argument can be NULL
543  * if it's a new one (from opponent) or it can be our previous
544  * temple. */
545 uint8_t strat_scan_do_action(struct scan_disc_result *scan_result,
546                              struct temple *temple, struct build_zone *zone)
547 {
548         uint8_t err;
549
550         /* remove the temple from the list */
551         if (scan_result->status != SCAN_VALID)
552                 return END_ERROR;
553
554         if (temple) {
555                 /* we were scanning a temple, remove it */
556                 if (scan_result->level != temple->level_l) {
557                         temple->flags = 0;
558                         temple = NULL;
559                 }
560         }
561
562         if (temple == NULL) {
563                 temple = strat_get_free_temple();
564                 if (temple == NULL)
565                         return END_ERROR;
566                 memset(temple, 0, sizeof(*temple));
567                 temple->level_l = scan_result->level;
568                 temple->level_r = scan_result->level;
569                 temple->flags = TEMPLE_F_OPPONENT | 
570                         TEMPLE_F_VALID | TEMPLE_F_LINTEL;
571                 temple->zone = zone;
572         }
573         zone->flags |= ZONE_F_BUSY;
574
575         switch (scan_result->action) {
576
577         case SCAN_ACTION_BUILD_COL:
578                 err = strat_grow_temple_column(temple);
579                 break;
580
581         case SCAN_ACTION_BUILD_TEMPLE:
582                 err = strat_grow_temple(temple);
583                 break;
584         default:
585                 err = END_TRAJ;
586                 break;
587         }
588         if (!TRAJ_SUCCESS(err))
589                 temple->flags = 0;
590         return err;
591 }
592
593 uint8_t strat_build_on_opponent_temple(void)
594 {
595         struct temple *temple;
596         uint8_t err;
597         struct scan_disc_result scan_result;
598         int16_t temple_angle;
599
600         if (time_get_s() < strat_infos.conf.scan_opp_min_time)
601                 return END_TRAJ;
602
603         strat_infos.conf.scan_opp_min_time = 
604                 time_get_s() + strat_infos.conf.delay_between_opp_scan;
605
606         /* scan on disc only */
607         if (strat_infos.conf.scan_opp_angle == -1) {
608                 temple = strat_get_our_temple_on_disc(0);
609
610                 /* scan the opposite of our temple if we found
611                  * one on disc */
612                 if (temple) {
613                         temple_angle = strat_get_temple_angle(temple);
614                         temple_angle += 180;
615                         if (temple_angle > 180)
616                                 temple_angle -= 360;
617                 }
618                 /* else scan at 0 deg (opponent side) */
619                 else {
620                         temple_angle = 0;
621                 }
622         }
623         else {
624                 /* user specified scan position */
625                 temple_angle = strat_infos.conf.delay_between_opp_scan;
626                 if (temple_angle > 180)
627                         temple_angle -= 360;
628         }
629         temple_angle = strat_temple_angle_to_scan_angle(temple_angle);
630         
631
632         err = strat_scan_disc(temple_angle, SCAN_MODE_SCAN_TEMPLE,
633                               &scan_result);
634         if (!TRAJ_SUCCESS(err))
635                 return err;
636
637         /* XXX on disc only */
638         err = strat_scan_do_action(&scan_result, NULL,
639                                    &strat_infos.zone_list[0]);
640
641         if (!TRAJ_SUCCESS(err))
642                 return err;
643         
644         err = strat_escape(&strat_infos.zone_list[0], TRAJ_FLAGS_STD);
645         return err;
646 }
647
648 uint8_t strat_check_temple_and_build(void)
649 {
650         struct temple *temple;
651         uint8_t err;
652         struct scan_disc_result scan_result;
653         int16_t temple_angle;
654
655         if (time_get_s() < strat_infos.conf.scan_our_min_time)
656                 return END_TRAJ;
657         strat_infos.conf.scan_our_min_time = 
658                 time_get_s() + strat_infos.conf.delay_between_our_scan;
659
660         /* on disc only, symetric only */
661         temple = strat_get_our_temple_on_disc(1);
662         if (temple == NULL)
663                 return END_TRAJ;
664
665         temple_angle = strat_get_temple_angle(temple);
666         temple_angle = strat_temple_angle_to_scan_angle(temple_angle);
667         
668         scan_result.level = temple->level_l;
669         err = strat_scan_disc(temple_angle, SCAN_MODE_CHECK_TEMPLE,
670                               &scan_result);
671         if (scan_result.status != SCAN_VALID) {
672                 temple->flags = 0;
673                 temple = NULL;
674         }
675         /* no column after a temple check */
676         else if (scan_result.action == SCAN_ACTION_BUILD_COL &&
677                  time_get_s() < 70)
678                 err = END_ERROR;
679         if (!TRAJ_SUCCESS(err))
680                 return err;
681
682         err = strat_scan_do_action(&scan_result, temple,
683                                    temple->zone);
684         if (!TRAJ_SUCCESS(err))
685                 return err;
686         
687         err = strat_escape(&strat_infos.zone_list[0], TRAJ_FLAGS_STD);
688         return err;
689 }