Low profile duplex UHF Repeater for Ham radio Emergency Communications

on .

A few years ago I start to think about a small, portable and very reliable duplex repeater for emergency communications.
The first condition was to be imune to spurious signals from other transmitters because in a real emergency, is presumed that on the air will be a lot of activity.
Another crucial condition was the ability to operate from a lead acid battery for a long period of time, at leas for 12-24 hrs, depending on the alternative power supply at the site of the repeater. But to be sure, a period of time of 24 hrs was considered.

Based on my experience with various radios, "the chosen one" was the small GP300 from Motorola.

Why?
Hmmm, because:
-On the receiving it has a smart PLL demodulator able to improve considerably the S/N ratio, making faint signals receivable;
-The receiving front end is absolutely imune to adiacent and unwanted signals, opening the squelch only on a signal on the right frequency; this happens also when you put it on a high gain external antenna!
-The transmitter is self-aware about the temperature and pretty resistant to high SWR
-The modulation is extremly well done with a great AGC
-The current consumption on standby is around 30 mA while the TX current is around 1.7A @ 5W (very efficient!)

Also, Motorola GP300 has a long history of reliability in various conditions and, very important, the inner built is very strong and has a good EM shield around the PCB.

And the last, they have a nice and clean schematic and is a pleasure to work with a very few components for a good repeater! This is the condition of reliability!

The only con I found is the power supply; the radio itself work at 7.5V and the batteries usually at 12-14 V. A DC-DC converter is needed! 

A couple of years ago I bought a small UHF duplex filter from a fellow ham. It's a 5 cell filter, with 2 cells on the low side and 3 cells on the high side. Of course, the receiving path of the repeater had to be on the upper side to have a better notch on the TX signal from the repeater itself.



The years gone by and recently, due to the increase interest in emergency communications, I started to think very serious about this forgotten project.

After a little search on the swap lists, I found two of them. Well, the versions I found are the narrow FM ones. I do have the filters for 25 kHz but is a pain in the s@&%#$ to change them so I decided to work with them instead of working against...

I did some modifications; the main mod is removing all the connectors form the top of the radio! The RF port, and the combined Earphon/Microphone. They take a lot of space! I also cut the shafts from the volume potentiometer and the channel selector... They are useles... Oh, don't forget to put the radio ON because after the shaft is cut, is very hard to do it! 

Receiving side

From the receiver radio, we basically need only to signals:
- de-emphasised audio
- Carrier/PL detect

Take a look on the schematic. The audio will be taken after Q406 and after the C433, directly at the volume potentiometer. The main advantage is that the audio is muted! What does mean? Simple, the uC send a positive voltage to the Base of Q406 when a signal greater than the designated squelch level is received. Immediatly after the signal is gone, the receiving audio is muted, hence the white noise is suppressed. The repeater will not have a white noise tail! If you like to have it, just take the audio with a capacitor from the emitter of Q406. Is a little hard to find but not impossible if you have the service manual. (which, like other Motorola radios, I do not have, do not insist, search engines are your best friend!)

If you plan to play around with the audio and to  extract the PL to work with on an external decoder, well, the things are a little bit complicated because you have to access the pin #7 of the AFIC circuit or pin # 28 of the Rx circuit (a modified topography of the classic MC3363). If you have the guts, you can connect to the uC pin # 41 where you have a nice digital signal extracted by AFIC, ready to b processed wherever you want!
You also can use the radio's low speed data slicer if you plan to use PL/DPL (CTCSS/DCS) to controll the repeater behaviour but I advice you to not because is hard to access the pins there! They are just near the uC chip.

Ok, so now we need a Carrier Detect signal. The carrier detect also cand work as a PL detect but only for the PL programmed in the radio's memory. If you need a PL, put it on the receiving side GP-300 and, voila! The repeater now open only at the right PL (CTCSS) or DPL (DCS)!

I took this signal directly from the Audio PA Vcc, U409 at pin # 1. There you will find the 7.5V when the radio receive a coherent signal.
Be aware, that path is open also when the radio want to send audio signals to the operator (battery low or other stuff like that).
By the way, I disabled all this signalling  from the RSS.

Transmitting side

The receiving signal from the other GP-300 will be "prepared" a little and after that, will be sent to the Mic input of the radio. Of course, due to the removal of the Mic connector, you will have to find the right pin on the PCB. Just put the radio into transmission and test the PCB holes where the connector was with a fine metal tip. On a control radio you will find the right pin very easy!
For the "engineers", take a look into the service manual and find C427... There you are!

