UKHAS Wiki

UK High Altitude Society

User Tools

Site Tools


guides:radio_modules:lmt2

LMT2

Overview

The Radiometrix LMT2 is a more advanced version of the NTX2. Its major advantage over the NTX2 is its frequency agility being able to switch channels between 433.850 to 434.650Mhz. Additionally its temperature stability is much better due to the inclusion of a TCXO.

Details

It is possible to linearise the input of the LMT2 by placing a 500k resistor in front of the TXD pin, this has the effect of turning the LMT2's input op amp gain to 1:1 effectively bypassing it. At this point an input voltage of between 0 - 3V on the TXD pin will result in an almost linear increase in output frequency meaning its possible, as with the NTX2, to product RTTY (or other modes).

The frequency change is about 0.000573v per Hz above 0.5V input. I.e about 0.244v will result in a shift in frequency of 425Hz. Below is the frequency response graph :

LMT2 modules can be powered directly from the batteries having its own internal regulator (3.1V to 15V in). The manufacturers datasheet is here LMT2 Datasheet

Legislative

The regulatory requirements for airborne operation in Europe on this frequency are that the channel bandwidth must be less than 25Khz and less that 10mW ERP. There are no duty cycle requirements in this frequency band. Use of this module in the United States requires operation under an amateur license.

Frequency Stability

The LMT2 temperature stability is very good due to the TCXO. Adequate insulation is still recommended, the manufacturer minimum operation temperature is -10'C. Ensuring the module isn't exposed to rapid temperature changes is essential.

Where to buy the LMT2

Other Notes & Further Reading

Radiometrix are understandably unable to offer technical support on these modules when used in high altitude ballooning therefore please direct all technical support queries regarding the operation of these modules to the UKHAS mailing list or preferably the #highaltitude IRC channel.

Frequency Selection

LMT2 frequency selection can be done in a very granular basis by amending the values of the N & R registers.

Frequency = N register value x Comparison frequency where 64 ⇐ N ⇐ 65535

Comparison Frequency = 13MHz (reference oscillator)/R register value

Comparison Frequency must > 10khz.

Frequency = (13Mhz * N)/R

Example :

434.121Mhz

Picking R as 1282 Gives Comparison frequency of 10140.40562Hz

So N= 434.121Mhz/10140.40562Hz = 42811

The actual frequency with these values is 434.120904Mhz so an error of 96Hz.

To aid the calculation of these values Daniel Richman has provided the following code :

/* From python fractions.Fraction.limit_denominator
 * Originally contributed by Sjoerd Mullender.
 * Significantly modified by Jeffrey Yasskin <jyasskin at gmail.com>.
 * Ported to C by Daniel Richman */
 
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
 
struct frequency_rational
{
    uint16_t n;
    uint16_t r;
};
 
static inline uint32_t hcf(uint32_t a, uint32_t b)
{
    while (b)
    {
        uint32_t temp;
        temp = a;
        a = b;
        b = temp % b;
    }
    return a;
}
 
/* 5 seconds on google suggests avr-gcc doesn't have llabs */
static inline int64_t abs64(int64_t v)
{
    if (v < 0)
        return -v;
    else
        return v;
}
 
/* I ported this blindly from the python fractions module without looking up
 * how it works. Besides, I'm more interested in the proof than the why
 * the implementation is correct */
 
#define max_denominator 1300
 
struct frequency_rational frequency_magic(uint32_t on)
{
    struct frequency_rational r;
 
    uint16_t p0 = 0, q0 = 1, p1 = 1, q1 = 0, k;
    uint32_t q2, a, n, d, h;
    uint32_t od = 13000000;
    int64_t compare_p0, compare_p1, compare_n;
 
    h = hcf(on, od);
    on /= h;
    od /= h;
 
    n = on;
    d = od;
 
    if (d <= max_denominator)
    {
        r.n = n;
        r.r = d;
        return r;
    }
 
    for (;;)
    {
        uint32_t tempd, tempp1, tempq1;
 
        tempd = d;
        a = n / d;  /* gcc should optimise this to one call to divmod. */
        d = n % d;
        n = tempd;
 
        /* if a > max, then q2 > max since after first loop q1 > 1.
         * This check ensures it doesn't overflow. */
        if (a > max_denominator)
            break;
        q2 = q0 + ((uint32_t) (((uint16_t) a) * q1));
        if (q2 > max_denominator)
            break;
 
        tempp1 = p1;
        tempq1 = q1;
        p1 = p0 + ((uint16_t) a) * p1;
        q1 = q2;
        p0 = tempp1;
        q0 = tempq1;
    }
 
    k = (max_denominator - q0) / q1;
 
    p0 = p0 + k * p1;
    q0 = q0 + k * q1;
 
    /* which of p0/q0  p1/q1 is closest. */
    compare_n  = ((uint64_t) on) * ((uint64_t) q0) *  ((uint64_t) q1);
    compare_p0 = ((uint64_t) od) * ((uint64_t) p0) *  ((uint64_t) q1);
    compare_p1 = ((uint64_t) od) * ((uint64_t) q0) *  ((uint64_t) p1);
 
