7.1 – Exemplos de uso
As funções apresentadas aqui permitem executar processamento em tempo real em diversas situações. Algumas delas são apresentadas a seguir, junto com um comentário a respeito das vantagens encontradas em cada estratégia. Todos os exemplos seguem o roteiro simplificado de um simulador na forma
while (t < t_end) {
t = get_current_time ();
sat_propagation();
no qual, obviamente, foram suprimidas as funções de configuração da simulação, cálculo do controle, armazenagem da atitude ou envio dos sinais dos sensores. Nos próximos exemplos as funções de tempo-real são destacadas em negrito.
1) Processamento com tempo de espera
Neste exemplo utiliza-se a função rt_wait até que o tempo de execução coincida com o tempo de propagação.
rt_reset_timer();
rt_wait (get_step_time()-rt_timer());
Caso haja uma interrupção causada pelo sistema operacional durante a execução da malha, esta estratégia consegue recuperar o sincronismo somente após vários ciclos de integração. Contudo, no teste efetuado num computador AMD Athlon 64 3400, 2.4 GHz e sistema operacional Windows XP 32 bits, o tempo de processamento necessário para a propagação da atitude ficou inferior ao incremento do relógio interno, de 0.015s. Neste computador, a função rt_timer retorna com um valor nulo ao ser chamada no argumento de rt_wait, que, por sua vez, irá deter o processamento por um intervalo de tempo igual ao próprio passo de integração.
Outra forma de se efetuar a espera para efetuar o sincronismo é com o uso da função rt_wait_till. Devido à forma com que a propagação de atitude é efetuada, este método é mais eficiente do que o anterior. O código simplificado é dado por:
istat = rt_wait_till (t);
Percebe-se que a estratégia de deter o processamento é eficiente apenas em intervalos grandes de tempo, maiores do que o incremento do relógio, que deve ser determinado para cada máquina. Se o passo de integração for inferior ao incremento do relógio haverá oscilação no sincronismo, como mostra a Figura 7.1, na qual utilizou-se um passo de 0,001s.
Fig. 7.1 – Erro de sincronismo com passo fixo de 0,001s e processamento com tempo de espera.
É óbvio que o erro máximo atingido por esta estratégia é próximo do incremento do relógio, que neste caso é de 15 ms.
2) Ajuste dinâmico do passo de integração
No exemplo a seguir procura-se ajustar dinamicamente o próximo passo de integração ao intervalo de tempo decorrido para calcular o último passo. Este método pode incorporar, com poucas alterações, uma mudança gradual do passo que passaria a ter um caráter de passo médio. O código simplificado para o ajuste dinâmico seria:
set_step_time (rt_timer());
set_int_step_time (get_step_time);
Se a velocidade de execução do computador for compatível com o incremento do relógio interno, então o procedimento apontado acima funciona adequadamente. Esta compatibilidade é verificada quando o tempo de processamento for superior ao incremento apresentado pelo relógio. Porém, quando a velocidade de processamento for elevada, pode acontecer do relógio interno acusar um intervalo de tempo nulo num ciclo de propagação, isto é, a função rt_timer retorna um valor nulo. Neste caso duas soluções são possíveis. Na primeira introduz-se um tempo de espera no processamento, semelhante aquele adotado no exemplo 1 acima, até que o processamento atinja um incremento do relógio interno. Na segunda solução, o passo só é ajustado quando houver leitura suficiente do relógio para isso. A primeira solução leva ao código:
if (rt_timer() == 0) {
rt_wait (rt_clock_resolution());
auxi = rt_timer();
set_step_time (auxi);
set_int_step_time (get_step_time());
Nota-se que esta solução despreza completamente o tempo de processamento de um ciclo de integração, ou seja, supõe que este tempo seja nulo, o qual, na verdade, não é. Uma vez que não há como medir intervalos menores do o incremento do relógio, então esta solução irá apresentar sempre uma defasagem no tempo com relação ao tempo real. Verificou-se, com isso, que o incremento do relógio interno não é de 1 ms, mas sim de 15 a 16 ms. Embora a função rt_wait seja utilizada para introduzir um tempo de espera de 1 ms (valor da resolução do relógio), na verdade ela aguarda cerca de 15 ms. Em outras palavras, não há como provocar uma espera no processamento de tempos inferiores a 15 ms. No teste efetuado o passo foi ajustado a cada ciclo para valores situados entre 15 e 16 ms.
O segundo método utiliza um código semelhante a
icont = 0;
tant = rt_timer();
if (tant < auxi) {
set_step_time ((auxi-tant)/(double)icont);
tant = auxi;
Assim, nesta solução o passo só é ajustado quando houver resolução suficiente no relógio interno. Para dar mais segurança, a variável scale produz um intervalo de tempo ainda maior, o que permite uma média também melhorada do passo. A variável icont enumera o número de propagações efetuadas neste intervalo de tempo, e é utilizada para definir o passo. A execução deste código no computador mencionado no primeiro exemplo gerou um passo médio de 45,8 ms, ou seja, uma freqüência de cerca de 21,8 kHz, muito superior àquela conseguida com tempo de espera no processamento. Contudo, percebe-se que este código não garante o sincronismo de tempo, uma vez que apenas o passo é ajustado, e não o sincronismo. De fato, na figura 7.2 percebe-se que o erro no tempo real, ou seja, na diferença entre o instante de propagação e o instante do relógio é sempre crescente. Percebe-se também que a defasagem entre estes tempos aumenta sempre que houver uma alteração na velocidade de execução. A figura 7.3 mostra a freqüência utilizada a cada ajuste do passo.
Fig. 7.2 – Erro no ajuste de sincronismo entre o tempo de processamento e o tempo do relógio interno.
Fig. 7.3 – Freqüência calculada para o passo de integração quando se utiliza passo variável.
Para corrigir esta defasagem no tempo, deve-se introduzir, além do ajuste do passo, também um critério de sincronização. Este critério deverá aumentar o número de ciclos sempre que houver uma defasagem no sincronismo, e, desta forma, manter o erro sempre próximo de um valor nulo. O código com ajuste de sincronismo fica:
set_step_time ((2*auxi-tant-t)/(double)icont);
Como pode ser visto na Figura 7.4, após esta correção o sincronismo fica garantido e o erro fica menor, com média nula.
Fig. 7.4 – Erro de sincronismo com ajuste dinâmico de passo e correção de sincronismo.
O algoritmo mostrado no código anterior foi implementado na função rt_adjust_step_time. O código abaixo exemplifica a utilização desta função. O resultado é idêntico ao ajuste automático de passo com sincronização dos instantes.
set_step_time(rt_adjust_step_time(t, get_step_time()));
set_int_step_time(get_step_time());
O sincronismo do processamento com tempo de espera é mais adequado para simular a malha de controle, uma vez que esta malha deve ser executada numa freqüência fixa, da ordem de 0,5 a 2 Hz, utilizada comumente em satélites. Por outro lado, o sincronismo com passo ajustável é propício para simulação e propagação da atitude, pois satisfaz dois requisitos: a) torna a malha de controle dessincronizada com relação à malha de propagação, e b) maximiza o número de amostras da atitude por unidade de tempo.