Commit 971a769d744581c9d055755966c3467d2f6c9d14

Authored by Arnaud Blanchard
0 parents

Initial basic applications

  1 +cmake_minimum_required(VERSION 2.6)
  2 +
  3 +project(bapps)
  4 +
  5 +#subdirs(i_read)
  6 +subdirs(i_oscillator)
  7 +#subdirs(i_keyboard)
  8 +
  9 +subdirs(f_max)
  10 +
  11 +
  1 +# Copyright ETIS — ENSEA, Université de Cergy-Pontoise, CNRS (2011 - 2016)
  2 +# Author: Arnaud Blanchard
  3 +
  4 +cmake_minimum_required(VERSION 2.6)
  5 +
  6 +get_filename_component(PROJECT_NAME ${CMAKE_CURRENT_SOURCE_DIR} NAME)
  7 +project(${PROJECT_NAME})
  8 +
  9 +find_package(blc_program)
  10 +find_package(blc_channel)
  11 +
  12 +add_definitions(${BL_DEFINITIONS})
  13 +include_directories(${BL_INCLUDE_DIRS})
  14 +add_executable(${PROJECT_NAME} main.cpp)
  15 +target_link_libraries(${PROJECT_NAME} ${BL_LIBRARIES})
  16 +
  1 +# Copyright ETIS — ENSEA, Université de Cergy-Pontoise, CNRS (2011 - 2016)
  2 +# Author: Arnaud Blanchard (November 2016)
  3 +# This software is governed by the CeCILL v2.1 license under French law and abiding by the rules of distribution of free software.
  4 +# You can use, modify and/ or redistribute the software under the terms of the CeCILL v2.1 license as circulated by CEA, CNRS and INRIA at the following URL "http://www.cecill.info".
  5 +# As a counterpart to the access to the source code and rights to copy, modify and redistribute granted by the license,
  6 +# users are provided only with a limited warranty and the software's author, the holder of the economic rights, and the successive licensors have only limited liability.
  7 +# In this respect, the user's attention is drawn to the risks associated with loading, using, modifying and/or developing or reproducing the software by the user in light of its specific status of free software,
  8 +# that may mean that it is complicated to manipulate, and that also therefore means that it is reserved for developers and experienced professionals having in-depth computer knowledge.
  9 +# Users are therefore encouraged to load and test the software's suitability as regards their requirements in conditions enabling the security of their systems and/or data to be ensured  and, more generally, to use and operate it in the same conditions as regards security.
  10 +# The fact that you are presently reading this means that you have had knowledge of the CeCILL v2.1 license and that you accept its terms.
  11 +
  12 +
  13 +cd `dirname $0`
  14 +echo
  15 +echo "Checking `basename $PWD`"
  16 +echo "===================="
  17 +echo
  18 +echo "No check for now"
  19 +echo
  1 +#We go in blaar dir
  2 +cd `dirname $0`/..
  3 +
  4 +
  5 +
  6 +
  7 +./run.sh i_keyboard --size=2 --type=FL32 --display | ./run.sh f_discretize | ./run.sh f_gnuplot --with=boxes
  1 +#include "blc_channel.h"
  2 +#include "blc_program.h"
  3 +#include <unistd.h> //getpid
  4 +#include <sys/time.h>
  5 +
  6 +static void discretize_floats(blc_array *output, blc_array const *input, int bins_nb, float max=1.0, float min=0.0){
  7 + int i, j, index;
  8 +
  9 + FOR(j, input->total_length) {
  10 + index=(input->floats[j]-min)/(max-min)*bins_nb;
  11 + if (index>=bins_nb) index=bins_nb-1;
  12 + else if (index<0) index=0;
  13 +
  14 + FOR(i, bins_nb){
  15 + output->floats[i+j*bins_nb] = (float)(i==index);
  16 + }
  17 + }
  18 +}
  19 +
  20 +int main(int argc, char **argv){
  21 + blc_array array;
  22 + blc_channel input_channel, output_channel;
  23 + char const *output_channel_name, *input_channel_name, *period_str, *bins_nb_str, *display;
  24 + float min=0, max=1;
  25 + int64_t period;
  26 + int iteration, initialized=0, bins_nb;
  27 +
  28 + blc_program_set_description("Discretize an analog signal in bins.");
  29 + blc_program_add_option(&display, 'D', "display", NULL, "display a text graph with result", NULL);
  30 + blc_program_add_option(&bins_nb_str, 'n', "bins_nb", "integer", "Number of bins where to distribute the discetize", "32");
  31 + blc_program_add_option(&period_str, 'p', "period", "integer", "sampling period in ms", "10");
  32 + blc_program_add_option(&output_channel_name, 'o', "output", "blc_channel", "name of the output channel", NULL);
  33 + blc_program_add_parameter(&input_channel_name, "blc_channel", 1, "Input channel", NULL);
  34 + blc_program_init(&argc, &argv, NULL);
  35 +
  36 + if (output_channel_name==NULL) SYSTEM_ERROR_CHECK(asprintf((char**)&output_channel_name, "/%s%d", blc_program_name, getpid()), -1, NULL); //This will not be freed
  37 +
  38 + input_channel.open(input_channel_name, BLC_CHANNEL_READ);
  39 + if(input_channel.type!='FL32') EXIT_ON_CHANNEL_ERROR(&input_channel, "Only type FL32 is managed");
  40 +
  41 + period=strtod(period_str, NULL)*1000;
  42 + bins_nb=strtod(bins_nb_str, NULL);
  43 +
  44 + if (input_channel.total_length==1) output_channel.create_or_open(output_channel_name, BLC_CHANNEL_WRITE, 'FL32', 'NDEF', 1, bins_nb);
  45 + else output_channel.create_or_open(output_channel_name, BLC_CHANNEL_WRITE, 'FL32', 'NDEF', 2, bins_nb, input_channel.total_length);
  46 +
  47 + output_channel.publish();
  48 +
  49 + iteration=0;
  50 + BLC_COMMAND_LOOP(period){
  51 + discretize_floats(&output_channel, &input_channel, bins_nb, max, min);
  52 + }
  53 +
  54 + return EXIT_SUCCESS;
  55 +}
  1 +# Set the minimum version of cmake required to build this project
  2 +cmake_minimum_required(VERSION 2.6)
  3 +
  4 +# Set the name of the project as the directory basename
  5 +project(f_max)
  6 +
  7 +find_package(blc_channel)
  8 +find_package(blc_program)
  9 +
  10 +add_definitions(${BL_DEFINITIONS})
  11 +include_directories(${BL_INCLUDE_DIRS})
  12 +add_executable(f_max f_max.cpp)
  13 +target_link_libraries(f_max ${BL_LIBRARIES})
  14 +
  15 +
  16 +
  17 +
  1 +//
  2 +// Created by Arnaud Blanchard on 22/12/15.
  3 +// Copyright ETIS 2015. All rights reserved.
  4 +//
  5 +#include "blc_channel.h"
  6 +#include "blc_program.h"
  7 +#include <unistd.h>
  8 +#include <math.h>
  9 +
  10 +#define DEFAULT_OUTPUT_NAME ":argmax<pid>"
  11 +
  12 +
  13 +static float find_float_max(float *floats, int floats_nb, float const *find_arg=NULL){
  14 + float max;
  15 + int i, imax;
  16 + max=floats[0];
  17 + imax=0;
  18 + for(i=1; i!=floats_nb; i++){
  19 + if(floats[i] > max){
  20 + max=floats[i];
  21 + imax=i;
  22 + }
  23 + }
  24 + if (find_arg) return (imax+0.5f)*(*find_arg);
  25 + else return max;
  26 +}
  27 +
  28 +static void start_channel_float_loop(blc_channel const *input, blc_channel *output, float const *find_arg=NULL)
  29 +{
  30 + BLC_COMMAND_LOOP(0){
  31 + output->floats[0]=find_float_max(input->floats, input->total_length, find_arg);
  32 + }
  33 +}
  34 +
  35 +static void start_print_float_loop(blc_channel const *input, float const* find_arg=NULL){
  36 + float imax;
  37 + BLC_COMMAND_LOOP(0){
  38 + imax=find_float_max(input->floats, input->total_length, find_arg);
  39 + printf("%f\n", imax);
  40 + if( blc_output_terminal) blc_eprint_cursor_up(1);
  41 + }
  42 + if( blc_output_terminal) blc_eprint_cursor_down(1);
  43 +}
  44 +
  45 +int main(int argc, char **argv){
  46 + blc_channel input, output;
  47 + char const *output_name, *input_name, *find_arg_str;
  48 + float *find_arg_gain=NULL;
  49 + float arg_gain;
  50 +
  51 + blc_program_set_description("Find the value of argument of the maximal value");
  52 + blc_program_add_option(&find_arg_str, 'a', "find_arg", "real", "Return argmax mutiplied by the real instead of max", NULL);
  53 + blc_program_add_option(&output_name, 'o', "output", "blc_channel-out", "channel name", DEFAULT_OUTPUT_NAME);
  54 + blc_program_add_parameter(&input_name, "blc_channel-in", 1, "channel you want to find max", NULL);
  55 + blc_program_init(&argc, &argv, NULL);
  56 +
  57 + input.open(input_name, BLC_CHANNEL_READ);
  58 +
  59 + blc_loop_try_add_waiting_semaphore(input.sem_new_data);
  60 + blc_loop_try_add_posting_semaphore(input.sem_ack_data);
  61 +
  62 + if (find_arg_str){
  63 + SSCANF(1, find_arg_str, "%f", &arg_gain);
  64 + find_arg_gain=&arg_gain;
  65 + if(strcmp(output_name, DEFAULT_OUTPUT_NAME)==0) asprintf((char**)&output_name, ":imax%d", getpid());
  66 + }
  67 + else if(strcmp(output_name, DEFAULT_OUTPUT_NAME)==0) asprintf((char**)&output_name, ":max%d", getpid());
  68 +
  69 +
  70 + //The output is on terminal
  71 + if (strcmp(output_name, "-")==0){
  72 + switch (input.type){
  73 + case 'FL32':start_print_float_loop(&input, find_arg_gain);
  74 + break;
  75 + default:EXIT_ON_CHANNEL_ERROR(&input, "Type is not managed. Only 'FL32' is.");
  76 + break;
  77 + }
  78 + }
  79 + else
  80 + {
  81 + output.create_or_open(output_name, BLC_CHANNEL_WRITE, input.type, 'NDEF', 1, 1);
  82 + output.publish();
  83 +
  84 + blc_loop_try_add_waiting_semaphore(output.sem_ack_data);
  85 + blc_loop_try_add_posting_semaphore(output.sem_new_data);
  86 +
  87 + switch (input.type){
  88 + case 'FL32':start_channel_float_loop(&input, &output, find_arg_gain);
  89 + break;
  90 + default:EXIT_ON_CHANNEL_ERROR(&input, "Type is not managed. Only 'FL32' is.");
  91 + break;
  92 + }
  93 + }
  94 +
  95 + return EXIT_SUCCESS;
  96 +}
  1 +# Copyright ETIS — ENSEA, Université de Cergy-Pontoise, CNRS (2011 - 2016)
  2 +# Author: Arnaud Blanchard
  3 +
  4 +cmake_minimum_required(VERSION 2.6)
  5 +
  6 +get_filename_component(PROJECT_NAME ${CMAKE_CURRENT_SOURCE_DIR} NAME)
  7 +project(${PROJECT_NAME})
  8 +
  9 +find_package(blc_program)
  10 +find_package(blc_channel)
  11 +
  12 +add_definitions(${BL_DEFINITIONS})
  13 +include_directories(${BL_INCLUDE_DIRS})
  14 +add_executable(${PROJECT_NAME} main.cpp)
  15 +target_link_libraries(${PROJECT_NAME} ${BL_LIBRARIES})
  16 +
  1 +# Copyright ETIS — ENSEA, Université de Cergy-Pontoise, CNRS (2011 - 2016)
  2 +# Author: Arnaud Blanchard (November 2016)
  3 +# This software is governed by the CeCILL v2.1 license under French law and abiding by the rules of distribution of free software.
  4 +# You can use, modify and/ or redistribute the software under the terms of the CeCILL v2.1 license as circulated by CEA, CNRS and INRIA at the following URL "http://www.cecill.info".
  5 +# As a counterpart to the access to the source code and rights to copy, modify and redistribute granted by the license,
  6 +# users are provided only with a limited warranty and the software's author, the holder of the economic rights, and the successive licensors have only limited liability.
  7 +# In this respect, the user's attention is drawn to the risks associated with loading, using, modifying and/or developing or reproducing the software by the user in light of its specific status of free software,
  8 +# that may mean that it is complicated to manipulate, and that also therefore means that it is reserved for developers and experienced professionals having in-depth computer knowledge.
  9 +# Users are therefore encouraged to load and test the software's suitability as regards their requirements in conditions enabling the security of their systems and/or data to be ensured  and, more generally, to use and operate it in the same conditions as regards security.
  10 +# The fact that you are presently reading this means that you have had knowledge of the CeCILL v2.1 license and that you accept its terms.
  11 +
  12 +
  13 +cd `dirname $0`
  14 +echo
  15 +echo "Checking `basename $PWD`"
  16 +echo "===================="
  17 +echo
  18 +echo "No check for now"
  19 +echo
  1 +#include "blc_channel.h"
  2 +#include "blc_program.h"
  3 +#include <unistd.h> //getpid
  4 +#include <sys/time.h>
  5 +
  6 +int main(int argc, char **argv){
  7 + blc_array array;
  8 + blc_channel input_channel, output_channel;
  9 + char const *output_channel_name, *input_channel_name, *delay_str, *period_str, *gain_str;
  10 + float gain;
  11 + int64_t period, delay;
  12 + int delay_iterations, iteration, i;
  13 +
  14 + blc_program_set_description("Copy the input signla in the output modifying it (delay, gain, ...)");
  15 + blc_program_add_option(&delay_str, 'd', "delay", "integer", "delay applying to the input signal in ms", NULL);
  16 + blc_program_add_option(&gain_str, 'g', "gain", "float", "gain to which the signal will be multiply", NULL);
  17 + blc_program_add_option(&period_str, 'p', "period", "integer", "sampling period in ms", "10");
  18 + blc_program_add_option(&output_channel_name, 'o', "output", "blc_channel", "name of the output channel", NULL);
  19 + blc_program_add_parameter(&input_channel_name, "blc_channel", 1, "Input channel", NULL);
  20 + blc_program_init(&argc, &argv, NULL);
  21 +
  22 + if (output_channel_name==NULL) SYSTEM_ERROR_CHECK(asprintf((char**)&output_channel_name, "/%s%d", blc_program_name, getpid()), -1, NULL); //This will not be free
  23 +
  24 + input_channel.open(input_channel_name, BLC_CHANNEL_READ);
  25 +
  26 + period=strtod(period_str, NULL)*1000;
  27 +
  28 + if (delay_str){
  29 + delay=strtod(delay_str, NULL)*1000;
  30 + delay_iterations=delay/period;
  31 + if (delay<=period) EXIT_ON_ERROR("Delay (%dms) cannot be shorter than the sampling period (%dms)", delay/1000, period/1000);
  32 + //We create a table capable of saving all intermediate iterations
  33 + array.init('UIN8', 'NDEF', 2, input_channel.size, delay_iterations);
  34 + }
  35 + else delay=0;
  36 +
  37 + if (gain_str) {
  38 + gain=strtof(gain_str, NULL);
  39 + }
  40 + else gain=1.0f;
  41 +
  42 + output_channel.create_or_open(output_channel_name, BLC_CHANNEL_WRITE, input_channel.type, input_channel.format, input_channel.dims_nb, input_channel.dims);
  43 + output_channel.publish();
  44 +
  45 + iteration=0;
  46 + BLC_COMMAND_LOOP(period){
  47 + if (delay){
  48 + if (gain_str){
  49 + switch (input_channel.type){
  50 + case 'UIN8':
  51 + FOR(i, input_channel.total_length){
  52 + array.uchars[array.dims[array.dims_nb-1].step*iteration+i]=CLIP_UCHAR(input_channel.uchars[i]*gain);
  53 + }
  54 + break;
  55 + case 'FL32':
  56 + FOR(i, input_channel.total_length){
  57 + array.floats[array.dims[array.dims_nb-1].step*iteration+i]=input_channel.floats[i]*gain;
  58 + }
  59 + break;
  60 + default:
  61 + EXIT_ON_ARRAY_ERROR(&input_channel, "You can only apply gain on type 'UIN8' or 'FL32'");
  62 + break;
  63 + }
  64 + }else memcpy(array.uchars+array.dims[array.dims_nb-1].step*iteration, input_channel.data, input_channel.size);
  65 +
  66 + iteration++;
  67 + if (iteration==delay_iterations) iteration=0;
  68 +
  69 + memcpy(output_channel.data, array.uchars+array.dims[array.dims_nb-1].step*iteration, output_channel.size);
  70 + }
  71 + else {
  72 + if (gain_str){
  73 + switch (input_channel.type){
  74 + case 'UIN8':
  75 + FOR(i, input_channel.total_length){
  76 + output_channel.uchars[i]=CLIP_UCHAR(input_channel.uchars[i]*gain);
  77 + }
  78 + break;
  79 + case 'FL32':
  80 + FOR(i, input_channel.total_length){
  81 + output_channel.floats[i]=input_channel.floats[i]*gain;
  82 + }
  83 + break;
  84 + default:
  85 + EXIT_ON_ARRAY_ERROR(&input_channel, "You can only apply gain on type 'UIN8' or 'FL32'");
  86 + break;
  87 + }
  88 + }
  89 + else memcpy(output_channel.data, input_channel.data, input_channel.size);
  90 + }
  91 + }
  92 +
  93 + return EXIT_SUCCESS;
  94 +}
  95 +
  96 +
  1 +# Copyright ETIS — ENSEA, Université de Cergy-Pontoise, CNRS
  2 +# Author: Arnaud Blanchard (November 2016)
  3 +# This software is governed by the CeCILL v2.1 license under French law and abiding by the rules of distribution of free software.
  4 +# You can use, modify and/ or redistribute the software under the terms of the CeCILL v2.1 license as circulated by CEA, CNRS and INRIA at the following URL "http://www.cecill.info".
  5 +# As a counterpart to the access to the source code and rights to copy, modify and redistribute granted by the license,
  6 +# users are provided only with a limited warranty and the software's author, the holder of the economic rights, and the successive licensors have only limited liability.
  7 +# In this respect, the user's attention is drawn to the risks associated with loading, using, modifying and/or developing or reproducing the software by the user in light of its specific status of free software,
  8 +# that may mean that it is complicated to manipulate, and that also therefore means that it is reserved for developers and experienced professionals having in-depth computer knowledge.
  9 +# Users are therefore encouraged to load and test the software's suitability as regards their requirements in conditions enabling the security of their systems and/or data to be ensured  and, more generally, to use and operate it in the same conditions as regards security.
  10 +# The fact that you are presently reading this means that you have had knowledge of the CeCILL v2.1 license and that you accept its terms.
  11 +
  12 +cmake_minimum_required(VERSION 2.6)
  13 +
  14 +project(i_keyboard)
  15 +
  16 +find_package(blc_channel REQUIRED)
  17 +find_package(blc_program REQUIRED)
  18 +
  19 +add_definitions(${BL_DEFINITIONS})
  20 +include_directories(${BL_INCLUDE_DIRS})
  21 +add_executable(i_keyboard src/main.cpp)
  22 +target_link_libraries(i_keyboard ${BL_LIBRARIES})
  23 +
  24 +
  25 +
  26 +
  1 +
  2 +Repos
  3 +0 0.3
  4 +1 0.1
  5 +4 0.2
  6 +5 0.5
  7 +8 0.2
  8 +
  9 +Index
  10 +0 0.3
  11 +1 0.1
  12 +2 1.0
  13 +3 0.8
  14 +4 0.2
  15 +5 0.1
  16 +
  17 +Poing
  18 +0 0.35
  19 +1 0.0
  20 +2 1.0
  21 +3 0.8
  22 +4 1.0
  23 +5 0.8
  24 +6 0.9
  25 +7 0.9
  26 +8 0.2
  1 +# Copyright ETIS — ENSEA, Université de Cergy-Pontoise, CNRS (2011 - 2016)
  2 +# Author: Arnaud Blanchard (November 2016)
  3 +# This software is governed by the CeCILL v2.1 license under French law and abiding by the rules of distribution of free software.
  4 +# You can use, modify and/ or redistribute the software under the terms of the CeCILL v2.1 license as circulated by CEA, CNRS and INRIA at the following URL "http://www.cecill.info".
  5 +# As a counterpart to the access to the source code and rights to copy, modify and redistribute granted by the license,
  6 +# users are provided only with a limited warranty and the software's author, the holder of the economic rights, and the successive licensors have only limited liability.
  7 +# In this respect, the user's attention is drawn to the risks associated with loading, using, modifying and/or developing or reproducing the software by the user in light of its specific status of free software,
  8 +# that may mean that it is complicated to manipulate, and that also therefore means that it is reserved for developers and experienced professionals having in-depth computer knowledge.
  9 +# Users are therefore encouraged to load and test the software's suitability as regards their requirements in conditions enabling the security of their systems and/or data to be ensured  and, more generally, to use and operate it in the same conditions as regards security.
  10 +# The fact that you are presently reading this means that you have had knowledge of the CeCILL v2.1 license and that you accept its terms.
  11 +
  12 +cd `dirname $0`
  13 +project=`basename $PWD`
  14 +log="/tmp/${project}_test.log"
  15 +echo
  16 +echo "Checking i_keyboard"
  17 +echo "==================="
  18 +echo "$project" > $log #We erase previous logs
  19 +echo "logs in $log"
  20 +../compile.sh . >> $log 2>&1 || { echo "ERROR: Fail compiling"; exit 1; }
  21 +echo "Compilation: ok";
  22 +echo
  23 +#Execution is hard to check as it needs to interact with the terminal
  1 +
  2 +cd `dirname $0`/..
  3 +
  4 +./run.sh i_keyboard --display
  1 +
  2 +cd `dirname $0`/..
  3 +
  4 +./run.sh i_keyboard --display | ./run.sh f_gnuplot
  1 +
  2 +cd `dirname $0`/..
  3 +
  4 +./run.sh i_keyboard --display --toggle | ./run.sh f_gnuplot --with=boxes
  1 +#include "blc_core.h"
  2 +#include "blc_channel.h"
  3 +#include "blc_program.h"
  4 +#include <unistd.h>
  5 +#include <termios.h>
  6 +#include <libgen.h> //basename
  7 +
  8 +enum{NORMAL_KEY=0, QUIT_KEY, NEUTRAL_KEY, INDEX_KEY, ARROW_CHANGE_INDEX, WRONG_KEY, DECREMENT_KEY, INCREMENT_KEY, FAST_DECREMENT_KEY, FAST_INCREMENT_KEY};
  9 +enum { LEFT_ARROW='D', RIGHT_ARROW='C', UP_ARROW='A', DOWN_ARROW='B'};
  10 +
  11 +blc_channel channel;
  12 +blc_mem table;
  13 +char *key_list;
  14 +char const *display;
  15 +int keys_nb;
  16 +uchar min, max;
  17 +uchar quitting_key, neutral_key;
  18 +float neutral_value, max_value, min_value, float_step_size=0.004;
  19 +uchar uchar_step_size=1;
  20 +
  21 +//quitting key return -1, neutral return -2;
  22 +static int update_index(int *index){
  23 + uchar answer[3];
  24 + ssize_t ret;
  25 + int index_tmp;
  26 + SYSTEM_ERROR_CHECK(ret=read(STDIN_FILENO, answer, 3), -1, "Waiting for keyboard input");
  27 + if (ret==1)
  28 + {
  29 + if (answer[0] >= min && answer[0] <= max && ((index_tmp=table.uchars[*answer-min]) != keys_nb)){
  30 + *index=index_tmp;
  31 + return INDEX_KEY;
  32 + }
  33 + else if (answer[0]==quitting_key) return QUIT_KEY;
  34 + else if (answer[0]==neutral_key) return NEUTRAL_KEY;
  35 + else fprintf(stderr, "\nThe key '%c' id '%d' is not possible. The possibilities are in '%s' or '%c' for quitting and '%c' for neutral value.\n", answer[0], answer[0], key_list, quitting_key, neutral_key);
  36 +
  37 + } if (ret==2){
  38 + switch (answer[1]){
  39 + default:fprintf(stderr, "\nImpossible key of '%ld' bytes. Byte 0 '%c','%d', byte 1, '%c','%d'\n", ret, answer[0], answer[0], answer[1], answer[1]);
  40 + break;
  41 + }
  42 + }
  43 + else if (ret==3){
  44 + if ((answer[0]==27 && answer[1]=='[') || (answer[0]==239 && answer[1]==156)) {
  45 + switch (answer[2]){
  46 + case LEFT_ARROW: case 130: *index=MAX(0, *index-1);
  47 + return ARROW_CHANGE_INDEX;
  48 + break;
  49 + case RIGHT_ARROW: case 131: *index=MIN(keys_nb-1, *index+1);
  50 + return ARROW_CHANGE_INDEX;
  51 + break;
  52 + case UP_ARROW:case 128: return INCREMENT_KEY;
  53 + break;
  54 + case DOWN_ARROW:case 129: return DECREMENT_KEY;
  55 + break;
  56 + /* case PAGE_UP:case 172: return FAST_INCREMENT_KEY;
  57 + break;
  58 + case PAGE_DOWN:case 173: return FAST_DECREMENT_KEY;
  59 + break;*/
  60 + default: fprintf(stderr, "This key does not have effect\n");
  61 + break;
  62 + }
  63 + }
  64 + printf("Answer 0 '%c','%d', answer 1, '%c','%d', 2: '%c','%d'\n" , answer[0], answer[0] , answer[1], answer[1], answer[2], answer[2]);
  65 + }else fprintf(stderr, "\nImpossible key of '%ld' bytes. Byte 0 '%c','%d', byte 1, '%c','%d'\n", ret, answer[0], answer[0], answer[1], answer[1]);
  66 +
  67 + return WRONG_KEY;
  68 +}
  69 +
  70 +/**Return display size*/
  71 +static int display_uchar(int index){
  72 + char select_char;
  73 + int i;
  74 + channel.fprint_graph_uchars(stderr, channel.name, 6, 255, 0);
  75 + fprintf(stderr, ">");
  76 + FOR(i, keys_nb){
  77 + if (index==i) select_char='#';
  78 + else select_char=' ';
  79 + fprintf(stderr, "|%c%c", select_char, key_list[i]);
  80 + }
  81 + fprintf(stderr, "|\n");
  82 + return 7;
  83 +}
  84 +
  85 +/**Return display size*/
  86 +static int display_float(int index){
  87 + char select_char;
  88 + int i;
  89 + channel.fprint_graph_floats(stderr, channel.name, 6, 1.0, 0);
  90 + fprintf(stderr, ">");
  91 + FOR(i, keys_nb){
  92 + if (index==i) select_char='#';
  93 + else select_char=' ';
  94 + fprintf(stderr, "|%c%c", select_char, key_list[i]);
  95 + }
  96 + fprintf(stderr, "|\n");
  97 + return 7;
  98 +}
  99 +
  100 +static void loop_toggle_uchar(){
  101 + int ret=NORMAL_KEY, index=0, graph_height;
  102 + if (display) graph_height=display_uchar(index);
  103 +
  104 + while (ret!=QUIT_KEY){
  105 + ret=update_index(&index);
  106 + switch (ret){
  107 + case INCREMENT_KEY:
  108 + channel.uchars[index]=BLC_NORMED_FLOAT_TO_UCHAR(max_value);
  109 + break;
  110 + case DECREMENT_KEY: channel.uchars[index]=BLC_NORMED_FLOAT_TO_UCHAR(min_value);
  111 + break;
  112 + case NEUTRAL_KEY:
  113 + channel.uchars[index]=BLC_NORMED_FLOAT_TO_UCHAR(neutral_value);
  114 + break;
  115 + case INDEX_KEY:
  116 + if (channel.uchars[index] > BLC_NORMED_FLOAT_TO_UCHAR(neutral_value)) channel.uchars[index]=BLC_NORMED_FLOAT_TO_UCHAR(min_value);
  117 + else channel.uchars[index]=BLC_NORMED_FLOAT_TO_UCHAR(max_value);
  118 + case ARROW_CHANGE_INDEX:case QUIT_KEY:
  119 + break;
  120 + default:color_eprintf(BLC_YELLOW, "Unknownd ret '%d'", ret);
  121 + }
  122 +
  123 + if (display){
  124 + graph_height=display_uchar(index);
  125 + blc_eprint_cursor_up(graph_height);
  126 + }
  127 + }
  128 +}
  129 +
  130 +static void loop_uchar(){
  131 + int ret=NORMAL_KEY, index=0, graph_height;
  132 + if (display) graph_height=display_uchar(index);
  133 +
  134 + while (ret!=QUIT_KEY){
  135 + if (display){
  136 + blc_eprint_cursor_up(graph_height);
  137 + graph_height=display_uchar(index);
  138 + }
  139 + ret=update_index(&index);
  140 + switch (ret){
  141 + case INCREMENT_KEY:
  142 + channel.uchars[index]=MIN(channel.uchars[index]+uchar_step_size, BLC_NORMED_FLOAT_TO_UCHAR(max_value));
  143 + break;
  144 + case DECREMENT_KEY: channel.uchars[index]=MAX(channel.uchars[index]-uchar_step_size, BLC_NORMED_FLOAT_TO_UCHAR(min_value));
  145 + break;
  146 + /* case FAST_INCREMENT_KEY:
  147 + channel.uchars[index]=MIN(channel.uchars[index]+10*uchar_step_size, UINT8_MAX);
  148 + previous_index=index;
  149 + break;
  150 + case FAST_DECREMENT_KEY:
  151 + channel.uchars[index]=MAX(channel.uchars[index]-10*uchar_step_size, 0);
  152 + break;*/
  153 + case NEUTRAL_KEY:
  154 + channel.uchars[index]=BLC_NORMED_FLOAT_TO_UCHAR(neutral_value);
  155 + break;
  156 + case INDEX_KEY:case QUIT_KEY:case ARROW_CHANGE_INDEX:
  157 + break;
  158 + default:EXIT_ON_ERROR("Unknownd ret '%d'", ret);
  159 + }
  160 + }
  161 +}
  162 +
  163 +static void loop_toggle_float(){
  164 + int ret=NORMAL_KEY, index=0, graph_height;
  165 + if (display) graph_height=display_float(index);
  166 +
  167 + while (ret!=QUIT_KEY){
  168 + if (display){
  169 + blc_eprint_cursor_up(graph_height);
  170 + graph_height=display_float(index);
  171 + }
  172 + ret=update_index(&index);
  173 + switch (ret){
  174 + case INCREMENT_KEY:
  175 + channel.floats[index]=max_value;
  176 + break;
  177 + case DECREMENT_KEY: channel.floats[index]=min_value;
  178 + break;
  179 + case NEUTRAL_KEY:
  180 + channel.floats[index]=neutral_value;
  181 + break;
  182 + case INDEX_KEY:
  183 + if (channel.floats[index] > neutral_value) channel.floats[index]=min_value;
  184 + else channel.floats[index]=max_value;
  185 + case ARROW_CHANGE_INDEX:case QUIT_KEY:
  186 + break;
  187 + default:color_eprintf(BLC_YELLOW, "Unknownd ret '%d'", ret);
  188 + }
  189 + }
  190 +}
  191 +
  192 +static void loop_float(){
  193 + int ret=0, index=0, previous_index=0, graph_height;
  194 + if (display) graph_height=display_float(index);
  195 +
  196 + while (ret!=QUIT_KEY){
  197 + if (display){
  198 + blc_eprint_cursor_up(graph_height);
  199 + graph_height=display_float(index);
  200 + }
  201 + ret=update_index(&index);
  202 + switch (ret){
  203 + case INCREMENT_KEY: channel.floats[index]=MIN(channel.floats[index]+float_step_size, max_value);
  204 + break;
  205 + case DECREMENT_KEY: channel.floats[index]=MAX(channel.floats[index]-float_step_size, min_value);
  206 + break;
  207 + /* case FAST_INCREMENT_KEY: channel.floats[index]+=float_step_size*10;
  208 + break;
  209 + case FAST_DECREMENT_KEY:channel.floats[index]-=float_step_size*10;
  210 + break;*/
  211 + case NEUTRAL_KEY:channel.floats[index]=neutral_value;
  212 + break;
  213 + case INDEX_KEY: case ARROW_CHANGE_INDEX:
  214 + previous_index=index;
  215 + case QUIT_KEY:break;
  216 + default:EXIT_ON_ERROR("Unknownd ret '%d'", ret);
  217 + }
  218 + }
  219 +}
  220 +
  221 +
  222 +int main(int argc, char** argv){
  223 + char *default_output=NULL;
  224 + char const *extension;
  225 + char const *channel_name, *type_str, *str_quitting_key, *str_neutral_key, *max_str, *min_str, *str_neutral_value, *toggle_mode, *step_size_str, *filename;
  226 + char const *key_nb_str;
  227 + int i;
  228 + char *pos;
  229 + uchar answer;
  230 +
  231 + asprintf(&default_output, "/%s%d", basename(argv[0]), getpid()); //This will not be free but it is only allocate once
  232 + blc_program_set_description("Get keyboard inputs");
  233 + blc_program_add_option(&display, 'd', "display", NULL, "Display a text graph (UIN8 only)", NULL);
  234 + blc_program_add_option(&filename, 'f', "file", "filename", "Initialize the values with a tsv file", NULL);
  235 + blc_program_add_option((char const**)&key_list, 'k', "key_list", "string", "Define all the keys that can be stroke", "0123456789abcdef");
  236 + blc_program_add_option(&min_str, 'm', "min", "real", "Define the normed minimum value", "0.0");
  237 + blc_program_add_option(&str_neutral_key, 'n', "neutral_key", "string", "Define the neutral key", "escape");
  238 + blc_program_add_option(&channel_name, 'o', "output", "blc_channel", "Define where the result will be put", default_output);
  239 + blc_program_add_option(&str_quitting_key, 'q', "quitting_key", "string", "Define the key used to quit", "q");
  240 + blc_program_add_option(&key_nb_str, 's', "size", "integer", "Size of key vector", NULL);
  241 + blc_program_add_option(&type_str, 't', "type", "UIN8|FL32", "Define the type of the result", "UIN8");
  242 + blc_program_add_option(&max_str, 'M', "max", "real", "Define the normed max value", "1.0");
  243 + blc_program_add_option(&str_neutral_value, 'N', "neutral_value", "real", "Define the normed neutral value", "0.5");
  244 + blc_program_add_option(&step_size_str, 'S', "step", "real", "Set the normed step size", NULL);
  245 + blc_program_add_option(&toggle_mode, 'T', "toggle", NULL, "Set in toggle mode", NULL);
  246 +
  247 + blc_program_init(&argc, &argv, blc_set_back_stdin_mode);
  248 +
  249 + if (strlen(str_quitting_key)!=1) EXIT_ON_ERROR("You can only have one quitting key. You propose %s", str_quitting_key);
  250 +
  251 + if (strcmp(str_neutral_key, "escape")==0) neutral_key=27; //Escape
  252 + else if (strlen(str_neutral_key)!=1) EXIT_ON_ERROR("You can only have one neutral key. You propose %s", str_neutral_key);
  253 + else neutral_key=str_neutral_key[0];
  254 +
  255 + neutral_value=strtof(str_neutral_value, NULL);
  256 + max_value=strtof(max_str, NULL);
  257 + min_value=strtof(min_str, NULL);
  258 +
  259 + quitting_key=str_quitting_key[0];
  260 +
  261 + if (key_nb_str) {
  262 + keys_nb=strtod(key_nb_str, NULL);
  263 + key_list=MANY_ALLOCATIONS(keys_nb, char);
  264 + FOR(i, keys_nb){
  265 + if (i<10) key_list[i]=48+i;
  266 + else key_list[i]=97+i-10;
  267 + }
  268 + }
  269 + else keys_nb=strlen(key_list);
  270 +
  271 + if (memchr(key_list, quitting_key, keys_nb)) EXIT_ON_ERROR("The quitting key '%c' is in your key list %*s, you need to select an other one with --quitting_key=...", quitting_key, keys_nb, quitting_key);
  272 + if (memchr(key_list, neutral_key, keys_nb)) EXIT_ON_ERROR("The neutral key '%c' is in your key list %*s, you need to select an other one with --neutral_key=...", quitting_key, keys_nb, neutral_key);
  273 +
  274 + channel.create_or_open(channel_name, BLC_CHANNEL_WRITE, STRING_TO_UINT32(type_str), 'NDEF', 1, keys_nb);
  275 +
  276 + if (filename){
  277 + extension = blc_get_filename_extension(filename);
  278 +
  279 + if(strcmp(extension, "blc")==0) channel.update_with_blc_file(filename);
  280 + else if (strcmp(extension, "tsv")==0) channel.update_with_tsv_file(filename);
  281 + else EXIT_ON_ERROR("'%s' is not a possible file type extension", extension);
  282 + }
  283 + else {
  284 + switch (STRING_TO_UINT32(type_str)){
  285 + case 'UIN8':
  286 + FOR(i, keys_nb) channel.uchars[i]=BLC_NORMED_FLOAT_TO_UCHAR(neutral_value);
  287 + break;
  288 + case 'FL32':
  289 + FOR(i, keys_nb) channel.floats[i]=neutral_value;
  290 + break;
  291 + default:EXIT_ON_ARRAY_ERROR(&channel, "Type not mnaged");
  292 + break;
  293 + }
  294 + }
  295 +
  296 + if (display){
  297 + if ((channel.type!='UIN8') && (channel.type!='FL32')) EXIT_ON_ARRAY_ERROR(&channel, "This type can not be displayed. Only 'UIN8' or 'FL32' can.");
  298 + }
  299 + blc_set_stdin_non_blocking_mode();
  300 +
  301 + fprintf(stderr, "Waiting for one key in '%s'. Quitting with '%c'\n", key_list, quitting_key);
  302 +
  303 + //We pre calculate the answers
  304 + max=min=key_list[0];
  305 + FOR(i, keys_nb) {
  306 + answer=key_list[i];
  307 + if (answer<min) min =answer;
  308 + else if (answer>max) max=answer;
  309 + }
  310 +
  311 + table.allocate(max-min+1);
  312 + FOR(i, table.size){
  313 + pos=strchr(key_list, i+min);
  314 + if (pos) table.uchars[i]=pos-key_list;
  315 + else table.uchars[i]=keys_nb;
  316 + }
  317 +
  318 + channel.publish();
  319 + switch (channel.type){
  320 + case 'UIN8':
  321 + if (step_size_str) uchar_step_size=strtod(step_size_str, NULL);
  322 + if (toggle_mode) loop_toggle_uchar();
  323 + else loop_uchar();
  324 + break;
  325 + case 'FL32':
  326 + if (step_size_str) float_step_size=strtof(step_size_str, NULL);
  327 + if (toggle_mode) loop_toggle_float();
  328 + else loop_float();
  329 + break;
  330 + default:
  331 + EXIT_ON_ARRAY_ERROR(&channel, "The type is not managed.");
  332 + break;
  333 + }
  334 + return EXIT_SUCCESS;
  335 +}
  336 +
  1 +# Set the minimum version of cmake required to build this project
  2 +cmake_minimum_required(VERSION 2.6)
  3 +
  4 +# Set the name of the project as the directory basename
  5 +project(i_oscillator)
  6 +
  7 +find_package(blc_channel)
  8 +find_package(blc_program)
  9 +
  10 +add_definitions(${BL_DEFINITIONS})
  11 +include_directories(${BL_INCLUDE_DIRS})
  12 +add_executable(i_oscillator i_oscillator.cpp)
  13 +target_link_libraries(i_oscillator ${BL_LIBRARIES})
  14 +
  15 +
  16 +
  17 +
  1 +//
  2 +// Created by Arnaud Blanchard on 22/12/15.
  3 +// Copyright ETIS 2015. All rights reserved.
  4 +//
  5 +#include "blc_core.h"
  6 +#include "blc_channel.h"
  7 +#include "blc_program.h"
  8 +#include <unistd.h>
  9 +#include <math.h>
  10 +
  11 +#define DEFAULT_OUTPUT_NAME ":oscillator<pid>"
  12 +
  13 +
  14 +int period;
  15 +float refresh_rate; //µs;
  16 +blc_channel output;
  17 +double step;
  18 +char const *display;
  19 +int display_height;
  20 +blc_mem graph_mem;
  21 +size_t i=0;
  22 +float gain=0;
  23 +
  24 +int columns_nb=64;
  25 +int rows_nb=16;
  26 +
  27 +static void info_cb(char const*, void*){
  28 + fprintf(stderr, "%d ", output.chars[0]);
  29 +}
  30 +
  31 +
  32 +static void period_cb(char const*argument, void*){
  33 + period=strtol(argument, NULL, 10);
  34 + step=(double)refresh_rate/(double)period;
  35 +}
  36 +
  37 +static void refresh_cb(char const*argument, void*){
  38 + refresh_rate=strtol(argument, NULL, 10);
  39 + step=(double)refresh_rate/(double)period;
  40 + blc_command_loop_period=refresh_rate*1000;
  41 +}
  42 +
  43 +static void start_float_loop(float frequency, float min, float max){
  44 + int i;
  45 + float time_gain, offset, gain;
  46 +
  47 + time_gain=frequency*2*M_PI/(float)output.total_length*refresh_rate/1000;
  48 + if (output.sem_ack_data) refresh_rate=0;
  49 +
  50 + gain=(max-min)/2.f; //amplitude sinus = 2
  51 + offset=min+gain; //min sinus*gain = -gain
  52 +
  53 + BLC_COMMAND_LOOP(refresh_rate*1000){
  54 + FOR(i, output.total_length){
  55 + output.floats[i]=sin((blc_loop_iteration*output.total_length+i)*time_gain)*gain+offset;
  56 + }
  57 + if(display){
  58 + blc_fprint_float_graph(stderr, output.floats, output.total_length, "oscillation", columns_nb, rows_nb, 1, 0, "Time", "Intensity");
  59 + blc_eprint_cursor_up(rows_nb);
  60 + }
  61 + }
  62 +}
  63 +
  64 +int main(int argc, char **argv){
  65 + char const *period_str, *refresh_rate_str, *display_str, *display_height_str, *channel_name, *buffer_length_str, *type_str, *frequency_str;
  66 + char const *min_str, *max_str;
  67 + float frequency, min, max;
  68 + int buffer_length;
  69 + uint32_t type;
  70 +
  71 + blc_program_add_option(&channel_name, 'o', "channel", "blc_channel", "channel name", DEFAULT_OUTPUT_NAME);
  72 + blc_program_add_option(&display, 'd', "display", NULL, "display value at each iteration", NULL);
  73 + blc_program_add_option(&buffer_length_str, 's', "size", "integer", "buffer size (items nb)", "1");
  74 + blc_program_add_option(&frequency_str, 'f', "frequency", "float", "frequency in Hz", "1");
  75 + blc_program_add_option(&min_str, 'm', "min", "float", "minimum value of oscillation", "0");
  76 + blc_program_add_option(&max_str, 'M', "max", "float", "maximum value of oscillation", "1");
  77 + blc_program_add_option(&refresh_rate_str, 'r', "refresh", "integer", "refresh rate in ms", "10");
  78 + blc_program_add_option(&type_str, 't', "type", "FL32", "type of data", "FL32");
  79 + blc_program_init(&argc, &argv, NULL);
  80 +
  81 + // period=strtol(period_str, NULL, 10);
  82 + SSCANF(1, refresh_rate_str, "%f", &refresh_rate);
  83 + step=(double)refresh_rate/(double)period;
  84 + type=STRING_TO_UINT32(type_str);
  85 +
  86 + if (strcmp(DEFAULT_OUTPUT_NAME, channel_name)==0) asprintf((char**)&channel_name, ":oscillator%d",getpid());
  87 +
  88 + SSCANF(1, buffer_length_str, "%d", &buffer_length);
  89 + SSCANF(1, frequency_str, "%f", &frequency);
  90 + SSCANF(1, min_str, "%f", &min);
  91 + SSCANF(1, max_str, "%f", &max);
  92 +
  93 + output.create_or_open(channel_name, BLC_CHANNEL_WRITE, type, 'NDEF', 1, buffer_length);
  94 + output.publish();
  95 +
  96 + /*
  97 + switch (output.type){
  98 + case 'INT8': gain=INT8_MAX;break;
  99 + case 'UIN8': gain=UINT8_MAX/2;break;
  100 + default:EXIT_ON_CHANNEL_ERROR(&output, "Type not managed");
  101 + }
  102 + gain *= amp;*/
  103 +
  104 + blc_command_add("i", info_cb, NULL, "display value", NULL);
  105 + blc_command_add("p", period_cb, "period(ms)", "signal period", NULL);
  106 + blc_command_add("r", refresh_cb, "step(ms)", "time step", NULL);
  107 +
  108 +
  109 + blc_loop_try_add_waiting_semaphore(output.sem_ack_data);
  110 + blc_loop_try_add_posting_semaphore(output.sem_new_data);
  111 +
  112 + start_float_loop(frequency, min, max);
  113 +
  114 + if (display) blc_eprint_cursor_down(rows_nb);
  115 +
  116 +return EXIT_SUCCESS;
  117 +}
  1 +# Copyright ETIS — ENSEA, Université de Cergy-Pontoise, CNRS
  2 +# Author: Arnaud Blanchard (November 2016)
  3 +# This software is governed by the CeCILL v2.1 license under French law and abiding by the rules of distribution of free software.
  4 +# You can use, modify and/ or redistribute the software under the terms of the CeCILL v2.1 license as circulated by CEA, CNRS and INRIA at the following URL "http://www.cecill.info".
  5 +# As a counterpart to the access to the source code and rights to copy, modify and redistribute granted by the license,
  6 +# users are provided only with a limited warranty and the software's author, the holder of the economic rights, and the successive licensors have only limited liability.
  7 +# In this respect, the user's attention is drawn to the risks associated with loading, using, modifying and/or developing or reproducing the software by the user in light of its specific status of free software,
  8 +# that may mean that it is complicated to manipulate, and that also therefore means that it is reserved for developers and experienced professionals having in-depth computer knowledge.
  9 +# Users are therefore encouraged to load and test the software's suitability as regards their requirements in conditions enabling the security of their systems and/or data to be ensured  and, more generally, to use and operate it in the same conditions as regards security.
  10 +# The fact that you are presently reading this means that you have had knowledge of the CeCILL v2.1 license and that you accept its terms.
  11 +
  12 +cmake_minimum_required(VERSION 2.6)
  13 +
  14 +#The name of the project is the basename of the directory
  15 +get_filename_component(PROJECT_NAME ${CMAKE_CURRENT_SOURCE_DIR} NAME)
  16 +project(${PROJECT_NAME})
  17 +
  18 +find_package(blc_channel REQUIRED)
  19 +find_package(blc_program REQUIRED)
  20 +
  21 +add_definitions(${BL_DEFINTIIONS})
  22 +include_directories(${BL_INCLUDE_DIRS})
  23 +add_executable(${PROJECT_NAME} i_read.cpp)
  24 +target_link_libraries(${PROJECT_NAME} ${BL_LIBRARIES})
  25 +
  26 +
  27 +
  28 +
  1 +
  2 +
  3 +Load a tsv file and put it in a blc_channel.
  4 +
  5 +It between can be done line by line --with=period value in ms separating the time two lines.
  1 +# Copyright ETIS — ENSEA, Université de Cergy-Pontoise, CNRS (2011 - 2016)
  2 +# Author: Arnaud Blanchard (November 2016)
  3 +# This software is governed by the CeCILL v2.1 license under French law and abiding by the rules of distribution of free software.
  4 +# You can use, modify and/ or redistribute the software under the terms of the CeCILL v2.1 license as circulated by CEA, CNRS and INRIA at the following URL "http://www.cecill.info".
  5 +# As a counterpart to the access to the source code and rights to copy, modify and redistribute granted by the license,
  6 +# users are provided only with a limited warranty and the software's author, the holder of the economic rights, and the successive licensors have only limited liability.
  7 +# In this respect, the user's attention is drawn to the risks associated with loading, using, modifying and/or developing or reproducing the software by the user in light of its specific status of free software,
  8 +# that may mean that it is complicated to manipulate, and that also therefore means that it is reserved for developers and experienced professionals having in-depth computer knowledge.
  9 +# Users are therefore encouraged to load and test the software's suitability as regards their requirements in conditions enabling the security of their systems and/or data to be ensured  and, more generally, to use and operate it in the same conditions as regards security.
  10 +# The fact that you are presently reading this means that you have had knowledge of the CeCILL v2.1 license and that you accept its terms.
  11 +
  12 +
  13 +cd `dirname $0`
  14 +echo
  15 +echo "Checking `basename $PWD`"
  16 +echo "===================="
  17 +echo
  18 +echo "No check for now"
  19 +echo
  1 +#include "blc_core.h"
  2 +#include "blc_channel.h"
  3 +#include "blc_program.h"
  4 +#include <unistd.h>
  5 +#include <termios.h>
  6 +#include <libgen.h> //basename
  7 +
  8 +
  9 +int file_get_lines_number(FILE* file){
  10 + int ch, lines_nb;
  11 +
  12 + lines_nb=0;
  13 + while(feof(file)==0){
  14 + ch = fgetc(file);
  15 +
  16 + if (ch == '\n') lines_nb++;
  17 + else if ( ch == EOF ) if (feof(file)==0) EXIT_ON_SYSTEM_ERROR("Reading file"</