The PTT is easy, just take a small piece of wire in parallel with the little switch on the left side of the radio. The lowest one. The PTT works when is put to the Ground and is tied up to 5V via a 10 kOhm resistor.


The controller


Well, the first was made with one transistor and two resistors. Neat and clean. Worked perfectly but the operators cannot listen to the tail. It was to short!
But provided a helpfull hand to test the repeater and the filter.

The second (and final) version was made around an Arduino Nano board.
The things were a little complicated because I want to have some sort of battery voltage indication to know when to shorten the QSOs.

And the solution was very simple. I measure the battery voltage before the DC/DC converter and change the courtesy tone structure according to the battery state.

A single high pitch short beep when the voltage is between 11.5-13.8 V, a low pitch followed by a high pitch when the battery voltage is over the 13.8V, a high pitch followe by a low pitch when the voltage drop under 11.5 V and is higher than 10.5V and a long and grave beep when is even lower.

Under 10V, I plan to put a latch relay to close the radios. Or to block the PTT command signal. I don't know yet...

I found that the repeater has some noise and, after checking the duplex filter I concluded that the noise is internall. Indeed, after I took some drastic measures to ground the radios and to shield the receiving radio, the received signal is very clean and pleasant.
Of course, the repeater have a beacon which transmit the callsign, the QTH and the CTCSS if it is provided.

First setup. Some will say is ugly. I agree!

 

 

Very quick PCB. Drawed by hand.

 

A little bit of Arduino Nano...

 

 

The conroller is assembled.

 

The little UHF Repeater.

 

 

 

 

Some more shielding.

 

 



And, here is the code:

/*
   This junk is meant to controll a small repeater in UHF made of two portable radios 
   Motorola GP300.
   
   Features in this version:
       -Courtesy tone  - since V 6.0
       -Beacon each 60 minutes   - since V 6.2
       -Battery status announced by beacon or by courtesy tone - since V 6.4-prepared
          working at from V 6.6
       -CW beacon with callsign, QTH and stuff   - since V 7.0
       -Tx radio disconnect after 5 minutes of inactivity
       -Mind control
       -etc
       
       The work is always on progress
       The program has been rewritten completely since 5.x version       
       This is version 7.1
       
       This code is released under "Beerware License" (buy me a beer when you see me).
       Author: Adrian Florescu YO3HJV
       http:///Această adresă de email este protejată contra spambots. Trebuie să activați JavaScript pentru a o vedea.

 */

/////////////// Simple Arduino CW Beacon //////////////////

// Written by Mark VandeWettering K6HX
// Email: Această adresă de email este protejată contra spambots. Trebuie să activați JavaScript pentru a o vedea.
//
// Thx Mark de YO3HJV !
//
struct t_mtab { char c, pat; } ;
struct t_mtab morsetab[] = {
      {'.', 106},
    {',', 115},
    {'?', 76},
    {'/', 41},
    {'A', 6},
    {'B', 17},
    {'C', 21},
    {'D', 9},
    {'E', 2},
    {'F', 20},
    {'G', 11},
    {'H', 16},
    {'I', 4},
    {'J', 30},
    {'K', 13},
    {'L', 18},
    {'M', 7},
    {'N', 5},
    {'O', 15},
    {'P', 22},
    {'Q', 27},
    {'R', 10},
    {'S', 8},
    {'T', 3},
    {'U', 12},
    {'V', 24},
    {'W', 14},
    {'X', 25},
    {'Y', 29},
    {'Z', 19},
    {'1', 62},
    {'2', 60},
    {'3', 56},
    {'4', 48},
    {'5', 32},
    {'6', 33},
    {'7', 35},
    {'8', 39},
    {'9', 47},
    {'0', 63}
        } ;
#define N_MORSE  (sizeof(morsetab)/sizeof(morsetab[0]))
#define CW_SPEED  (22)
#define DOTLEN  (1200/CW_SPEED)
#define DASHLEN  (3.5*(1200/CW_SPEED))  // CW weight  3.5 / 1

////////// end CW section ////////////

