Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AD9850 DDS as a RF generator #10

Closed
RoLorenzoni opened this issue Apr 28, 2017 · 13 comments
Closed

AD9850 DDS as a RF generator #10

RoLorenzoni opened this issue Apr 28, 2017 · 13 comments
Labels

Comments

@RoLorenzoni
Copy link

Hi!
I am new with programming, but I found your project really interesting.
I live in a country that does not have the time signal available, so I have no other choice but constructing a transmitter.
I tried your "minimal" project but it wasn't able to set my citizen watches. The Rx indicator shows some signal reception but it always fails.
Then I got a AD9850 DDS RF generator and it's really easy to create a sine 60khz carrier.
Changing a WWVB sample code from vinmarshall (https://github.com/vinmarshall/WWVB-Clock), I could generate a WWVB signal that was correctly received by the watches.
Now, I'm trying to combine the ad9850 code with your wwvb library, but with no sucess.
I would be very happy if you could help me with that, because I am new to programming.
I am using an arduino nano, a ublox 6 GPS module to get acurate time and the AD9850 to broadcast it.
Thanks in advance.

here is the code I used to generate the carrier.

/* WWVB Test Signal v 1.0
*

  • This code uses an Arduino to simulate the C-Max CMMR-6P TCO positive
  • output signal. This was written for debugging WWVB Clock
  • Receiver code.
  • This code is adapted from the WWVB Receiver Simulator provided by
  • Capt.Tagon at duinolab.blogspot.com. It expands on that code by
  • incrementing the time and date starting from that set in the
  • setup() routine. This also contains debugging code to display
  • this value on the Serial monitor.
  • This code supports the "Atomic Clock" article in the April 2010 issue
  • of Popular Science. There is also a schematic for this project. There
  • is also WWVB signal simulator code, to facilitate debugging and
  • hacking on this project when the reception of the WWVB signal
  • itself is less than stellar.
  • The code for both the clock and the WWVB simulator, and the schematic
  • are available online at:
  • http://www.popsci.com/diy/article/2010-03/build-clock-uses-atomic-timekeeping
  • and on GitHub at: http://github.com/vinmarshall/WWVB-Clock
  • Copyright (c) 2010 Vin Marshall (vlm@2552.com, www.2552.com)
  • Permission is hereby granted, free of charge, to any person
  • obtaining a copy of this software and associated documentation
  • files (the "Software"), to deal in the Software without
  • restriction, including without limitation the rights to use,
  • copy, modify, merge, publish, distribute, sublicense, and/or sell
  • copies of the Software, and to permit persons to whom the
  • Software is furnished to do so, subject to the following
  • conditions:
  • The above copyright notice and this permission notice shall be
  • included in all copies or substantial portions of the Software.
  • THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  • EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
  • OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  • NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  • HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  • WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  • FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  • OTHER DEALINGS IN THE SOFTWARE.

*/

#include <AD9850.h>

const int W_CLK_PIN = 7;
const int FQ_UD_PIN = 8;
const int DATA_PIN = 10;
const int RESET_PIN = 11;

double freq = 60000;
double trimFreq = 124999500;

int phase = 0;

/* WWVB time format struct - acts as an overlay on wwvbRxBuffer to extract time/date data.

  • All this points to a 64 bit buffer wwvbRxBuffer that the bits get read from as the
  • sample data stream is transmitted. (Thanks to Capt.Tagon @ duinolab.blogspot.com)

*/

struct wwvbBuffer {
unsigned long long U12 :4; // no value, empty four bits only 60 of 64 bits used
unsigned long long Frame :1; // framing
unsigned long long Dst :2; // dst flags
unsigned long long Leapsec :1; // leapsecond
unsigned long long Leapyear :1; // leapyear
unsigned long long U11 :1; // no value
unsigned long long YearOne :4; // year (5 -> 2005)
unsigned long long U10 :1; // no value
unsigned long long YearTen :4; // year (5 -> 2050)
unsigned long long U09 :1; // no value
unsigned long long OffVal :4; // offset value
unsigned long long U08 :1; // no value
unsigned long long OffSign :3; // offset sign
unsigned long long U07 :2; // no value
unsigned long long DayOne :4; // day ones
unsigned long long U06 :1; // no value
unsigned long long DayTen :4; // day tens
unsigned long long U05 :1; // no value
unsigned long long DayHun :2; // day hundreds
unsigned long long U04 :3; // no value
unsigned long long HourOne :4; // hours ones
unsigned long long U03 :1; // no value
unsigned long long HourTen :2; // hours tens
unsigned long long U02 :3; // no value
unsigned long long MinOne :4; // minutes ones
unsigned long long U01 :1; // no value
unsigned long long MinTen :3; // minutes tens
unsigned long long U00 :1;
};

// We point the struct and the unsigned long long at the
// same memory space so we can access the bits using
// both paradigms.
struct wwvbBuffer * buffer = (struct wwvbBuffer *) malloc(sizeof(struct wwvbBuffer));
unsigned long long * timeBits = (unsigned long long *) buffer;

/*

  • setup
  • uC Initialization
    */

void setup() {

// Setup the Serial port out and the WWVB signal output pin

DDS.begin(W_CLK_PIN, FQ_UD_PIN, DATA_PIN, RESET_PIN);
DDS.calibrate(trimFreq);

    // Preset the time struct 
    *timeBits = 0x0000000000000000;
    buffer->MinTen = 5;
    buffer->MinOne = 7; 
    buffer->HourTen = 2;
    buffer->HourOne = 3;
    buffer->DayHun = 3;
    buffer->DayTen = 6;
    buffer->DayOne = 5;
    buffer->OffSign = 5;  // 2 -> -  5 -> +
    buffer->OffVal = 3;
    buffer->YearTen = 1;
    buffer->YearOne = 0;
    buffer->Dst = 3; // 0 no dst, 1 dst ending, 2 dst starting, 3 dst
    buffer->Leapyear = 0;
    buffer->Leapsec = 0;

}

/*

  • loop
  • Main program loop
    */

void loop() {

// Print the Date & Time to the Serial port for debugging.
int year = (buffer->YearTen * 10) + buffer->YearOne;
int day = (buffer->DayHun * 100) + (buffer->DayTen * 10) + buffer->DayOne;
int hour = (buffer->HourTen * 10) + buffer->HourOne;
int minute = (buffer->MinTen * 10) + buffer->MinOne;
char date[30];
sprintf(date, "%.2i:%.2i %.3i, 20%.2i\n", hour, minute, day, year);
Serial.print(date);

// Step through each bit in this frame
int position = 0;
for (int i = 63; i >= 4; i--) {
// Mask off all bits but the one in question.
// This singles out one bit, moving from MSB to LSB
unsigned long long mask = (unsigned long long) 1 << i;
unsigned long long masked = (*timeBits) & mask;

// Determine if there was a 1 in that bit position
int bit = ((*timeBits) & mask)?1:0;

            // Determine if we're at a Marker position
            int mark = 0;
            if ( (position == 0) || 
                 ((position + 1) % 10 == 0) ) {
                   mark = 1;
            }
            position++;

// Rattle off each bit

// Debug output to the Serial port
// Print in groups of 4. Makes cross ref to hex easier
if ( (i+1) % 4 == 0) {
  Serial.println("");
} 

            if (mark) { 
              Serial.print("M"); 
              sendMark();
            } else if (bit == 0) {
              Serial.print("0");
              sendUnweighted();
            } else if (bit == 1) {
              Serial.print("1");
              sendWeighted();
            }

}
Serial.println("");

// Increment the Time and Date
if (++(buffer->MinOne) == 10) {
buffer->MinOne = 0;
buffer->MinTen++;
}

if (buffer->MinTen == 6) {
buffer->MinTen = 0;
buffer->HourOne++;
}

    if (buffer->HourOne == 10) {
            buffer->HourOne = 0;
            buffer->HourTen++;
    }

    if ( (buffer->HourTen == 2) && (buffer->HourOne == 4) ) {
            buffer->HourTen = 0;
            buffer->HourOne = 0;
            buffer->DayOne++;
    }
    
    if (buffer->DayOne == 10) {
            buffer->DayOne = 0;
            buffer->DayTen++;
    }
    
    if (buffer->DayTen == 10) {
            buffer->DayTen = 0;
            buffer->DayHun++;
    }
    
    if ( (buffer->DayHun == 3) && 
         (buffer->DayTen == 6) &&
         (buffer->DayOne == (6 + (int) buffer->Leapyear)) ) {
             // Happy New Year.
             buffer->DayHun = 0;
             buffer->DayTen = 0;
             buffer->DayOne = 1;
             buffer->YearOne++;
     }
     
     if (buffer->YearOne == 10) {
       buffer->YearOne = 0;
       buffer->YearTen++;
     }
     
     if (buffer->YearTen == 10) {
       buffer->YearTen = 0;
     }

}

/*

  • sendMark
  • Output a Frame / Position marker bit
    */

void sendMark() {

// Send low for 0.8 sec
DDS.down();
delay(799);

// Send high for 0.2 sec
DDS.setfreq(freq, phase);
delay(199);

return;
}

/*

  • sendWeighted
  • Output a Weighted bit (1)
    */

void sendWeighted() {

// Send low for 0.5 sec
DDS.down();
delay(499);

// Send high for 0.5 sec
DDS.setfreq(freq, phase);
delay(499);

return;
}

/*

  • sendUnweighted
  • Output an Unweighted bit (0)
    */

void sendUnweighted() {

// Send low for 0.2 sec
DDS.down();
delay(199);

// Send high for 0.8 sec
DDS.setfreq(freq, phase);
delay(799);

return;
}

@micooke
Copy link
Owner

micooke commented Apr 29, 2017

Yeah im Australian, no wwvb here either!

Thanks for pointing me to that library, its actually a similar approach to what my new wwvb_jjy library is taking - but i use a union to access the bits as a buffer, jjy or wwvb. Interesting that it syncs on your watch, i will have to test it out against my clock (BALDR brand, same as @mr-sneezy) which arrived yesterday.

Also, i ordered that exact dds board a week ago so should be able to use the same test setup (when it eventually arrives). My library has worked with desk clocks, but not watches - i had been wondering if this is because the arduino outputs a 0 to 5v square wave instead of a -5 to 5v sine, looks like it may.

Ill have a look at that library and get back to you. In the meantime, if you wanted to test against an early release have a look at the tx example in my wwvb_jjy library. It requires my pwm library. Initialise the dds in setup, turn it off in the overflow isr and turn it on in the compare isr.

Cheers

@micooke micooke reopened this May 1, 2017
@micooke
Copy link
Owner

micooke commented May 1, 2017

Okay im dumb. Those updates failed, i should be able to fix it easily enough though. I tested last night and got my new library syncing my desk clock but haven't updated the changes yet - needs a tidy up of debug code.

Ive reopened this until the new library is tested and working, then ill transition to that.

Cheers

@RoLorenzoni
Copy link
Author

Hello.
As I'm not a good programmer, I'm testing the range of the signal from the ad9850.
I connected an AM loop antenna on the analog1 output pin of the DDS.
Even without a proper modulation, I could make a simple program that switches the 60khs carrier on and off in a regular half second interval.
Of course, the watch doesn't sync, but, it listens to the signal and shows "strong" signal even 3 meters away from the antenna.
An electronic technician told me that it's easy to extend the range even more by using a MOSFET transistor to amplify the signal.
My idea is that one transmitter could have the range all over the house.
cheers

@RoLorenzoni
Copy link
Author

RoLorenzoni commented May 1, 2017

Oh, forgot to say...
Differently from de PWM output at arduino board, you should connect the other end of the antenna to the gnd pin in order to have a good transmission.

@micooke
Copy link
Owner

micooke commented May 1, 2017

Regarding the transmitter, you will need to check the applicable regulations on your country regarding transmit power. The example i use is current limited by grounding the antenna through a led - this is more to protect the arduino pin, although they are internally limited to 20mA anyways.

Generally you will be limited to 100mW ERP, which is Pt(av) × Gt. The max theoretical duty cycle is 73% over a 60s frame (7 markers at 20%, 53 low at 80%). Pt(av) = 5V × 20mA ×73% = 0.073W = 73mW. To keep under 100mW the max antenna gain would be 10×log10(100/73) = 1.36dB.

If you are not limited to 100mW you have a few options. You could use the arduino to switch in a higher voltage using a fet or op amp, or pull a higher current through a fet. The 2n2222 (800mA max) is a good common BJT which should work well, or a 2n7000 (200mA) if you wanted a mosfet (or BS170 - 500mA, a less common 2n7000 equivalent).

@baradhili
Copy link

remember the regs are always about ERP - effective radiated power.... at 60kHz - you will be more worried about planning regulations than transmitted power as the 1/4 length is huge....

Though careful matching is always a good idea to protect your transmitter driver..

@micooke
Copy link
Owner

micooke commented May 2, 2017

You mean I can't install a 1.25km antenna out the back 😄

Actually I did forget how terrible the gain of these antennas are. They are classified as being electrically small, and i make no effort at impedance matching so probably 'worse than -10dB' if my memory serves me correctly.

So @RoLorenzoni - ignore my warning. Also, @mr-sneezy tells me its 10mW, not 100mW.

@baradhili
Copy link

baradhili commented May 2, 2017 via email

@micooke micooke added the wontfix label May 2, 2017
@micooke
Copy link
Owner

micooke commented May 2, 2017

Ill keep this open for a little bit - but the wwvb_jjy library is now working for a simple example so ill resolved this issue in that repo 🤞

@RoLorenzoni
Copy link
Author

Oh thanks.
I'll test the wwvb_jjy today.
I found this app on the web.
http://www.jrcomputing.com.au/Set_Watch/Set_Watch_Manual.html
May be useful, specially for you, because it's JJY too.

@micooke
Copy link
Owner

micooke commented May 4, 2017

Thanks for that, ill have to check it out.
Im closing this as i have an (untested) example in the new wwvb_jjy library - https://github.com/micooke/wwvb_jjy/blob/master/examples/AD9850_WWVB_tx/AD9850_WWVB_tx.ino

@micooke micooke closed this as completed May 4, 2017
@micooke
Copy link
Owner

micooke commented May 31, 2017

Hey @baradhili did you end up getting a board from Wingsy?

https://forum.allaboutcircuits.com/threads/i-built-it-now-can-i-sell-it.130898/page-2#post-1089916

I wound be interested in the schematic and what antenna you are using.

Cheers

@baradhili
Copy link

baradhili commented May 31, 2017 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants