Любительский пеленгатор
https://vhfdx.in.ua/index.php/pub/tech/28-lyubitel-skij-pelengator
В процессе подготовки к запускам воздушных шаров возникла задача определения направления на шар, который передает телеметрию по радиоканалу. Для этих целей был собран простой квазидопплеровский пеленгатор.Доплеровский пеленгатор относится к фазовым пеленгаторам, которые извлекают информацию о направлении распространения электромагнитной волны из пространственного расположения линий или поверхностей с одинаковой фазой. Принцип действия такого пеленгатора сводится к использованию обусловленной эффектом Допплера фазовой модуляции, возникающей при круговом вращении приемной антенны. Относительное перемещение приемника и передатчика приводит к изменению частоты (фазы) принимаемых колебаний. В этом случае частота принимаемых колебаний меняется и отличается от частоты передатчика.
Обусловленное вращением антенны приращение фазы наводимой в ней ЭДС отрицательно в промежутки времени, когда антенна удаляется от передатчика. В этом случае проекция вектора скорости движения антенны на линию OO' совпадает с направлением распространения радиоволны (рис. 1). Приращение фазы положительно, когда антенна приближается к передатчику, и равно нулю, когда антенна движется перпендикулярно направлению распространения.
Если принятые колебания усилить и подать на фазовый детектор, то при малых значениях R/λ на выходе детектора можно получить колебания модулирующей частоты Ω, фаза которых соответствует азимуту на пеленгуемую радиостанцию.
На практике вместо вращающихся антенн применяют системы расположенных по окружности неподвижных антенн, которые каким-либо способом поочередно с частотой Ω подключаются ко входу приемника. Такой пеленгатор получил название квазидоплеровского пеленгатора.
Конструкция такого квазидопплеровского пеленгатора была реализована с использованием дешевого SDR приемника, антенного коммутатора на микросхеме HMC253, переключением которого управляет контроллер Arduino Nano, а обработка и отображение информации на экране обеспечивает программа, созданная в Gnuradio, установленного на микрокомпьютере под управлением одного из клонов ОС Linux (структурная схема представлена ниже).
Антенный коммутатор установлен на монтажной плате вместе с SMA разъемами для подключения антенн по ВЧ кабелям (см. фото платы ниже) и управляется сигналами, которые подаются с Arduino Nano.
Arduino получает аудио сигнал от микрокомпьютера, предварительно преобразованного из синусоидального сигнала в меандр с помощью триггера Шмитта на микросхеме 7414. Скетч (программа Arduino) вначале выполняет измерение входной частоты и определяет с помощью цифрового компаса начальный азимут установленной антенны. После этого скетч определяет по возрастанию переднего фронта меандра начало цикла коммутации антенн и формирует сигналы управления переключения антеннами (выводы 5-8 Arduino Nano), которые соответствуют 1/4 периода входного сигнала (для количества антенн равного 4).
Прототип антенного коммутатора представлен на фото ниже:
Текст скетча:
Код
/* UKRHAB copyright (C) 2013 UY0LL Alexander Doschich
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
// Frequency counter sketch, for measuring frequencies low enough to execute an interrupt for each cycle
// Connect the frequency source to the INT0 pin (digital pin 2 on an Arduino Nano)
#include <digitalWriteFast.h>
#include <Wire.h>
#include <HMC5883L.h>
#define HEADING_MULTIPLIER 1
HMC5883L compass;
int ant, ant_prev, timer;
volatile unsigned long firstPulseTime;
volatile unsigned long lastPulseTime;
volatile unsigned long numPulses;
float freq, n_ant=4;
unsigned int latency;
unsigned int latencySum;
unsigned int sampleCount;
unsigned char timerLoadValue;
#define TIMER_CLOCK_FREQ 250000.0 //250kHz for /64 prescale from 16MHz
// interrupt service routine (ISR) is called when the timer interrupt
ISR(TIMER2_COMPA_vect)
{
switch(ant)
{
case 5: {
digitalWriteFast(6,HIGH);
digitalWriteFast(5,LOW);
// EIMSK |= (1 << INT0); // Разрешить прерывание
ant_prev = 5;
ant++;
SetupTimer2(freq*n_ant); // Разрешить прерывание
break;
}
case 6: {
digitalWriteFast(7,HIGH);
digitalWriteFast(6,LOW);
ant_prev++;
ant++;
SetupTimer2(freq*n_ant); // Разрешить прерывание
break;
}
case 7: {
digitalWriteFast(8,HIGH);
digitalWriteFast(7,LOW);
TIMSK2 = 0x00;
break;
}
}
}
//Setup Timer2.
//Configures the ATMegay168 8-Bit Timer2 to generate an interrupt at the specified frequency.
//Returns the time load value which must be loaded into TCNT2 inside your ISR routine.
//See the example usage below.
unsigned char SetupTimer2(float timeoutFrequency){
unsigned char timer;
//Calculate the timer load value
timer = (int)((TIMER_CLOCK_FREQ/(timeoutFrequency*1.02))+0.5); //the 0.5 is for rounding;
//Timer2 Settings: Timer Prescaler /8, mode 0
//Timmer clock = 16MHz/64 = 250khz or 4us
//The /8 prescale gives us a good range to work with so we just hard code this for now.
TCCR2B = 0<<CS22 | 1<<CS21 | 1<<CS20;
// set WGM to CTC mode (010)
// In this mode Timer2 counts up until it matches OCR2A
// we need to use OCR2A instead of the overflow so we can interrupt
// more often
TCCR2A = _BV(WGM21);
OCR2A = timer;
// When the OCR2A register matches the Timer2 count, cause an interrupt
TIMSK2 = _BV(OCIE2A);
}
void isr()
{
unsigned long now = micros();
if (numPulses == 1)
{
firstPulseTime = now;
}
else
{
lastPulseTime = now;
}
++numPulses;
}
void audio()
{
digitalWriteFast(5,HIGH);
digitalWriteFast(8,LOW);
ant_prev = 8;
ant = 5;
SetupTimer2(freq*n_ant); // Разрешить прерывание
}
// Measure the frequency over the specified sample time in milliseconds, returning the frequency in Hz
float readFrequency(unsigned int sampleTime)
{
numPulses = 0; // prime the system to start a new reading
attachInterrupt(0, isr, RISING); // enable the interrupt
delay(sampleTime);
detachInterrupt(0);
return (numPulses < 3) ? 0 : (1000000.0 * (float)(numPulses - 2))/(float)(lastPulseTime - firstPulseTime);
}
void setupHMC5883L(){
//Setup the HMC5883L, and check for errors
int error;
error = compass.SetScale(1.3); //Set the scale of the compass.
if(error != 0) Serial.println(compass.GetErrorText(error)); //check if there is an error, and print if so
compass.SetMeasurementMode(Measurement_Continuous); // Set the measurement mode to Continuous
}
float getHeading(){
//Get the reading from the HMC5883L and calculate the heading
MagnetometerScaled scaled = compass.ReadScaledAxis(); //scaled values from compass.
float heading = atan2(scaled.YAxis, scaled.XAxis);
// Correct for when signs are reversed.
if(heading < 0) heading += 2*PI;
if(heading > 2*PI) heading -= 2*PI;
return heading * RAD_TO_DEG; //radians to degrees
}
void setup()
{
int i=0;
float freq1=0, azimuth = 0;
String azimuth_string;
Wire.begin();
Serial.begin(19200); // this is here so that we can print the result
compass = HMC5883L(); //new instance of HMC5883L library
setupHMC5883L(); //setup the HMC5883L
pinMode(5, OUTPUT);
pinMode(6, OUTPUT);
pinMode(7, OUTPUT);
pinMode(8, OUTPUT);
digitalWrite(5,0);
digitalWrite(6,0);
digitalWrite(7,0);
digitalWrite(8,0);
while(i <= 19)
{
azimuth = azimuth + getHeading();
i++;
delay(100);
}
azimuth=azimuth/20;
// Serial.println(azimuth);
// The C command that reports azimuth
Serial.print(F("Az= "));
azimuth_string = String(int(azimuth/HEADING_MULTIPLIER), DEC);
if (azimuth_string.length() == 1) {
Serial.print(F("00"));
} else {
if (azimuth_string.length() == 2) {
Serial.print(F("0"));
}
}
Serial.print(azimuth_string);
Serial.println("grad");
while(readFrequency(1000) < 200) { delay(1000); }
freq = readFrequency(1000);
while( (int)freq != (int)freq1 ){
freq1=freq;
freq = readFrequency(1000);
delay(1000);
}
Serial.print("Freq= ");
Serial.print(freq);
Serial.println("Hz");
attachInterrupt(0, audio, RISING); // enable the interrupt
}
void loop()
{
}
Принятый радиосигнал с антенного коммутатора поступает на SDR приемник и полученный поток IQ данных попадает через USB порт на компьютер для обработки и отображения результатов измерения. Программа обработки в виде флоуграфа Gnuradio представлена ниже (тект в файле df.grc):
Программа выполняет следующие задачи :
- формирует аудиосигнал управления переключением антенн;
- получает поток данных от SDR приемника и демодулирует узкополосную ЧМ;
- с помощью DSP выделяет из аудио сигнала частотную составляющую, соответствующую опорному сигналу антенного коммутатора;
- определяет разность фаз между опорным сигналом и частотной составляющей опорного сигнала в принятом радиосигнале;
- отображает спектр сигнала и направление на источник сигнала.
Пример отображения процесса измерений в прилагаемом видео:
http://vhfdx.in.ua/images/public/pbl/df/phase.flv
Портативный вариант комплекта, состоящего из микрокомпьютера и LCD экрана:
Дополнительные данные по микросхемам:Антенный коммутатор HMC253
Триггер Шмитта 7414 (аналог 555ТЛ2)
Трех осевой компас HMC5883
[ На главную] [ В раздел Пеленгация]