///////// define repeater beacon Messages ////////////

    #define  RPT_call       ("YO3RVSU")   // Call sign of the repeater 
    
   // #define  RPT_ctcss      ("79.7")    // CTCSS tone uncomment if needed

    #define  RPT_loc        ("kn34bk")    // Repeater QTH locator    
    #define  END_msg        ("SK")        // Goodby Message
    


//CW   tone output
    #define  CW_pin          (5)     // Pin for CW audio out
    #define  CW_PITCH        (900)   // CW pitch 
    
    
    
// PANEL LEDs

   #define ledtx            (2)    // pin for TX LED
    

                          int rx = 6;                        // Carrier Detect digital input pin
                          int ptt = 12;                      // PTT digital output pin
                          
                              float slaV = 0;                //  variable to store voltage from the ADC after the resistive divider
                              int vMath = 0;                 //  variable used for maths with ADC and voltage
                              float volts = 0;               //  volts from ADC readings
   
                   unsigned long previousMillis = 0;         // will store last time beacon transmitted            
                   const long interval = 900000;            // 60 min interval at which to beacon (milliseconds)  3600000 15 min=900000  
              
   
                              int cDet = 0;                  // current state of the Carrier Detect
                              int lastcDet = 0;              // previous state of the Carrier Detect
    
   
  
        void setup () {
     
          //  Serial.begin(9600);              // for debugging
               
               pinMode(CW_pin, OUTPUT);      // Output pin for beacon and audio
                //  digitalWrite(CW_pin, LOW);           // CW pin init
    
               pinMode(10, INPUT) ;          //  Carrier Detect on pin 10 with transistor, tied up with 10 kOhm. 
                                             //  when signal is received goes LOW.                              
               pinMode(12, OUTPUT);          //  Here we have PTT. When transmission is needed, this pin goes HIGH.
               
               pinMode(2, OUTPUT);           //  TX LED
                                     }
  
    /* ******************************** Functions here  ************************ */
    
        // What it does when signal is receiving.
        
        void repeat ()    
                   {
                cDet = digitalRead(rx);
                              if (cDet != lastcDet) {                 // if the state has changed
                                                                     
                                   if (cDet == LOW) {                 // if the current state is HIGH then the button
                                                                      // went from off to on:
                                   digitalWrite(ptt, HIGH);           //  Tx PTT @ +5V 
                                   digitalWrite(ledtx, HIGH);         //  TX LED ON
                                                     } 
                              else {                                  // same state @cDet input
                                      delay(20);                      // wait a little before courtesy tone
                                      courtesy();                     // execute 'courtesy' function
                                      delay(70);                      // Wait a little while TX
                                      digitalWrite(ptt, LOW);         // put Tx PTT @ GND, go to StandBy
                                      digitalWrite(ledtx, LOW); 
                                   }
                             delay(50);                               // Delay a little bit to avoid bouncing
                                                      }
                                                   
                 lastcDet = cDet;                                     // save the current state as the last state 
                                                                      // for next time through the loop
                   }  // END of 'repeat' function
      
      
      
      
          // Function to return the battery voltage      
          // Better to call it when transmitting to have a 'worst case' measurement       
                    
          float battery() {
                     delay(50);                                       //  hold on before ADC reading
                     int vMath = analogRead(A0);                      //  Load the SLA BAttery voltage through resistive divider on ADC A0 
                     float slaV = vMath * (15 / 1023.0);              //  slaV will be calculated in Volts  with max value 15V                        
                     return slaV;                                     //  The 'battery' function returning voltage measuring
                     }    
  
   
      // *************  MAIN LOOP  ***************
               
      void loop() {    
                repeat();                                    // called  at the beggining
                       
                delay(100);                                  // AntiKerchunk delay
                                                             // Beacon timer   
                 unsigned long currentMillis = millis();     // Read millis (from first start of the board) and load value to current Millis variable

                                if (currentMillis - previousMillis >= interval)  {  
                                    previousMillis = currentMillis;                  // Time to beacon (INTERVAL) has come
                                    cDet = digitalRead(rx);                          // Read the Carrier Detect Input pin 
                             
                                       if (cDet == HIGH){                            // If not QSO on the Rx frequency
                                            digitalWrite(ptt, HIGH);                 // Start Tx
                                            digitalWrite(ledtx, HIGH);               // PTT LED ON
                                            delay(100);                              // Wait a little
                                             beacon1();                              // Transmitting Audio Beacon 1 
                                            delay(100);                              // Wait a little
                                            digitalWrite(ptt, LOW);                  // Go in StandBy
                                            digitalWrite(ledtx, LOW);                // LED TX OFF
                                                         }
                           
                                        else if (cDet == LOW) {                      // If a QSO on the RX frequency
                                           delay(100);                               // Wait a little           
                                             beacon2();                              // Transmitting Audio Beacon 2
                                           digitalWrite(ptt, HIGH);   }              // Keep the repeater transmitting
                                           digitalWrite(ledtx, HIGH);                // LED TX ON
                                                                                   }
                                else {}                                              // If time to beacone hasn't come (INTERVAL), do nothing      
    
               }
  
  
        // **** Audio Beacon 1 - for StandBy (No QSO in progress)
        void beacon1() {
       
          delay (500);
       
  delay (100);                                     // Wait a little
              sendmsg(RPT_call) ;                  //send call
              delay(7*DOTLEN) ;
              sendmsg("QRV") ;                     //send info
              delay(7*DOTLEN) ;
             //  sendmsg(RPT_ctcss);                // uncomment this two lines if there is a CTCSS enabled on the repeater
             //  delay(7*DOTLEN) ;
              sendmsg(RPT_loc) ;                   //send qth locator
              delay(7*DOTLEN) ;
              sendmsg(END_msg);
              delay(3*DOTLEN) ;
                         return;  }
                           
        // **** Audio Beacon 2 - For repeating (QSO in progress)
        
        void beacon2() { 
              delay(100);
            tone(5, 2880, 100);
       delay(200);
            tone(5, 1740, 100);
       delay(200);
            tone(5, 1200, 100);
       delay(500);
            tone(5, 880, 200);
                          return; }
    
      void courtesy() {
                                                                     // We read the voltage of the battery via 'battery' external function
                     volts = battery() ;
                     
               Serial.println();
               Serial.print(volts);
                    delay(50);                                      // Wait for the portable radio who stops transmitting to get into the receiving mode to be able to hear the tone


            if ((volts >= 0) && (volts < 10.5)) {                         // Practically, this will end the normal operation leaving only the receiver section open to monitor the frequency. Beep is just for tests.
    
                tone(5, 440, 300);
                delay(301);
                              }
            
            else if ( volts >= 10.5 && volts < 11.5) {     // Watch out! The battery goes low, limit comms!

                tone(5, 1880, 200); 
                delay(201); 
                tone(5, 440, 200);
                delay(201);
                                                   }
            
            
            else if ( volts >=11.5 && volts < 13.6) {     // This is OK, normal operations

                tone(5, 2900, 70);
                delay(71);
                                                    }
            
            else if (volts >= 13.6) {                     // Over voltage for some reasons. Not normal!

                tone (5, 1880, 200); 
                delay(201); 
                tone(5, 2880, 200);                   // We have to do something to discharge the battery!
                delay(201);
                                    }

               
                       }




