Inhaltsverzeichnis

I/Q Modulator

Es soll ein I/Q Modulator entstehen mit dem man auf einfache Art und Weise HF erzeugen und modulieren kann. Einzig ein Lokaler Oszillator(LO) und eine Soundkarte wird benötigt.

Projektstatus

Schaltungsbeschreibung

Die Signale einer handelsüblichen (preiswerten) Soundkarte sind massebezogen(single-ended). Die Basisbandsignale(I/Q) aus der Soundkarte werden in differentielle Signale umgewandelt. Für die Verwendung der Schaltung mit einem Rechtecksignal als LO dient das Filter am LO-Eingang. Danach wird der LO einem Puffer zugeführt, der differentielle Signale für den Modulator bereitstellt. Zusätzlich kann das LO-Signal per Dämpfungsglied im Pegel angepasst werden

Zu erreichbare Daten:

LO-Filter

Das Design des Filters ist schon erprobt, es macht sich gut im 2m-LNA vom SDR. Initial bekam ich damals ™ eine 1cmx2cm große Leiterplatte von Winni DL2AWT mit genau dieser Filtertopologie. Folglich simulierte ich das Ding mal und fand passende Werte, die es auch bei Reichelt gibt. C2 und C3 sind dabei Trimmkondensatoren(3-10pF) mit 47pF Keramikkondensator parrallel geschalten.

LTSpice-File

Erreichbare Daten:

Untersuchung verschiedener LOs

Si570

<spoiler> Überblick Spektrum bei Sollfrequenz 144,6MHz:

Spektrum mit 2m-Filter:

Spektrum mit 70cm-Filter:

</spoiler>

VNWA

<spoiler> Überblick Spektrum bei Sollfrequenz 144,6MHz:

Spektrum mit 2m-Filter:

Überblick Spektrum bei Sollfrequenz 433,8MHz:

Spektrum mit 70cm-Filter:

</spoiler>

Raspberry Pi per GPCLK0

<spoiler> Spektrum APRS(144,800MHz) links direkt, rechts mit 2m-Filter:

Spektrum 145,450MHz links direkt, rechts mit 2m-Filter:

C-Code zur Ansteuerung: <spoiler>

PiLO.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#include <dirent.h>
#include <math.h>
#include <fcntl.h>
#include <assert.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#include <malloc.h>
#include <time.h>
 
#define BCM2708_PERI_BASE        0x20000000
#define GPIO_BASE                (BCM2708_PERI_BASE + 0x200000) /* GPIO controller */
#define PAGE_SIZE (4*1024)
#define BLOCK_SIZE (4*1024)
 
int  mem_fd;
char *gpio_mem, *gpio_map;
char *spi0_mem, *spi0_map;
 
// I/O access
volatile unsigned *gpio = NULL;
volatile unsigned *allof7e = NULL;
 
// GPIO setup macros. Always use INP_GPIO(x) before using OUT_GPIO(x) or SET_GPIO_ALT(x,y)
#define INP_GPIO(g) *(gpio+((g)/10)) &= ~(7<<(((g)%10)*3))
#define OUT_GPIO(g) *(gpio+((g)/10)) |=  (1<<(((g)%10)*3))
#define SET_GPIO_ALT(g,a) *(gpio+(((g)/10))) |= (((a)<=3?(a)+4:(a)==4?3:2)<<(((g)%10)*3))
 
#define GPIO_SET *(gpio+7)  // sets   bits which are 1 ignores bits which are 0
#define GPIO_CLR *(gpio+10) // clears bits which are 1 ignores bits which are 0
#define GPIO_GET *(gpio+13) // sets   bits which are 1 ignores bits which are 0
 
#define ACCESS(base) *(volatile int*)((int)allof7e+base-0x7e000000)
#define SETBIT(base, bit) ACCESS(base) |= 1<<bit
#define CLRBIT(base, bit) ACCESS(base) &= ~(1<<bit)
#define CM_GP0CTL (0x7e101070)
#define GPFSEL0 (0x7E200000)
#define PADS_GPIO_0_27  (0x7e10002c)
#define CM_GP0DIV (0x7e101074)
#define CLKBASE (0x7E101000)
 
struct GPCTL {
    char SRC         : 4;
    char ENAB        : 1;
    char KILL        : 1;
    char             : 1;
    char BUSY        : 1;
    char FLIP        : 1;
    char MASH        : 2;
    unsigned int     : 13;
    char PASSWD      : 8;
};
 
void txon()
{
    if(allof7e == NULL){
      allof7e = (unsigned *)mmap(
                  NULL,
                  0x01000000,  //len
                  PROT_READ|PROT_WRITE,
                  MAP_SHARED,
                  mem_fd,
                  0x20000000  //base
              );
      if ((int)allof7e==-1) exit(-1);
    }
 
    SETBIT(GPFSEL0 , 14);
    CLRBIT(GPFSEL0 , 13);
    CLRBIT(GPFSEL0 , 12);
 
    // Set GPIO drive strength, more info: http://www.scribd.com/doc/101830961/GPIO-Pads-Control2
    //ACCESS(PADS_GPIO_0_27) = 0x5a000018 + 0;  //2mA -3.4dBm
    //ACCESS(PADS_GPIO_0_27) = 0x5a000018 + 1;  //4mA +2.1dBm
    //ACCESS(PADS_GPIO_0_27) = 0x5a000018 + 2;  //6mA +4.9dBm
    ACCESS(PADS_GPIO_0_27) = 0x5a000018 + 3;  //8mA +6.6dBm(default)
    //ACCESS(PADS_GPIO_0_27) = 0x5a000018 + 4;  //10mA +8.2dBm
    //ACCESS(PADS_GPIO_0_27) = 0x5a000018 + 5;  //12mA +9.2dBm
    //ACCESS(PADS_GPIO_0_27) = 0x5a000018 + 6;  //14mA +10.0dBm
    //ACCESS(PADS_GPIO_0_27) = 0x5a000018 + 7;  //16mA +10.6dBm
 
    struct GPCTL setupword = {5/*SRC*/, 1, 0, 0, 0, 1,0x5a};
    ACCESS(CM_GP0CTL) = *((int*)&setupword);
}
 
