uni

Thing1's amazing uni repo
Log | Files | Refs

writeup.tex (19127B)


      1 \documentclass[a4paper,12pt]{article}
      2 
      3 \usepackage{tabularx}
      4 \usepackage{geometry} 
      5 \usepackage{pgfplots}
      6 \usepackage{titling} 
      7 \usepackage{titlesec}
      8 \usepackage[english]{babel} 
      9 \usepackage[hidelinks]{hyperref} 
     10 \usepackage{listings} 
     11 \usepackage{graphicx} 
     12 \usepackage{float}
     13 
     14 \graphicspath{ {./images} }
     15 
     16 \titleformat{\section} {\large\bfseries} {} {0em} {}[\titlerule] 
     17 \titleformat{\subsection} {\bfseries} {} {0em} {}[\titlerule] 
     18 \geometry{a4paper,total={170mm,257mm},left=25mm,right=25mm}
     19 \setlength{\parindent}{0pt}
     20 \setlength{\parskip}{10pt}
     21 
     22 \pgfplotsset{width=15cm,compat=1.9}
     23 
     24 \author{Lucas Standen} 
     25 \title{Robotics report}
     26 
     27 
     28 \begin{document} 
     29 \maketitle
     30 \newpage
     31 \section{How does the robot work?}
     32 I designed my system to repeatedly poll the sensors, and adjusting the motors based on the input, I made functions called \lstinline{stepMove} and \lstinline{stepTurn} which each moved or turned the robot a small amount. These function ran in a very short amount of time, allowing them to be called as often as possible, letting me read the sensors more often. This aloud me to make my robot react to changes, such as going off course or touching the barcode, a lot faster than if I halted the program while the robot moved. Thanks to this I didn't have to worry about distances or angles in my code, as I could tell the robot to keep moving until something happened.
     33 
     34 The sensors were polled by a function called \lstinline{getState}, which would be called as often as possible and updated the readings of the sensors in the robot, and updated the state of the motors depending on how long they had be turned on for. My step function worked by storing a time stamp of when they were called (using \lstinline{millis}), then every time the get state function was called, I could see if it had been over 19 milliseconds since the motors had been told to move, if it had been, I could turn them off. The reason I did this was to avoid the need to turn the motors off in my behaviour functions and line following functions, as having that in them, would have made them more complex than they needed to be.
     35 
     36 \subsection{What went well?}
     37 I was happy with the robots attraction and avoidance behaviours, they both worked very well, and were reliable. The LED state functioned as expected, flashing repeatedly, this worked in a similar way to the motors, checking when the last call was made to flash the LED, allowing the code to spend more time on the sensors readings than the state of the LED. 
     38 
     39 I was also happy with my use of a state \lstinline{struct} to hold all of the robots internal values, thanks to this my robot used almost no global variables. I declared it as \lstinline{static} inside of the \lstinline{getState} function, this meant that I could reassign the state variable in my \lstinline{loop} function, while not losing data, such as LED flash state.
     40 
     41 A final thing that I was proud of in my code was my debug macros, I wrote a macro that aloud entire code segments to be toggled, in a neat and clean way. I used this a lot over my code to get readings from all the sensors. It also is used to enable testing upon starting the robot.
     42 
     43 \subsection{What didn't go well?}
     44 
     45 I found my robot can sometimes get stuck while reading barcodes, if it did not enter them straight on, it would think that it needs to turn. This was partially fixed by adding an error correction, that would try to detect when this happened and turn in the opposite direction, to line the robot up with the barcode. This did help, however it still got stuck sometimes.
     46 
     47 \section{Comparing my code to the design brief}
     48 \noindent\begin{minipage}{\textwidth}
     49 \subsection{Working with Light Dependant Resistors}
     50 	When the robot starts up, and has been compiled with DEBUG enabled, it will start a testing program, this program will perform all tests on the LDRs. The code that does so is presented here. This code will read 10 values from each sensor and output the readings, it will also write the values to EEPROM. I think this code properly achieves the target of testing so I would say it is worth 70\% of the maximum mark for this section.
     51 
     52 \begin{verbatim}
     53 void
     54 testLDR()
     55 {
     56 	SEprintf("Testing LDRS\n");
     57 	for (int i = 0; i < 2; i++) {
     58 		SEprintf("Please turn on the lights, and place the robot 
     59 			  on a %s surface (press button A to continue)\n", colors[i]);
     60 
     61 		waitButton(BUTTONA, HIGH);
     62 		reading r = readSensorsMean(TESTREADINGS);
     63 
     64 		if (strcmp(colors[i], "black") == 0) {
     65 			EEPROM.put(6, (uint16_t)r.left + 100);
     66 			EEPROM.put(10,(uint16_t)r.mid + 100);
     67 			EEPROM.put(14, (uint16_t)r.right + 100);
     68 		} else {
     69 			EEPROM.put(4, (uint16_t)r.left);
     70 			EEPROM.put(8,(uint16_t)r.mid);
     71 			EEPROM.put(12, (uint16_t)r.right);
     72 		}
     73 
     74 		SEprintf("Test complete, please turn off the lights 
     75 			  (press button A to continue)\n");
     76 
     77 		waitButton(BUTTONA, HIGH);
     78 		readSensorsMean(TESTREADINGS);
     79 	}
     80 	SEprintf("finished testing LDRS, please check the values are expected 
     81 		  for lighting conditions\n");
     82 }
     83 
     84 \end{verbatim}
     85 \end{minipage}
     86 
     87 \begin{center}
     88 	\textit{These lines have been wrapped, so this code will not compile.}
     89 \end{center}
     90 
     91 Note the use of the \lstinline{SEprintf} function, this is one of many helper functions I made. It uses \lstinline{snprintf} from the C stdlib adn then prints it using the Arduino's \lstinline{Serial} class. This makes it equivalent to the C \lstinline{printf} function.
     92 
     93 \subsection{Light source following and obstacle detection}
     94 \noindent\begin{minipage}{\textwidth}
     95 To make the robot follow the line, I made a move function that checks the current state of the LDRs and will adjusts the course accordingly.
     96 
     97 \begin{verbatim}
     98 void
     99 move(state *s)
    100 {
    101 	/* small adjustments */
    102 	if (s->right && s->mid)
    103 		stepTurn(RGT, s);
    104 	else if (s->left && s->mid)
    105 		stepTurn(LFT, s);
    106 
    107 	/* large adjustments */
    108 	else if (s->right) {
    109 		s->prevDir = RGT;
    110 		stepTurn(RGT, s);
    111 	} else if (s->left) {
    112 		s->prevDir = LFT;
    113 		stepTurn(LFT, s);
    114 
    115 	/* drive straight, nominal */
    116 	} else if (s->mid) {
    117 		for (int i = 0; i < 20; i++)
    118 			stepMove(FWD, s);
    119 	} else {
    120 		/* try to recover from not being on the line by moving in the prevDir
    121 		   only updated on large turns */
    122 		while (!s->mid) {
    123 			s = getState();
    124 			stepTurn(s->prevDir,s);
    125 		}
    126 	}
    127 }
    128 \end{verbatim}
    129 \end{minipage}
    130 
    131 \noindent\begin{minipage}{\textwidth}
    132 	To make the robot stop and wait when an obstacle, I simply have this code at the top of my loop function, if an obstacle is seen, the code will just keep on going to the top of the loop function. The \lstinline{flash} function will cause the LED to flash.
    133 
    134 \begin{verbatim}
    135 void
    136 loop()
    137 {
    138 	state *s = getState();
    139 
    140 	/* do nothing while there is an obstacle */
    141 	while (isObstacle()) {
    142 		flash(YELLOW_LED, s);
    143 		return;
    144 	}
    145 	.... 
    146 \end{verbatim}
    147 \end{minipage}
    148 
    149 \noindent\begin{minipage}{\textwidth}
    150 	The barcode reading function compares the time that the robot was last on a barcode from the current time and if it is longer than a certain time, then a barcode has been passed.
    151 
    152 \begin{verbatim}
    153 void
    154 checkBarcode(state *s)
    155 {
    156 	long t = millis();
    157 	if (s->timeStamp > 500) {
    158 		/* long barcode */
    159 		if ((t - s->timeStamp) > 3000)
    160 			runBehave(&stepAvoidanceBehave);
    161 
    162 		/* short barcode */
    163 		else if ((t - s->timeStamp) > 800)
    164 			runBehave(&stepAttractionBehave);
    165 	}
    166 	s->timeStamp = t;
    167 }
    168 \end{verbatim}
    169 \end{minipage}
    170 
    171 \noindent\begin{minipage}{\textwidth}
    172 	The avoidance and attraction behaviours are both very similar, just having inverted directions, they work by comparing the raw values of the LDRs, meaning they should work in many light conditions.
    173 
    174 \begin{verbatim}
    175 void
    176 stepAvoidanceBehave(state *s)
    177 {
    178 	flash(RED_LED, s);
    179 	DBG({
    180 		/* WARNING this causes the program to stutter when left on,
    181 		     not too bad for debugging but very bad for general use! */
    182 		SEprintf("Avoid: %d, %d, %d\n", s->rawleft, s->rawmid, s->rawright);
    183 	});
    184 
    185 	/* left is noticably larger */
    186 	if (s->rawleft > s->rawright + 50)
    187 		stepTurn(RGT,s);
    188 	/* right is noticably larger */
    189 	else if (s->rawright > s->rawleft + 50)
    190 		stepTurn(LFT,s);
    191 	else
    192 		stepMove(FWD, s);
    193 }
    194 
    195 
    196 void
    197 stepAttractionBehave(state *s)
    198 {
    199 	flash(GREEN_LED, s);
    200 	DBG({
    201 		SEprintf("Attract: %d, %d, %d\n", s->rawleft, s->rawmid, s->rawright);
    202 	});
    203 
    204 	/* left is noticably larger */
    205 	if (s->rawleft > s->rawright + 50)
    206 		stepTurn(LFT,s);
    207 	/* right is noticably larger */
    208 	else if (s->rawright > s->rawleft +50)
    209 		stepTurn(RGT,s);
    210 	else
    211 		stepMove(FWD, s);
    212 }
    213 
    214 void
    215 runBehave(void (*stepBehave)(state *))
    216 {
    217 	while (true) {
    218 		state *s = getState();
    219 		stepBehave(s);
    220 	}
    221 }
    222 \end{verbatim}
    223 \end{minipage}
    224 
    225 For this section I believe my code was effective and defensive, I included debug statements, didn't rely on global variables and split my program into manageable functions. While testing I found it didn't always work perfectly on edge cases, like the robot coming onto the line from an angle, because of this I believe my code is worth 60\% of the total marks for this section.
    226 
    227 \noindent\begin{minipage}{\textwidth}
    228 \subsection{Robot states}
    229 	The robots states were handled through a \lstinline{flash} function that could cause any one LED to flash at a given point. The code was effective and worked nicely, however the \lstinline{flash} and \lstinline{getState} functions, have to be called very frequently to properly flash the LEDs. Due to this I believe this code is worth 65\% of the maximum mark for the section.
    230 
    231 \begin{verbatim}
    232 void
    233 flash(int pin, state *s)
    234 {
    235 	switch (pin) {
    236 	case GREEN_LED: s->gstate = millis(); break;
    237 	case YELLOW_LED: s->ystate = millis(); break;
    238 	case RED_LED: s->rstate = millis(); break;
    239 	}
    240 }
    241 
    242 void
    243 stepFlash(int pin)
    244 {
    245 	/* we define this a static to preserve its value across function calls */
    246 	static uint8_t flashCount;
    247 
    248 	if (flashCount < FLASHTIME)
    249 		digitalWrite(pin, HIGH);
    250 	else if (flashCount > FLASHTIME / 2)
    251 		digitalWrite(pin, LOW);
    252 
    253 	/* this value wraps which is by design, this will cause the flashing to reset */
    254 	flashCount++;
    255 }
    256 
    257 void
    258 updateFlash(state *s)
    259 {
    260 	/* turn off the leds if they arent ment to be on anymore */
    261 	if (millis() - s->gstate > FLASHTIME) {
    262 		s->gstate = 0;
    263 		digitalWrite(GREEN_LED, LOW);
    264 	} if (millis() - s->ystate > FLASHTIME) {
    265 		s->ystate = 0;
    266 		digitalWrite(YELLOW_LED, LOW);
    267 	} if (millis() - s->rstate > FLASHTIME) {
    268 		s->rstate = 0;
    269 		digitalWrite(RED_LED, LOW);
    270 	}
    271 
    272 	/* turn on the leds if needed */
    273 	if (s->gstate) stepFlash(GREEN_LED);
    274 	if (s->ystate) stepFlash(YELLOW_LED);
    275 	if (s->rstate) stepFlash(RED_LED);
    276 }
    277 \end{verbatim}
    278 \end{minipage}
    279 
    280 \subsection{EEPROM}
    281 \noindent\begin{minipage}{\textwidth}
    282 	My code only relied on the black calibration value, along with the motor values, so not all the values were read. I wrote a simple program called calibrate that could however fill all values, and perform simple calibration of the LDR's (although the \lstinline{testLDRs} function was more effective at finding proper values). I believe, as I achieved the brief of filling in all the values and reading them too, this code is worth 70\% of the available marks for this section.
    283 
    284 	\begin{center}
    285 		\textit{Code for reading values from EEPROM} 
    286 	\end{center}
    287 \begin{verbatim}
    288 ....
    289 
    290 /* let the user attach the battery, attaching the battery 
    291    while code is running caused strange behaviour */
    292 
    293 delay(3000);
    294 setupRobot();
    295 
    296 /* servo speed values */
    297 EEPROM.get(0, SERVOA_ZERO);
    298 EEPROM.get(1, SPEEDA);
    299 EEPROM.get(2, SERVOB_ZERO);
    300 EEPROM.get(3, SPEEDB);
    301 
    302 ....
    303 
    304 /* read the calibrated values that should be in EEPROM, 
    305    these values will be made by the calibrate program */
    306 
    307 EEPROM.get(6, LDRA_SWITCH);
    308 EEPROM.get(10, LDRB_SWITCH);
    309 EEPROM.get(14, LDRC_SWITCH);
    310 ....
    311 \end{verbatim}
    312 \end{minipage}
    313 \newpage
    314 \noindent\begin{minipage}{\textwidth}
    315 	\begin{center}
    316 		\textit{Code for setting default values into EEPROM} 
    317 	\end{center}
    318 \begin{verbatim}
    319 /* repeatedly changes calibration values for each ldr 
    320    until returning consistant data */
    321 
    322 while (isLDRBright(LDRA)) LDRA_SWITCH++;
    323 while (isLDRBright(LDRB)) LDRB_SWITCH++;
    324 while (isLDRBright(LDRC)) LDRC_SWITCH++;
    325 
    326 SEprintf("%d, %d, %d\n\n", LDRA_SWITCH + 100, 
    327 	 LDRB_SWITCH + 100, LDRC_SWITCH + 100);
    328 
    329 EEPROM.put(6, LDRA_SWITCH + 100);
    330 EEPROM.put(10, LDRB_SWITCH + 100);
    331 EEPROM.put(14, LDRB_SWITCH + 100);
    332 
    333 /* setting robots values, my robots values for fwd motion, and turns */
    334 
    335 EEPROM.put(0, (uint8_t)SERVOA_ZERO);
    336 EEPROM.put(1, (uint8_t)SPEEDA);
    337 EEPROM.put(2, (uint8_t)SERVOB_ZERO);
    338 EEPROM.put(3, (uint8_t)SPEEDB);
    339 \end{verbatim}
    340 \end{minipage}
    341 
    342 
    343 \section{Testing}
    344 \begin{table}[H]
    345 	\resizebox{\linewidth}{!}{
    346 		\begin{tabular}{|| l | p{0.6\linewidth} | p{0.4\linewidth} ||}
    347 			\hline
    348 			\textbf{Test} & \textbf{Method}  & \textbf{Result} \\
    349 			\hline 
    350 			\hline 
    351 			Push-button left & 
    352 			I made a function called \lstinline{testButton} repeatedly asks the user to press the button, counting how many times the program receives the button press, the user can compare this to how many times the button was pressed. &
    353 			Pass \\
    354 			\hline 
    355 			Push-button right & 
    356 			My \lstinline{testButton} function is requires an argument of the pin number, meaning a button on any pin could be tested. &
    357 			Pass \\
    358 			\hline
    359 			Servo left & 
    360 			I made a \lstinline{testServo} function, it spins the servo at its default speed for 10 seconds, the user can see if this acts as expected. &
    361 			Pass \\
    362 			\hline
    363 			Servo right & 
    364 			Like with the \lstinline{testButton} function, any value can be passed into this function, allow the user to test the other servo. &
    365 			Pass \\
    366 			\hline
    367 			IR transmitter \& receiver & 
    368 			I made a function called \lstinline{testIR} that expects the user to hold an item in front of the sensor and takes multiple readings, checking if the value outputted was consistent for more than 8 times out of 10. &
    369 			Pass \\
    370 			\hline
    371 			LDRs & 
    372 			I made a function called \lstinline{testLDRS} that asks the user to place the robot on different colours and in different lighting conditions, then takes many readings, the user can then compares these to expected values for the input. &	
    373 			Pass \\
    374 			\hline
    375 			\hline
    376 			Object avoidance &
    377 			To test the object avoidance, I simply used the robot repeatedly, in a variety of lighting conditions, and confirmed that the robot stops and that the orange LED turns on. I ran this test 10 times, with the object \~10cm away from the robot. &
    378 			8 / 10 Passed. 2 times resulted in robot entering attraction behaviour due to detecting shadow from object \\
    379 			\hline
    380 			Line following &
    381 			To test the line following, I placed the robot on the start of the line, and turned it on, I then saw if the robot was able to follow a straight black line. I repeated this 10 times. &
    382 			7 / 10 Passed. The robot sometimes has issues when travelling over barcodes if it doesn't go straight on, as this causes it to act as if it is on the line diagonally. \\
    383 			\hline
    384 			Barcode reading &
    385 			To test the barcode reading, I let the robot run over the 2 types of barcode 10 times each, and recorded how many times it was able to determine the correct type of barcode. For this test I repeated it if the robot failed to follow the line, as this was to test if the robot could read the barcode, not follow the line. &
    386 			9 / 10 Passed for long barcode.
    387 			8 / 10 Passed for short barcode.
    388 			All the times this test failed, was because the robot was detecting the other kind of barcode, this is most likely due to the reading being based on time since last line, causing a slow travel to detect as the long barcode, and vice versa. \\
    389 			\hline
    390 			Light attraction and avoidance &
    391 			To test the light attraction and avoidance, I modified my program to put the robot straight into these modes, I then used my phones flashlight to see if the robot did the expected behaviour. I repeated this 10 times for each behaviour and assessed it based on how well it worked. &
    392 			All 10 times were consistent for each behaviour, however I found while the robot was turning that it wasn't very smooth, I am not sure why this is, as turning in the line following behaviour was smooth, this did not effect its ability to move towards or away from the light. \\
    393 			\hline
    394 		\end{tabular}
    395 	}
    396 	\caption{\centering{\textit{All tests were performed in a well lit room, using lines and barcodes provided, unless otherwise stated}}}
    397 \end{table}
    398 
    399 \begin{center}
    400 \begin{tikzpicture}
    401 	\begin{axis}[
    402 			title=Graph showing LDR readings on a white background with the room bright,
    403 			ymin=0, ymax=600,
    404 		]
    405 		\addplot[
    406 			color=blue,
    407 			mark=square,
    408 		]
    409 		coordinates {
    410 			(0,223)
    411 			(1,226)
    412 			(2,225)
    413 			(3,215)
    414 			(4,233)
    415 			(5,270)
    416 			(6,304)
    417 			(7,321)
    418 			(8,323)
    419 			(9,308)
    420 		};
    421 		\addplot[
    422 			color=red,
    423 			mark=square,
    424 		]
    425 		coordinates {
    426 			(0,182)
    427 			(1,187)
    428 			(2,189)
    429 			(3,183)
    430 			(4,191)
    431 			(5,218)
    432 			(6,243)
    433 			(7,256)
    434 			(8,259)
    435 			(9,248)
    436 		};
    437 		\addplot[
    438 			color=green,
    439 			mark=square,
    440 		]
    441 		coordinates {
    442 			(0,214)
    443 			(1,219)
    444 			(2,220)
    445 			(3,217)
    446 			(4,228)
    447 			(5,255)
    448 			(6,278)
    449 			(7,290)
    450 			(8,290)
    451 			(9,292)
    452 		};
    453 		\legend{LDR A, LDR B, LDR C}
    454 	\end{axis}
    455 \end{tikzpicture}
    456 
    457 \begin{tikzpicture}
    458 	\begin{axis}[
    459 			title=Graph showing LDR readings on a white background with the room dark,
    460 			ymin=0, ymax=50,
    461 		]
    462 		\addplot[
    463 			color=blue,
    464 			mark=square,
    465 		]
    466 		coordinates {
    467 			(0,11)
    468 			(1,11)
    469 			(2,11)
    470 			(3,11)
    471 			(4,11)
    472 			(5,11)
    473 			(6,11)
    474 			(7,11)
    475 			(8,11)
    476 			(9,11)
    477 		};
    478 		\addplot[
    479 			color=red,
    480 			mark=square,
    481 		]
    482 		coordinates {
    483 			(0,15)
    484 			(1,14)
    485 			(2,14)
    486 			(3,15)
    487 			(4,15)
    488 			(5,15)
    489 			(6,16)
    490 			(7,15)
    491 			(8,15)
    492 			(9,15)
    493 		};
    494 		\addplot[
    495 			color=green,
    496 			mark=square,
    497 		]
    498 		coordinates {
    499 			(0,8)
    500 			(1,8)
    501 			(2,8)
    502 			(3,8)
    503 			(4,9)
    504 			(5,9)
    505 			(6,9)
    506 			(7,9)
    507 			(8,9)
    508 			(9,9)
    509 		};
    510 		\legend{LDR A, LDR B, LDR C}
    511 	\end{axis}
    512 \end{tikzpicture}
    513 
    514 \begin{tikzpicture}
    515 	\begin{axis}[
    516 			title=Graph showing LDR readings on a black background with the room bright,
    517 			ymin=0, ymax=200,
    518 		]
    519 		\addplot[
    520 			color=blue,
    521 			mark=square,
    522 		]
    523 		coordinates {
    524 			(0,110)
    525 			(1,108)
    526 			(2,102)
    527 			(3,101)
    528 			(4,105)
    529 			(5,112)
    530 			(6,118)
    531 			(7,119)
    532 			(8,114)
    533 			(9,104)
    534 		};
    535 		\addplot[
    536 			color=red,
    537 			mark=square,
    538 		]
    539 		coordinates {
    540 			(0,77)
    541 			(1,89)
    542 			(2,86)
    543 			(3,86)
    544 			(4,93)
    545 			(5,99)
    546 			(6,104)
    547 			(7,105)
    548 			(8,99)
    549 			(9,90)
    550 
    551 		};
    552 		\addplot[
    553 			color=green,
    554 			mark=square,
    555 		]
    556 		coordinates {
    557 			(0,88)
    558 			(1,101)
    559 			(2,99)
    560 			(3,99)
    561 			(4,105)
    562 			(5,111)
    563 			(6,116)
    564 			(7,116)
    565 			(8,111)
    566 			(9,103)
    567 		};
    568 		\legend{LDR A, LDR B, LDR C}
    569 	\end{axis}
    570 \end{tikzpicture}
    571 
    572 \begin{tikzpicture}
    573 	\begin{axis}[
    574 			title=Graph showing LDR readings on a black background with the room dark,
    575 			ymin=0, ymax=10,
    576 		]
    577 		\addplot[
    578 			color=blue,
    579 			mark=square,
    580 		]
    581 		coordinates {
    582 			(0,1)
    583 			(1,1)
    584 			(2,1)
    585 			(3,1)
    586 			(4,1)
    587 			(5,1)
    588 			(6,1)
    589 			(7,1)
    590 			(8,1)
    591 			(9,1)
    592 
    593 		};
    594 		\addplot[
    595 			color=red,
    596 			mark=square,
    597 		]
    598 		coordinates {
    599 			(0,2)
    600 			(1,2)
    601 			(2,2)
    602 			(3,2)
    603 			(4,2)
    604 			(5,2)
    605 			(6,2)
    606 			(7,2)
    607 			(8,2)
    608 			(9,2)
    609 		};
    610 		\addplot[
    611 			color=green,
    612 			mark=square,
    613 		]
    614 		coordinates {
    615 			(0,0)
    616 			(1,0)
    617 			(2,0)
    618 			(3,0)
    619 			(4,0)
    620 			(5,0)
    621 			(6,0)
    622 			(7,0)
    623 			(8,0)
    624 			(9,0)
    625 		};
    626 		\legend{LDR A, LDR B, LDR C}
    627 	\end{axis}
    628 \end{tikzpicture}
    629 \end{center}
    630 
    631 
    632 \end{document}