/////////////////////////    CW GENERATION ROUTINES  //////////////////////////
void dash() {
    tone(CW_pin,CW_PITCH); 
    delay(DASHLEN);
    noTone(CW_pin);     
    delay(DOTLEN) ;
    }
////////////////////////////
void dit() {
  tone(CW_pin,CW_PITCH); 
  delay(DOTLEN);
  noTone(CW_pin);    
  delay(DOTLEN);
  }
///////////////////////////
void send(char c) {
  int i ;
    if (c == ' ') {
    delay(7*DOTLEN) ;
    return ;
    }
   if (c == '+') {
    delay(4*DOTLEN) ; 
    dit();
    dash();
    dit();
    dash();
    dit();
    delay(4*DOTLEN) ; 
    return ;
    }    
    
  for (i=0; i<N_MORSE; i++) {
    if (morsetab[i].c == c) {
      unsigned char p = morsetab[i].pat ;
      while (p != 1) {
          if (p & 1)
            dash() ;
          else
            dit() ;
          p = p / 2 ;
          }
      delay(2*DOTLEN) ;
     // wdt_reset();        // Reset Watchdog timer
      return ;
      }
    }
  }
///////////////////////////
void sendmsg(char *str) {
  while (*str)
    send(*str++) ;
  }
  

         
/////////////////////////  END  CW GENERATION ROUTINES  //////////////////////////

 

YO3HJV

Adaugă comentariu


Codul de securitate
Actualizează