guides:radio_modules:lmt2
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
guides:radio_modules:lmt2 [2013/06/14 05:42] – upu | guides:radio_modules:lmt2 [2016/09/08 14:47] (current) – Removed purchase link to HAB Supplies. upu | ||
---|---|---|---|
Line 26: | Line 26: | ||
==== Where to buy the LMT2 ==== | ==== Where to buy the LMT2 ==== | ||
- | *[[http:// | ||
*[[http:// | *[[http:// | ||
Line 32: | Line 31: | ||
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 # | 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 # | ||
+ | |||
+ | ==== 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)/ | ||
+ | |||
+ | 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/ | ||
+ | |||
+ | 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 : | ||
+ | |||
+ | <code c> | ||
+ | |||
+ | /* From python fractions.Fraction.limit_denominator | ||
+ | * Originally contributed by Sjoerd Mullender. | ||
+ | * Significantly modified by Jeffrey Yasskin < | ||
+ | * Ported to C by Daniel Richman */ | ||
+ | |||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | |||
+ | 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' | ||
+ | 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' | ||
+ | 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 | ||
+ | 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, | ||
+ | struct frequency_rational r; | ||
+ | |||
+ | if(argc != 2) { | ||
+ | printf(" | ||
+ | return 1; | ||
+ | } | ||
+ | |||
+ | freq_target_hz = atoi(argv[1]); | ||
+ | printf(" | ||
+ | |||
+ | 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(" | ||
+ | r.n, r.r, freq_out, error_out); | ||
+ | |||
+ | return 0; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | 8-Bit Arduino/AVR Version here : | ||
+ | <code c> | ||
+ | |||
+ | /* From python fractions.Fraction.limit_denominator | ||
+ | * Originally contributed by Sjoerd Mullender. | ||
+ | * Significantly modified by Jeffrey Yasskin < | ||
+ | * 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, | ||
+ | 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(" | ||
+ | } | ||
+ | void loop() { | ||
+ | freq_target_hz = 434201241L; | ||
+ | struct frequency_rational r; | ||
+ | sprintf(txstring," | ||
+ | Serial.println(txstring); | ||
+ | startTime = millis(); | ||
+ | while(wibble< | ||
+ | r = frequency_magic(freq_target_hz); | ||
+ | wibble++; | ||
+ | } | ||
+ | sprintf(txstring," | ||
+ | Serial.println(txstring); | ||
+ | freq_out = (uint64_t)(13000000L * (uint64_t)r.n) / (uint32_t)r.r; | ||
+ | error_out = freq_target_hz - freq_out; | ||
+ | |||
+ | sprintf(txstring," | ||
+ | Serial.print(txstring); | ||
+ | Serial.print(error_out); | ||
+ | Serial.println(" | ||
+ | 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' | ||
+ | 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 | ||
+ | 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.1371188536.txt.gz · Last modified: 2013/06/14 05:42 by upu