TETRA (1) uplink (päätelaitteesta tukiasemaan) kanavien digitaalista aikajakoista (TDMA-kanavointi) radioliikennettä voi havaita tavallisella radioskannerilla, esim. Uniden UBC-3300XLT skannerilla (2). Uplink kanavien taajuudet kannattaa tallentaa muistipaikkoihin ja lähetelajiksi laittaa WFM tai AM. Häiriöiden vähentämiseksi kannattaa käyttää TETRA bandille tarkoitettua antennia (3). Kaupunkialueilla skannaus voi tökkiä häiriöihin vähän väliä vaikka olisi bandille viritetty antenni.
TETRA uplink lähete kuuluu skannerin kaiuttimesta "napsutus" äänenä.

Signaalien havainnoinnissa voi käyttää myös softaa ja UBC-3300XLT:n tietokoneohjaus (COM, sarjaportti) protokollan SG komentoa (5). SG (Signal stength) komennolla saadaan 3300 skannerista signaalinvoimakkuustieto tarkemmalla tasolla kuin skannerin näytöllä näkyvistä S-mittarin palkeista. Artikkelin lopussa oleva (5. kuvaan liittyvä) koodi on kirjoitettu Borland C++ Builderillä (Windows) ja se käyttää ilmaista TComPort sarjaportti komponenttia (4). SG komennon lisäksi signaalin tasosta saa tietoa lähettämällä skanneriin WI (Windows voltage level) komennon. SG ja WI palauttavat arvon 0-255 ja ne toimivat ainakin UBC-3300XLT, UBC-780XLT ja UBC-785XLT skannerien tietokoneohjaus protokollassa.  
 

1. http://fi.wikipedia.org/wiki/TETRA
2. http://www.rigpix.com/bearcat/ubc3300xlt.htm
3. http://www.antennaexperts.in/product-detail.asp?id=35
4. http://sourceforge.net/projects/comport/

5.
tetrameterscreen4.jpg

//---------------------------------------------------------------------------

#include <vcl.h>
#include <exception>
#include <iostream>
#pragma hdrstop

#include "main.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma link "CPort"
#pragma resource "*.dfm"

TfrmMain *frmMain;

int  nCmd = 0;
int  nPos = 125;
bool bBA  = false;
bool bBEEP = false;
bool bLOG = false;
int  nFileHandle = -1;
int  nStatFile = -1;
AnsiString sRx("");

int nMin = 999;
int nMax = -1;
int nAMin = 999;
int nAMax = -1;

Cardinal nCount = 0;
Cardinal nACount = 0;
Cardinal nCountSum = 0;
Cardinal nACountSum = 0;

Cardinal *sg_stat;

const int SG_STAT_SIZE          = 256;
const char *KModuleName         = "TetraMeter3300C";
const char *KModuleDesc         = "Tetra (TDMA) signal interceptor for the Uniden Bearcat UBC-3300XLT";
const char *KModuleNote         = "";
const char *KVersion            = "version 1.05";
const char *KCopyright          = "";
const char *KDeveloped          = "TetraMeter3300C is developed with Borland C++ Builder Personal version 6.0";

//---------------------------------------------------------------------------
__fastcall TfrmMain::TfrmMain(TComponent* Owner)
        : TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TfrmMain::ComPortRxChar(TObject *Sender, int Count)
{
 AnsiString buf = "";
 int nSG;

 if (ComPort->ReadStr(buf, Count));
 {
     sRx.operator +=(buf);
     if (sRx.Length() > 13)
     {
     switch (nCmd)
     {
        case 2:
        {
           AnsiString sSG = sRx.SubString(2, 3);
           nSG = sSG.ToInt();
           sg_stat[nSG] = sg_stat[nSG] + 1;
           if (nSG > nPos)
           {
               nACountSum += nSG;
               nACount++;
               lbACntVal->Caption = IntToStr(nACount);
               lbAAvgVal->Caption = FormatFloat("0.00", nACountSum / nACount);
               if (nSG > nAMax) { nAMax = nSG; lbAMaxVal->Caption = sSG; }
               if (nSG < nAMin) { nAMin = nSG; lbAMinVal->Caption = sSG; }
               lbAlert->Caption = sSG.operator +("\t").operator +
                                 (sRx.SubString(7, 4).operator +(".").operator +(sRx.SubString(11, 4))).operator +("\t").operator +(Now());
               lbLastAlert->Caption = lbAlert->Caption;
               if ((bBA) && (!bBEEP))
               {
                   MessageBeep(0xFFFFFFFF);
                   bBEEP = true;
               }
               if (bLOG)
               {
                   sRx = lbAlert->Caption +("\r\n");
                   FileWrite(nFileHandle, sRx.c_str(), sRx.Length());
               }
           }
           else
           {
               nCount++;
               nCountSum += nSG;
               lbGAvgVal->Caption = FormatFloat("0.00", nCountSum / nCount);
               if (nSG > nMax) { nMax = nSG; lbGMaxVal->Caption = sSG; }
               if (nSG < nMin) { nMin = nSG; lbGMinVal->Caption = sSG; }
               lbGCntVal->Caption = IntToStr(nCount);
               lbAlert->Caption = "";
               bBEEP = false;
           }
           sRx = "";
           ComPort->WriteStr("SG\r\n");
           break;
        }
        case 1:
        {
           stMain->Panels->Items[0]->Text = sRx.SubString(4, 10);
           pbStart->Enabled = true;
           pbConnect->Enabled = false;
           pbStop->Enabled = false;
           nCmd = 0;
           break;
        }
     }
     }
 }
}
//---------------------------------------------------------------------------