    if (abs64(compare_p0 - compare_n) < abs64(compare_p1 - compare_n))
    {
        r.n = p0;
        r.r = q0;
    }
    else
    {
        r.n = p1;
        r.r = q1;
    }
    return r;
}
 
/* borrowed from Adam's gist */
int main(int argc, char* argv[])
{
    uint32_t freq_target_hz, freq_out, error_out;
    struct frequency_rational r;
 
    if(argc != 2) {
        printf("Usage: %s <frequency to find N and R for, in Hz>\n", argv[0]);
        return 1;
    }
 
    freq_target_hz = atoi(argv[1]);
    printf("Target: %uHz\n", freq_target_hz);
 
    r = frequency_magic(freq_target_hz);
 
    freq_out = (13000000L * (uint32_t)r.n) / (uint32_t)r.r;
    error_out = abs(freq_out - (freq_target_hz));
    printf("Optimum solution: n=%u, r=%u, giving f=%uHz (error=%uHz)\n",
           r.n, r.r, freq_out, error_out);
 
    return 0;
}

8-Bit Arduino/AVR Version here :

/* From python fractions.Fraction.limit_denominator
 * Originally contributed by Sjoerd Mullender.
 * Significantly modified by Jeffrey Yasskin <jyasskin at gmail.com>.
 * Ported to C by Daniel Richman 
 * Ported to AVR with help from Daniel Richman by Anthony Stirk */
 
#define max_denominator 1300
unsigned long startTime;
char txstring[120];
int32_t freq_target_hz, freq_target_khz, error_out;
uint64_t freq_out;
uint16_t best_r = 0, best_n = 0;
int wibble=0;
struct frequency_rational
{
  uint16_t n;
  uint16_t r;
};
static inline uint32_t hcf(uint32_t a, uint32_t b)
{
  while (b)
  {
    uint32_t temp;
    temp = a;
    a = b;
    b = temp % b;
  }
  return a;
}
static inline int64_t abs64(int64_t v)
{
  if (v < 0)
    return -v;
  else
    return v;
}
void setup() {                
  Serial.begin(9600);
  Serial.println("Calculate R and N register values for an LMT2 PLL");
}
void loop() {
  freq_target_hz = 434201241L;
  struct frequency_rational r;
  sprintf(txstring,"Target: %luHz",freq_target_hz);
  Serial.println(txstring);
  startTime = millis(); 
  while(wibble<1000) {
    r = frequency_magic(freq_target_hz);
    wibble++;
  }
  sprintf(txstring,"Run time=%lu milliseconds",(millis()-startTime));
  Serial.println(txstring);
  freq_out = (uint64_t)(13000000L * (uint64_t)r.n) / (uint32_t)r.r; // This line takes 4kb on the Arduino :/
  error_out = freq_target_hz - freq_out;
 
  sprintf(txstring,"Optimum solution: n=%u, r=%u, giving f=%luHz (error=", r.n, r.r, freq_out);
  Serial.print(txstring);
  Serial.print(error_out);
  Serial.println("Hz)");
  wibble=0;
}
 
 
struct frequency_rational frequency_magic(uint32_t on)
{
    struct frequency_rational r;
 
    uint16_t p0 = 0, q0 = 1, p1 = 1, q1 = 0, k;
    uint32_t q2, a, n, d, h;
    uint32_t od = 13000000;
    int64_t compare_p0, compare_p1, compare_n;
 
    h = hcf(on, od);
    on /= h;
    od /= h;
 
    n = on;
    d = od;
 
    if (d <= max_denominator)
    {
        r.n = n;
        r.r = d;
        return r;
    }
 
    for (;;)
    {
        uint32_t tempd, tempp1, tempq1;
 
        tempd = d;
        a = n / d;  /* gcc should optimise this to one call to divmod. */
        d = n % d;
        n = tempd;
 
        /* if a > max, then q2 > max since after first loop q1 > 1.
         * This check ensures it doesn't overflow. */
        if (a > max_denominator)
            break;
        q2 = q0 + ((uint32_t) (((uint16_t) a) * q1));
        if (q2 > max_denominator)
            break;
 
        tempp1 = p1;
        tempq1 = q1;
        p1 = p0 + ((uint16_t) a) * p1;
        q1 = q2;
        p0 = tempp1;
        q0 = tempq1;
    }
 
    k = (max_denominator - q0) / q1;
 
    p0 = p0 + k * p1;
    q0 = q0 + k * q1;
 
    /* which of p0/q0  p1/q1 is closest. */
    compare_n  = ((uint64_t) on) * ((uint64_t) q0) *  ((uint64_t) q1);
    compare_p0 = ((uint64_t) od) * ((uint64_t) p0) *  ((uint64_t) q1);
    compare_p1 = ((uint64_t) od) * ((uint64_t) q0) *  ((uint64_t) p1);
 
    if (abs64(compare_p0 - compare_n) < abs64(compare_p1 - compare_n))
    {
        r.n = p0;
        r.r = q0;
    }
    else
    {
        r.n = p1;
        r.r = q1;
    }
    return r;
}
guides/radio_modules/lmt2.txt · Last modified: 2016/09/08 14:47 by upu

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki