2018-04-20 20:33:26 +00:00
|
|
|
/*! \file counter.c
|
|
|
|
\brief Frequency Counter Application
|
|
|
|
|
|
|
|
Howdy y'all,
|
|
|
|
|
|
|
|
Once upon a time, a neighbor was in Heidelberg for the Troopers
|
|
|
|
conference, and he wanted to join the staff radio networks. So he
|
|
|
|
performed the out-of-band-TX mod to his Kenwood TH-D74, and borrowed
|
|
|
|
a laptop and SDR to find the staff simplex frequency. From an audio
|
|
|
|
recording, he identified the squelch tones and was on the air.
|
|
|
|
|
|
|
|
But why did he need an SDR, when his wristwatch ought to have been
|
|
|
|
able to quickly find the peak during transmission? This applet
|
|
|
|
seeks to remedy that problem by providing a basic frequency counter,
|
|
|
|
displaying the center frequency of the strongest signal yet
|
|
|
|
observed. It is loosely based on Michael Ossmann's spectrum
|
|
|
|
analyzer, written for the Girltech IMME in 2010.
|
|
|
|
|
2018-04-22 20:14:10 +00:00
|
|
|
This implementation does a wideband sweep from 410 to 470
|
|
|
|
MHz when the 0 button is pressed. The 1 button repeats the broad
|
|
|
|
sweep without forgetting the old peak.
|
|
|
|
|
|
|
|
Generally, you will just run these all by pressing 0, but for TDMA
|
|
|
|
protocols like DMR/MotoTrbo, it's handy to rerun the scan by
|
|
|
|
starting with the 1 button.
|
|
|
|
|
|
|
|
The result will be off by up to 100kHz, so just treat the result as
|
|
|
|
a decent guess. Later versions will scan other bands.
|
2018-04-20 20:33:26 +00:00
|
|
|
|
2018-04-22 20:30:31 +00:00
|
|
|
Press '=' to copy the frequency to the VLO.
|
|
|
|
|
2018-04-20 20:33:26 +00:00
|
|
|
--Travis
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include<stdio.h>
|
|
|
|
#include<string.h>
|
|
|
|
#include<msp430.h>
|
|
|
|
#include "api.h"
|
|
|
|
|
|
|
|
|
|
|
|
/* Settings were prototyped first in Python. This is basic OOK with
|
|
|
|
no preamble, no CRC, and 341µs symbol times.
|
|
|
|
*/
|
|
|
|
#define LEN 16
|
|
|
|
static const uint8_t counter_settings[]={
|
|
|
|
/* IF of 457.031 kHz */
|
|
|
|
FSCTRL1, 0x12,
|
|
|
|
FSCTRL0, 0x00,
|
|
|
|
|
|
|
|
/* disable 3 highest DVGA settings */
|
|
|
|
//AGCCTRL2 |= AGCCTRL2_MAX_DVGA_GAIN;
|
|
|
|
|
|
|
|
/* frequency synthesizer calibration */
|
|
|
|
FSCAL3, 0xEA,
|
|
|
|
FSCAL2, 0x2A,
|
|
|
|
FSCAL1, 0x00,
|
|
|
|
FSCAL0, 0x1F,
|
|
|
|
|
|
|
|
/* "various test settings" */
|
|
|
|
TEST2, 0x88,
|
|
|
|
TEST1, 0x31,
|
|
|
|
TEST0, 0x09,
|
|
|
|
|
|
|
|
/* no automatic frequency calibration */
|
|
|
|
MCSM0, 0,
|
|
|
|
|
|
|
|
|
2018-04-22 20:14:10 +00:00
|
|
|
/* Filter bandwidth. 270kHz seems to work best,
|
|
|
|
even though it could be narrower. */
|
|
|
|
//MDMCFG4, 0xFC, /* 58.0357 kHz */
|
2018-04-20 20:33:26 +00:00
|
|
|
//MDMCFG4, 0xEC, /* 67.708333 kHz */
|
|
|
|
MDMCFG4, 0x6C, /* 270.833333 kHz */
|
2018-04-22 20:14:10 +00:00
|
|
|
//MDMCFG4, 0x0C, /* 812.5 kHz */
|
2018-04-20 20:33:26 +00:00
|
|
|
|
|
|
|
//End with null terminator.
|
|
|
|
0,0
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/* This enum manages the state machine for the frequency counter. The
|
|
|
|
state will be IDLE before and after the sweep.
|
|
|
|
*/
|
|
|
|
static enum {IDLE, SWEEP} counter_state;
|
|
|
|
static int best_rssi;
|
|
|
|
static float best_freq;
|
2018-04-22 20:14:10 +00:00
|
|
|
#define MAX_BIGFREQ 470000000.0 //Range is 410 to 470 for now.
|
|
|
|
#define MIN_BIGFREQ 410000000.0 //Will open more bands later.
|
|
|
|
#define BIGSTEP_FREQ 100000.0 //100kHz/Step
|
2018-04-20 20:33:26 +00:00
|
|
|
static float current_freq;
|
|
|
|
|
|
|
|
//! Try a given frequency, and update display if it's best.
|
|
|
|
static void try_freq(float freq){
|
|
|
|
int rssi;
|
2018-04-22 20:14:10 +00:00
|
|
|
|
2018-04-20 20:33:26 +00:00
|
|
|
//Set the frequency.
|
|
|
|
radio_setfreq(freq);
|
2018-04-22 20:14:10 +00:00
|
|
|
|
2018-04-20 20:33:26 +00:00
|
|
|
//Get the RSSI.
|
|
|
|
rssi=radio_getrssi();
|
|
|
|
|
|
|
|
//Compare it.
|
|
|
|
if(rssi>best_rssi){
|
|
|
|
best_rssi=rssi;
|
2018-04-22 20:14:10 +00:00
|
|
|
best_freq=freq;
|
2018-04-20 20:33:26 +00:00
|
|
|
}
|
2018-04-22 20:14:10 +00:00
|
|
|
|
2018-04-20 20:33:26 +00:00
|
|
|
}
|
|
|
|
|
2018-04-22 20:14:10 +00:00
|
|
|
|
|
|
|
|
|
|
|
//! Try the next frequency in the wide set.
|
|
|
|
static void try_nextbig(){
|
2018-04-20 20:33:26 +00:00
|
|
|
//Enforce the range here.
|
2018-04-22 20:14:10 +00:00
|
|
|
if(current_freq<MIN_BIGFREQ)
|
|
|
|
current_freq=MIN_BIGFREQ;
|
2018-04-20 20:33:26 +00:00
|
|
|
|
|
|
|
//Try the next center freq and step ahead.
|
|
|
|
try_freq(current_freq);
|
2018-04-22 20:14:10 +00:00
|
|
|
current_freq+=BIGSTEP_FREQ;
|
2018-04-20 20:33:26 +00:00
|
|
|
|
|
|
|
//We're done!
|
2018-04-22 20:14:10 +00:00
|
|
|
if(current_freq>MAX_BIGFREQ){
|
|
|
|
current_freq=MIN_BIGFREQ;
|
2018-04-20 20:33:26 +00:00
|
|
|
counter_state=IDLE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//! Enter the Counter application.
|
|
|
|
void counter_init(){
|
|
|
|
/* This enters the application.
|
|
|
|
We ignore the codeplug frequency and set our own.
|
|
|
|
*/
|
|
|
|
if(has_radio){
|
|
|
|
//Faster processing time, for rapid packet succession.
|
|
|
|
ucs_fast();
|
|
|
|
|
|
|
|
radio_on();
|
|
|
|
radio_writesettings(counter_settings);
|
|
|
|
|
|
|
|
//Initialize state variables.
|
|
|
|
counter_state=IDLE;
|
|
|
|
best_rssi=0;
|
|
|
|
best_freq=0;
|
|
|
|
}else{
|
|
|
|
app_next();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//! Exit the Counter application.
|
|
|
|
int counter_exit(){
|
|
|
|
//Cut the radio off and drop the CPU frequency.
|
|
|
|
radio_off();
|
|
|
|
ucs_slow();
|
|
|
|
|
|
|
|
//Allow the exit.
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-04-20 21:13:34 +00:00
|
|
|
//! Draw the counter's status.
|
|
|
|
static void counter_drawstatus(){
|
|
|
|
lcd_number(current_freq/10);
|
|
|
|
}
|
|
|
|
|
2018-04-20 20:33:26 +00:00
|
|
|
//! Draw the Counter screen.
|
|
|
|
void counter_draw(){
|
|
|
|
switch(counter_state){
|
|
|
|
case IDLE:
|
|
|
|
if(best_rssi==0)
|
|
|
|
lcd_string("CNT IDLE");
|
|
|
|
else
|
|
|
|
lcd_number(best_freq/10);
|
|
|
|
break;
|
|
|
|
case SWEEP:
|
2018-04-22 20:14:10 +00:00
|
|
|
//lcd_string("SWEEPING");
|
2018-04-20 20:33:26 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//! Keypress handler for the Counter applet.
|
|
|
|
int counter_keypress(char ch){
|
2018-04-20 21:13:34 +00:00
|
|
|
int i=0;
|
2018-04-22 20:14:10 +00:00
|
|
|
|
|
|
|
|
|
|
|
/* No break statements because the stages run in order.
|
|
|
|
*/
|
2018-04-20 20:33:26 +00:00
|
|
|
switch(ch){
|
2018-04-22 20:14:10 +00:00
|
|
|
case '0': //Begin a new broad sweep.
|
2018-04-20 20:33:26 +00:00
|
|
|
best_rssi=0;
|
2018-04-22 20:14:10 +00:00
|
|
|
|
|
|
|
case '1': //Second pass of the broad sweep sweep.
|
2018-04-20 20:33:26 +00:00
|
|
|
counter_state=SWEEP;
|
2018-04-22 20:14:10 +00:00
|
|
|
current_freq = MIN_BIGFREQ;
|
|
|
|
|
2018-04-20 20:33:26 +00:00
|
|
|
//Do the sweep.
|
2018-04-20 21:13:34 +00:00
|
|
|
while(counter_state==SWEEP){
|
2018-04-22 20:14:10 +00:00
|
|
|
try_nextbig();
|
2018-04-20 21:13:34 +00:00
|
|
|
//Draw every 64th channel.
|
|
|
|
if((i++&0x3f)==0)
|
|
|
|
counter_drawstatus();
|
|
|
|
}
|
2018-04-22 20:14:10 +00:00
|
|
|
|
2018-04-20 20:33:26 +00:00
|
|
|
break;
|
2018-04-22 20:30:31 +00:00
|
|
|
|
|
|
|
case '=': //Copy best freq to VFO.
|
|
|
|
codeplug_setvfofreq(best_freq);
|
|
|
|
break;
|
2018-04-20 20:33:26 +00:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|