void __fastcall TfrmMain::itemExitClick(TObject *Sender)
{
 frmMain->Close();
}
//---------------------------------------------------------------------------

void __fastcall TfrmMain::FormClose(TObject *Sender, TCloseAction &Action)
{
 if (ComPort->Connected)
 {
     ComPort->Close();
 }
 if (nFileHandle >= 0)
 {
     FileClose(nFileHandle);
 }
 if (nStatFile >= 0)
 {
     FileClose(nStatFile);
 }
 if (sg_stat != NULL) delete[] sg_stat;
}
//---------------------------------------------------------------------------

void __fastcall TfrmMain::itemAboutClick(TObject *Sender)
{
 AnsiString aboutText;
 aboutText.sprintf("%s %s\n%s\n%s\n%s", KModuleName, KVersion, KModuleDesc, KModuleNote, KCopyright);
 MessageDlg(aboutText, mtInformation, TMsgDlgButtons() << mbOK, 0);
}
//---------------------------------------------------------------------------

void __fastcall TfrmMain::FormShow(TObject *Sender)
{
 ComPort->Close();
 stMain->Panels->Items[0]->Text = "NO CONNECTION";
 stMain->Panels->Items[1]->Text = "";
 InitValues();
 sg_stat = NULL;
 try
 {
   sg_stat = new Cardinal[SG_STAT_SIZE];
 }
 catch (std::bad_alloc)
 {
  sg_stat = NULL;
 }
}
//---------------------------------------------------------------------------

void __fastcall TfrmMain::tbSGChange(TObject *Sender)
{
 nPos = tbSG->Position;
 lbSG->Caption = IntToStr(tbSG->Position);
}
//---------------------------------------------------------------------------

void __fastcall TfrmMain::pbConnectClick(TObject *Sender)
{
 ComPort->ShowSetupDialog();

 try
 {
    ComPort->Open();
 }
 catch (Exception &ex)
 {
    MessageDlg(ex.Message, mtError, TMsgDlgButtons() << mbOK, 0);
    return;
 }
 nCmd = 1;
 sRx = "";
 ComPort->WriteStr("SI\r\n");
}
//---------------------------------------------------------------------------

void __fastcall TfrmMain::pbStartClick(TObject *Sender)
{
 if (FileExists("SG_STATS.TXT"))
     nStatFile = FileOpen("SG_STATS.TXT", fmOpenWrite);
 else
     nStatFile = FileCreate("SG_STATS.TXT");

 if (nStatFile >= 0)
     stMain->Panels->Items[1]->Text = ExtractFilePath(ParamStr(0)).operator +("SG_STATS.TXT");

 if (FileExists("ALERTLOG.TXT"))
     nFileHandle = FileOpen("ALERTLOG.TXT", fmOpenWrite);
 else
     nFileHandle = FileCreate("ALERTLOG.TXT");

 if (nFileHandle < 0)
     bLOG = false;
 else
     bLOG = cbLOG->Checked;

 pbStart->Enabled = false;
 pbStop->Enabled = true;
 bBEEP = false;
 InitValues();
 sRx = "";
 nCmd = 2;
 ComPort->WriteStr("SG\r\n");
}
//---------------------------------------------------------------------------

void __fastcall TfrmMain::cbBAClick(TObject *Sender)
{
 bBA = cbBA->Checked;
}
//---------------------------------------------------------------------------

void __fastcall TfrmMain::pbStopClick(TObject *Sender)
{
 AnsiString sStatRow;
 nCmd = 0;
 if (nFileHandle >= 0)
 {
     FileClose(nFileHandle);
     nFileHandle = -1;
 }
 if (nStatFile >= 0)
 {
     for (int n = 0; n < SG_STAT_SIZE; n++)
     {
        sStatRow = IntToStr(n).operator +("\t").operator +(IntToStr(sg_stat[n])).operator +("\r\n");
        if (sg_stat[n] > 0) FileWrite(nStatFile, sStatRow.c_str(), sStatRow.Length());
     }
     sStatRow = "\r\n\r\n";
     FileWrite(nStatFile, sStatRow.c_str(), sStatRow.Length());
     FileClose(nStatFile);
     nStatFile = -1;
 }
 pbStart->Enabled = true;
 pbStop->Enabled = false;
}
//---------------------------------------------------------------------------

void __fastcall TfrmMain::cbLOGClick(TObject *Sender)
{
 bLOG = cbLOG->Checked;
}

void TfrmMain::InitValues(void)
{
 lbGMinVal->Caption     = "";
 lbGMaxVal->Caption     = "";
 lbGCntVal->Caption     = "";
 lbGAvgVal->Caption     = "";
 lbAMinVal->Caption     = "";
 lbAMaxVal->Caption     = "";
 lbACntVal->Caption     = "";
 lbAAvgVal->Caption     = "";
 lbAlert->Caption       = "";
 lbLastAlert->Caption   = "";
 
 nMin           = 999;
 nMax           = -1;
 nAMin          = 999;
 nAMax          = -1;
 nCount         = 0;
 nACount        = 0;
 nCountSum      = 0;
 nACountSum     = 0;

 if (sg_stat != NULL)
 {
   for (int n = 0; n < SG_STAT_SIZE; n++) sg_stat[n] = 0;
 }
}

//---------------------------------------------------------------------------