void txoff()
{
    struct GPCTL setupword = {5/*SRC*/, 0, 0, 0, 0, 1,0x5a};
    ACCESS(CM_GP0CTL) = *((int*)&setupword);
}
 
void handSig() {
  exit(0);
}
 
// Set up a memory regions to access GPIO
void setup_io()
{
    /* open /dev/mem */
    if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0) {
        printf("can't open /dev/mem \n");
        exit (-1);
    }
 
    /* mmap GPIO */
 
    // Allocate MAP block
    if ((gpio_mem = malloc(BLOCK_SIZE + (PAGE_SIZE-1))) == NULL) {
        printf("allocation error \n");
        exit (-1);
    }
 
    // Make sure pointer is on 4K boundary
    if ((unsigned long)gpio_mem % PAGE_SIZE)
        gpio_mem += PAGE_SIZE - ((unsigned long)gpio_mem % PAGE_SIZE);
 
    // Now map it
    gpio_map = (unsigned char *)mmap(
                   gpio_mem,
                   BLOCK_SIZE,
                   PROT_READ|PROT_WRITE,
                   MAP_SHARED|MAP_FIXED,
                   mem_fd,
                   GPIO_BASE
               );
 
    if ((long)gpio_map < 0) {
        printf("mmap error %d\n", (int)gpio_map);
        exit (-1);
    }
 
    // Always use volatile pointer!
    gpio = (volatile unsigned *)gpio_map;
 
 
}
 
int main(int argc, char *argv[])
{
  setup_io();
  txon();
  ACCESS(CM_GP0DIV) = (0x5a << 24) | (6 << 12) | (896<<2); // 6, 928 für aprs 144,796; 6, 912 für 145,125; 6, 896 für 145,450
  return 0;
}

</spoiler> </spoiler>

LO Frequenz Leistung
Si570 144,6MHz 6,3dBm
Si570 433,8MHz -1dBm
VNWA 144,6MHz -19,2dBm
VNWA 433,8MHz -24,6dBm

Der I/Q-Modulator will -10dBm..0dBm am LO-Eingang. Der LO-Buffer verstärkt das LO-Signal wie folgt:

Somit muss der LO mindestens -26dBm liefern.

TODO:

Schaltplan

Revision: 3

Vorberechnung des Dämpfungsglieds

Dämpfung R15,R16 R14,R17 R18
0dB - 100Ω -
-20dB 120Ω 56Ω 237Ω
-25dB 120Ω 56Ω 475Ω

Links zu den verwendeten Schaltkreisen:

Inspiriert durch: Khaled Al Rifai 

Änderungen

Revision 1

Sebastian Weiß 2014/01/31 22:53

Review:

Revision 2

Sebastian Weiß 2014/02/01 15:54

Review:

Revision 3

Sebastian Weiß 2014/02/03 06:52

Review

Layout

Revision: 3

Änderungen

Revision 1:

Review:

Revision 2:

Review:

Revision 3:

Review:

Anleitung zur Inbetriebnahme

  1. Bestückung Spannungsaufbereitung
    1. 0..10V an VIN anlegen
    2. Auslösespannung TVS-Diode: 6,8 V(Soll: 6,5V)
    3. Verpolungstest: 0..-30V, Spannung an Betriebsspannung der Schaltkreise: max. -0,8V
  2. Bestückung X1(SMA, LO-Eingang) und Filter ohne Dämpfungsglied; Koax von Mittenanzapfung L3/L4 zu VNA
    1. VNWA-Messung LO-Filter:
    2. Spektrum Si570 mit Filter: Spektrum Si570 mit Filter
  3. Bestückung Rest, 50Ohm-Abschluss an X1 + X2
    1. Stromaufnahme bei VIN=5V: xxx mA (Soll:90..120mA) TODO
    2. Prüfung der Spannung über C3, C4, C5, C8, C10 (Soll: VIN) OK
    3. Spannng und Ripple VCM: 0,8 V(Soll: 0,7V)
  4. Messungen per Oszilloskop, Signalgenerator an X1 f=144MHz; f=1kHz an IN_I/IN_Q:
    1. Phasenbeziehung zwischen LO+ und LO-: xxx°(Soll: 180°) entfällt
    2. Phasenbeziehung zwischen BB_I+ und BB_I-: xxx°(Soll: 180°) entfällt
    3. Phasenbeziehung zwischen BB_Q+ und BB_Q-: xxx°(Soll: 180°) entfällt
  5. Spektrumanalyzer an X2, Signalgenerator an X1 f=144MHz, IN_I und IN_Q mit 1kHz SSB-Signal
    1. Spiegelfrequenzunterdrückung: xxx dB
    2. LO Leakage: xxx dBc

Inbetriebnahme-Schaltplan

Ansteuerung

Um normale Audio-Dateien FM-moduliert als I/Q-Signale mit einer Soundkarte ausgeben zu können, gibt es folgendes kleines Tool: fmmod. Dieses ist im Xplorer-Github zu finden.