diff --git a/CW-CARRIER wiring.png b/CW-CARRIER wiring.png new file mode 100644 index 0000000..fb90970 Binary files /dev/null and b/CW-CARRIER wiring.png differ diff --git a/Instructions utilisateur-french.md b/Instructions utilisateur-french.md new file mode 100644 index 0000000..c4447e9 --- /dev/null +++ b/Instructions utilisateur-french.md @@ -0,0 +1,333 @@ +## Instructions utilisateur pour le Raduino_v1.28 +(French translation by Gilles F1BFU) + +**IMPORTANT**: Cette version du croquis nécessite la librairie ["PinChangeInterrupt"](https://playground.arduino.cc/Main/PinChangeInterrupt) pour le support d'interruption. Utiliser votre IDE pour [l'installer](installer-la-bibliothèque-PinChangeInterrupt-french.md) avant de compiler ce croquis! + +Après une mise à jour, toutes les données d'étalonnage, les paramètres de niveau de lecteur, etc. seront réinitialisés aux valeurs d'usine. +Avant de mettre à jour, inscrivez vos valeurs d'étalonnage, etc. Après la mise à jour, utilisez le bouton de fonction pour les rétablir. +Sans aucune modification matérielle, le croquis fournit des fonctionnalités de base LSB. En fonction du choix de l'utilisateur, des fonctionnalités supplémentaires fournies par ce logiciel peuvent être activées en installant les modifications matérielles (minimales) associées. +Voir le tableau ci-dessous indiquant quelles modifications sont requises pour chaque fonction. Les détails de chaque modification sont décrits ci-dessous. + +![Table des modifications matérielles](hardware%20modification%20overview.PNG) + +## Prévention des transmissions hors bandes + + +Le logiciel restreint la transmission à la bande radio-amateur 40m allouée. Pas défaut c'est celle de l'UIT région 2 (Ameriques : 7000-7300 kHz). Si vous êtes localisés dans une UIT différente, dé-commenter UNE des lignes 44-47 en fonction de votre emplacement, et recompiler. + +Si la [PTTsense-mod](#cablage-de-ptt-sense-) est installée, l'émission (TX) hors bande sera désactivée, mais la réception (RX) est encore possible en dehors de la bande. +Si la [PTTsense-mod](#cablage-de-ptt-sense-) n'est PAS installée, l'émission(TX) et la réception(RX) seront toutes les deux désactivés en dehors de la bande. + +Pour inhiber cette fonctionnalité, dé-commenter la ligne 47 (define REGION 0) (normallement pas recommandé!). + +## POTENTIOMETRE D'ACCORD 10-TOURS + +La plage de fréquence par défaut du potentiomètre 1 tour standard fourni n'est que de 50 kHz. +Si vous installez un potentiomètre de 10 tours, vous pouvez étendre la portée pour une couverture complète de la bande des 40 m. + +![Image du potentiomètre 10-tours connecté](Vishay%20100K%2C%2010-turn%20pot%20wire%20up.jpg) + +Utilisation du Bouton de Fonction, aller au menu SETTINGS [réglage de la plage de réglage](#gamme-daccord). + +## CABLAGE DU BOUTON DE FONCTION : + +Connecter un bouton poussoir momentané entre la broche A3 (connecteur P1, fil orange) et la masse. +Les résistances de tirage internes de l'Arduino sont utilisées, donc n'installez PAS de résistance de tirage externe! + +Vous n'avez pas de bouton de fonction (encore)? +Si vous n'installez pas de bouton poussoir, les fonctions de base LSB continueront de fonctionner. +(bien sûr, vous allez manquer le double VFO, RIT, SPLIT, USB, CW, etc.). +L'étalonnage peut encore être effectué à l'ancienne en utilisant le bouton CAL (connecteur P1, broche A2 - fil rouge). +Le réglage de la plage d'accord peut être «codé en dur» en éditant les lignes 34 à 36 et en adaptant les valeurs à vos besoins. Après la recompilation et le téléchargement sur votre Raduino, basculez momentanément le fil CAL rouge sur masse pour initialiser les nouveaux paramètres. + +## PIN LAYOUT + +![Raduino pin layout](raduino_pin_layout.png) + +### Connecteur P1 (8 broches) + +* A0 (noir): PTTSense +* A1 (marron): clé, "point" +* A2 (rouge): CAL +* A3 (orange): Bouton de Fonction +* GND (jaune) +* +5V (vert) +* A6 (bleu): non utilisé +* A7 (violet): Potentiomètre d'Accord + +### Connecteur P3 (16 broches) + +Les 11 premières broches n'ont pas d'entête (tampons seulement): + +* D7: TX-RX +* D6: CW Carrier +* D5: CW Side Tone +* D4: CW SPOT Button +* D3: clé, "trait" +* ??: Non utilisé +* ??: Non utilisé +* ??: Non utilisé +* ??: Non utilisé +* ??: Non utilisé +* ??: Non utilisé + +La broche 5 entête + +* GND (noire) +* GND (marron) +* CLK2 (rouge) +* +5V (orange) +* ?? (jaune): Non utilisé + + +## Cablage de PTT SENSE : + +Connecter la broche A0 (connecteur P1, fil noir) via une résistance de 10K à la sortie de U3 (régulateur LM7805) sur la carte du BITX40. + +![Cablage du PTT Sense](PTT%20SENSE%20wiring.png) + +Quand le PTT n'est pas appuyé (mode réception RX), le régulateur sera éteint, on verra 0 volt sur la broche A0 (LOW). +Quand le PTT est appuyé (mode émission TX), le régulateur sera allumé, alors on verra +5 volts sur la broche A0 (HIGH). +Le PTT SENSE est nécessaire pour les fonctionnalités CW, RIT, SPLIT, and for disabling frequency updating during TX +(to prevent "FM-ing"). Si vous n'installez pas le PTT SENSE, les opérations LSB et USB fonctionneront toujours normalement. + +## CONNEXION D'UNE CLE MORSE DROITE ou d'un BOUTON «TUNE» : + +Une clé droite (ou un manipulateur électronique externe) peut être connectée à la broche A1 du Raduino (connecteur P1, fil marron). +Il est recommandé d'installer une résistance série 1Kohm pour protéger l'entrée Arduino. Lorsque la clé est ouverte, la broche A1 sera HIGH. +Lorsque la clé est abaissée (fermée, court-circuitée à la masse), la broche A1 sera LOW et une porteuse sera transmise. + +Vous pouvez également câbler un simple bouton-poussoir au lieu de connecter une clé morse. La porteuse CW générée peut être utilisée pour régler votre antenne. Dans ce cas, veuillez noter que vous transmettez une porteuse à plein cycle, par conséquent, ne maintenez pas le bouton de réglage enfoncé trop longtemps pour éviter la surchauffe du final (PA)! + +## CLE AUTOMATIQUE - CONNEXION D'UN PADDLE : + +Le Raduino est configuré pour opérer avec une clé droite par défaut. Si vous souhaitez utiliser un manipulateur automatique, accédez au menu SETTINGS et sélectionnez «CW parameters» => «Key-type», puis sélectionnez «paddle», «Rev». paddle '(pour les opérateurs gauchers), 'bug' ou'rev.bug'. +Connectez le contact "point" à la broche A1 du Raduino (connecteur P1, fil marron). +Connectez le contact 'trait' à la broche D3 du Raduino (connecteur P3). +Il est recommandé d'installer des résistances série 1Kohm pour protéger les entrées de l'Arduino. +Le clé intégrée fournit le mode Iambic A et la fonctionnalité "bug" (émulation Vibroplex) et les palettes peuvent être inversées. + +## CONTRÔLE DE VITESSE DE LA CLE CW : + +La vitesse peut être contrôlée à partir du panneau avant (plage de 1 à 50 WPM). Tout en tapant, appuyez et relâchez le FB pour augmenter la vitesse ou le bouton SPOT pour réduire la vitesse. Le réglage de la vitesse CW est mémorisé dans l'EEPROM. + +## CLE TOUCHE CAPACITIVE : + +Le croquis prend en charge la fonctionnalité tactile capacitive. Avec cette fonctionnalité, il est possible d'utiliser des capteurs tactiles au lieu d'une clé ou d'une palette mécanique. La clé droite manuelle ainsi que le fonctionnement automatique du manipulateur sont possibles via les capteurs tactiles. +Voir la démonstration suivante : https://www.youtube.com/watch?v=9MWM6UVy9k4 + +Une modification minimale (ajouter quatre résistances) est requise pour cette fonction. +Remarque : certains constructeurs ont signalé que le capteur tactile n’a pas été détecté de manière fiable lors de la mise sous tension. Dans ce cas, ajoutez de petits condensateurs (3-22pF) aux deux entrées afin d’augmenter légèrement la capacité interne de base. + +![Modification Clé touche capacitive](capacitive%20touch%20keyer%20modification.png) + +Les capteurs tactiles capacitifs sont désactivés par défaut. Pour les activer, accédez au menu SETTINGS et sélectionnez «CW parameters» => «Touch sensor», puis utilisez le bouton de réglage pour définir la sensibilité souhaitée du capteur tactile. + +Une bonne valeur de sensibilité pour commencer est 22. Augmentez la valeur pour plus de sensibilité. La valeur maximale est de 25. +Le réglage de la sensibilité est assez délicat et dépend de vos goûts personnels. Les meilleurs résultats sont généralement obtenus avec une sensibilité élevée. Cependant, si la sensibilité est trop élevée, la radio peut être asservie par des parasites électriques ou des objets à proximité des pavés tactiles. Il peut également arriver que la manipulation iambique se produise même si une seule tablette tactile est touchée. Réduisez la sensibilité si ces symptômes apparaissent. Par ailleurs, si la sensibilité est trop faible, le manipulateur peut ne pas répondre correctement aux touches tactiles, par exemple des doubles POINTS ou TRAITS peuvent être générés. Certaines expérimentations peuvent être nécessaires pour trouver le réglage optimal. +Les capteurs tactiles sont calibrés automatiquement au démarrage. Si vous souhaitez les recalibrer, éteignez et rallumez-le simplement, sans toucher les électrodes du capteur. Les données d'étalonnage du capteur seront affichées sur l'écran LCD au démarrage. +Remarque : Lorsque le manipulateur tactile est activé, le fonctionnement normal de la palette n’est pas possible. Si vous souhaitez utiliser une palette standard, désactivez les capteurs tactiles en réglant la sensibilité sur 0 (capteur tactile désactivé). + +## CÂBLAGE CW-CARRIER: + +Ceci est requis pour le fonctionnement en CW (ou pour générer une porteuse pour le réglage) +Connectez un fil de sortie Raduino D6 (connecteur P3, broche 15), via une résistance série 4.7K, à l'entrée du mélangeur. + +![CÂBLAGE CW-CARRIER](CW-CARRIER%20wiring.png) + +Lorsque la clé est enfoncée, la sortie D6 sera HIGH. Cela injecte un peu de courant continu dans le mélangeur pour qu'il soit déséquilibré. +En conséquence, une porteuse CW sera générée. + +Remarque : Si la porteuse n’est pas générée à pleine puissance de sortie, vous devrez peut-être réduire la résistance de la résistance série 4.7K à une valeur inférieure pour plus d'entraînement. Cependant, essayez de la garder aussi haut que possible pour garder un signal CW propre. N'utilisez jamais une résistance inférieure à 1K! +Conseil supplémentaire : à des fins de réglage, une porteuse réduite est généralement souhaitée. Vous pouvez éventuellement connecter un potentiomètre de 100K en série avec la résistance de 4.7K, cela vous permettra de réduire la force de la porteuse à un niveau approprié. + +Le CW-CARRIER est uniquement requis pour la fonctionnalité CW. Si vous n'installez pas cette ligne, tout le reste fonctionnera normalement. + +## CABLAGE TONALITE LATERALE CW (Side Tone) : + +Une tonalité latérale est disponible sur la sortie Raduino D5 (connecteur P3, broche 14). Ce signal peut être envoyé au haut-parleur / au casque en parallèle avec la sortie de l'amplificateur audio existant. + +![Câblage CW Side Tone](sidetone%20wiring.png) + +La tonalité latérale(Side Tone) souhaitée peut être réglée à l'aide de la touche de fonction du menu SETTINGS. +La tonalité latérale(Side Tone) CW est uniquement utilisée pour le fonctionnement en CW. Si vous n'installez pas cette ligne, tout le reste fonctionnera normalement. + +## CABLAGE TX-RX : + +![CABLAGE TX-RX](TX-RX%20line%20wiring.png) + +Ceci est nécessaire pour le fonctionnement en CW. + +Lorsque la clé est enfoncée, la sortie D7 (connecteur P3, broche 16) sera HIGH. Il ne repassera à LOW que lorsque la clé est allumée depuis au moins 350 ms (cette valeur de temporisation peut être modifiée via le menu SETTINGS). +Ce signal est utilisé pour piloter un transistor NPN qui est connecté en parallèle au commutateur PTT existant, afin de contourner le commutateur PTT pendant le fonctionnement CW. Par conséquent, les relais seront activés tant que D7 est HIGH. +(Suggestion : si vous avez un connecteur combiné microphone/PTT, le transistor de dérivation PTT peut être soudé directement à l’arrière). +(Le transistor de dérivation PTT peut également être utilisé dans le futur pour la fonctionnalité VOX). + +## Bouton CW SPOT/FINE TUNE : + +Connecter un bouton-poussoir momentané entre la broche D4 (connecteur P3) et la masse. +Les résistances de traction internes d'Arduino sont utilisées, donc n'installez PAS de résistance de traction externes! +Lors de l'utilisation de la CW, il est important que les deux stations transmettent leurs porteuses sur la même fréquence. +Lorsque la touche SPOT est enfoncée alors que la radio est en mode réception(RX), le RIT sera désactivé et l’effet local sera généré. +(mais aucune porteuse ne sera transmise). + +Tant que la touche SPOT est maintenue enfoncée, la radio passe temporairement en mode "FINE TUNE", ce qui permet de régler le VFO avec une précision de 1 Hz. Cette fonctionnalité fonctionne également en mode SSB (sauf qu'aucune zone latérale ne sera générée à ce moment-là). +Réglez le potentiomètre de manière à ce que la hauteur du signal CW reçu soit égale à la tonalité CW. +En alignant le ton CW Spot pour correspondre à la hauteur du signal d’une station entrante, vous provoquerez votre signal et +le signal de l'autre station est exactement sur la même fréquence (temps nul). +(Le bouton SPOT n'est qu'une aide supplémentaire et n'est pas strictement requis pour CW - si vous ne l'installez pas, le fonctionnement en CW est toujours possible). + +## FONCTION DE VEROUILLAGE DU CADRAN + +Appuyez simultanément sur le Bouton de Fonction et sur le bouton SPOT pour verrouiller le cadran. +Lorsque le cadran est verrouillé, l'accord sera désactivé, PTT et CW sont toujours possibles. +Appuyez à nouveau sur le bouton de fonction pour déverrouiller. + +## PREVENTION DE RAFALE DE PARASITES + +Afin d'éviter qu'une rafale parasite soit émise lors du passage de réception(RX) à émission(TX), un court délai (TX_DELAY) est appliqué. +Cette fonctionnalité ne fonctionnera que lorsque le [PTTsense-mod](#cablage-de-ptt-sense-) est installé. +Par défaut, le TX_DELAY est défini sur 65 ms. Le temps de retard peut être ajusté en éditant la ligne 80 si nécessaire. + +## UTILISATION DU BOUTON DE FONCTION : + +Plusieurs fonctions sont disponibles avec un seul bouton poussoir. +Certaines options de menu n'apparaîtront pas lorsque les modifications matérielles associées ne seront pas installées. + +### Mode Opératoire + +En mode opératoire normal : + +* 1 pression courte - bascule VFO A/B +* 2 pressions courtes - RIT on (PTT sense est nécessaire pour cette fonction) (presser FB à nouveau pour basculer le RIT off) +* 3 pressions courtes - bascule SPLIT on/off (PTT sense est nécessaire pour cette fonction) +* 4 pressions courtes - bascule de mode (défiler à travers LSB-USB-CWL-CWU) +* 5 pressions courtes - début du mode SCAN de fréquence +* 6 pressions courtes - début du mode suivi VFO A/B +* pression longue (> 1 seconde) - VFO A=B + + +### Mode Settings (paramètres) + +Pour entrer dans le menu SETTINGS, presser et maintenir le Bouton de Fonction pendant un temps VRAIEMENT long (>3 secondes). + +#### Balayage de fréquence (SCAN) + +1 pression courte - régler les paramètres de fréquence SCAN (limite inférieure, limite supérieure, taille du pas, délai du pas) + + - utiliser le potentiomètre d'accord, définir la limite de balayage de fréquence inférieure souhaitée + - presser le Bouton de Fonction (FB) + - utiliser le potentiomètre d'accord, définir la limite de balayage de fréquence supérieure souhaitée + - presser le Bouton de Fonction (FB) + - utiliser le potentiomètre d'accord, set the desired scan step size + - presser le Bouton de Fonction (FB) + - utiliser le potentiomètre d'accord, set the desired scan step delay (also used for A/B monitoring mode) + - presser à nouveau le Bouton de Fonction (FB) pour sauvegarder les paramètres + +#### Configuration CW + +2 pressions courtes - régler les paramètres de la CW (tonalité de sidetone, type de clé CW, semiQSK on / off, délai QSK) (disponible uniquement lorsque la ligne PTTsense est installée) + + - utiliser le potentiomètre d'accord, sélectionner "straight" pour opérer avec une clé droite, "paddle" pour une clé CW automatique, "paddle" pour les opérateurs CW gauchers, "bug" ou "rev. bug". + - presser le Bouton de Fonction (FB) + - utiliser le potentiomètre d'accord, sélectionnez Auto-space ON ou OFF (uniquement lorsque le manipulateur CW automatique a été sélectionné) + - presser le Bouton de Fonction (FB) + - utiliser le potentiomètre d'accord, définir la sensibilité souhaitée du manipulateur tactile (0 = OFF) (uniquement lorsque la modification du capteur tactile est installée) + - presser le Bouton de Fonction (FB) + - utiliser le potentiomètre d'accord, sélectionnez semiQSK ON ou OFF (uniquement lorsque la modification TX-RX est installée) + - presser le Bouton de Fonction (FB) + - utiliser le potentiomètre d'accord, définir la valeur de temporisation souhaitée (ms) (uniquement lorsque semiQSK est activé) + - presser à nouveau le Bouton de Fonction (FB) pour sauvegarder les paramètres + - utiliser le potentiomètre d'accord, régler la hauteur de tonalité latérale désirée (side-tone pitch) + - presser le Bouton de Fonction (FB) + +#### Etalonnage VFO, LSB + +3 pressions courtes - Calibrage de la fréquence du VFO en mode LSB + + - utiliser un autre émetteur-récepteur pour générer une porteuse à une fréquence connue (par exemple 7100,0 kHz) +(ou demander à un ami d'émettre une porteuse à une fréquence connue) + - avant de passer en mode d'étalonnage, réglez d'abord le VFO sur 7100,0 kHz en mode LSB +(le signal reçu n'est peut-être pas encore au battement zéro - zero beat) + - passer en mode d'étalonnage LSB (3 pressions courtes) + - en utilisant le potentiomètre d'accord, ajustez la valeur de correction (ppm) pour exactement le battement zéro (zero beat) + - appuyez à nouveau sur le Bouton de Fonction pour enregistrer le paramétrage + +#### Etalonnage VFO, USB + +4 pressions courtes - Étalonnage de la fréquence VFO en mode USB + + - L'étalonnage USB dépend de l'étalonnage LSB. Assurez-vous donc que l'étalonnage LSB a été effectué en premier! + - utiliser un autre émetteur-récepteur pour générer une porteuse à une fréquence connue (par exemple 7100,0 kHz) + (ou demander à un ami de transmettre une porteuse à une fréquence connue) + - avant de passer en mode étalonnage, réglez d'abord le VFO sur 7100,0 kHz en mode USB + (le signal reçu ne peut pas encore être au battement zéro à ce stade) + - passer en mode étalonnage USB (4 pressions courtes) + - en utilisant le potentiomètre d'accord, ajustez le décalage USB pour exactement le battement zéro + - appuyez à nouveau sur le Bouton de Fonction pour enregistrer le paramétrage + +#### Niveau d'entraînement du VFO, LSB + +5 courtes pressions - réglez le niveau d'entraînement du VFO en mode LSB + + - syntoniser à 7199 kHz, sur la plupart des émetteurs-récepteurs BITX40, un birdie fort est entendu en mode LSB + - donner 3 courtes pressions sur le Bouton de Fonction pour entrer dans le réglage du niveau d'entraînement du VFO + - le niveau d'entraînement par défaut en mode LSB est de 4mA + - en utilisant le potentiomètre d'accord, essayer différents niveaux d'entraînement (2,4,6,8 mA) pour minimiser la force du birdie + - appuyez à nouveau sur le Bouton de Fonction pour enregistrer le réglage + +#### Niveau d'entraînement VFO, USB + +6 courtes pressions - réglez le niveau d'entraînement du VFO en mode USB + + - syntoniser un signal faible + - donner 4 petites pressions sur le Bouton de Fonction pour entrer dans le réglage du niveau d'entraînement du VFO + - le niveau d'entraînement par défaut en mode USB est de 8mA + - en utilisant le potentiomètre d'accord, essayez différents niveaux d'entraînement (2,4,6,8 mA) pour un rapport signal/bruit maximal + - presser le Bouton de Fonction à nouveau pour enregistrer le réglage + Note supplémentaire : Si le niveau d'entraînement maximum de 8mA est encore insuffisant pour le mode USB, la suppression de C91 et C92 peut aider. +Ces bouchons atténuent le signal du VFO à des fréquences plus élevées. Ils ne sont en fait nécessaires que pour le VFO analogique et peuvent être supprimés en toute sécurité si vous utilisez le DDS Raduino au lieu du VFO analogique. + +#### Gamme d'accord + +7 pressions cortes - réglage de la plage de réglage (fréquence minimum, fréquence maximum, durée du potentiomètre) + + - en utilisant le potentiomètre d'accord, réglez la fréquence de syntonisation minimale et appuyez sur le Bouton de Fonction + - en utilisant le potentiomètre d'accord, réglez la fréquence de syntonisation maximale et appuyez sur le Bouton de Fonction à nouveau + La durée de potentiomètre par défaut est de 50 kHz, ceci est correct pour un potentiomètre standard à 1 tour + Si vous installez un potentiomètre multi-tours, vous pouvez étendre l'a durée'intervalle du potentiomètre + - en utilisant le potentiomètre d'accord, définir l'intervalle du potentiomètre souhaité + valeur recommendée : 50 kHz pour un potentiomètre 1-tour, 200 kHz pour un potentiomètre 10 tours + (si la radio est principalement utilisée pour la CW : une plage de 10-25 kHz est recommandée) + - presser le Bouton de Fonction à nouveau pour enregistrer le réglage + +#### Sortie du menu Settings (Paramètres) + +Un appui long (> 1 seconde) permet de quitter le menu SETTINGS et de revenir au mode de fonctionnement normal (# mode de fonctionnement) + +Tous les paramètres utilisateur sont stockés dans EEPROM et récupérés lors du démarrage. + +### Paramètres d'usine + +Pour réinitialiser tous les paramètres utilisés aux valeurs "usine", appuyez sur le Bouton de Fonction et maintenez-le enfoncé pendant la mise sous tension. Les paramètres d'usine sont : + +* VFO calibration value : 180 ppm +* VFO calibration offset (USB) : 1500 Hz +* VFO drive level (LSB) : 4mA +* VFO drive level (USB) : 8mA +* Minimum frequency : 7000 kHz +* Maximum frequency : 7300 kHz +* Tuning pot span : 50 kHz +* Mode LSB for both VFO A and B +* CW side tone : 800 Hz +* CW key-type: Straight key +* Touch sensors: OFF +* Auto-space: OFF +* semiQSK: ON +* QSK delay: 350 ms +* Lower scan limit: 7100 kHz +* Upper scan limit: 7150 kHz +* Scan step: 1 kHz +* Scan step delay: 500 ms + +Un message d'avertissement "VFO uncalibrated" sera affiché jusqu'à ce que vous réétalonniez à nouveau le VFO. diff --git a/PTT SENSE wiring.png b/PTT SENSE wiring.png new file mode 100644 index 0000000..354b9be Binary files /dev/null and b/PTT SENSE wiring.png differ diff --git a/README.md b/README.md index 0180bf4..588ed53 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,250 @@ # bitx40 + BITX40 sketch for Raduino + +This sketch is intended as universal, standard Raduino software that should always work, even on a unmodified out-of-the-box BITX40 + raduino board. Without any hardware modifications the sketch provides the standard basic LSB functionality. +The sketch provides additional features such as USB, CW, RIT/SPLIT, KEYER etc., but these will only become functional when the related (minimal) hardware mods are made. + +![Hardware mod overview](hardware%20modification%20overview.PNG) + +See the [operating and modification instructions](operating-instructions.md) and the [software installation instructions](installation_instructions/0-software-installation.md) for full details. + +**Note 1:** Since v1.20 it is no longer required to download and install the SI5351 library. Minimalist routines to drive the SI5351 are now embedded in the sketch. + +**Note 2:** Since v1.27 the library [PinChangeInterrupt](https://playground.arduino.cc/Main/PinChangeInterrupt) is required for interrupt handling. Use your IDE to [install](library-install.md) it before compiling this sketch! + +**Note 3:** Instead of compiling and uploading using the IDE, you can also simply install the [precompiled binary (hex) file](raduino_v1.29.ino.with_bootloader.eightanaloginputs.hex) using [xLoader](http://xloader.russemotto.com/). + +## Donate + +I develop and maintain ham radio software as a hobby and distribute it for free. However, if you like this software, please consider to donate a small amount to my son's home who stays in an institute for kids with an intellectual disability and autism. The money will be used for adapted toys, a tricycle, a trampoline or a swing. Your support will be highly appreciated by this group of 6 young adolescents! + + [![Donate](https://www.paypalobjects.com/en_US/GB/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=PTAMBM6QT8LP8) + +## Revision record + +1.29 +- Added Roger Beep function (NASA style "Quindar Tone" as used in Apollo missions) + +1.28.1 +- Fixed a bug that the PTTsense mod was not reliably detected on some radios. As a result, CW operation was disabled even when the mod was installed (tks Gary K4VIG and Joe IZ0WIT) + +1.28 +- prevention of out-of-band transmission depending on ITU region (tks Akira JJ1EPE) +- allow the Arduino inputs to settle for 100 ms after power on (tks Ion VA3NOI) + +1.27.7 +- Slightly revised the code so that interrupts are only enabled when the PTTsense mod is installed + +1.27.6 +- improved the code so that the si5351 does not keep receiving tuning updates once the frequency has reached the upper or lower limit +- corrected a bug that when semiQSK is ON, switching between modes did not work correctly for CWL/CWU. +- increased the default delay time for the Function Button for easier operation +- updated the instructions for the CW-CARRIER mod: Advise to use a 4.7K resistor instead of 10K so as to ensure full output power in CW + +1.27.5 +- line 33: Added the ability to set the step delay time for fast tuning (when the tuning pot is at the upper/lower limit) (tks Bob, N4FV) + +1.27.4 +- Line 32: Added the ability to specify text (e.g. your callsign) on the second line of the LCD when it would otherwise be blank (tks Richard, VE3YSH). +- some code clean up + +1.27.3 +- corrected the si5351 25mhz crystal load capacitance setting (tks Daniel KB3MUN) +- changed the initial calibration value to 180 ppm + +1.27.2 +- fixed a bug that with short QSK DELAY time, the radio did not return from CW to SSB mode (tks Gary N3GO for testing) + +1.27.1 +- fixed a bug that the spurious carrier burst was not suppressed in CW-SPLIT mode +- In semiQSK mode, the initial CW element is now delayed by 65ms (to prevent the carrier burst), instead of canceled +- fixed a bug that the radio did not reliably switch from LSB to CWL in semiQSK mode +- fixed a bug that the display got messed up when the VFO is above 10 MHz + +1.27 +- Improved the suppression of the spurious carrier when switching from RX to TX. This function requires the library [PinChangeInterrupt](https://playground.arduino.cc/Main/PinChangeInterrupt) for interrupt handling. Use your IDE to [install](library-install.md) it before compiling this sketch! + +v1.26 +- Rearranged the menu structure, skip menu items that aren't available when related mods are not installed +- Suppress the spurious carrier burst when the radio swithes to TX (tks Dave M0WID) +- For VFO calibration, use multiplicative correction (ppm) for better accuracy over a wide frequency range (tks Jerry KE7ER) +- Improved method for saving/restoring user settings to EEPROM (tks Pavel CO7WT) + +v1.25.1 +- some minor bug corrections to the touch keyer calibration code. + +v1.25 +- Added Capacitive Touch Keyer support. + +v1.24 +- Optimized CW keyer timing characteristics for smoother keying (tks Hidehiko, JA9MAT) +- Added DIAL LOCK function: Press the Function Button and then the SPOT button simultanuously to lock the dial. Press the FB again to unlock. + +v1.23.1 +- corrected bug that the 'auto-space' setting interfered with the 'maximum frequency' setting due to incorrect EEPROM location +- corrected bug that the display became cluttered up in the SETTINGS menu (CW parameters), when CW key was down + +v1.23 +- It is now possible to enable/disable the keyer's auto-space function from the SETTINGS menu (default setting is OFF). +- Added Vibroplex bug emulation to the CW keyer +- Moved all user setting parameters to the top of the sketch for in case you want to edit them manually +- Optimized some code in the keyer routine (tks Pavel CO7WT) + +v1.22 +- Added some functionality allowing the user to choose "paddle" or "reversed paddle" from the SETTINGS menu + +v1.21 +- Added automatic CW keyer functionality. + The default setup is for straight key operation. If you want to use the automatic keyer, connect a paddle keyer to + pin A1 (dit) and pin D3 (dah). In the SETTINGS menu, set CW-key type to "paddle". + While keying, the keyer speed can be adjusted by pressing the Function Button (speed up) or the SPOT button (speed down). + Keyer speed can be set from 1 - 50 WPM. +- It is now possible to set the minimum and maximum tuning frequency via the SETTINGS menu (no longer need to edit the sketch). +- Improved the tuning pot behaviour at the lower and upper ends of the pot. + +v1.20.1 +- Added some constraints so that frequency limits are respected during fast up/down scanning + +v1.20 +- Embedded Jerry Gaffke's, KE7ER, "minimalist standalone si5351bx routines". This not only makes the sketch independant from an + external SI5351 library, but it greatly reduces the memory usage as well. The program space is needed for future development + of additional features that would otherwise not fit in a Nano. Thanks Jerry! + +v1.19 +- Improved responsiveness of the tuning pot for smoother tuning (less need to fiddle up and down to set the correct frequency) +- Improved "Fast Tune" (at either ends of the tuning pot). + The step size is now variable: the closer to the pot limit, the larger the step size. +- In CW SPOT tuning mode, short side tone pulses will be generated instead of a continuous tone. + This makes SPOT tuning easier when tuning to weak CW signals. +- Calibration can now done at 1 Hz precision + +v1.18 +- improved CW performance at higher CW speeds: + reduced the delay at the start of CW transmissions (first dit is no longer lost at high speed CW) + optimized code so that 1:3 CW-ratio is kept even at high CW speed +- improved FINE TUNE mode so that exact frequency (at 1 Hz precision) is displayed while the SPOT button is held pressed +- added an extra option in the SETTINGS menu for setting semiQSK ON or OFF. + This may be useful for CW operators who want to manually activate the PTT (e.g. using a foot switch). + if semiQSK is ON: + radio will automatically switch to CWL (or CWU), and go into TX mode when the morse key goes down + go back to RX automatically when the QSKdelay time is exceeded + radio will switch back to LSB (or USB) when the operator presses the PTT switch + if semiQSK is OFF: + operator must activate PTT manually to move the radio in TX + pressing the PTT does not affect the mode. Use the Function Button to select the desired mode (LSB-USB-CWL-CWU) +- corrected a bug that FINE TUNE was not properly applied in USB mode + +v1.17.1 +- corrected a bug in v1.17 in the shiftBase() routine that the radio didn't return to the correct frequency after switching + VFO's, RIT, SPLIT, FINE TUNE etc. + +v1.17 +- Added "Fine Tune" capability to SPOT button + While the SPOT button is held pressed, the radio will temporarily go into "FINE TUNE" mode, allowing the VFO to be set at 1Hz + precision. This feature works also in SSB mode (except that no sidetone will be generated then). + +v1.16 +- Added CW SPOT TONE button for exact zero beating. + Connect a pushbutton to Arduino pin D4. A SPOT tone will be heard when D4 is connected to ground. + By aligning the CW Spot tone to match the pitch of an incoming station's signal, you will cause your signal and the + other station's signal to be exactly on the same frequency (zero beat). + +v1.15.1 +- RIT offset should only be applied during RX. Due to a small bug the RIT offset was not turned off during transmitting CW. + (RIT in SSB was OK). This has been corrected - RIT works correctly now in all modes. + +v1.15 +- Added true RIT functionality (adjustable RX offset while TX frequency remains fixed) (2 Function Button presses) +- The old 'RIT' function, based on switching between VFOs A/B, is now called "SPLIT" (3 presses) +- Mode selection (4 presses) now rotates between LSB-USB-CWL-CWU +- Major code cleanup to reduce memory usage +- Inserted some delay in various routines to prevent annoying buzzing sound in SETTINGS menu + +v1.14.1 +- Corrected small bug in v1.14 that caused slight ticking noise when the radio was left idle. + +v1.14 +- added VFO A/B monitoring mode (press Function Button 5 times) +- use RX-offset instead of TX-offset in CW mode - the display now shows the correct TX frequency in CW +- changed the way to switch from CW to SSB mode: press PTT to return to SSB mode (tks Willy W1LY) +- restored the functionality for old way calibration method +- simplified the method for sidetone setting: hold key down to hear sidetone +- improved the display during "fast scan" at tuning pot limits (tks Paul KC8WBK) + +v1.13 +- added frequency scanning capability +- added functionality so that the user can set the CW timout value via the SETTINGS menu +- added decimal point to the VFO for better readability, like so: A 7.123.4 LSB +- simplified calibration routine and cleaned up the code to preserve memory space + +v1.12 +- improved responsiveness of Function Button for better user experience +- corrected Tuning Range and SideTone setting procedures + +v1.11 +- added menu beeps (needs CW sidetone to be wired up) +- corrected a minor bug that "TX" is always shown when PTT-SENSE line has not been installed + +v1.10 +- added CW functionality (for straight morse key). This function can also be used just for tuning up. + This requires the CW-CARRIER line connected to Raduino output D6 (connector P3, pin 15). + (see https://github.com/amunters/bitx40/blob/master/CW-CARRIER%20wiring.png for wiring instructions). + The morse key itself shall be connected to Raduino pin A1 (brown wire). + Both sidebands (CWU or CWL) are available for CW operation. +- Semi break-in for CW + This requires the TX-RX line from Raduino output D7 (connector P3, pin 16) to override the existing PTT + switch using a NPN transistor. + (see https://github.com/amunters/bitx40/blob/master/TX-RX%20line%20wiring.png for wiring instructions) +- CW side-tone + This requires some wiring from Raduino output D5 (connector P3, pin 14) to the speaker. + (see https://github.com/amunters/bitx40/blob/master/sidetone%20wiring.png for wiring instructions). + The desired sidetone pitch can be set using the Function Button in the SETTINGS menu. +- Frequency tuning is disabled during TX (to prevent flutter or "FM-ing" during TX). + This requires the PTT SENSE line connected to pin A0 (black wire). + (see https://github.com/amunters/bitx40/blob/master/PTT%20SENSE%20wiring.png for wiring instructions). + +v1.09 +- added RIT (SPLIT) functionality. This requires a PTT sense line connected to pin A0 (black wire) + (see https://github.com/amunters/bitx40/blob/master/PTT%20SENSE%20wiring.png for wiring instructions) +- simplified tuning range setting in SETTINGS menu +- less frequent EEPROM writes so that EEPROM will last longer + +v1.08 +- mode (LSB or USB) of each VFO A and B is now also memorized +- the BITX status (VFO frequencies, modes) is now stored in EEPROM every 10 seconds and retrieved during start up +- a warning message "uncalibrated" is displayed when calibration data has been erased + +v1.07 +- Added functionality via the Function Button: + Use a pusbutton to momentarily ground pin A3 (orange wire). Do NOT install an external pull-up restistor! +- dual VFO capability (RIT is not yet working though) +- LSB/USB mode +- Settings menu for calibration, tuning range, VFO drive level +- All settings are stored in EEPROM and read during startup + +v1.06 +- no functional changes in this version, only improved the updateDisplay routine (Jack Purdum, W8TEE) + (replaced fsprint commmands by str commands for code space reduction) + +v1.05 +- in setup(): increase the VFO drive level to 4mA to kill the birdie at 7199 kHz (Allard, PE1NWL) + (4mA seems the optimum value in most cases, but you may try different drive strengths for best results - + accepted values are 2,4,6,8 mA) + +v1.04 +- Sketch now allows the (optional) use of a 10-turn potentiometer for complete band coverage (Allard, PE1NWL) +- Standard settings are still for a 1-turn pot. +- But if you want to use a 10-turn pot instead, change the values for 'TUNING_RANGE' and 'baseTune' + in lines 189 and 190 to your liking + +v1.03 +- improved tuning "flutter fix" (Jerry, KE7ER) + +v1.02 +- fixed the calibration routine (Allard, PE1NWL) +- fetch the calibration correction factor from EEPROM at startup (Allard, PE1NWL) +- added some changes to comply with si5351 library v2. (Allard, PE1NWL) + +v1.01 +- original BITX40 sketch (Ashhar Farhan) diff --git a/TX-RX line wiring.png b/TX-RX line wiring.png new file mode 100644 index 0000000..68ed545 Binary files /dev/null and b/TX-RX line wiring.png differ diff --git a/Vishay 100K, 10-turn pot wire up.jpg b/Vishay 100K, 10-turn pot wire up.jpg new file mode 100644 index 0000000..ee3279f Binary files /dev/null and b/Vishay 100K, 10-turn pot wire up.jpg differ diff --git a/capacitive touch keyer modification.png b/capacitive touch keyer modification.png new file mode 100644 index 0000000..2ecb022 Binary files /dev/null and b/capacitive touch keyer modification.png differ diff --git a/hardware modification overview.PNG b/hardware modification overview.PNG new file mode 100644 index 0000000..83207a9 Binary files /dev/null and b/hardware modification overview.PNG differ diff --git a/installation_instructions/0-instructions-d'installation-french.md b/installation_instructions/0-instructions-d'installation-french.md new file mode 100644 index 0000000..2ecb8f4 --- /dev/null +++ b/installation_instructions/0-instructions-d'installation-french.md @@ -0,0 +1,218 @@ + +# INSTRUCTIONS D'INSTALLATION (pour Windows 10) +(FRENCH translation by Jacques F1APY) + +## APERCU + +1. [Installation](#installer-ide-arduino) l'environnement de développement intégré sur votre PC (IDE) + +2. [Installation](#installer-la-bibliothèque-pinchangeinterrupt) installer la librairie PinChangeInterrupt sur votre PC + +3. [Téléchargement](#télécharger-le-croquis) télécharger le croquis (sketch) Raduino sur votre PC + +4. [Ouvrir](#ouverture-du-sketch) Ouverture du croquis raduino dans l'IDE arduino + +5. [Compiler](#compiler-le-croquis) compiler/vérifier le croquis raduino + +6. Eteindre la radio + +7. Connecter le cable USB + +8. [Télécharger](#télécharger-le-sketch) charger le firmware dans la carte raduino + +9. Déconnecter le cable USB + +10. Mise en marche de la radio + +## Installer IDE Arduino + +L'IDE ARDUINO est le logiciel nécessaire à la compilation du programme raduino, il permet de telecharger celui-ci dans la puce microcontroleur arduino. +Le programme Raduino(appele "SKETCH" ou croquis en francais) est ecrit en C (Langage de programmation). Les instructions du programme doivent être translatées en code machine de façon a ce que le microcontrôleur arduino puisse éxécuter les instructions : c'est la compilation. + +le logiciel de l'IDE Arduino peut être téléchargé a l'adresse suivante: +https://www.arduino.cc/en/Main/Software +Cliquez sur le lien "Windows Installer, for Windows XP and up" : + +![IDEinstall1](IDEinstall1.png) + +Cliquez sur 'just download': + +![IDEinstall2](IDEinstall2.png) + +Sauvegarder le fichier téléchargé et lancer le programme d'installation ( il est conseillé d'utliser l'option "executer en tant qu'administrateur", clic bouton droit sur le fichier a éxécuter) + +Voulez vous autoriser ce programme à effectuer des changements sur votre ordinateur? +Cliquer 'OUI'. + +Accepter "the License Agreement:" + +![IDEinstall3](IDEinstall3.png) + +laisser tous les "checkboxs" sur on, juste appuyer sur 'Next': + +![IDEinstall3a](IDEinstall3a.png) + +Laisser le répertoire de destination tel quel, juste appuyer sur 'Install': + +![IDEinstall4](IDEinstall4.png) + +L'installation démarre, cela va prendre quelques minutes pour extraire et installer tous les fichiers + +![IDEinstall5](IDEinstall5.png) + +Quand l'installation est terminée, presser "Close": + +![IDEinstall6](IDEinstall6.png) + +Une nouvelle icone a été créée sur le bureau. Faire un double-click pour lancer le programme IDE: + +![IDEinstall7](IDEinstall7.PNG) + +le logiciel IDE arduino démarre, cela prend quelques secondes + +![IDEinstall8](IDEinstall8.PNG) + +Quand celui-ci est enfin prêt, aller dans "files" cliquez sur "preferences", une fenêtre s'ouvre a la ligne "choose language" sélectionner "language système" cela mettra le menu en français si votre pc est en français, il faudra relancer le programme pour que cela prenne effet. + +Quand le programme est démarré, aller dans "Outils" => "Type de carte" => sélectionner arduino Nano: + +![IDEinstall9](IDEinstall9.png) + +puis ensuite aller dans "Outils" => "Processeur:" => selectionner ATmega328P: + +![IDEinstall10](IDEinstall10.png) + +puis ensuite aller dans "Outil" => "Programateur:" => sélectionner AVRISP mkII: + +![IDEinstall11](IDEinstall11.png) + +## Installer la bibliothèque PinChangeInterrupt + +Cette version de croquis (sketch) requiert la bibliothèque ["PinChangeInterrupt"](https://playground.arduino.cc/Main/PinChangeInterrupt) pour la gestion des interruptions. +C'est une librairie non-standard, c'est a dire qu'elle n'est pas installée par défaut. Executer les différentes étapes suivantes pour l'installer sur votre PC +Vous devez faire seulement ceci: + +1. Aller dans 'Croquis' => 'Inclure une Bibliothèque' => 'Gérer les Bibliothèques...': + +![libray-install1](library-install1.PNG) + +2. Le gestionnaire de bibliothèques devrait démarrer. Attendez jusqu'à ce que la liste complete des bibliothèque soit affichée: + +![libray-install2](library-install2.PNG) + +3. Dans la fenêtre de recherche entrer "pinchangeinterrupt": + +![libray-install3](library-install3.PNG) + +4. Sélectioner la bibliothèque nommée "PinChangeInterrupt by NicoHood", puis presser "installer": + +![libray-install4](library-install4.PNG) + +5. Attendez que l'installation soit terminée et presser "fermer" + +![libray-install5](library-install5.PNG) + +## Télécharger le croquis + +Sur la page GitHub cliquez sur le bouton vert "Clone or download": + +![download-sketch1](download_sketch1.png) + +puis cliquez sur 'download ZIP': + +![download-sketch2](download_sketch2.png) + +Le fichier sera téléchargé dans le répertoire "Téléchargements" +Allez dans ce répertoire , cherchez le fichier nommé "bitx40-master", +et double-cliquer sur celui-ci : + +![download-sketch3](download_sketch3.png) + +Cliquez sur "Extract": + +![download-sketch4](download_sketch4.png) + +Et ensuite sur "Extract All": + +![download-sketch5](download_sketch5.png) + +Si vous le souhaitez, vous pouvez changer de répertoire de destination ou seront extraits les fichiers du croquis +ensuite cliquez sur "Extract": + +![download-sketch6](download_sketch6.png) + +un nouveau répertoire nommé "bitx40-master" sera créé à l'endroit que vous avez choisi dans l'étape précédente + +![download-sketch7](download_sketch7.png) + +Le nouveau répertoire créé "bitx40-master" contient différents fichiers. l'un d'eux se nomme "raduino_v1.xxx.ino". C'est le programme actuel Raduino (firmware). + +![download-sketch8](open_sketch1.png) + +## Ouverture du sketch + +Dans le répertoire "bitx40-master",cherchez le fichier "raduino_v1.xxx.ino" et double-cliquez dessus : + +![open-sketch1](open_sketch1.png) + +L'IDE Arduino devrait démarrer.: + +![open-sketch1a](IDEinstall8.PNG) + +Le message suivant devrait apparaître. Pressez OK: + +![open-sketch2](open_sketch2.png) + +Le croquis devrait être ouvert le le code du programme devrait être affiché sur l'IDE: + +![open-sketch3](open_sketch3.PNG) + +## Compiler le croquis + +Dans l' IDE, pressez le bouton "vérifier/compiler" : + +![compile-sketch1](compile_sketch1.png) + +La compilation démarre et prend quelques minutes : + +![compile-sketch2](compile_sketch2.png) + +Quand la compilation est terminée, l'écran suivant apparait: + +![compile-sketch3](compile_sketch3.png) + +## Télécharger le sketch + +En premier éteindre la radio +Connecter le câble USB. + +L'afficheur du Raduino devrait s'allumer parce qu'il reçoit une alimentation du PC via le câble USB, par contre la radio elle ne marche pas + +Dans l'IDE pressez le bouton "Téléchargement" : + +![upload-sketch1](upload-sketch1.png) + +Le croquis sera de nouveau compilé, cela prends quelques minutes: + +![upload-sketch1a](compile_sketch2.png) + +Puis le croquis sera téléchargé, cela devrait prendre une minute ou plus: + +![upload-sketch2](upload-sketch2.png) + +Téléchargement Terminé: + +![upload-sketch3](upload-sketch3.png) + +Le Raduino redémarre, le numéro de version du nouveau croquis (firmware) devrait apparaître brièvement sur l'afficheur : + +![upload-sketch4](upload-sketch4.png) + +Déconnectez le cable USB. + +Allumez la radio. le Raduino démarre. + +Amusez-vous avec le nouveau Raduino. Happy BitX-ing! +73' de Jacques F1APY + + diff --git a/installation_instructions/0-software-installation.md b/installation_instructions/0-software-installation.md new file mode 100644 index 0000000..ef387d2 --- /dev/null +++ b/installation_instructions/0-software-installation.md @@ -0,0 +1,207 @@ +# INSTALLATION INSTRUCTIONS (for Windows 10) + +## OVERVIEW + +1. [Install](#installing-the-arduino-ide) the Arduino IDE on your PC + +2. [Install](#installing-the-pinchangeinterrupt-library) the PinChangeInterrupt Library on your PC + +3. [Download](#downloading-the-sketch) the Raduino sketch to your PC + +4. [Open](#opening-the-sketch) the Raduino sketch in your IDE + +5. [Compile](#compiling-the-sketch) the Raduino sketch + +6. Power OFF the radio + +7. Connect the USB cable + +8. [Upload](#uploading-the-sketch) the firmware to the Raduino board + +9. Disconnect the USB cable + +10. Power ON the radio + +## Installing the Arduino IDE + +The Arduino IDE (Integrated Development Environment) is a piece of software that is needed to "compile" the software and then upload it to the Arduino microcontroller chip. +The Raduino software (or "sketch") is written in C (a programming language). The program statements in the sketch must be "translated" to digital instruction codes that the Arduino microcontroller can understand and execute. This translation process is called "compiling". + +The Arduino IDE software can be downloaded for free from https://www.arduino.cc/en/Main/Software +Click on the link 'Windows Installer, for Windows XP and up': + +![IDEinstall1](IDEinstall1.png) + +Then click on 'just download': + +![IDEinstall2](IDEinstall2.png) + +Save the downloaded file and execute it. +Do you want to allow this app to make changes to your device? Click 'YES'. + +Accept the License Agreement: + +![IDEinstall3](IDEinstall3.png) + +Leave all checkboxes on, just press 'Next': + +![IDEinstall3a](IDEinstall3a.png) + +Leave the Destination Folder as is, just press 'Install': + +![IDEinstall4](IDEinstall4.png) + +The installation will start, it will take a minute or so to extract and install all files: + +![IDEinstall5](IDEinstall5.png) + +When the installation is completed, press "Close": + +![IDEinstall6](IDEinstall6.png) + +A new Arduino icon has been created on your desktop. Double-click it to start the IDE: + +![IDEinstall7](IDEinstall7.PNG) + +The IDE software will start: + +![IDEinstall8](IDEinstall8.PNG) + +When it is ready, go to "Tools" => "Board:" => select Arduino Nano: + +![IDEinstall9](IDEinstall9.png) + +Then go to "Tools" => "Processor:" => select ATmega328P: + +![IDEinstall10](IDEinstall10.png) + +Then go to "Tools" => "Programmer:" => select AVRISP mkii: + +![IDEinstall11](IDEinstall11.png) + +## Installing the PinChangeInterrupt Library + +This sketch version requires the library ["PinChangeInterrupt"](https://playground.arduino.cc/Main/PinChangeInterrupt) for interrupt handling. +This is a non-standard library (it is not installed by default). Execute the following steps to install it onto your PC. +You only need to do this once. + +1. Go to 'Sketch' => 'Include Library' => 'Manage Libraries...': + +![libray-install1](library-install1.PNG) + +2. The Library Manager will be started. Wait until the list of installed libraries is updated: + +![libray-install2](library-install2.PNG) + +3. In the search box, enter "pinchangeinterrupt": + +![libray-install3](library-install3.PNG) + +4. Select the libary named PinChangeInterrupt by NicoHood, then press "install": + +![libray-install4](library-install4.PNG) + +5. Wait until the installation is completed, then press "close": + +![libray-install5](library-install5.PNG) + +## Downloading the sketch + +On the github page, click the green button "Clone or download": + +![download-sketch1](download_sketch1.png) + +Then click on 'download ZIP': + +![download-sketch2](download_sketch2.png) + +The file will be downloaded to the download folder on your PC. +Go to your downloads folder, find the file named "bitx40-master", and double-click it: + +![download-sketch3](download_sketch3.png) + +Click on "Extract": + +![download-sketch4](download_sketch4.png) + +And then "Extract All": + +![download-sketch5](download_sketch5.png) + +Optionally, use the "browse" button to change the location where the files will be extracted to. +Then press "Extract": + +![download-sketch6](download_sketch6.png) + +A new folder named "bitx40-master" will be created on the location you selected in the previous step: + +![download-sketch7](download_sketch7.png) + +The newly created folder "bitx40-master" contains several files. One of them is named "raduino_v1.xxx.ino". This is the actual Raduino firmware. + +![download-sketch8](open_sketch1.png) + +## Opening the sketch + +In the folder "bitx40-master", locate the file "raduino_v1.xxx.ino" and double-click it: + +![open-sketch1](open_sketch1.png) + +The Arduino IDE will be started: + +![open-sketch1a](IDEinstall8.PNG) + +The following message box may appear. Press OK: + +![open-sketch2](open_sketch2.png) + +The sketch will now be opened and the program code will be shown in your IDE: + +![open-sketch3](open_sketch3.PNG) + +## Compiling the sketch + +In the IDE, press the "verify/compile" button: + +![compile-sketch1](compile_sketch1.png) + +Compilation will now start, it may take several minutes to complete: + +![compile-sketch2](compile_sketch2.png) + +When the compilation is completed following screen will be shown: + +![compile-sketch3](compile_sketch3.png) + +## Uploading the sketch + +First power OFF the radio +Then connect the USB cable. +The Raduino display will light up because at this time it receives power from the PC via the USB cable - the radio itself will not work though. + +In the IDE, press the "upload" button: + +![upload-sketch1](upload-sketch1.png) + +The sketch will first be compiled again, this may take a few minutes: + +![upload-sketch1a](compile_sketch2.png) + +Then the sketch will be uploaded to the Arduino, this will take another minute or so: + +![upload-sketch2](upload-sketch2.png) + +Uploading is completed: + +![upload-sketch3](upload-sketch3.png) + +The Raduino will boot up again, the version number of the new firmware will briefly be shown on the display: + +![upload-sketch4](upload-sketch4.png) + +Disconnect the USB cable. + +Power ON the radio. The Raduino will boot up. + +Enjoy the new firmware. Happy BitX-ing! + diff --git a/installation_instructions/IDEinstall1.png b/installation_instructions/IDEinstall1.png new file mode 100644 index 0000000..cc2381b Binary files /dev/null and b/installation_instructions/IDEinstall1.png differ diff --git a/installation_instructions/IDEinstall10.png b/installation_instructions/IDEinstall10.png new file mode 100644 index 0000000..f77c234 Binary files /dev/null and b/installation_instructions/IDEinstall10.png differ diff --git a/installation_instructions/IDEinstall11.png b/installation_instructions/IDEinstall11.png new file mode 100644 index 0000000..feaa0ba Binary files /dev/null and b/installation_instructions/IDEinstall11.png differ diff --git a/installation_instructions/IDEinstall2.png b/installation_instructions/IDEinstall2.png new file mode 100644 index 0000000..5dfeca4 Binary files /dev/null and b/installation_instructions/IDEinstall2.png differ diff --git a/installation_instructions/IDEinstall3.png b/installation_instructions/IDEinstall3.png new file mode 100644 index 0000000..39f1b6d Binary files /dev/null and b/installation_instructions/IDEinstall3.png differ diff --git a/installation_instructions/IDEinstall3a.png b/installation_instructions/IDEinstall3a.png new file mode 100644 index 0000000..1747ef6 Binary files /dev/null and b/installation_instructions/IDEinstall3a.png differ diff --git a/installation_instructions/IDEinstall4.png b/installation_instructions/IDEinstall4.png new file mode 100644 index 0000000..562b294 Binary files /dev/null and b/installation_instructions/IDEinstall4.png differ diff --git a/installation_instructions/IDEinstall5.png b/installation_instructions/IDEinstall5.png new file mode 100644 index 0000000..385c96c Binary files /dev/null and b/installation_instructions/IDEinstall5.png differ diff --git a/installation_instructions/IDEinstall6.png b/installation_instructions/IDEinstall6.png new file mode 100644 index 0000000..e6d77c4 Binary files /dev/null and b/installation_instructions/IDEinstall6.png differ diff --git a/installation_instructions/IDEinstall7.PNG b/installation_instructions/IDEinstall7.PNG new file mode 100644 index 0000000..51ce517 Binary files /dev/null and b/installation_instructions/IDEinstall7.PNG differ diff --git a/installation_instructions/IDEinstall8.PNG b/installation_instructions/IDEinstall8.PNG new file mode 100644 index 0000000..ade494a Binary files /dev/null and b/installation_instructions/IDEinstall8.PNG differ diff --git a/installation_instructions/IDEinstall9.png b/installation_instructions/IDEinstall9.png new file mode 100644 index 0000000..7f3a755 Binary files /dev/null and b/installation_instructions/IDEinstall9.png differ diff --git a/installation_instructions/compile_sketch1.png b/installation_instructions/compile_sketch1.png new file mode 100644 index 0000000..f480764 Binary files /dev/null and b/installation_instructions/compile_sketch1.png differ diff --git a/installation_instructions/compile_sketch2.png b/installation_instructions/compile_sketch2.png new file mode 100644 index 0000000..383aaa2 Binary files /dev/null and b/installation_instructions/compile_sketch2.png differ diff --git a/installation_instructions/compile_sketch3.png b/installation_instructions/compile_sketch3.png new file mode 100644 index 0000000..e1bf629 Binary files /dev/null and b/installation_instructions/compile_sketch3.png differ diff --git a/installation_instructions/download_sketch1.png b/installation_instructions/download_sketch1.png new file mode 100644 index 0000000..b2d2b48 Binary files /dev/null and b/installation_instructions/download_sketch1.png differ diff --git a/installation_instructions/download_sketch2.png b/installation_instructions/download_sketch2.png new file mode 100644 index 0000000..df4e4a2 Binary files /dev/null and b/installation_instructions/download_sketch2.png differ diff --git a/installation_instructions/download_sketch3.png b/installation_instructions/download_sketch3.png new file mode 100644 index 0000000..aad3ea3 Binary files /dev/null and b/installation_instructions/download_sketch3.png differ diff --git a/installation_instructions/download_sketch4.png b/installation_instructions/download_sketch4.png new file mode 100644 index 0000000..35da356 Binary files /dev/null and b/installation_instructions/download_sketch4.png differ diff --git a/installation_instructions/download_sketch5.png b/installation_instructions/download_sketch5.png new file mode 100644 index 0000000..d69c1da Binary files /dev/null and b/installation_instructions/download_sketch5.png differ diff --git a/installation_instructions/download_sketch6.png b/installation_instructions/download_sketch6.png new file mode 100644 index 0000000..0fc1356 Binary files /dev/null and b/installation_instructions/download_sketch6.png differ diff --git a/installation_instructions/download_sketch7.png b/installation_instructions/download_sketch7.png new file mode 100644 index 0000000..4c0c513 Binary files /dev/null and b/installation_instructions/download_sketch7.png differ diff --git a/installation_instructions/library-install1.PNG b/installation_instructions/library-install1.PNG new file mode 100644 index 0000000..c0e1ec9 Binary files /dev/null and b/installation_instructions/library-install1.PNG differ diff --git a/installation_instructions/library-install2.PNG b/installation_instructions/library-install2.PNG new file mode 100644 index 0000000..a13cd43 Binary files /dev/null and b/installation_instructions/library-install2.PNG differ diff --git a/installation_instructions/library-install3.PNG b/installation_instructions/library-install3.PNG new file mode 100644 index 0000000..bae517d Binary files /dev/null and b/installation_instructions/library-install3.PNG differ diff --git a/installation_instructions/library-install4.PNG b/installation_instructions/library-install4.PNG new file mode 100644 index 0000000..852ceb2 Binary files /dev/null and b/installation_instructions/library-install4.PNG differ diff --git a/installation_instructions/library-install5.PNG b/installation_instructions/library-install5.PNG new file mode 100644 index 0000000..d369a0d Binary files /dev/null and b/installation_instructions/library-install5.PNG differ diff --git a/installation_instructions/open_sketch1.png b/installation_instructions/open_sketch1.png new file mode 100644 index 0000000..1d6415c Binary files /dev/null and b/installation_instructions/open_sketch1.png differ diff --git a/installation_instructions/open_sketch2.png b/installation_instructions/open_sketch2.png new file mode 100644 index 0000000..6788972 Binary files /dev/null and b/installation_instructions/open_sketch2.png differ diff --git a/installation_instructions/open_sketch3.PNG b/installation_instructions/open_sketch3.PNG new file mode 100644 index 0000000..15d8640 Binary files /dev/null and b/installation_instructions/open_sketch3.PNG differ diff --git a/installation_instructions/upload-sketch1.png b/installation_instructions/upload-sketch1.png new file mode 100644 index 0000000..ba757c7 Binary files /dev/null and b/installation_instructions/upload-sketch1.png differ diff --git a/installation_instructions/upload-sketch2.png b/installation_instructions/upload-sketch2.png new file mode 100644 index 0000000..5d95be7 Binary files /dev/null and b/installation_instructions/upload-sketch2.png differ diff --git a/installation_instructions/upload-sketch3.png b/installation_instructions/upload-sketch3.png new file mode 100644 index 0000000..cfa54b5 Binary files /dev/null and b/installation_instructions/upload-sketch3.png differ diff --git a/installation_instructions/upload-sketch4.png b/installation_instructions/upload-sketch4.png new file mode 100644 index 0000000..a8cc85b Binary files /dev/null and b/installation_instructions/upload-sketch4.png differ diff --git "a/installer-la-biblioth\303\250que-PinChangeInterrupt-french.md" "b/installer-la-biblioth\303\250que-PinChangeInterrupt-french.md" new file mode 100644 index 0000000..f38dc95 --- /dev/null +++ "b/installer-la-biblioth\303\250que-PinChangeInterrupt-french.md" @@ -0,0 +1,25 @@ +## Installer la bibliothèque PinChangeInterrupt + +Cette version de croquis (sketch) requiert la bibliothèque ["PinChangeInterrupt"](https://playground.arduino.cc/Main/PinChangeInterrupt) pour la gestion des interruptions. +C'est une librairie non-standard, c'est a dire qu'elle n'est pas installée par défaut. Executer les différentes étapes suivantes pour l'installer sur votre PC +Vous devez faire seulement ceci: + +1. Aller dans 'Croquis' => 'Inclure une Bibliothèque' => 'Gérer les Bibliothèques...': + +![libray-install1](installation_instructions/library-install1.PNG) + +2. Le gestionnaire de bibliothèques devrait démarrer. Attendez jusqu'à ce que la liste complete des bibliothèque soit affichée: + +![libray-install2](installation_instructions/library-install2.PNG) + +3. Dans la fenêtre de recherche entrer "pinchangeinterrupt": + +![libray-install3](installation_instructions/library-install3.PNG) + +4. Sélectioner la bibliothèque nommée "PinChangeInterrupt by NicoHood", puis presser "installer": + +![libray-install4](installation_instructions/library-install4.PNG) + +5. Attendez que l'installation soit terminée et presser "fermer" + +![libray-install5](installation_instructions/library-install5.PNG) diff --git a/library-install.md b/library-install.md new file mode 100644 index 0000000..fcca032 --- /dev/null +++ b/library-install.md @@ -0,0 +1,25 @@ +## Installing the PinChangeInterrupt Library + +This sketch version requires the library ["PinChangeInterrupt"](https://playground.arduino.cc/Main/PinChangeInterrupt) for interrupt handling. +This is a non-standard library (it is not installed by default). Execute the following steps to install it onto your PC. +You only need to do this once. + +1. Go to 'Sketch' => 'Include Library' => 'Manage Libraries...': + +![libray-install1](installation_instructions/library-install1.PNG) + +2. The Library Manager will be started. Wait until the list of installed libraries is updated: + +![libray-install2](installation_instructions/library-install2.PNG) + +3. In the search box, enter "pinchangeinterrupt": + +![libray-install3](installation_instructions/library-install3.PNG) + +4. Select the libary named PinChangeInterrupt by NicoHood, then press "install": + +![libray-install4](installation_instructions/library-install4.PNG) + +5. Wait until the installation is completed, then press "close": + +![libray-install5](installation_instructions/library-install5.PNG) diff --git a/operating-instructions.md b/operating-instructions.md new file mode 100644 index 0000000..c46e692 --- /dev/null +++ b/operating-instructions.md @@ -0,0 +1,359 @@ +## User instructions for Raduino_v1.29 + +**IMPORTANT**: This sketch version requires the library ["PinChangeInterrupt"](https://playground.arduino.cc/Main/PinChangeInterrupt) for interrupt handling. Use your IDE to [install](library-install.md) it before compiling this sketch! + +After a version update all calibration data, drive level settings, etc will be reset to 'factory' values. +Before updating note down your cal values etc. After the update use the Function Button to set them back again. +Without any hardware modifications the sketch provides basic LSB functionality. Depending on the user's choice, +additional functionality provided by this software can be activated by installing the related (minimal) hardware mods. +See the table below showing which mods are required for each function. Details of each mod are described below. + +![Table of hardware modifications](hardware%20modification%20overview.PNG) + +## Prevention of out-of-band transmissions + +The software restricts transmission to the 40m ham band allocation. By default this is ITU region 2 (Americas: 7000-7300 kHz). If you are located in a different ITU region, uncomment ONE of the lines 44-47 depending on your location, and recompile. + +If the [PTTsense-mod](#ptt-sense-wiring) is installed, out-of-band TX will be disabled, but RX is still possible outside the band. +If the [PTTsense-mod](#ptt-sense-wiring) is NOT installed, both TX as well as RX will be disabled outside the band. + +To completely inhibit this feature, uncomment line 47 (define REGION 0) (not normally recommended!). + +## 10-TURN TUNING POT + +The default frequency span of the standard supplied 1-turn tuning pot is only 50 kHz. +If you install a 10-turn pot instead you can extend the span for full 40m band coverage. + +![Image of 10-turn pot hookup](Vishay%20100K%2C%2010-turn%20pot%20wire%20up.jpg) + +Using the Function Button, go to the SETTINGS menu and [set the desired pot span](#tuning-range). + +## FUNCTION BUTTON WIRING: + +Connect a momentary pushbutton between pin A3 (connector P1, orange wire) and ground. +Arduino's internal pull-up resistors are used, therefore do NOT install an external pull-up resistor! + +Don't have a Function Button (yet)? +If you don't install a pushbutton then the basic LSB functions will still work. +(of course you will miss the dual VFO, RIT, SPLIT, USB, CW, etc. then). +Calibration can still be done in the old fashioned way by using the CAL button (connector P1, pin A2 - red wire). +The tuning range setting can be 'hard coded' by editing lines 34-36 and adapting the values to your needs. After recompiling and uploading to your Raduino, momentarily switch the red CAL wire to ground to initialize the new settings. + +## PIN LAYOUT + +![Raduino pin layout](raduino_pin_layout.png) + +### Connector P1 (8 pin) + +* A0 (black): PTTSense +* A1 (brown): Key, "dit" +* A2 (red): CAL +* A3 (orange): Function Button +* GND (yellow) +* +5V (green) +* A6 (blue): unused +* A7 (purple): Tuning Pot + +### Connector P3 (16 pin) + +The first 11 pins have no headers (pads only): + +* D7: TX-RX +* D6: CW Carrier +* D5: CW Side Tone +* D4: CW SPOT Button +* D3: Key, "dah" +* D2: PULSE for capacitive touch keyer +* ??: Unused +* ??: Unused +* ??: Unused +* ??: Unused +* ??: Unused + +The 5 pin header + +* GND (black) +* GND (brown) +* CLK2 (red) +* +5V (orange) +* ?? (yellow): Unused + + +## PTT SENSE WIRING: + +Connect pin A0 (connector P1, black wire) via a 10K resistor to the output of U3 (LM7805 regulator) on the BITX40 board. + +![PTT Sense wiring](PTT%20SENSE%20wiring.png) + +When the PTT is not pressed (RX mode), the regulator will be off, so pin A0 will see 0V (LOW). +When the PTT is pressed (TX mode), the regulator will be on, so pin A0 will see +5V (HIGH). +The PTT SENSE is required for the CW, RIT, SPLIT functionality, and for disabling frequency updating during TX +(to prevent "FM-ing"). If you don't install the PTT sense, LSB and USB operation will still work normally. + +## CONNECTING A STRAIGHT MORSE KEY or 'TUNE' BUTTON: + +A straight key (or external electronic keyer) can be connected to Raduino pin A1 (connector P1, brown wire). +It is recommended to install a 1K series resistor to protect the Arduino input. When the key is up (open) pin A1 will be HIGH. +When the key is down (closed, shorted to ground) pin A1 will be LOW, and a carrier will be transmitted. + +You could also wire up a simple push button instead of connecting a morse key. The generated CW carrier can be used for +tuning up your antenna. In that case please note that you will be transmitting a carrier at full duty cycle, therefore don't +keep the tune button pressed for too long to prevent overheating the final! + +## AUTOMATIC KEYER - CONNECTING A PADDLE: + +The Raduino is set up for straight key operation by default. If you want to use the automatic keyer, go to the SETTINGS menu +and to 'CW parameters' => 'Key-type' and select 'paddle', 'rev. paddle' (for left-handed operators), 'bug', or 'rev. bug'. +Connect the 'dit' contact to Raduino pin A1 (connector P1, brown wire). +Connect the 'dah' contact to Raduino pin D3 (connector P3). +It is recommended to install 1K series resistors to protect the Arduino inputs. +The built-in keyer provides Iambic mode A and 'bug'-mode (Vibroplex emulation) functionality and the paddles can be reversed. + +## CW KEYER SPEED CONTROL: + +The speed can be controlled from the front panel (range 1-50 WPM). While keying, press and release the FB to increase the speed, or the SPOT button to reduce the speed. The CW speed setting is memorized in EEPROM. + +## CAPACITIVE TOUCH KEYER: + +The sketch supports Capacitive Touch functionality. With this feature it is possible to use touch sensors instead of a +mechanical morse key or paddle. Manual straight key as well as automatic keyer operation is possible via the touch sensors. +See the following demo video: +[![](http://img.youtube.com/vi/9MWM6UVy9k4/0.jpg)](http://www.youtube.com/watch?v=9MWM6UVy9k4 "touch keyer demo") + +A minimal modification (add four resistors) is required for this function. +Note: some builders reported that the touch sensor wasn't reliably detected during power on, in that case add some small (3-22pF) capacitors to both inputs in order to slightly increase the internal 'base' capacitance. + +![Capacitive Touch Keyer Mod](capacitive%20touch%20keyer%20modification.png) + +The capacitive touch sensors are disabled by default. To enable them, go to the SETTINGS menu and to 'CW parameters' => +'Touch sensor', and use the tuning knob to set the desired touch sensor sensitivity. + +A good sensitivity value to start with is 22. Increase the value for more sensitivity. The maximum value is 25. +The sensitivity setting is quite delicate and depends on your personal taste. The best results are usually obtained with +high sensitivity. However, if the sensitivity is too high, the radio may be keyed by electrical noise or objects nearby +the touch pads. It may also happen that iambic keying will occur even when only one touch pad is touched. Reduce the +sensitivity if these symptons occur. On the other hand, if the sensitivity is too low, the keyer may not respond properly +to the touch pads, for example double DITs or DAHs may be generated. Some experimentation may be required to find the optimum +setting. + +The touch sensors are calibrated automatically at start up. If you want to recalibrate them, simply power OFF and ON again, +while NOT touching the sensor pads. The sensor calibration data will be shown on the LCD display at start up. +Note: When the touch keyer is enabled, normal paddle operation is not possible. If you want to use a standard paddle, disable the touch sensors by setting the sensitivity to 0 (touch sensor OFF). + +## CW-CARRIER WIRING: + +This is required for CW operation (or when you want to generate a carrier for tuning) +Connect a wire from Raduino ouput D6 (connector P3, pin 15), via a 4.7K series resistor, to the input of the mixer. + +![CW Carrier Wiring](CW-CARRIER%20wiring.png) + +When the key is down ouput D6 will be HIGH. This injects some DC current into the mixer so that it becomes unbalanced. +As a result a CW carrier will be generated. + +Note: If the carrier is not generated at full output power, you may need to reduce the 4.7K series resistor to a lower value +for more drive. However try to keep it as high as possible to keep a clean CW signal. Never use a resistor less than 1K! +Extra tip: For tuning purposes a reduced carrier is usually desired. You can optionally connect a 100K pot in series with the +4.7K resistor, this will allow you to reduce the strength of the carrier to a suitable level. + +The CW-CARRIER is only required for CW functionality. If you don't install this line everything else +will still work normally. + +## CW SIDE TONE WIRING: + +A side tone is available at Raduino output D5 (connector P3, pin 14). This signal can be fed to the speaker/headphones +in parallel to the output from the existing audio amplifier. + +![CW Side Tone Wiring](sidetone%20wiring.png) + +The desired side tone pitch can be set using the Function Button in the SETTINGS menu. +The CW-side tone is only used for CW operation. If you don't install this line everything else +will still work normally. + +## TX-RX WIRING: + +![TX-RX Wiring](TX-RX%20line%20wiring.png) + +This is required for CW operation. + +When the key is down output D7 (connector P3, pin 16) will be HIGH. It will only go LOW again when the key has been up for at +least 350 ms (this timeout value can be changed via the SETTINGS menu). +This signal is used to drive an NPN transistor which is connected in parallel to the existing PTT switch, so that it will +bypass the PTT switch during CW operation. As as result the relays will be activated as long as D7 is HIGH. +(Suggestion: If you have a combined microphone/PTT connector, the PTT bypass transistor can be soldered directly on the +back of it). +(The PTT bypass transistor may be used in the future for VOX functionality as well). + +## CW SPOT/FINE TUNE Button: + +Connect a momentary pushbutton between pin D4 (connector P3) and ground. +Arduino's internal pull-up resistors are used, therefore do NOT install an external pull-up resistor! +When operating CW it is important that both stations transmit their carriers on the same frequency. +When the SPOT button is pressed while the radio is in RX mode, the RIT will be turned off and the sidetone will be generated +(but no carrier will be transmitted). + +While the SPOT button is held pressed, the radio will temporarily go into "FINE TUNE" mode, allowing the VFO to be set at 1Hz +precision. This feature works also in SSB mode (except that no sidetone will be generated then). +Tune the tuning pot so that the pitch of the received CW signal is equal to the pitch of the CW Spot tone. +By aligning the CW Spot tone to match the pitch of an incoming station's signal, you will cause your signal and +the other station's signal to be exactly on the same frequency (zero beat). +(The SPOT button is just an extra tuning aid and is not strictly required for CW - if you don't install it, CW operation is +still possible). + +## DIAL LOCK FUNCTION + +Press the Function Button and then the SPOT button simultanuously to lock the dial. +When the dial is locked tuning will be disabled, PTT and CW is still possible. +Press the Function Button again to unlock. + +## SPURIOUS BURST PREVENTION + +In order to prevent that a spurious burst is emitted when switching from RX to TX, a short delay (TX_DELAY) is applied. +This feature will only work when the [PTTsense-mod](#ptt-sense-wiring) is installed. +By default the TX_DELAY is set to 65 ms. The delay time can be adjusted by editing line 80 as necessary. + +## FUNCTION BUTTON USAGE: + +Several functions are available with just one pushbutton. +Certain menu options will not appear when the related hardware mods are not installed. + +### Operating Mode + +In normal operation mode: + +* 1 short press - toggle VFO A/B +* 2 short presses - RIT on (PTT sense is required for this function) (press FB again to switch RIT off) +* 3 short presses - toggle SPLIT on/off (PTT sense is required for this function) +* 4 short presses - switch mode (rotate through LSB-USB-CWL-CWU) +* 5 short presses - start frequency SCAN mode +* 6 short presses - start VFO A/B monitoring mode +* 7 short presses - toggle Roger Beep on/off (PTT sense is required for this function) +* long press (> 1 second) - VFO A=B + + +### Settings Mode + +To enter SETTINGS menu, press and hold the Function Button for a VERY long (>3 seconds). + +#### Frequency Scan + +1 short press - set frequency SCAN parameters (lower limit, upper limit, step size, step delay) + + - using the tuning pot, set the desired lower frequency scan limit + - press the FB + - using the tuning pot, set the desired upper frequency scan limit + - press the FB + - using the tuning pot, set the desired scan step size + - press the FB + - using the tuning pot, set the desired scan step delay (also used for A/B monitoring mode) + - press the FB again to save the settings + +#### CW Configuration + +2 short presses - set CW paramaters (sidetone pitch, CW-key type, semiQSK on/off, QSK delay) + (only available when PTTsense line is installed) + + - using the tuning pot, select "straight" for straight CW key operation, "paddle" for automatic CW keyer, + "rev. paddle" for left-handed CW-operators, "bug", or "rev. bug". + - press the FB + - using the tuning pot, select Auto-space ON or OFF (only when automatic CW keyer was selected) + - press the FB + - using the tuning pot, set the desired touch keyer sensitivity (0=OFF) (only when touch sensor mod is installed) + - press the FB + - using the tuning pot, select semiQSK ON or OFF (only when TX-RX mod is installed) + - press the FB + - using the tuning pot, set the desired timeout value (ms) (only when semiQSK in ON) + - press the FB again to save the settings + - using the tuning pot, set the desired sidetone pitch + - press the FB + +#### VFO Calibration, LSB + +3 short presses - VFO frequency calibration in LSB mode + + - use another transceiver to generate a carrier at a known frequency (for example 7100.0 kHz) + (or ask a friend to transmit a carrier at a known frequency) + - before going into the calibration mode, first set the VFO to 7100.0 kHz in LSB mode + (the received signal may not yet be zero beat at this point) + - go into the LSB calibration mode (3 short presses) + - using the tuning pot, adjust the correction value (ppm) for exactly zero beat + - press the Function Button again to save the setting + +#### VFO Calibration, USB + +4 short presses - VFO frequency calibration in USB mode + + - USB calibration depends on LSB calibration, so make sure that LSB calibration has been done first! + - use another transceiver to generate a carrier at a known frequency (for example 7100.0 kHz) + (or ask a friend to transmit a carrier at a known frequency) + - before going into the calibration mode, first set the VFO to 7100.0 kHz in USB mode + (the received signal may not yet be zero beat at this point) + - go into the USB calibration mode (4 short presses) + - using the tuning pot, adjust the USB offset for exactly zero beat + - press the Function Button again to save the setting + +#### VFO Drive Level, LSB + +5 short presses - set VFO drive level in LSB mode + + - tune to 7199 kHz, on most BITX40 transceivers a strong birdie is heard in LSB mode + - give 3 short presses to the FB to enter the VFO drive level adjustment + - the default drive level in LSB mode is 4mA + - using the tuning pot, try different drive levels (2,4,6,8 mA) to minimize the strength of the birdie + - press the FB again to save the setting + +#### VFO Drive Level, USB + +6 short presses - set VFO drive level in USB mode + + - tune to a weak signal + - give 4 short presses to the FB to enter the VFO drive level adjustment + - the default drive level in USB mode is 8mA + - using the tuning pot, try different drive levels (2,4,6,8 mA) for maximum signal to noise ratio + - press the FB again to save the setting + Extra Note: If the max. drive level of 8mA is still insufficient for USB mode, removal of C91 and C92 may help. + These caps attenuate the VFO signal at higher frequencies. They're actually only needed for the analog VFO + and can safely be removed if you use the Raduino DDS instead of the analog VFO. + +#### Tuning Range + +7 short presses - set tuning range (min frequency, max frequency, pot span) + + - using the tuning pot, set the minimum tuning frequency and press the FB + - using the tuning pot, set the maximum tuning frequency and press the FB again + The default pot span is 50 kHz, this is OK for a standard 1-turn pot + If you install a multi-turn pot instead you can extend the pot span + - using the tuning pot, set the desired pot span + recommended value: 50 kHz for a 1-turn pot, 200 kHz for a 10-turn pot + (if the radio is mainly used for CW: a pot span of 10-25 kHz is recommended) + - press the FB again to save the setting + +#### Exit Settings menu + +One long press (>1 second) will exit the SETTINGS menu and return to normal [operating mode](#operating-mode) + +All user settings are stored in EEPROM and retrieved during startup. + +### Factory Settings + +To reset all used settings to "factory" values, press and hold the Function button during power on. The factory settings are: + +* VFO calibration value: 180 ppm +* VFO calibration offset (USB): 1500 Hz +* VFO drive level (LSB): 4mA +* VFO drive level (USB): 8mA +* Minimum frequency: 7000 kHz +* Maximum frequency: 7300 kHz +* Tuning pot span: 50 kHz +* Mode LSB for both VFO A and B +* CW side tone: 800 Hz +* CW key-type: Straight key +* Touch sensors: OFF +* Auto-space: OFF +* semiQSK: ON +* QSK delay: 350 ms +* Lower scan limit: 7100 kHz +* Upper scan limit: 7150 kHz +* Scan step: 1 kHz +* Scan step delay: 500 ms + +A warning message "VFO uncalibrated" will be displayed until you recalibrate the VFO again. diff --git a/raduino.ino b/raduino.ino deleted file mode 100644 index b1225b5..0000000 --- a/raduino.ino +++ /dev/null @@ -1,631 +0,0 @@ -/** - * This source file is under General Public License version 3. - * - * Most source code are meant to be understood by the compilers and the computers. - * Code that has to be hackable needs to be well understood and properly documented. - * Donald Knuth coined the term Literate Programming to indicate code that is written be - * easily read and understood. - * - * The Raduino is a small board that includes the Arduin Nano, a 16x2 LCD display and - * an Si5351a frequency synthesizer. This board is manufactured by Paradigm Ecomm Pvt Ltd - * - * To learn more about Arduino you may visit www.arduino.cc. - * - * The Arduino works by firt executing the code in a function called setup() and then it - * repeatedly keeps calling loop() forever. All the initialization code is kept in setup() - * and code to continuously sense the tuning knob, the function button, transmit/receive, - * etc is all in the loop() function. If you wish to study the code top down, then scroll - * to the bottom of this file and read your way up. - * - * Below are the libraries to be included for building the Raduino - * - * The EEPROM library is used to store settings like the frequency memory, caliberation data, - * callsign etc . - */ - -#include - -/** - * The main chip which generates upto three oscillators of various frequencies in the - * Raduino is the Si5351a. To learn more about Si5351a you can download the datasheet - * from www.silabs.com although, strictly speaking it is not a requirment to understand this code. - * Instead, you can look up the Si5351 library written by Jason Mildrum, NT7S. You can download and - * install it from https://github.com/etherkit/Si5351Arduino to complile this file. - * The Wire.h library is used to talk to the Si5351 and we also declare an instance of - * Si5351 object to control the clocks. - */ -#include -#include -Si5351 si5351; -/** - * The Raduino board is the size of a standard 16x2 LCD panel. It has three connectors: - * - * First, is an 8 pin connector that provides +5v, GND and six analog input pins that can also be - * configured to be used as digital input or output pins. These are referred to as A0,A1,A2, - * A3,A6 and A7 pins. The A4 and A5 pins are missing from this connector as they are used to - * talk to the Si5351 over I2C protocol. - * - * A0 A1 A2 A3 +5v GND A6 A7 - * BLACK BROWN RED ORANGE YELLOW GREEN BLUE VIOLET - * - * Second is a 16 pin LCD connector. This connector is meant specifically for the standard 16x2 - * LCD display in 4 bit mode. The 4 bit mode requires 4 data lines and two control lines to work: - * Lines used are : RESET, ENABLE, D4, D5, D6, D7 - * We include the library and declare the configuration of the LCD panel too - */ - -#include -LiquidCrystal lcd(8,9,10,11,12,13); - -/** - * The Arduino, unlike C/C++ on a regular computer with gigabytes of RAM, has very little memory. - * We have to be very careful with variables that are declared inside the functions as they are - * created in a memory region called the stack. The stack has just a few bytes of space on the Arduino - * if you declare large strings inside functions, they can easily exceed the capacity of the stack - * and mess up your programs. - * We circumvent this by declaring a few global buffers as kitchen counters where we can - * slice and dice our strings. These strings are mostly used to control the display or handle - * the input and output from the USB port. We must keep a count of the bytes used while reading - * the serial port as we can easily run out of buffer space. This is done in the serial_in_count variable. - */ -char serial_in[32], c[30], b[30], printBuff[32]; -int count = 0; -unsigned char serial_in_count = 0; - -/** - * We need to carefully pick assignment of pin for various purposes. - * There are two sets of completely programmable pins on the Raduino. - * First, on the top of the board, in line with the LCD connector is an 8-pin connector - * that is largely meant for analog inputs and front-panel control. It has a regulated 5v output, - * ground and six pins. Each of these six pins can be individually programmed - * either as an analog input, a digital input or a digital output. - * The pins are assigned as follows: - * A0, A1, A2, A3, +5v, GND, A6, A7 - * BLACK BROWN RED ORANGE YELLW GREEN BLUEVIOLET - * (while holding the board up so that back of the board faces you) - * - * Though, this can be assigned anyway, for this application of the Arduino, we will make the following - * assignment - * A2 will connect to the PTT line, which is the usually a part of the mic connector - * A3 is connected to a push button that can momentarily ground this line. This will be used to switch between different modes, etc. - * A6 is to implement a keyer, it is reserved and not yet implemented - * A7 is connected to a center pin of good quality 100K or 10K linear potentiometer with the two other ends connected to - * ground and +5v lines available on the connector. This implments the tuning mechanism - */ - -#define ANALOG_KEYER (A1) -#define CAL_BUTTON (A2) -#define FBUTTON (A3) -#define PTT (A6) -#define ANALOG_TUNING (A7) - -/** - * The second set of 16 pins on the bottom connector are have the three clock outputs and the digital lines to control the rig. - * This assignment is as follows : - * Pin 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 - * +5V +5V CLK0 GND GND CLK1 GND GND CLK2 GND D2 D3 D4 D5 D6 D7 - * These too are flexible with what you may do with them, for the Raduino, we use them to : - * - TX_RX line : Switches between Transmit and Receive after sensing the PTT or the morse keyer - * - CW_KEY line : turns on the carrier for CW - * These are not used at the moment. - */ - -#define TX_RX (7) -#define CW_TONE (6) -#define CW_KEY (5) -#define TX_LPF_SEL (4) - -/** - * The raduino has a number of timing parameters, all specified in milliseconds - * CW_TIMEOUT : how many milliseconds between consecutive keyup and keydowns before switching back to receive? - * The next set of three parameters determine what is a tap, a double tap and a hold time for the funciton button - * TAP_DOWN_MILLIS : upper limit of how long a tap can be to be considered as a button_tap - * TAP_UP_MILLIS : upper limit of how long a gap can be between two taps of a button_double_tap - * TAP_HOLD_MILIS : many milliseconds of the buttonb being down before considering it to be a button_hold - */ - -#define TAP_UP_MILLIS (500) -#define TAP_DOWN_MILLIS (600) -#define TAP_HOLD_MILLIS (2000) -#define CW_TIMEOUT (600l) // in milliseconds, this is the parameter that determines how long the tx will hold between cw key downs - -/** - * The Raduino supports two VFOs : A and B and receiver incremental tuning (RIT). - * we define a variables to hold the frequency of the two VFOs, RITs - * the rit offset as well as status of the RIT - * - * To use this facility, wire up push button on A3 line of the control connector - */ -#define VFO_A 0 -#define VFO_B 1 -char ritOn = 0; -char vfoActive = VFO_A; -unsigned long vfoA=7100000L, vfoB=14200000L, ritA, ritB, sideTone=800; - -/** - * Raduino needs to keep track of current state of the transceiver. These are a few variables that do it - */ - -char inTx = 0; -char keyDown = 0; -char isUSB = 0; -unsigned long cwTimeout = 0; -unsigned char txFilter = 0; - -/** Tuning Mechanism of the Raduino - * We use a linear pot that has two ends connected to +5 and the ground. the middle wiper - * is connected to ANALOG_TUNNING pin. Depending upon the position of the wiper, the - * reading can be anywhere from 0 to 1024. - * The tuning control works in steps of 50Hz each for every increment between 50 and 950. - * Hence the turning the pot fully from one end to the other will cover 50 x 900 = 45 KHz. - * At the two ends, that is, the tuning starts slowly stepping up or down in 10 KHz steps - * To stop the scanning the pot is moved back from the edge. - * To rapidly change from one band to another, you press the function button and then - * move the tuning pot. Now, instead of 50 Hz, the tuning is in steps of 50 KHz allowing you - * rapidly use it like a 'bandset' control. - * To implement this, we fix a 'base frequency' to which we add the offset that the pot - * points to. We also store the previous position to know if we need to wake up and change - * the frequency. - */ - -#define INIT_BFO_FREQ (1199800L) -unsigned long baseTune = 7100000L; -unsigned long bfo_freq = 11998000L; - -int old_knob = 0; - -#define CW_OFFSET (800l) -#define LOWEST_FREQ (6995000l) -#define HIGHEST_FREQ (7500000l) - -long frequency, stepSize=100000; - -/** - * The raduino can be booted into multiple modes: - * MODE_NORMAL : works the radio normally - * MODE_CALIBRATION : used to calibrate Raduino. - * To enter this mode, hold the function button down and power up. Tune to exactly 10 MHz on clock0 and release the function button - */ - #define MODE_NORMAL (0) - #define MODE_CALIBRATE (1) - char mode = MODE_NORMAL; - -/** - * Display Routines - * These two display routines print a line of characters to the upper and lower lines of the 16x2 display - */ - -void printLine1(char *c){ - if (strcmp(c, printBuff)){ - lcd.setCursor(0, 0); - lcd.print(c); - strcpy(printBuff, c); - count++; - } -} - -void printLine2(char *c){ - lcd.setCursor(0, 1); - lcd.print(c); -} - -/** - * Building upon the previous two functions, - * update Display paints the first line as per current state of the radio - * - * At present, we are not using the second line. YOu could add a CW decoder or SWR/Signal strength - * indicator - */ - -void updateDisplay(){ - sprintf(b, "%08ld", frequency); - sprintf(c, "%s:%.2s.%.4s", vfoActive == VFO_A ? "A" : "B" , b, b+2); - if (isUSB) - strcat(c, " USB"); - else - strcat(c, " LSB"); - - if (inTx) - strcat(c, " TX"); - else if (ritOn) - strcat(c, " +R"); - else - strcat(c, " "); - - printLine1(c); -} - -/** - * To use calibration sets the accurate readout of the tuned frequency - * To calibrate, follow these steps: - * 1. Tune in a signal that is at a known frequency. - * 2. Now, set the display to show the correct frequency, - * the signal will no longer be tuned up properly - * 3. Press the CAL_BUTTON line to the ground - * 4. tune in the signal until it sounds proper. - * 5. Release CAL_BUTTON - * In step 4, when we say 'sounds proper' then, for a CW signal/carrier it means zero-beat - * and for SSB it is the most natural sounding setting. - * - * Calibration is an offset that is added to the VFO frequency by the Si5351 library. - * We store it in the EEPROM's first four bytes and read it in setup() when the Radiuno is powered up - */ -void calibrate(){ - int32_t cal; - - // The tuning knob gives readings from 0 to 1000 - // Each step is taken as 10 Hz and the mid setting of the knob is taken as zero - cal = (analogRead(ANALOG_TUNING) * 10)-5000; - - // if the button is released, we save the setting - // and delay anything else by 5 seconds to debounce the CAL_BUTTON - // Debounce : it is the rapid on/off signals that happen on a mechanical switch - // when you change it's state - if (digitalRead(CAL_BUTTON) == HIGH){ - mode = MODE_NORMAL; - printLine1("Calibrated "); - - //scale the caliberation variable to 10 MHz - cal = (cal * 10000000l) / frequency; - //Write the 4 bytes into the eeprom memory. - EEPROM.write(0, (cal & 0xFF)); - EEPROM.write(1, ((cal >> 8) & 0xFF)); - EEPROM.write(2, ((cal >> 16) & 0xFF)); - EEPROM.write(3, ((cal >> 24) & 0xFF)); - printLine2("Saved. "); - delay(5000); - } - else { - // while the calibration is in progress (CAL_BUTTON is held down), keep tweaking the - // frequency as read out by the knob, display the chnage in the second line - si5351.set_freq((bfo_freq + cal - frequency) * 100LL, SI5351_PLL_FIXED, SI5351_CLK2); - sprintf(c, "offset:%d ", cal); - printLine2(c); - } -} - - -/** - * The setFrequency is a little tricky routine, it works differently for USB and LSB - * - * The BITX BFO is permanently set to lower sideband, (that is, the crystal frequency - * is on the higher side slope of the crystal filter). - * - * LSB: The VFO frequency is subtracted from the BFO. Suppose the BFO is set to exactly 12 MHz - * and the VFO is at 5 MHz. The output will be at 12.000 - 5.000 = 7.000 MHz - * - * USB: The BFO is subtracted from the VFO. Makes the LSB signal of the BITX come out as USB!! - * Here is how it will work: - * Consider that you want to transmit on 14.000 MHz and you have the BFO at 12.000 MHz. We set - * the VFO to 26.000 MHz. Hence, 26.000 - 12.000 = 14.000 MHz. Now, consider you are whistling a tone - * of 1 KHz. As the BITX BFO is set to produce LSB, the output from the crystal filter will be 11.999 MHz. - * With the VFO still at 26.000, the 14 Mhz output will now be 26.000 - 11.999 = 14.001, hence, as the - * frequencies of your voice go down at the IF, the RF frequencies will go up! - * - * Thus, setting the VFO on either side of the BFO will flip between the USB and LSB signals. - */ - -void setFrequency(unsigned long f){ - uint64_t osc_f; - - if (isUSB){ - si5351.set_freq((bfo_freq + f) * 100ULL, SI5351_PLL_FIXED, SI5351_CLK2); - } - else{ - si5351.set_freq((bfo_freq - f) * 100ULL, SI5351_PLL_FIXED, SI5351_CLK2); - } - - frequency = f; -} - -/** - * The Checkt TX toggles the T/R line. The current BITX wiring up doesn't use this - * but if you would like to make use of RIT, etc, You must wireup an NPN transistor to to the PTT line - * as follows : - * emitter to ground, - * base to TX_RX line through a 4.7K resistr(as defined at the top of this source file) - * collecter to the PTT line - * Now, connect the PTT to the control connector's PTT line (see the definitions on the top) - * - * Yeah, surprise! we have CW supported on the Raduino - */ - -void checkTX(){ - - // We don't check for ptt when transmitting cw - // as long as the cwTimeout is non-zero, we will continue to hold the - // radio in transmit mode - if (cwTimeout > 0) - return; - - if (digitalRead(PTT) == 0 && inTx == 0){ - inTx = 1; - digitalWrite(TX_RX, 1); - updateDisplay(); - } - - if (digitalRead(PTT) == 1 && inTx == 1){ - inTx = 0; - digitalWrite(TX_RX, 0); - updateDisplay(); - } -} - -/* CW is generated by injecting the side-tone into the mic line. - * Watch http://bitxhacks.blogspot.com for the CW hack - * nonzero cwTimeout denotes that we are in cw transmit mode. - * - * This function is called repeatedly from the main loop() hence, it branches - * each time to do a different thing - * - * There are three variables that track the CW mode - * inTX : true when the radio is in transmit mode - * keyDown : true when the CW is keyed down, you maybe in transmit mode (inTX true) - * and yet between dots and dashes and hence keyDown could be true or false - * cwTimeout: Figures out how long to wait between dots and dashes before putting - * the radio back in receive mode - */ - -void checkCW(){ - - if (keyDown == 0 && analogRead(ANALOG_KEYER) < 50){ - //switch to transmit mode if we are not already in it - if (inTx == 0){ - digitalWrite(TX_RX, 1); - //give the relays a few ms to settle the T/R relays - delay(50); - } - inTx = 1; - keyDown = 1; - tone(CW_TONE, sideTone); - updateDisplay(); - } - - //reset the timer as long as the key is down - if (keyDown == 1){ - cwTimeout = CW_TIMEOUT + millis(); - } - - //if we have a keyup - if (keyDown == 1 && analogRead(ANALOG_KEYER) > 150){ - keyDown = 0; - noTone(CW_TONE); - cwTimeout = millis() + CW_TIMEOUT; - } - - //if we are in cw-mode and have a keyuup for a longish time - if (cwTimeout > 0 && inTx == 1 && cwTimeout < millis()){ - //move the radio back to receive - digitalWrite(TX_RX, 0); - inTx = 0; - cwTimeout = 0; - updateDisplay(); - } -} - -/** - * A trivial function to wrap around the function button - */ -int btnDown(){ - if (digitalRead(FBUTTON) == HIGH) - return 0; - else - return 1; -} - -/** - * A click on the function button toggles the RIT - * A double click switches between A and B vfos - * A long press copies both the VFOs to the same frequency - */ -void checkButton(){ - int i, t1, t2, knob, new_knob, duration; - - //rest of this function is interesting only if the button is pressed - if (!btnDown()) - return; - - // we are at this point because we detected that the button was indeed pressed! - // we wait for a while and confirm it again so we can be sure it is not some noise - delay(50); - if (!btnDown()) - return; - - // note the time of the button going down and where the tuning knob was - t1 = millis(); - knob = analogRead(ANALOG_TUNING); - duration = 0; - - // if you move the tuning knob within 3 seconds (3000 milliseconds) of pushing the button down - // then consider it to be a coarse tuning where you you can move by 100 Khz in each step - // This is useful only for multiband operation. - while (btnDown() && duration < 3000){ - - new_knob = analogRead(ANALOG_TUNING); - //has the tuninng knob moved while the button was down from its initial position? - if (abs(new_knob - knob) > 10){ - int count = 0; - /* track the tuning and return */ - while (btnDown()){ - frequency = baseTune = ((analogRead(ANALOG_TUNING) * 30000l) + 1000000l); - setFrequency(frequency); - updateDisplay(); - count++; - delay(200); - } - delay(1000); - return; - } /* end of handling the bandset */ - - delay(100); - duration += 100; - } - - //we reach here only upon the button being released - - // if the button has been down for more thn TAP_HOLD_MILLIS, we consider it a long press - // set both VFOs to the same frequency, update the display and be done - if (duration > TAP_HOLD_MILLIS){ - printLine2("VFOs reset!"); - vfoA= vfoB = frequency; - delay(300); - updateDisplay(); - return; - } - - // t1 holds the duration for the first press - t1 = duration; - //now wait for another click - delay(100); - // if there a second button press, toggle the VFOs - if (btnDown()){ - - //swap the VFOs on double tap - if (vfoActive == VFO_B){ - vfoActive = VFO_A; - vfoB = frequency; - frequency = vfoA; - } - else{ - vfoActive = VFO_B; - vfoA = frequency; - frequency = vfoB; - } - //printLine2("VFO swap! "); - delay(600); - updateDisplay(); - - } - // No, there was not more taps - else { - //on a single tap, toggle the RIT - if (ritOn) - ritOn = 0; - else - ritOn = 1; - updateDisplay(); - } -} - -/** - * The Tuning mechansim of the Raduino works in a very innovative way. It uses a tuning potentiometer. - * The tuning potentiometer that a voltage between 0 and 5 volts at ANALOG_TUNING pin of the control connector. - * This is read as a value between 0 and 1000. Hence, the tuning pot gives you 1000 steps from one end to - * the other end of its rotation. Each step is 50 Hz, thus giving approximately 50 Khz of tuning range. - * When the potentiometer is moved to either end of the range, the frequency starts automatically moving - * up or down in 10 Khz increments - */ - -void doTuning(){ - unsigned long newFreq; - - int knob = analogRead(ANALOG_TUNING)-10; - unsigned long old_freq = frequency; - - // the knob is fully on the low end, move down by 10 Khz and wait for 200 msec - if (knob < 10 && frequency > LOWEST_FREQ) { - baseTune = baseTune - 10000l; - frequency = baseTune; - updateDisplay(); - setFrequency(frequency); - delay(200); - } - // the knob is full on the high end, move up by 10 Khz and wait for 200 msec - else if (knob > 1010 && frequency < HIGHEST_FREQ) { - baseTune = baseTune + 10000l; - frequency = baseTune + 50000l; - setFrequency(frequency); - updateDisplay(); - delay(200); - } - // the tuning knob is at neither extremities, tune the signals as usual - else if (knob != old_knob){ - frequency = baseTune + (50l * knob); - old_knob = knob; - setFrequency(frequency); - updateDisplay(); - } -} - -/** - * setup is called on boot up - * It setups up the modes for various pins as inputs or outputs - * initiliaizes the Si5351 and sets various variables to initial state - * - * Just in case the LCD display doesn't work well, the debug log is dumped on the serial monitor - * Choose Serial Monitor from Arduino IDE's Tools menu to see the Serial.print messages - */ -void setup() -{ - int32_t cal; - - lcd.begin(16, 2); - printBuff[0] = 0; - printLine1("Raduino v1.01"); - printLine2(" "); - - // Start serial and initialize the Si5351 - Serial.begin(9600); - analogReference(DEFAULT); - Serial.println("*Raduino booting up\nv0.01\n"); - - //configure the function button to use the external pull-up - pinMode(FBUTTON, INPUT); - digitalWrite(FBUTTON, HIGH); - - pinMode(PTT, INPUT); - digitalWrite(PTT, HIGH); - - pinMode(CAL_BUTTON, INPUT); - digitalWrite(CAL_BUTTON, HIGH); - - pinMode(CW_KEY, OUTPUT); - pinMode(CW_TONE, OUTPUT); - digitalWrite(CW_KEY, 0); - digitalWrite(CW_TONE, 0); - digitalWrite(TX_RX, 0); - delay(500); - - si5351.init(SI5351_CRYSTAL_LOAD_8PF,25000000l); - - Serial.println("*Initiliazed Si5351\n"); - - si5351.set_pll(SI5351_PLL_FIXED, SI5351_PLLA); - si5351.set_pll(SI5351_PLL_FIXED, SI5351_PLLB); - Serial.println("*Fixed PLL\n"); - //si5351.drive_strength(SI5351_CLK2, SI5351_DRIVE_2MA); - si5351.output_enable(SI5351_CLK0, 0); - si5351.output_enable(SI5351_CLK1, 0); - si5351.output_enable(SI5351_CLK2, 1); - Serial.println("*Output enabled PLL\n"); - si5351.set_freq(500000000l , SI5351_PLL_FIXED, SI5351_CLK2); - - Serial.println("*Si5350 ON\n"); - mode = MODE_NORMAL; - delay(10); -} - -void loop(){ - - - if (digitalRead(CAL_BUTTON) == LOW && mode == MODE_NORMAL){ - mode = MODE_CALIBRATE; - si5351.set_correction(0); - printLine1("Calibrating: Set"); - printLine2("to zerobeat. "); - delay(2000); - return; - } - else if (mode == MODE_CALIBRATE){ - calibrate(); - delay(50); - return; - } -/* - checkCW(); - checkTX(); - checkButton(); */ - doTuning(); - delay(50); -} - diff --git a/raduino_diag_v3.ino b/raduino_diag_v3.ino new file mode 100644 index 0000000..2f04ec6 --- /dev/null +++ b/raduino_diag_v3.ino @@ -0,0 +1,992 @@ +/** + Raduino_diag v3 for BITX40 - Allard Munters PE1NWL (pe1nwl@gooddx.net) + This source file is under General Public License version 3. + + This sketch tests some basic hardware functions: + tuning pot reading + Function Button/ SPOT button switches + PTT switch + capacitive touch sensors + + Results are shown on the LCD display + additional messages are output to the serial monitor + (in the IDE, select tools => serial monitor) + +*/ + +#define DEBUG_BOOT //uncomment this to print debug messages to the serial monitor +#define DEBUG_RUN //uncomment this to print debug messages to the serial monitor + +// *** USER PARAMETERS *** + +// tuning range parameters +#define MIN_FREQ 7000000UL // absolute minimum tuning frequency in Hz +#define MAX_FREQ 7300000UL // absolute maximum tuning frequency in Hz +#define TUNING_POT_SPAN 50 // tuning pot span in kHz [accepted range 10-500 kHz] +// recommended pot span for a 1-turn pot: 50kHz, for a 10-turn pot: 100 to 200kHz +// recommended pot span when radio is used mainly for CW: 10 to 25 kHz + +// USB/LSB parameters +#define CAL_VALUE 1575 // VFO calibration value (180 ppm) +#define OFFSET_USB 1500 // USB offset in Hz [accepted range -10000Hz to 10000Hz] +#define VFO_DRIVE_LSB 4 // VFO drive level in LSB mode in mA [accepted values 2,4,6,8 mA] +#define VFO_DRIVE_USB 8 // VFO drive level in USB mode in mA [accepted values 2,4,6,8 mA] + +// CW parameters +#define CW_SHIFT 800 // RX shift in CW mode in Hz, equal to sidetone pitch [accepted range 200-1200 Hz] +#define SEMI_QSK true // whether we use semi-QSK (true) or manual PTT (false) +#define CW_TIMEOUT 350 // time delay in ms before radio goes back to receive [accepted range 10-1000 ms] + +// CW keyer parameters +#define CW_KEY_TYPE 0 // type of CW-key (0:straight, 1:paddle, 2:rev. paddle, 3:bug, 4:rev. bug) +#define CW_SPEED 16 // CW keyer speed in words per minute [accepted range 1-50 WPM] +#define AUTOSPACE false // whether or not auto-space is enabled [accepted values: true or false] +#define DIT_DELAY 5 // debounce delay (ms) for DIT contact (affects the DIT paddle sensitivity) +#define DAH_DELAY 15 // debounce delay (ms) for DAH contact (affects the DAH paddle sensitivity) + +// Capacitive touch keyer +#define CAP_SENSITIVITY 22 // capacitive touch keyer sensitivity 0 (OFF) [accepted range 0-25] + +// frequency scanning parameters +#define SCAN_START 7100 // Scan start frequency in kHz [accepted range MIN_FREQ - MAX_FREQ, see above] +#define SCAN_STOP 7150 // Scan stop frequency in kHz [accepted range SCAN_START - MAX_FREQ, see above] +#define SCAN_STEP 1000 // Scan step size in Hz [accepted range 50Hz to 10000Hz] +#define SCAN_STEP_DELAY 500 // Scan step delay in ms [accepted range 0-2000 ms] + +// Function Button +#define CLICK_DELAY 750 // max time (in ms) between function button clicks + +// RX-TX burst prevention +#define TX_DELAY 65 // delay (ms) to prevent spurious burst that is emitted when switching from RX to TX + +// all variables that will be stored in EEPROM are contained in this 'struct': +struct userparameters { + byte raduino_version; // version identifier + byte wpm = CW_SPEED; // CW keyer speed (words per minute) + int USB_OFFSET = OFFSET_USB; // VFO offset in Hz for USB mode + int cal = CAL_VALUE; // VFO calibration value + byte LSBdrive = VFO_DRIVE_LSB; // VFO drive level in LSB mode + byte USBdrive = VFO_DRIVE_USB; // VFO drive level in USB mode + bool semiQSK = SEMI_QSK; // whether semi QSK is ON or OFF + unsigned int POT_SPAN = TUNING_POT_SPAN; // tuning pot span (kHz) + unsigned int CW_OFFSET = CW_SHIFT; // RX shift (Hz) in CW mode, equal to side tone pitch + unsigned long vfoA = 7125000UL; // frequency of VFO A + unsigned long vfoB = 7125000UL; // frequency of VFO B + byte mode_A = 0; // operating mode of VFO A + byte mode_B = 0; // operating mode of VFO B + bool vfoActive = false; // currently active VFO (A = false, B = true) + bool splitOn = false; // whether SPLIT is ON or OFF + unsigned int scan_start_freq = SCAN_START; // scan start frequency (kHz) + unsigned int scan_stop_freq = SCAN_STOP; // scan stop frequency (kHz) + unsigned int scan_step_freq = SCAN_STEP; // scan step size (Hz) + unsigned int scan_step_delay = SCAN_STEP_DELAY; // scan step delay (ms) + unsigned int QSK_DELAY = CW_TIMEOUT; // word [accepted range 10-1000 ms] + byte key_type = CW_KEY_TYPE; // CW key type (0:straight, 1:paddle, 2:rev. paddle, 3:bug, 4:rev. bug) + unsigned long LOWEST_FREQ = MIN_FREQ; // absolute minimum dial frequency (Hz) + unsigned long HIGHEST_FREQ = MAX_FREQ; // absolute maximum dial frequency (Hz) + bool autospace = AUTOSPACE; // whether auto character space is ON or OFF + byte cap_sens = CAP_SENSITIVITY; // sensitivity of the touch keyer sensors +}; + +// we can access each of the variables inside the above struct by "u.variablename" +struct userparameters u; + +#ifdef DEBUG_BOOT +#define DEBUG_PRINT(x) Serial.print (x) +#define DEBUG_PRINTLN(x) Serial.println (x) +#else +#define DEBUG_PRINT(x) +#define DEBUG_PRINTLN(x) +#endif + +#ifdef DEBUG_RUN +#define DEBUG_PRINT2(x) Serial.print (x) +#define DEBUG_PRINTLN2(x) Serial.println (x) +#else +#define DEBUG_PRINT2(x) +#define DEBUG_PRINTLN2(x) +#endif + +/** + Below are the libraries to be included for building the Raduino + The EEPROM library is used to store settings like the frequency memory, calibration data, etc. +*/ + +#include + +/** + The Wire.h library is used to talk to the Si5351 and we also declare an instance of + Si5351 object to control the clocks. +*/ +#include + +/** + The PinChangeInterrupt.h library is used for handling interrupts +*/ +#include // https://github.com/NicoHood/PinChangeInterrupt + +/** + The main chip which generates upto three oscillators of various frequencies in the + Raduino is the Si5351a. To learn more about Si5351a you can download the datasheet + from www.silabs.com although, strictly speaking it is not a requirement to understand this code. + We no longer use the standard SI5351 library because of its huge overhead due to many unused + features consuming a lot of program space. Instead of depending on an external library we now use + Jerry Gaffke's, KE7ER, lightweight standalone mimimalist "si5351bx" routines (see further down the + code). Here are some defines and declarations used by Jerry's routines: +*/ + +#define BB0(x) ((uint8_t)x) // Bust int32 into Bytes +#define BB1(x) ((uint8_t)(x>>8)) +#define BB2(x) ((uint8_t)(x>>16)) + +#define SI5351BX_ADDR 0x60 // I2C address of Si5351 (typical) +#define SI5351BX_XTALPF 2 // 1:6pf 2:8pf 3:10pf + +// If using 27mhz crystal, set XTAL=27000000, MSA=33. Then vco=891mhz +#define SI5351BX_XTAL 25000000 // Crystal freq in Hz +#define SI5351BX_MSA 35 // VCOA is at 25mhz*35 = 875mhz + +// User program may have reason to poke new values into these 3 RAM variables +uint32_t si5351bx_vcoa = (SI5351BX_XTAL*SI5351BX_MSA); // 25mhzXtal calibrate +uint8_t si5351bx_rdiv = 0; // 0-7, CLK pin sees fout/(2**rdiv) +uint8_t si5351bx_drive[3] = {1, 1, 1}; // 0=2ma 1=4ma 2=6ma 3=8ma for CLK 0,1,2 +uint8_t si5351bx_clken = 0xFF; // Private, all CLK output drivers off + +/** + The Raduino board is the size of a standard 16x2 LCD panel. It has three connectors: + First, is an 8 pin connector that provides +5v, GND and six analog input pins that can also be + configured to be used as digital input or output pins. These are referred to as A0,A1,A2, + A3,A6 and A7 pins. The A4 and A5 pins are missing from this connector as they are used to + talk to the Si5351 over I2C protocol. + A0 A1 A2 A3 GND +5V A6 A7 + BLACK BROWN RED ORANGE YELLOW GREEN BLUE VIOLET (same color coding as used for resistors) + Second is a 16 pin LCD connector. This connector is meant specifically for the standard 16x2 + LCD display in 4 bit mode. The 4 bit mode requires 4 data lines and two control lines to work: + Lines used are : RESET, ENABLE, D4, D5, D6, D7 + We include the library and declare the configuration of the LCD panel too +*/ + +#include +LiquidCrystal lcd(8, 9, 10, 11, 12, 13); + +/** + The Arduino, unlike C/C++ on a regular computer with gigabytes of RAM, has very little memory. + We have to be very careful with variables that are declared inside the functions as they are + created in a memory region called the stack. The stack has just a few bytes of space on the Arduino + if you declare large strings inside functions, they can easily exceed the capacity of the stack + and mess up your programs. + We circumvent this by declaring a few global buffers as kitchen counters where we can + slice and dice our strings. These strings are mostly used to control the display or handle + the input and output from the USB port. We must keep a count of the bytes used while reading + the serial port as we can easily run out of buffer space. This is done in the serial_in_count variable. +*/ + +char c[17], b[10], printBuff[2][17]; + +/** + We need to carefully pick assignment of pin for various purposes. + There are two sets of completely programmable pins on the Raduino. + First, on the top of the board, in line with the LCD connector is an 8-pin connector + that is largely meant for analog inputs and front-panel control. It has a regulated 5v output, + ground and six pins. Each of these six pins can be individually programmed + either as an analog input, a digital input or a digital output. + The pins are assigned as follows: + A0, A1, A2, A3, GND, +5V, A6, A7 + pin 8 7 6 5 4 3 2 1 (connector P1) + BLACK BROWN RED ORANGE YELLW GREEN BLUE VIOLET + (while holding the board up so that back of the board faces you) + Though, this can be assigned anyway, for this application of the Arduino, we will make the following + assignment: + A0 (digital input) for sensing the PTT. Connect to the output of U3 (LM7805) of the BITX40. + This way the A0 input will see 0V (LOW) when PTT is not pressed, +5V (HIGH) when PTT is pressed. + A1 (digital input) is to connect to a straight key, or to the 'Dit' contact of a paddle keyer. Open (HIGH) during key up, switch to ground (LOW) during key down. + A2 (digital input) can be used for calibration by grounding this line (not required when you have the Function Button at A3) + A3 (digital input) is connected to a push button that can momentarily ground this line. This Function Button will be used to switch between different modes, etc. + A4 (already in use for talking to the SI5351) + A5 (already in use for talking to the SI5351) + A6 (analog input) is not currently used + A7 (analog input) is connected to a center pin of good quality 100K or 10K linear potentiometer with the two other ends connected to + ground and +5v lines available on the connector. This implements the tuning mechanism. +*/ + +#define PTT_SENSE (A0) +#define KEY (A1) + +#define FBUTTON (A3) +#define CLARIFIER (A6) +#define ANALOG_TUNING (A7) + +bool PTTsense_installed; //whether or not the PTT sense line is installed (detected automatically during startup) + +/** + The second set of 16 pins on the bottom connector P3 have the three clock outputs and the digital lines to control the rig. + This assignment is as follows : + Pin 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 (connector P3) + +12V +12V CLK2 GND GND CLK1 GND GND CLK0 GND D2 D3 D4 D5 D6 D7 + These too are flexible with what you may do with them, for the Raduino, we use them to : + output D2 - PULSE : is used for the capacitive touch keyer + input D3 - DAH : is connected to the 'Dah' contact of an paddle keyer (switch to ground). + input D4 - SPOT : is connected to a push button that can momentarily ground this line. When the SPOT button is pressed a sidetone will be generated for zero beat tuning. + output D5 - CW_TONE : Side tone output + output D6 - CW_CARRIER line : turns on the carrier for CW + output D7 - TX_RX line : Switches between Transmit and Receive in CW mode +*/ + +#define PULSE (2) +#define DAH (3) +#define SPOT (4) +#define CW_TONE (5) +#define CW_CARRIER (6) +#define TX_RX (7) + +bool TXRX_installed = true; // whether or not the TX_RX mod (PTT bypass transistor) is installed (set automatically at startup) + +/** + Raduino has 4 modes of operation: +*/ + +#define LSB (0) +#define USB (1) +#define CWL (2) +#define CWU (3) + +/** + Raduino needs to keep track of current state of the transceiver. These are a few variables that do it +*/ +unsigned long vfoA; // the frequency (Hz) of VFO A +unsigned long vfoB; // the frequency (Hz) of VFO B +byte mode = LSB; // mode of the currently active VFO +bool inTx = false; // whether or not we are in transmit mode +volatile bool TXstart = false; // this flag will be set by the ISR as soon as the PTT is keyed +int clar, clar_old; + + +// some variable used by the capacitive touch keyer: + +bool CapTouch_installed = true; // whether or not the capacitive touch modification is installed (detected automatically during startup) +byte base_sens_KEY; // base delay (in us) when DIT pad is NOT touched (measured automatically by touch sensor calibration routine) +byte base_sens_DAH; // base delay (in us) when DAH pad is NOT touched (measured automatically by touch sensor calibration routine) +bool capaKEY, capaDAH; + +#define bfo_freq (11998800UL) +unsigned long baseTune = 7100000UL; // frequency (Hz) when tuning pot is at minimum position +int old_knob = 0; +unsigned long frequency; // the 'dial' frequency as shown on the display + + +// ************* SI5315 routines - tks Jerry Gaffke, KE7ER *********************** + +// An minimalist standalone set of Si5351 routines. +// VCOA is fixed at 875mhz, VCOB not used. +// The output msynth dividers are used to generate 3 independent clocks +// with 1hz resolution to any frequency between 4khz and 109mhz. + +// Usage: +// Call si5351bx_init() once at startup with no args; +// Call si5351bx_setfreq(clknum, freq) each time one of the +// three output CLK pins is to be updated to a new frequency. +// A freq of 0 serves to shut down that output clock. + +// The global variable si5351bx_vcoa starts out equal to the nominal VCOA +// frequency of 25mhz*35 = 875000000 Hz. To correct for 25mhz crystal errors, +// the user can adjust this value. The vco frequency will not change but +// the number used for the (a+b/c) output msynth calculations is affected. +// Example: We call for a 5mhz signal, but it measures to be 5.001mhz. +// So the actual vcoa frequency is 875mhz*5.001/5.000 = 875175000 Hz, +// To correct for this error: si5351bx_vcoa=875175000; + +// Most users will never need to generate clocks below 500khz. +// But it is possible to do so by loading a value between 0 and 7 into +// the global variable si5351bx_rdiv, be sure to return it to a value of 0 +// before setting some other CLK output pin. The affected clock will be +// divided down by a power of two defined by 2**si5351_rdiv +// A value of zero gives a divide factor of 1, a value of 7 divides by 128. +// This lightweight method is a reasonable compromise for a seldom used feature. + +void si5351bx_init() { // Call once at power-up, start PLLA + uint8_t reg; uint32_t msxp1; + Wire.begin(); + i2cWrite(149, 0); // SpreadSpectrum off + i2cWrite(3, si5351bx_clken); // Disable all CLK output drivers + i2cWrite(183, SI5351BX_XTALPF << 6); // Set 25mhz crystal load capacitance + msxp1 = 128 * SI5351BX_MSA - 512; // and msxp2=0, msxp3=1, not fractional + uint8_t vals[8] = {0, 1, BB2(msxp1), BB1(msxp1), BB0(msxp1), 0, 0, 0}; + i2cWriten(26, vals, 8); // Write to 8 PLLA msynth regs + i2cWrite(177, 0x20); // Reset PLLA (0x80 resets PLLB) + // for (reg=16; reg<=23; reg++) i2cWrite(reg, 0x80); // Powerdown CLK's + // i2cWrite(187, 0); // No fannout of clkin, xtal, ms0, ms4 +} + +void si5351bx_setfreq(uint8_t clknum, uint32_t fout) { // Set a CLK to fout Hz + uint32_t msa, msb, msc, msxp1, msxp2, msxp3p2top; + if ((fout < 500000) || (fout > 109000000)) // If clock freq out of range + si5351bx_clken |= 1 << clknum; // shut down the clock + else { + msa = si5351bx_vcoa / fout; // Integer part of vco/fout + msb = si5351bx_vcoa % fout; // Fractional part of vco/fout + msc = fout; // Divide by 2 till fits in reg + while (msc & 0xfff00000) { + msb = msb >> 1; + msc = msc >> 1; + } + msxp1 = (128 * msa + 128 * msb / msc - 512) | (((uint32_t)si5351bx_rdiv) << 20); + msxp2 = 128 * msb - 128 * msb / msc * msc; // msxp3 == msc; + msxp3p2top = (((msc & 0x0F0000) << 4) | msxp2); // 2 top nibbles + uint8_t vals[8] = { BB1(msc), BB0(msc), BB2(msxp1), BB1(msxp1), + BB0(msxp1), BB2(msxp3p2top), BB1(msxp2), BB0(msxp2) + }; + i2cWriten(42 + (clknum * 8), vals, 8); // Write to 8 msynth regs + i2cWrite(16 + clknum, 0x0C | si5351bx_drive[clknum]); // use local msynth + si5351bx_clken &= ~(1 << clknum); // Clear bit to enable clock + } + i2cWrite(3, si5351bx_clken); // Enable/disable clock +} + +void i2cWrite(uint8_t reg, uint8_t val) { // write reg via i2c + Wire.beginTransmission(SI5351BX_ADDR); + Wire.write(reg); + Wire.write(val); + Wire.endTransmission(); +} + +void i2cWriten(uint8_t reg, uint8_t *vals, uint8_t vcnt) { // write array + Wire.beginTransmission(SI5351BX_ADDR); + Wire.write(reg); + while (vcnt--) Wire.write(*vals++); + Wire.endTransmission(); +} + +// *********** End of Jerry's si5315bx routines ********************************************************* + +/** + Display Routine + This display routine prints a line of characters to the upper or lower line of the 16x2 display + linenmbr = 0 is the upper line + linenmbr = 1 is the lower line +*/ + +void printLine(char linenmbr, char *c) { + if (strcmp(c, printBuff[linenmbr])) { // only refresh the display when there was a change + lcd.setCursor(0, linenmbr); // place the cursor at the beginning of the selected line + lcd.print(c); + strcpy(printBuff[linenmbr], c); + + for (byte i = strlen(c); i < 16; i++) { // add white spaces until the end of the 16 characters line is reached + lcd.print(' '); + } + } +} + +/** + Building upon the previous routine, + update Display paints the first line as per current state of the radio +*/ + +void updateDisplay() { + // tks Jack Purdum W8TEE + // replaced fsprint commmands by str commands for code size reduction + + memset(c, 0, sizeof(c)); + memset(b, 0, sizeof(b)); + + ultoa((frequency + 50), b, DEC); // construct the frequency string + if (!u.vfoActive) + strcpy(c, "A "); // display which VFO is active (A or B) + else + strcpy(c, "B "); + + + byte p = strlen(b); // the length of the frequency string (<10 Mhz: 7, >10 MHz: 8) + + strncat(c, &b[0], p - 6); // display the megahertzes + strcat(c, "."); + strncat(c, &b[p - 6], 3); // display the kilohertzes + strcat(c, "."); + + strncat(c, &b[p - 3], 1); // display the frequency at 100 Hz precision + + switch (mode) { // show the operating mode + case LSB: + strcat(c, " LSB"); + break; + case USB: + strcat(c, " USB"); + break; + case CWL: + strcat(c, " CWL"); + break; + case CWU: + strcat(c, " CWU"); + break; + } + + if (inTx) // show the state (TX, SPLIT, or nothing) + strcat(c, " TX"); + else if (u.splitOn) + strcat(c, " SP"); + + c[16] = '\0'; // cut off any excess characters (string length is max 16 postions) + printLine(0, c); // finally print the constructed string to the first line of the display +} + +// routine to generate a bleep sound (FB menu) +void bleep(int pitch, int duration, byte repeat) { + for (byte i = 0; i < repeat; i++) { + tone(CW_TONE, pitch); + delay(duration); + noTone(CW_TONE); + delay(duration); + } +} + +void setFrequency() { + + DEBUG_PRINT("BFO "); + DEBUG_PRINT(bfo_freq); + DEBUG_PRINT(", Si5351: "); + if (mode & 1) { // if we are in UPPER side band mode + si5351bx_setfreq(2, (bfo_freq + frequency - u.USB_OFFSET)); + DEBUG_PRINT(bfo_freq + frequency - u.USB_OFFSET); + DEBUG_PRINT(", VFO "); + DEBUG_PRINT(frequency); + } + else { // if we are in LOWER side band mode + si5351bx_setfreq(2, (bfo_freq - frequency)); + DEBUG_PRINT(bfo_freq - frequency); + DEBUG_PRINT(", VFO "); + DEBUG_PRINT(frequency); + } + DEBUG_PRINT(", tuning pot "); + DEBUG_PRINT(analogRead(ANALOG_TUNING)); + DEBUG_PRINT(", CLAR pot "); + DEBUG_PRINTLN(analogRead(CLARIFIER) - 512); + updateDisplay(); +} + +/** + The checkTX toggles the T/R line. If you would like to make use of RIT, etc, + you must connect pin A0 (black wire) via a 10K resistor to the output of U3 + This is a voltage regulator LM7805 which goes on during TX. We use the +5V output + as a PTT sense line (to tell the Raduino that we are in TX). +*/ + +void SetSideBand(byte drivelevel) { + + set_drive_level(drivelevel); + setFrequency(); + if (!u.vfoActive) // if VFO A is active + u.mode_A = mode; + else // if VFO B is active + u.mode_B = mode; +} + +/* interrupt service routine + When the PTT is keyed (either manually or by the semiQSK function), normal program execution + will be interrupted and the interrupt service routine (ISR) will be executed. After that, normal + program execution will continue from the point it was interrupted. + The only thing this ISR does, is setting the TXstart flag. This flag is frequently polled by the + knob_position routine. + The knob_position routine takes 100 readings from the tuning pot for extra precision. However + this takes more time (about 10ms). Normally this is not a problem, however when we key the PTT + we need to quickly turn off the VFO in order to prevent spurious emission. In that case 10ms is + too much delay. + In order to prevent this delay, the knob-position routine keeps checking the TXstart flag + and when it is set it will terminate immediately. +*/ + +void ISRptt(void) { + TXstart = true; // the TXstart flag will be set as soon as the PTT is keyed +} + +// routine to read the position of the tuning knob at high precision (Allard, PE1NWL) +int knob_position() { + unsigned long knob = 0; + // the knob value normally ranges from 0 through 1023 (10 bit ADC) + // in order to increase the precision by a factor 10, we need 10^2 = 100x oversampling + for (byte i = 0; i < 100; i++) { // it takes about 10 ms to run this loop 100 times + if (TXstart) { // when the PTT was keyed, in order to prevent 10 ms delay: + DEBUG_PRINTLN2("RX => TX"); + TXstart = false; // reset the TXstart flag that was set by the interrupt service routine + return old_knob; // then exit this routine immediately and return the old knob value + } + else + knob = knob + analogRead(ANALOG_TUNING); // take 100 readings from the ADC + } + knob = (knob + 5L) / 10L; // take the average of the 100 readings and multiply the result by 10 + //now the knob value ranges from 0 through 10,230 (10x more precision) + knob = knob * 10000L / 10230L; // scale the knob range down to 0-10,000 + return (int)knob; +} + +/* + Many BITX40's suffer from a strong birdie at 7199 kHz (LSB). + This birdie may be eliminated by using a different VFO drive level in LSB mode. + In USB mode, a high drive level may be needed to compensate for the attenuation of + higher VFO frequencies. + The drive level for each mode can be set in the SETTINGS menu +*/ + +void set_drive_level(byte level) { + si5351bx_drive[2] = level / 2 - 1; + setFrequency(); +} + +/* + routine to align the current knob position with the current frequency + If we switch between VFO's A and B, the frequency will change but the tuning knob + is still in the same position. We need to apply some offset so that the new frequency + corresponds with the current knob position. + This routine reads the current knob position, then it shifts the baseTune value up or down + so that the new frequency matches again with the current knob position. +*/ + +void shiftBase() { + setFrequency(); + unsigned long knob = knob_position(); // get the current tuning knob position + baseTune = frequency - (knob * (unsigned long)u.POT_SPAN / 10UL); +} + +/* + The Tuning mechansim of the Raduino works in a very innovative way. It uses a tuning potentiometer. + The tuning potentiometer that a voltage between 0 and 5 volts at ANALOG_TUNING pin of the control connector. + This is read as a value between 0 and 1023. By 100x oversampling this range is expanded by a factor 10. + Hence, the tuning pot gives you 10,000 steps from one end to the other end of its rotation. Each step is 50 Hz, + thus giving maximum 500 Khz of tuning range. The tuning range is scaled down depending on the POT_SPAN value. + The standard tuning range (for the standard 1-turn pot) is 50 Khz. But it is also possible to use a 10-turn pot + to tune accross the entire 40m band. The desired POT_SPAN can be set via the Function Button in the SETTINGS menu. + When the potentiometer is moved to either end of the range, the frequency starts automatically moving + up or down in 10 Khz increments, so it is still possible to tune beyond the range set by POT_SPAN. +*/ + +void doTuning() { + int knob = analogRead(ANALOG_TUNING); // get the current tuning knob position + if (digitalRead(PTT_SENSE)) { + inTx = true; + printLine(1, (char *)"PTT is ON"); + DEBUG_PRINTLN("PTT is ON"); + updateDisplay(); + noTone(CW_TONE); // stop generating the sidetone + delay(250); + } + else if (!digitalRead(SPOT)) { + inTx = false; + printLine(1, (char *)"SPOT pressed"); + DEBUG_PRINTLN("SPOT pressed"); + updateDisplay(); + noTone(CW_TONE); // stop generating the sidetone + delay(250); + } + else if (!digitalRead(FBUTTON)) { + inTx = false; + printLine(1, (char *)"F-Button pressed"); + DEBUG_PRINTLN("F-Button pressed"); + updateDisplay(); + noTone(CW_TONE); // stop generating the sidetone + delay(250); + } + else if (!CapTouch_installed && !digitalRead(KEY)) { + inTx = false; + printLine(1, (char *)"KEY/DIT is ON"); + DEBUG_PRINTLN("KEY/DIT is ON"); + updateDisplay(); + tone(CW_TONE, 800); // generate sidetone + delay(250); + } + else if (!CapTouch_installed && !digitalRead(DAH)) { + inTx = false; + printLine(1, (char *)"DAH is ON"); + DEBUG_PRINTLN("DAH is ON"); + updateDisplay(); + tone(CW_TONE, 800); // generate sidetone + delay(250); + } + else if (CapTouch_installed && capaKEY) { + inTx = false; + printLine(1, (char *)"DIT sensor"); + DEBUG_PRINTLN("DIT sensor touched"); + updateDisplay(); + tone(CW_TONE, 800); // generate sidetone + delay(250); + } + else if (CapTouch_installed && capaDAH) { + inTx = false; + printLine(1, (char *)"DAH sensor"); + DEBUG_PRINTLN("DAH sensor touched"); + updateDisplay(); + tone(CW_TONE, 800); // generate sidetone + delay(250); + } + else { + inTx = false; + itoa(knob, b, DEC); + strcpy(c, "tuning pot "); + strcat(c, b); + printLine(1, c); + noTone(CW_TONE); // stop generating the sidetone + updateDisplay(); + } + knob = knob * 100000 / 10230; // get the current tuning knob position + clar = analogRead(CLARIFIER) - 512; + + // tuning is disabled during TX (only when PTT sense line is installed) + if (inTx && abs(knob - old_knob) > 20) { + printLine(1, (char *)"dial is locked"); + shiftBase(); + return; + } + else if (inTx) { // no tuning in TX or when dial is locked + return; + } + else if (abs(knob - old_knob) < 20) + save_frequency(); // only save VFO frequencies to EEPROM when tuning pot is not being turned + + knob = knob_position(); // get the precise tuning knob position + // the knob is fully on the low end, do fast tune: move down by 10 Khz and wait for 300 msec + // if the POT_SPAN is very small (less than 25 kHz) then use 1 kHz steps instead + + if (knob == 0) { + if (frequency > u.LOWEST_FREQ) { + if (u.POT_SPAN < 25) + baseTune = baseTune - 1000UL; // fast tune down in 1 kHz steps + else + baseTune = baseTune - 10000UL; // fast tune down in 10 kHz steps + frequency = baseTune + (unsigned long)knob * (unsigned long)u.POT_SPAN / 10UL; + delay(300); + } + if (frequency <= u.LOWEST_FREQ) + baseTune = frequency = u.LOWEST_FREQ; + setFrequency(); + old_knob = 0; + } + + // the knob is full on the high end, do fast tune: move up by 10 Khz and wait for 300 msec + // if the POT_SPAN is very small (less than 25 kHz) then use 1 kHz steps instead + + else if (knob == 10000) { + if (frequency < u.HIGHEST_FREQ) { + if (u.POT_SPAN < 25) + baseTune = baseTune + 1000UL; // fast tune up in 1 kHz steps + else + baseTune = baseTune + 10000UL; // fast tune up in 10 kHz steps + frequency = baseTune + (unsigned long)knob * (unsigned long)u.POT_SPAN / 10UL; + delay(300); + } + if (frequency >= u.HIGHEST_FREQ) { + baseTune = u.HIGHEST_FREQ - (u.POT_SPAN * 1000UL); + frequency = u.HIGHEST_FREQ; + } + setFrequency(); + old_knob = 10000; + } + + // the tuning knob is at neither extremities, tune the signals as usual + else { + if (abs(knob - old_knob) > 4 || abs(clar - clar_old) > 1) { // improved "flutter fix": only change frequency when the current knob position is more than 4 steps away from the previous position + knob = (knob + old_knob) / 2; // tune to the midpoint between current and previous knob reading + old_knob = knob; + frequency = constrain(baseTune + (unsigned long)knob * (unsigned long)u.POT_SPAN / 10UL, u.LOWEST_FREQ, u.HIGHEST_FREQ); + setFrequency(); + delay(10); + } + } + clar_old = clar; + if (!u.vfoActive) // VFO A is active + vfoA = frequency; + else + vfoB = frequency; +} + +void factory_settings() { + + printLine(0, (char *)"loading standard"); + printLine(1, (char *)"settings..."); + + EEPROM.put(0, u); // save all user parameters to EEPROM + delay(1000); +} + +// routine to save both VFO frequencies when they haven't changed for more than 500Hz in the past 30 seconds + +void save_frequency() { + static unsigned long t3; + if (abs(vfoA - u.vfoA) > 500UL || abs(vfoB - u.vfoB) > 500UL) { + if (millis() - t3 > 30000UL) { + u.vfoA = vfoA; + u.vfoB = vfoB; + t3 = millis(); + } + } + else + t3 = millis(); +} + +/* + Routine to calibrate the touch key pads + (measure the time it takes the capacitance to discharge while the touch pads are NOT touched) + Even when the touch pads are NOT touched, there is some internal capacitance + The internal capacitance may vary depending on the length and routing of the wiring + We measure the base capacitance so that we can use it as a baseline reference + (threshold delay when the pads are not touched). +*/ + +void calibrate_touch_pads() { + // disable the internal pullups + pinMode(KEY, INPUT); + pinMode(DAH, INPUT); + + bool triggered; + // first we calibrate the KEY (DIT) touch pad + base_sens_KEY = 0; // base capacity of the KEY (DIT) touch pad + do { + base_sens_KEY++; // increment the delay time until the KEY touch pad is no longer triggered by the base capacitance + triggered = false; + for (int i = 0; i < 100; i++) { + digitalWrite(PULSE, HIGH); // bring the KEY input to a digital HIGH level + delayMicroseconds(50); // wait a short wile to allow voltage on the KEY input to stabalize + digitalWrite(PULSE, LOW); // now bring the KEY input to a digital LOW value + delayMicroseconds(base_sens_KEY); // wait few microseconds + if (digitalRead(KEY)) { // check 100 times if KEY input is still HIGH + triggered = true; // if KEY is still high, then it was triggered by the base capacitance + i = 100; + } + } + } while (triggered && base_sens_KEY != 255); // keep trying until KEY input is no longer triggered + DEBUG_PRINT("DIT touch sensor: "); + DEBUG_PRINT(base_sens_KEY); + DEBUG_PRINTLN(" us"); + + // Next we calibrate the DAH pad + base_sens_DAH = 0; // base capacity of the DAH touch pad + do { + base_sens_DAH++; // increment the delay time until the DAH touch pad is no longer triggered by the base capacitance + triggered = false; + for (int i = 0; i < 100; i++) { + digitalWrite(PULSE, HIGH); // bring the KEY input to a digital HIGH level + delayMicroseconds(50); // wait a short wile to allow voltage on the DAH input to stabalize + digitalWrite(PULSE, LOW); // now bring the DAH input to a digital LOW value + delayMicroseconds(base_sens_DAH); // wait few microseconds + if (digitalRead(DAH)) { // check 100 times if DAH input is still HIGH + triggered = true; // if KEY is still high, then it was triggered by the base capacitance + i = 100; + } + } + } while (triggered && base_sens_DAH != 255); // keep trying until KEY input is no longer triggered + DEBUG_PRINT("DAH touch sensor: "); + DEBUG_PRINT(base_sens_DAH); + DEBUG_PRINTLN(" us"); + DEBUG_PRINT("touch sensor detected: "); + + if (base_sens_KEY == 255 || base_sens_DAH == 255 || base_sens_KEY == 1 || base_sens_DAH == 1) { // if inputs are still triggered even with max delay (255 us) + CapTouch_installed = false; // then the base capacitance is too high (or the mod is not installed) so we can't use the touch keyer + u.cap_sens = 0; // turn capacitive touch keyer OFF + printLine(0, (char *)"touch sensors"); + printLine(1, (char *)"not detected"); + DEBUG_PRINTLN("NO"); + } + else if (u.cap_sens > 0) { + DEBUG_PRINTLN("YES"); + printLine(0, (char *)"touch key calibr"); + strcpy(c, "DIT "); + itoa(base_sens_KEY, b, DEC); + strcat(c, b); + strcat(c, ", DAH "); + itoa(base_sens_DAH, b, DEC); + strcat(c, b); + strcat(c, " us"); + printLine(1, c); + } + else { + printLine(1, (char *)"touch keyer OFF"); + DEBUG_PRINTLN("YES"); + } + + delay(2000); + updateDisplay(); + + //configure the morse keyer inputs + if (u.cap_sens == 0) { // enable the internal pull-ups if touch keyer is disabled + pinMode(KEY, INPUT_PULLUP); + pinMode(DAH, INPUT_PULLUP); + } +} + +void touch_key() { + static unsigned long KEYup = 0; + static unsigned long KEYdown = 0; + static unsigned long DAHup = 0; + static unsigned long DAHdown = 0; + + digitalWrite(PULSE, LOW); // send LOW to the PULSE output + delayMicroseconds(30); + //delayMicroseconds(base_sens_KEY + 25 - u.cap_sens); // wait few microseconds for the KEY capacitance to discharge + if (digitalRead(KEY)) { // test if KEY input is still HIGH + KEYdown = millis(); // KEY touch pad was touched + if (!capaKEY && millis() - KEYup > 1) { + capaKEY = true; + } + } + else { // KEY touch pad was not touched + KEYup = millis(); + if (capaKEY && millis() - KEYdown > 10) + capaKEY = false; + } + + digitalWrite(PULSE, HIGH); // send HIGH to the PULSE output + delayMicroseconds(50); // allow the capacitance to recharge + + digitalWrite(PULSE, LOW); // send LOW to the PULSE output + delayMicroseconds(base_sens_DAH + 25 - u.cap_sens); // wait few microseconds for the DAH capacitance to discharge + + if (digitalRead(DAH)) { // test if DAH input is still HIGH + DAHdown = millis(); // DAH touch pad was touched + if (!capaDAH && millis() - DAHup > 1) { + capaDAH = true; + } + } + else { // DAH touch pad was not touched + DAHup = millis(); + if (capaDAH && millis() - DAHdown > 10) + capaDAH = false; + } + + digitalWrite(PULSE, HIGH); // send HIGH to the PULSE output + delayMicroseconds(50); // allow the capacitance to recharge + +} + +/* + setup is called on boot up + It setups up the modes for various pins as inputs or outputs + initiliaizes the Si5351 and sets various variables to initial state + Just in case the LCD display doesn't work well, the debug log is dumped on the serial monitor + Choose Serial Monitor from Arduino IDE's Tools menu to see the Serial.print messages +*/ + +void setup() { + u.raduino_version = 27; + strcpy (c, "Raduino Diag v3"); + factory_settings(); + lcd.begin(16, 2); + + Serial.begin(9600); + DEBUG_PRINTLN(c); + printLine(0, c); + delay(1000); + + + analogReference(DEFAULT); + DEBUG_PRINTLN("starting up..."); + + //configure the function button to use the internal pull-up + pinMode(FBUTTON, INPUT_PULLUP); + + //configure the SPOT button to use the internal pull-up + pinMode(SPOT, INPUT_PULLUP); + + pinMode(TX_RX, INPUT_PULLUP); + DEBUG_PRINT("TX-RX line installed: "); + if (digitalRead(TX_RX)) { // Test if TX_RX line is installed + pinMode(TX_RX, OUTPUT); + digitalWrite(TX_RX, 0); + TXRX_installed = false; + u.semiQSK = false; + u.QSK_DELAY = 10; + DEBUG_PRINTLN("NO"); + } + else { + pinMode(TX_RX, OUTPUT); + digitalWrite(TX_RX, 0); + DEBUG_PRINTLN("YES"); + printLine(0, (char *)"TX-RX line"); + printLine(1, (char *)"installed"); + delay(1000); + } + + pinMode(CW_CARRIER, OUTPUT); + digitalWrite(CW_CARRIER, 0); + pinMode(CW_TONE, OUTPUT); + digitalWrite(CW_TONE, 0); + pinMode(PULSE, OUTPUT); + digitalWrite(PULSE, 0); + + + + //configure the PTT SENSE to use the internal pull-up + pinMode(PTT_SENSE, INPUT_PULLUP); + delay(100); + // check if PTT sense line is installed + PTTsense_installed = !digitalRead(PTT_SENSE); + pinMode(PTT_SENSE, INPUT); //disable the internal pull-up + + // attach interrupt to the PTT_SENSE input + // when PTT_SENSE goes from LOW to HIGH (so when PTT is keyed), execute the ISRptt routine + attachPCINT(digitalPinToPCINT(PTT_SENSE), ISRptt, RISING); + delay(1000); + + //retrieve user settings from EEPROM + EEPROM.get(0, u); + + DEBUG_PRINT("PTTsense installed: "); + if (PTTsense_installed) { + DEBUG_PRINTLN("YES"); + printLine(0, (char *)"PTT_SENSE mod"); + printLine(1, (char *)"installed"); + delay(1000); + calibrate_touch_pads(); // measure the base capacitance of the touch pads while they're not being touched + } + else + DEBUG_PRINTLN("NO"); +#ifdef DEBUG_BOOT + DEBUG_PRINT("Function button pressed: "); + if (digitalRead(FBUTTON)) + DEBUG_PRINTLN("NO"); + else + DEBUG_PRINTLN("YES"); + DEBUG_PRINT("SPOT button pressed: "); + if (digitalRead(SPOT)) + DEBUG_PRINTLN("NO"); + else + DEBUG_PRINTLN("YES"); + DEBUG_PRINT("analog reading tuning pot: "); + DEBUG_PRINTLN(analogRead(ANALOG_TUNING)); +#endif + + + //initialize the SI5351 + si5351bx_init(); + si5351bx_vcoa = (SI5351BX_XTAL * SI5351BX_MSA) + u.cal * 100L; // apply the calibration correction factor + + vfoA = u.vfoA; + vfoB = u.vfoB; + + frequency = vfoA; + mode = LSB; + + SetSideBand(u.LSBdrive); + + shiftBase(); //align the current knob position with the current frequency + DEBUG_PRINT("display line 1: "); + DEBUG_PRINTLN(c); + + DEBUG_PRINT("VFO: "); + DEBUG_PRINTLN(frequency); + DEBUG_PRINT("BFO: "); + DEBUG_PRINTLN(bfo_freq); + + DEBUG_PRINT("Si5351: "); + DEBUG_PRINTLN(bfo_freq - frequency); + + bleep(u.CW_OFFSET, 60, 3); + bleep(u.CW_OFFSET, 180, 1); + + DEBUG_PRINTLN(); + DEBUG_PRINTLN("boot completed"); + + si5351bx_drive[0] = 0; // lowest BFO drive level for less tuning clicks + si5351bx_setfreq(0, bfo_freq); +} + +void loop() { + touch_key(); + doTuning(); +} diff --git a/raduino_pin_layout.png b/raduino_pin_layout.png new file mode 100644 index 0000000..63d9011 Binary files /dev/null and b/raduino_pin_layout.png differ diff --git a/raduino_v1.29.ino b/raduino_v1.29.ino new file mode 100644 index 0000000..5b76a9e --- /dev/null +++ b/raduino_v1.29.ino @@ -0,0 +1,2529 @@ +/** + Raduino_v1.29 for BITX40 - Allard Munters PE1NWL (pe1nwl@gooddx.net) + + This source file is under General Public License version 3. + + Most source code are meant to be understood by the compilers and the computers. + Code that has to be hackable needs to be well understood and properly documented. + Donald Knuth coined the term Literate Programming to indicate code that is written be + easily read and understood. + + The Raduino is a small board that includes the Arduino Nano, a 16x2 LCD display and + an Si5351a frequency synthesizer. This board is manufactured by Paradigm Ecomm Pvt Ltd. + + To learn more about Arduino you may visit www.arduino.cc. + + The Arduino works by first executing the code in a routine called setup() and then it + repeatedly keeps calling loop() forever. All the initialization code is kept in setup() + and code to continuously sense the tuning knob, the function button, transmit/receive, + etc is all in the loop() routine. If you wish to study the code top down, then scroll + to the bottom of this file and read your way up. + + First we define all user parameters. The parameter values can be changed by the user + via SETTINGS menu, and are stored in EEPROM. + The parameters values will be set to initial 'factory' settings after each + version upgrade, or when the Function Button is kept pressed during power on. + It is also possible to manually edit the values below. After that, initialize the + settings to the new values by keeping the F-Button pressed during power on, or by + switching the CAL wire to ground. +*/ + +// *** USER PARAMETERS *** +#define MY_CALLSIGN "" // callsign here will display on line 2 when otherwise blank (tks Richard, VE3YSH) +#define FAST_TUNE_DELAY 300 // fast tuning step delay in ms (when tuning pot is at the upper or lower limit)(tks Bob, N4FV) + +// tuning range parameters +#define MIN_FREQ 7000000UL // absolute minimum tuning frequency in Hz +#define MAX_FREQ 7300000UL // absolute maximum tuning frequency in Hz +#define TUNING_POT_SPAN 50 // tuning pot span in kHz [accepted range 10-500 kHz] +// recommended pot span for a 1-turn pot: 50kHz, for a 10-turn pot: 100 to 200kHz +// recommended pot span when radio is used mainly for CW: 10 to 25 kHz + +// prevent out-of-band transmission depending on ham band allocation (out-of-band RX is still possible if PTTsense mod is installed) +// uncomment only ONE of the following 4 lines depending on your location: +//#define REGION 1 // ham band allocation for ITU Region 1 (Europe, Africa, Middle-East, former Sovjet Union: TX restricted to 7000-7200 kHz) +#define REGION 2 // ham band allocation for ITU Region 2 (Americas: TX restricted to 7000-7300 kHz) +//#define REGION 3 // ham band allocation for ITU Region 3 (Asia except FSU, Oceania, Australia: TX restricted to 7000-7200 kHz) +//#define REGION 0 // general coverage, TX unrestricted (not normally recommended) + +// USB/LSB parameters +#define CAL_VALUE 1575 // Initial VFO calibration value: 180 ppm +#define OFFSET_USB 1500 // USB offset in Hz [accepted range -10000Hz to 10000Hz] +#define VFO_DRIVE_LSB 4 // VFO drive level in LSB mode in mA [accepted values 2,4,6,8 mA] +#define VFO_DRIVE_USB 8 // VFO drive level in USB mode in mA [accepted values 2,4,6,8 mA] + +// CW parameters +#define CW_SHIFT 800 // RX shift in CW mode in Hz, equal to sidetone pitch [accepted range 200-1200 Hz] +#define SEMI_QSK true // whether we use semi-QSK (true) or manual PTT (false) +#define CW_TIMEOUT 350 // time delay in ms before radio goes back to receive [accepted range 10-1000 ms] + +// CW keyer parameters +#define CW_KEY_TYPE 0 // type of CW-key (0:straight, 1:paddle, 2:rev. paddle, 3:bug, 4:rev. bug) +#define CW_SPEED 16 // CW keyer speed in words per minute [accepted range 1-50 WPM] +#define AUTOSPACE false // whether or not auto-space is enabled [accepted values: true or false] +#define DIT_DELAY 5 // debounce delay (ms) for DIT contact (affects the DIT paddle sensitivity) +#define DAH_DELAY 15 // debounce delay (ms) for DAH contact (affects the DAH paddle sensitivity) + +// Capacitive touch keyer +#define CAP_SENSITIVITY 0 // capacitive touch keyer sensitivity 0 (OFF) [accepted range 0-25] + +// frequency scanning parameters +#define SCAN_START 7100 // Scan start frequency in kHz [accepted range MIN_FREQ - MAX_FREQ, see above] +#define SCAN_STOP 7150 // Scan stop frequency in kHz [accepted range SCAN_START - MAX_FREQ, see above] +#define SCAN_STEP 1000 // Scan step size in Hz [accepted range 50Hz to 10000Hz] +#define SCAN_STEP_DELAY 500 // Scan step delay in ms [accepted range 0-2000 ms] + +// Function Button +#define CLICK_DELAY 1500 // max time (in ms) between function button clicks + +// RX-TX burst prevention +#define TX_DELAY 65 // delay (ms) to prevent spurious burst that is emitted when switching from RX to TX + +// Roger Beep function +#define ROGER_BEEP false // whether roger beep is enabled (true) or disabled (false) + +// all variables that will be stored in EEPROM are contained in this 'struct': +struct userparameters { + byte raduino_version; // version identifier + byte wpm = CW_SPEED; // CW keyer speed (words per minute) + int USB_OFFSET = OFFSET_USB; // VFO offset in Hz for USB mode + int cal = CAL_VALUE; // VFO calibration value + byte LSBdrive = VFO_DRIVE_LSB; // VFO drive level in LSB mode + byte USBdrive = VFO_DRIVE_USB; // VFO drive level in USB mode + bool semiQSK = SEMI_QSK; // whether semi QSK is ON or OFF + unsigned int POT_SPAN = TUNING_POT_SPAN; // tuning pot span (kHz) + unsigned int CW_OFFSET = CW_SHIFT; // RX shift (Hz) in CW mode, equal to side tone pitch + unsigned long vfoA = 7125000UL; // frequency of VFO A + unsigned long vfoB = 7125000UL; // frequency of VFO B + byte mode_A = 0; // operating mode of VFO A + byte mode_B = 0; // operating mode of VFO B + bool vfoActive = false; // currently active VFO (A = false, B = true) + bool splitOn = false; // whether SPLIT is ON or OFF + unsigned int scan_start_freq = SCAN_START; // scan start frequency (kHz) + unsigned int scan_stop_freq = SCAN_STOP; // scan stop frequency (kHz) + unsigned int scan_step_freq = SCAN_STEP; // scan step size (Hz) + unsigned int scan_step_delay = SCAN_STEP_DELAY; // scan step delay (ms) + unsigned int QSK_DELAY = CW_TIMEOUT; // word [accepted range 10-1000 ms] + byte key_type = CW_KEY_TYPE; // CW key type (0:straight, 1:paddle, 2:rev. paddle, 3:bug, 4:rev. bug) + unsigned long LOWEST_FREQ = MIN_FREQ; // absolute minimum dial frequency (Hz) + unsigned long HIGHEST_FREQ = MAX_FREQ; // absolute maximum dial frequency (Hz) + bool autospace = AUTOSPACE; // whether auto character space is ON or OFF + byte cap_sens = CAP_SENSITIVITY; // sensitivity of the touch keyer sensors + bool RogerBeep = ROGER_BEEP; // whether Roger Beep is enabled or disabled +}; + +// we can access each of the variables inside the above struct by "u.variablename" +struct userparameters u; + +/** + + Below are the libraries to be included for building the Raduino + + The EEPROM library is used to store settings like the frequency memory, calibration data, etc. +*/ + +#include + +/** + The Wire.h library is used to talk to the Si5351 and we also declare an instance of + Si5351 object to control the clocks. +*/ +#include + +/** + The PinChangeInterrupt.h library is used for handling interrupts +*/ +#include // https://github.com/NicoHood/PinChangeInterrupt + +/** + The main chip which generates upto three oscillators of various frequencies in the + Raduino is the Si5351a. To learn more about Si5351a you can download the datasheet + from www.silabs.com although, strictly speaking it is not a requirement to understand this code. + + We no longer use the standard SI5351 library because of its huge overhead due to many unused + features consuming a lot of program space. Instead of depending on an external library we now use + Jerry Gaffke's, KE7ER, lightweight standalone mimimalist "si5351bx" routines (see further down the + code). Here are some defines and declarations used by Jerry's routines: +*/ + +#define BB0(x) ((uint8_t)x) // Bust int32 into Bytes +#define BB1(x) ((uint8_t)(x>>8)) +#define BB2(x) ((uint8_t)(x>>16)) + +#define SI5351BX_ADDR 0x60 // I2C address of Si5351 (typical) +#define SI5351BX_XTALPF 2 // 1:6pf 2:8pf 3:10pf + +// If using 27mhz crystal, set XTAL=27000000, MSA=33. Then vco=891mhz +#define SI5351BX_XTAL 25000000 // Crystal freq in Hz +#define SI5351BX_MSA 35 // VCOA is at 25mhz*35 = 875mhz + +// User program may have reason to poke new values into these 3 RAM variables +uint32_t si5351bx_vcoa = (SI5351BX_XTAL*SI5351BX_MSA); // 25mhzXtal calibrate +uint8_t si5351bx_rdiv = 0; // 0-7, CLK pin sees fout/(2**rdiv) +uint8_t si5351bx_drive[3] = {1, 1, 1}; // 0=2ma 1=4ma 2=6ma 3=8ma for CLK 0,1,2 +uint8_t si5351bx_clken = 0xFF; // Private, all CLK output drivers off + +/** + The Raduino board is the size of a standard 16x2 LCD panel. It has three connectors: + + First, is an 8 pin connector that provides +5v, GND and six analog input pins that can also be + configured to be used as digital input or output pins. These are referred to as A0,A1,A2, + A3,A6 and A7 pins. The A4 and A5 pins are missing from this connector as they are used to + talk to the Si5351 over I2C protocol. + + A0 A1 A2 A3 GND +5V A6 A7 + BLACK BROWN RED ORANGE YELLOW GREEN BLUE VIOLET (same color coding as used for resistors) + + Second is a 16 pin LCD connector. This connector is meant specifically for the standard 16x2 + LCD display in 4 bit mode. The 4 bit mode requires 4 data lines and two control lines to work: + Lines used are : RESET, ENABLE, D4, D5, D6, D7 + We include the library and declare the configuration of the LCD panel too +*/ + +#include +LiquidCrystal lcd(8, 9, 10, 11, 12, 13); + +/** + The Arduino, unlike C/C++ on a regular computer with gigabytes of RAM, has very little memory. + We have to be very careful with variables that are declared inside the functions as they are + created in a memory region called the stack. The stack has just a few bytes of space on the Arduino + if you declare large strings inside functions, they can easily exceed the capacity of the stack + and mess up your programs. + We circumvent this by declaring a few global buffers as kitchen counters where we can + slice and dice our strings. These strings are mostly used to control the display or handle + the input and output from the USB port. We must keep a count of the bytes used while reading + the serial port as we can easily run out of buffer space. This is done in the serial_in_count variable. +*/ + +char c[17], b[10], printBuff[2][17]; + +/** + We need to carefully pick assignment of pin for various purposes. + There are two sets of completely programmable pins on the Raduino. + First, on the top of the board, in line with the LCD connector is an 8-pin connector + that is largely meant for analog inputs and front-panel control. It has a regulated 5v output, + ground and six pins. Each of these six pins can be individually programmed + either as an analog input, a digital input or a digital output. + The pins are assigned as follows: + A0, A1, A2, A3, GND, +5V, A6, A7 + pin 8 7 6 5 4 3 2 1 (connector P1) + BLACK BROWN RED ORANGE YELLW GREEN BLUE VIOLET + (while holding the board up so that back of the board faces you) + + Though, this can be assigned anyway, for this application of the Arduino, we will make the following + assignment: + + A0 (digital input) for sensing the PTT. Connect to the output of U3 (LM7805) of the BITX40. + This way the A0 input will see 0V (LOW) when PTT is not pressed, +5V (HIGH) when PTT is pressed. + A1 (digital input) is to connect to a straight key, or to the 'Dit' contact of a paddle keyer. Open (HIGH) during key up, switch to ground (LOW) during key down. + A2 (digital input) can be used for calibration by grounding this line (not required when you have the Function Button at A3) + A3 (digital input) is connected to a push button that can momentarily ground this line. This Function Button will be used to switch between different modes, etc. + A4 (already in use for talking to the SI5351) + A5 (already in use for talking to the SI5351) + A6 (analog input) is not currently used + A7 (analog input) is connected to a center pin of good quality 100K or 10K linear potentiometer with the two other ends connected to + ground and +5v lines available on the connector. This implements the tuning mechanism. +*/ + +#define PTT_SENSE (A0) +#define KEY (A1) +#define CAL_BUTTON (A2) +#define FBUTTON (A3) +#define ANALOG_TUNING (A7) + +bool PTTsense_installed; //whether or not the PTT sense line is installed (detected automatically during startup) + +/** + The second set of 16 pins on the bottom connector P3 have the three clock outputs and the digital lines to control the rig. + This assignment is as follows : + Pin 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 (connector P3) + +12V +12V CLK2 GND GND CLK1 GND GND CLK0 GND D2 D3 D4 D5 D6 D7 + These too are flexible with what you may do with them, for the Raduino, we use them to : + + output D2 - PULSE : is used for the capacitive touch keyer + input D3 - DAH : is connected to the 'Dah' contact of an paddle keyer (switch to ground). + input D4 - SPOT : is connected to a push button that can momentarily ground this line. When the SPOT button is pressed a sidetone will be generated for zero beat tuning. + output D5 - CW_TONE : Side tone output + output D6 - CW_CARRIER line : turns on the carrier for CW + output D7 - TX_RX line : Switches between Transmit and Receive in CW mode +*/ + +#define PULSE (2) +#define DAH (3) +#define SPOT (4) +#define CW_TONE (5) +#define CW_CARRIER (6) +#define TX_RX (7) + +bool TXRX_installed = true; // whether or not the TX_RX mod (PTT bypass transistor) is installed (set automatically at startup) + +/** + The Raduino supports two VFOs : A and B and receiver incremental tuning (RIT). + we define a variables to hold the frequency of the two VFOs, RIT, SPLIT + the rit offset as well as status of the RIT + + To use this facility, wire up push button on A3 line of the control connector (Function Button) +*/ + +bool ritOn = false; // whether or not the RIT is on +int RIToffset = 0; // RIT offset (Hz) +int RIT = 0; // actual RIT offset that is applied during RX when RIT is on +int RIT_old; +byte region = REGION; // ham band allocation depending on ITU Region +bool outofband; // whether or not the frequency is outside the 40m ham band allocation + +bool firstrun = true; +char clicks; // counter for function button clicks +bool locked = false; // whether or not the dial is locked +byte param; + + +/** + Raduino has 4 modes of operation: +*/ + +#define LSB (0) +#define USB (1) +#define CWL (2) +#define CWU (3) + +/** + Raduino needs to keep track of current state of the transceiver. These are a few variables that do it +*/ +unsigned long vfoA; // the frequency (Hz) of VFO A +unsigned long vfoB; // the frequency (Hz) of VFO B +byte mode = LSB; // mode of the currently active VFO +bool inTx = false; // whether or not we are in transmit mode +bool keyDown = false; // whether we have a key up or key down +unsigned long TimeOut = 0; // time in ms since last key down +volatile bool TXstart = false; // this flag will be set by the ISR as soon as the PTT is keyed + +//some variables used for the autokeyer function: + +bool keyeron = false; // will be true while auto-keying +unsigned long released = 0; +bool ditlatch = false; +bool dahlatch = false; +byte gap = 1; // space between elements (1) or characters (3) +unsigned long dit; +unsigned long dah; +unsigned long space = 0; + +// some variable used by the capacitive touch keyer: + +bool CapTouch_installed = true; // whether or not the capacitive touch modification is installed (detected automatically during startup) +byte base_sens_KEY; // base delay (in us) when DIT pad is NOT touched (measured automatically by touch sensor calibration routine) +byte base_sens_DAH; // base delay (in us) when DAH pad is NOT touched (measured automatically by touch sensor calibration routine) +bool capaKEY = false; // true when DIT pad is touched +bool capaDAH = false; // true when DAH pad is touched + +/** + Tks Pavel CO7WT: + Use pointers for reversing the behaviour of the dit/dah paddles, this way we can simply + refer to them as two paddles regardless it's normal or reversed + Macros don't like pointers, so we create two byte vars to hold the values +**/ + +byte _key = KEY; +byte _dah = DAH; +byte *paddleDIT = &_key; // Paddle DIT bind to KEY +byte *paddleDAH = &_dah; // Paddle DAH bind to DAH + +/** Tuning Mechanism of the Raduino + We use a linear pot that has two ends connected to +5 and the ground. the middle wiper + is connected to ANALOG_TUNNING pin. Depending upon the position of the wiper, the + reading can be anywhere from 0 to 1023. + If we want to use a multi-turn potentiometer covering 500 kHz and a step + size of 50 Hz we need 10,000 steps which is about 10x more than the steps that the ADC + provides. Arduino's ADC has 10 bits which results in 1024 steps only. + We can artificially expand the number of steps by a factor 10 by oversampling 100 times. + As a result we get 10240 steps. + The tuning control works in steps of 50Hz each for every increment between 0 and 10000. + Hence the turning the pot fully from one end to the other will cover 50 x 10000 = 500 KHz. + But if we use the standard 1-turn pot, then a tuning range of 500 kHz would be too much. + (tuning would become very touchy). In the SETTINGS menu we can limit the pot span + depending on the potentiometer used and the band section of interest. Tuning beyond the + limits is still possible by the fast 'scan-up' and 'scan-down' mode at the end of the pot. + At the two ends, that is, the tuning starts stepping up or down in 10 KHz steps. + To stop the scanning the pot is moved back from the edge. +*/ + +#define bfo_freq (11998700UL) + +unsigned long baseTune = 7100000UL; // frequency (Hz) when tuning pot is at minimum position +int old_knob = 0; +int RXshift = 0; // the actual frequency shift that is applied during RX depending on the operation mode +unsigned long frequency; // the 'dial' frequency as shown on the display +int fine = 0; // fine tune offset (Hz) + +/** + The raduino has multiple RUN-modes: +*/ +#define RUN_NORMAL (0) // normal operation +#define RUN_CALIBRATE (1) // calibrate VFO frequency in LSB mode +#define RUN_DRIVELEVEL (2) // set VFO drive level +#define RUN_TUNERANGE (3) // set the range of the tuning pot +#define RUN_CWOFFSET (4) // set the CW offset (=sidetone pitch) +#define RUN_SCAN (5) // frequency scanning mode +#define RUN_SCAN_PARAMS (6) // set scan parameters +#define RUN_MONITOR (7) // frequency scanning mode +#define RUN_FINETUNING (8) // fine tuning mode + +byte RUNmode = RUN_NORMAL; + +// ************* SI5315 routines - tks Jerry Gaffke, KE7ER *********************** + +// An minimalist standalone set of Si5351 routines. +// VCOA is fixed at 875mhz, VCOB not used. +// The output msynth dividers are used to generate 3 independent clocks +// with 1hz resolution to any frequency between 4khz and 109mhz. + +// Usage: +// Call si5351bx_init() once at startup with no args; +// Call si5351bx_setfreq(clknum, freq) each time one of the +// three output CLK pins is to be updated to a new frequency. +// A freq of 0 serves to shut down that output clock. + +// The global variable si5351bx_vcoa starts out equal to the nominal VCOA +// frequency of 25mhz*35 = 875000000 Hz. To correct for 25mhz crystal errors, +// the user can adjust this value. The vco frequency will not change but +// the number used for the (a+b/c) output msynth calculations is affected. +// Example: We call for a 5mhz signal, but it measures to be 5.001mhz. +// So the actual vcoa frequency is 875mhz*5.001/5.000 = 875175000 Hz, +// To correct for this error: si5351bx_vcoa=875175000; + +// Most users will never need to generate clocks below 500khz. +// But it is possible to do so by loading a value between 0 and 7 into +// the global variable si5351bx_rdiv, be sure to return it to a value of 0 +// before setting some other CLK output pin. The affected clock will be +// divided down by a power of two defined by 2**si5351_rdiv +// A value of zero gives a divide factor of 1, a value of 7 divides by 128. +// This lightweight method is a reasonable compromise for a seldom used feature. + +void si5351bx_init() { // Call once at power-up, start PLLA + uint8_t reg; uint32_t msxp1; + Wire.begin(); + i2cWrite(149, 0); // SpreadSpectrum off + i2cWrite(3, si5351bx_clken); // Disable all CLK output drivers + i2cWrite(183, ((SI5351BX_XTALPF << 6) | 0x12)); // Set 25mhz crystal load capacitance (tks Daniel KB3MUN) + msxp1 = 128 * SI5351BX_MSA - 512; // and msxp2=0, msxp3=1, not fractional + uint8_t vals[8] = {0, 1, BB2(msxp1), BB1(msxp1), BB0(msxp1), 0, 0, 0}; + i2cWriten(26, vals, 8); // Write to 8 PLLA msynth regs + i2cWrite(177, 0x20); // Reset PLLA (0x80 resets PLLB) + // for (reg=16; reg<=23; reg++) i2cWrite(reg, 0x80); // Powerdown CLK's + // i2cWrite(187, 0); // No fannout of clkin, xtal, ms0, ms4 +} + +void si5351bx_setfreq(uint8_t clknum, uint32_t fout) { // Set a CLK to fout Hz + uint32_t msa, msb, msc, msxp1, msxp2, msxp3p2top; + if ((fout < 500000) || (fout > 109000000)) // If clock freq out of range + si5351bx_clken |= 1 << clknum; // shut down the clock + else { + msa = si5351bx_vcoa / fout; // Integer part of vco/fout + msb = si5351bx_vcoa % fout; // Fractional part of vco/fout + msc = fout; // Divide by 2 till fits in reg + while (msc & 0xfff00000) { + msb = msb >> 1; + msc = msc >> 1; + } + msxp1 = (128 * msa + 128 * msb / msc - 512) | (((uint32_t)si5351bx_rdiv) << 20); + msxp2 = 128 * msb - 128 * msb / msc * msc; // msxp3 == msc; + msxp3p2top = (((msc & 0x0F0000) << 4) | msxp2); // 2 top nibbles + uint8_t vals[8] = { BB1(msc), BB0(msc), BB2(msxp1), BB1(msxp1), + BB0(msxp1), BB2(msxp3p2top), BB1(msxp2), BB0(msxp2) + }; + i2cWriten(42 + (clknum * 8), vals, 8); // Write to 8 msynth regs + i2cWrite(16 + clknum, 0x0C | si5351bx_drive[clknum]); // use local msynth + si5351bx_clken &= ~(1 << clknum); // Clear bit to enable clock + } + i2cWrite(3, si5351bx_clken); // Enable/disable clock +} + +void i2cWrite(uint8_t reg, uint8_t val) { // write reg via i2c + Wire.beginTransmission(SI5351BX_ADDR); + Wire.write(reg); + Wire.write(val); + Wire.endTransmission(); +} + +void i2cWriten(uint8_t reg, uint8_t *vals, uint8_t vcnt) { // write array + Wire.beginTransmission(SI5351BX_ADDR); + Wire.write(reg); + while (vcnt--) Wire.write(*vals++); + Wire.endTransmission(); +} + +// *********** End of Jerry's si5315bx routines ********************************************************* + +/** + Display Routine + This display routine prints a line of characters to the upper or lower line of the 16x2 display + linenmbr = 0 is the upper line + linenmbr = 1 is the lower line +*/ + +void printLine(char linenmbr, const char * const c) { + if (strcmp(c, printBuff[linenmbr])) { // only refresh the display when there was a change + lcd.setCursor(0, linenmbr); // place the cursor at the beginning of the selected line + lcd.print(c); + strcpy(printBuff[linenmbr], c); + + for (byte i = strlen(c); i < 16; i++) { // add white spaces until the end of the 16 characters line is reached + lcd.print(' '); + } + } +} + +/** + Building upon the previous routine, + update Display paints the first line as per current state of the radio +*/ + +void updateDisplay() { + // tks Jack Purdum W8TEE + // replaced fsprint commmands by str commands for code size reduction + + memset(c, 0, sizeof(c)); + memset(b, 0, sizeof(b)); + + if (locked || RUNmode == RUN_FINETUNING) { + ultoa((frequency + fine), b, DEC); // construct the frequency string + strcpy(c, ""); + } + else { + ultoa((frequency + 50), b, DEC); // construct the frequency string + if (!u.vfoActive) + strcpy(c, "A "); // display which VFO is active (A or B) + else + strcpy(c, "B "); + } + + byte p = strlen(b); // the length of the frequency string (<10 Mhz: 7, >10 MHz: 8) + + strncat(c, &b[0], p - 6); // display the megahertzes + strcat(c, "."); + strncat(c, &b[p - 6], 3); // display the kilohertzes + strcat(c, "."); + + if (locked || RUNmode == RUN_FINETUNING) + strncat(c, &b[p - 3], 3); // display the frequency at 1 Hz precision + else + strncat(c, &b[p - 3], 1); // display the frequency at 100 Hz precision + + switch (mode) { // show the operating mode + case LSB: + strcat(c, " LSB"); + break; + case USB: + strcat(c, " USB"); + break; + case CWL: + strcat(c, " CWL"); + break; + case CWU: + strcat(c, " CWU"); + break; + } + + if (inTx && !outofband) // show the state (TX, SPLIT, or nothing) + strcat(c, " TX"); + else if (u.splitOn) + strcat(c, " SP"); + + c[16] = '\0'; // cut off any excess characters (string length is max 16 postions) + printLine(0, c); // finally print the constructed string to the first line of the display +} + +// routine to generate a bleep sound (FB menu) +void bleep(int pitch, int duration, byte repeat) { + for (byte i = 0; i < repeat; i++) { + tone(CW_TONE, pitch); + delay(duration); + noTone(CW_TONE); + delay(duration); + } +} + +bool calbutton = false; + +/** + To use calibration sets the accurate readout of the tuned frequency + To calibrate, follow these steps: + 1. Tune in a LSB signal that is at a known frequency. + 2. Now, set the display to show the correct frequency, + the signal will no longer be tuned up properly + 3. Use the "LSB calibrate" option in the "Settings" menu (or Press the CAL_BUTTON line to the ground (pin A2 - red wire)) + 4. tune in the signal until it sounds proper. + 5. Press the FButton (or Release CAL_BUTTON) + In step 4, when we say 'sounds proper' then, for a CW signal/carrier it means zero-beat + and for LSB it is the most natural sounding setting. + + Calibration is an offset value that is added to the VFO frequency. + We store it in the EEPROM and read it in setup() when the Radiuno is powered up. + + Then select the "USB calibrate" option in the "Settings" menu and repeat the same steps for USB mode. +*/ + +int shift, current_setting; +void calibrate() { + int knob = analogRead(ANALOG_TUNING); // get the current tuning knob position + + if (RUNmode != RUN_CALIBRATE) { + + if (mode == USB) + current_setting = u.USB_OFFSET; + else + current_setting = u.cal; + + shift = current_setting - knob; + } + + // The tuning knob gives readings from 0 to 1000 + + if (mode == USB) { + u.USB_OFFSET = constrain(knob + shift, -10000, 10000); + + if (knob < 5 && u.USB_OFFSET > -10000) + shift = shift - 10; + else if (knob > 1020 && u.USB_OFFSET < 10000) + shift = shift + 10; + } + else { + u.cal = constrain(knob + shift, -10000, 10000); + + if (knob < 5 && u.cal > -10000) + shift = shift - 10; + else if (knob > 1020 && u.cal < 10000) + shift = shift + 10; + } + + // if Fbutton is pressed again (or when the CAL button is released), we save the setting + if (!digitalRead(FBUTTON) || (calbutton && digitalRead(CAL_BUTTON))) { + RUNmode = RUN_NORMAL; + bleep(600, 50, 2); + printLine(1, "--- SETTINGS ---"); + shiftBase(); //align the current knob position with the current frequency + } + + else { + // while offset adjustment is in progress, keep tweaking the + // frequency as read out by the knob, display the change in the second line + RUNmode = RUN_CALIBRATE; + + if (mode == USB) { + setFrequency(); + itoa(u.USB_OFFSET, b, DEC); + strcpy(c, "offset "); + strcat(c, b); + strcat(c, " Hz"); + } + + else { + si5351bx_vcoa = (SI5351BX_XTAL * SI5351BX_MSA) + u.cal * 100L; + si5351bx_setfreq(2, bfo_freq - frequency); + ltoa(u.cal * 100L / 875, b, DEC); + strcpy(c, "corr "); + strcat(c, b); + strcat(c, " ppm"); + } + + printLine(1, c); + } +} + +/** + The setFrequency is a little tricky routine, it works differently for USB and LSB + The BITX BFO is permanently set to lower sideband, (that is, the crystal frequency + is on the higher side slope of the crystal filter). + + LSB: The VFO frequency is subtracted from the BFO. Suppose the BFO is set to exactly 12 MHz + and the VFO is at 5 MHz. The output will be at 12.000 - 5.000 = 7.000 MHz + USB: The BFO is subtracted from the VFO. Makes the LSB signal of the BITX come out as USB!! + Here is how it will work: + Consider that you want to transmit on 14.000 MHz and you have the BFO at 12.000 MHz. We set + the VFO to 26.000 MHz. Hence, 26.000 - 12.000 = 14.000 MHz. Now, consider you are whistling a tone + of 1 KHz. As the BITX BFO is set to produce LSB, the output from the crystal filter will be 11.999 MHz. + With the VFO still at 26.000, the 14 Mhz output will now be 26.000 - 11.999 = 14.001, hence, as the + frequencies of your voice go down at the IF, the RF frequencies will go up! + + Thus, setting the VFO on either side of the BFO will flip between the USB and LSB signals. + + In addition we add some offset to USB mode so that the dial frequency is correct in both LSB and USB mode. + The amount of offset can be set in the SETTING menu as part of the calibration procedure. + + Furthermore we add/substract the sidetone frequency only when we receive CW, to assure zero beat + between the transmitting and receiving station (RXshift) + The desired sidetone frequency can be set in the SETTINGS menu. +*/ + +void setFrequency() { + outofband = false; + switch (region) { + case 1: + case 3: + if (frequency < 7000000UL || frequency > 7200000UL) + outofband = true; + break; + case 2: + if (frequency < 7000000UL || frequency > 7300000UL) + outofband = true; + break; + } + + if (PTTsense_installed || !outofband) { + if (mode & 1) // if we are in UPPER side band mode + si5351bx_setfreq(2, (bfo_freq + frequency - RXshift + RIT + fine - u.USB_OFFSET)); + else // if we are in LOWER side band mode + si5351bx_setfreq(2, (bfo_freq - frequency - RXshift - RIT - fine)); + } + updateDisplay(); +} + +/** + The checkTX toggles the T/R line. If you would like to make use of RIT, etc, + you must connect pin A0 (black wire) via a 10K resistor to the output of U3 + This is a voltage regulator LM7805 which goes on during TX. We use the +5V output + as a PTT sense line (to tell the Raduino that we are in TX). +*/ + +void checkTX() { + // We don't check for ptt when transmitting cw in semi-QSK mode + // as long as the TimeOut is non-zero, we will continue to hold the + // radio in transmit mode + if (TimeOut > 0 && u.semiQSK) + return; + + if (digitalRead(PTT_SENSE) && !inTx) { + // go in transmit mode + si5351bx_setfreq(2, 0); // temporarily disable CLK2 to prevent spurious emission (tks Dave M0WID) + inTx = true; + RXshift = RIT = RIT_old = 0; // no frequency offset during TX + + if (u.semiQSK) { + mode = mode & B11111101; // leave CW mode, return to SSB mode + + if (!u.vfoActive) // if VFO A is active + u.mode_A = mode; + else // if VFO B is active + u.mode_B = mode; + } + delay(TX_DELAY); // wait till RX-TX burst is over + shiftBase(); // this will enable CLK2 again + updateDisplay(); + + if (u.splitOn) { // when SPLIT is on, swap the VFOs + swapVFOs(); + } + } + + if (!digitalRead(PTT_SENSE) && inTx) { + delay(50); + if (!digitalRead(PTT_SENSE)) { + // send roger beep before switching back to receive + // "Quindar Key" as used by NASA in the Apollo missions + // 2450 Hz tone with 250 ms duration at the end of each transmission + if (mode < 2 && u.RogerBeep) { // when we are in SSB mode and Roger Beep is enabled + digitalWrite(TX_RX, 1); // activate the PTT relay - keep the radio in transmit for a while + digitalWrite(CW_CARRIER, 1); // generate carrier + tone(CW_TONE, 2450); // generate sidetone + // temporarily shift TX frequency by 2450 Hz + if (mode & 1) // if we are in UPPER side band mode + si5351bx_setfreq(2, (bfo_freq + frequency + fine - u.USB_OFFSET + 2450)); + else // if we are in LOWER side band mode + si5351bx_setfreq(2, (bfo_freq - frequency - fine + 2450)); + delay(250); + digitalWrite(CW_CARRIER, 0); // stop generating carrier + noTone(CW_TONE); // stop generating sidetone + digitalWrite(TX_RX, 0); // release the PTT switch - move the radio back to receive + } + + //go in receive mode + inTx = false; + setFrequency(); + updateDisplay(); + if (u.splitOn) { // when SPLIT was on, swap the VFOs back to original state + swapVFOs(); + } + if (mode & 2) { // if we are in CW mode + RXshift = u.CW_OFFSET; // apply the frequency offset in RX + shiftBase(); + } + } + } +} + +/* CW is generated by unbalancing the mixer when the key is down. + During key down, the output CW_CARRIER is HIGH (+5V). + This output is connected via a 10K resistor to the mixer input. The mixer will + become unbalanced when CW_CARRIER is HIGH, so a carrier will be transmitted. + During key up, the output CW_CARRIER is LOW (0V). The mixer will remain balanced + and the carrrier will be suppressed. + + The radio will go into CW mode automatically as soon as the key goes down, and + return to normal LSB/USB mode when the key has been up for some time. + + There are three variables that track the CW mode + inTX : true when the radio is in transmit mode + keyDown : true when the CW is keyed down, you maybe in transmit mode (inTX true) + and yet between dots and dashes and hence keyDown could be true or false + TimeOut: Figures out how long to wait between dots and dashes before putting + the radio back in receive mode + + When we transmit CW, we need to apply some offset (800Hz) to the TX frequency, in + order to keep zero-beat between the transmitting and receiving station. The shift + depends on whether upper or lower sideband CW is used: + In CW-U (USB) mode we must shift the TX frequency 800Hz up + In CW-L (LSB) mode we must shift the TX frequency 800Hz down + + The default offset (CW_OFFSET) is 800Hz, the default timeout (QSK_DELAY) is 350ms. + The user can change these in the SETTINGS menu. + +*/ + +void checkCW() { + if (!keyDown && ((u.cap_sens == 0 && !digitalRead(KEY)) || (u.cap_sens != 0 && capaKEY) || (u.key_type > 0 && ((u.cap_sens == 0 && !digitalRead(DAH)) || (u.cap_sens != 0 && capaDAH))))) { + keyDown = true; + + if (u.semiQSK) { + mode = mode | 2; // if semiQSK is on, switch to CW + } + + if (u.key_type > 0 && mode & 2) { // if mode is CW and if keyer is enabled + keyeron = true; + released = 0; + + // put the paddles pointers in the correct position + if (u.key_type & 1) { + // paddle not reversed + paddleDAH = &_dah; + paddleDIT = &_key; + } + else { + // paddle reversed + paddleDAH = &_key; + paddleDIT = &_dah; + } + + if ((u.cap_sens == 0 && !digitalRead(*paddleDIT)) || (u.cap_sens != 0 && capaKEY)) + dit = millis(); + if ((u.cap_sens == 0 && !digitalRead(*paddleDAH)) || (u.cap_sens != 0 && capaDAH)) + dah = millis(); + } + + if (!inTx && u.semiQSK) { // switch to transmit mode if we are not already in it + si5351bx_setfreq(2, 0); // temporarily disable CLK2 to prevent spurious emission (tks Dave M0WID) + digitalWrite(TX_RX, 1); // key the PTT - go in transmit mode + inTx = true; + + if (!u.vfoActive) // if VFO A is active + u.mode_A = mode; + else // if VFO B is active + u.mode_B = mode; + + RXshift = RIT = RIT_old = 0; // no frequency offset during TX + delay(TX_DELAY); // wait till RX-TX burst is over + + if (u.splitOn) // when SPLIT is on, swap the VFOs + swapVFOs(); // this will also enable CLK2 again + else + shiftBase(); // this will also enable CLK2 again + + dit = dit + TX_DELAY + 7; // delay the initial dit + dah = dah + TX_DELAY + 7; // delay the initial dah + } + } + + //keep resetting the timer as long as the key is down + if (keyDown) + TimeOut = millis() + u.QSK_DELAY; + + //if the key goes up again after it's been down + if (keyDown && ((u.cap_sens == 0 && digitalRead(KEY)) || (u.cap_sens != 0 && !capaKEY))) { + keyDown = false; + TimeOut = millis() + u.QSK_DELAY; + } + + // if we are in semi-QSK mode and have a keyup for a "longish" time (QSK_DELAY value in ms) + // then go back to RX + + if (!keyeron && TimeOut > 0 && inTx && TimeOut < millis() && u.semiQSK) { + + inTx = false; + TimeOut = 0; // reset the CW timeout counter + RXshift = u.CW_OFFSET; // apply the frequency offset in RX + shiftBase(); + + if (u.splitOn) // then swap the VFOs back when SPLIT was on + swapVFOs(); + + digitalWrite(TX_RX, 0); // release the PTT switch - move the radio back to receive + delay(10); //give the relays a few ms to settle the T/R relays + } + + if (u.key_type == 0 && keyDown && mode & B00000010) { + digitalWrite(CW_CARRIER, 1); // generate carrier + tone(CW_TONE, u.CW_OFFSET); // generate sidetone + } + else if (u.key_type == 0 && digitalRead(SPOT) == HIGH) { + digitalWrite(CW_CARRIER, 0); // stop generating the carrier + noTone(CW_TONE); // stop generating the sidetone + } +} + +void keyer() { + + static bool FBpressed = false; + static bool SPOTpressed = false; + + if (!digitalRead(FBUTTON)) // Press and release F-Button to increase keyer speed + FBpressed = true; + if (FBpressed && digitalRead(FBUTTON) && u.wpm < 50) { + FBpressed = false; + u.wpm++; + } + if (!digitalRead(SPOT)) // Press and release SPOT button to reduce keyer speed + SPOTpressed = true; + if (SPOTpressed && digitalRead(SPOT) && u.wpm > 1) { + SPOTpressed = false; + u.wpm--; + } + + if (u.key_type > 2) { // bug mode + if ((u.cap_sens == 0 && !digitalRead(*paddleDAH)) || (u.cap_sens != 0 && capaDAH)) + dah = millis(); + else + dah = 0; + } + + unsigned long element = 1200UL / u.wpm; + + if (space == 0 && (millis() - dit < element || millis() - dah < 3 * element)) { + digitalWrite(CW_CARRIER, 1); // generate carrier + tone(CW_TONE, u.CW_OFFSET); // generate sidetone + keyDown = true; + } + else { + digitalWrite(CW_CARRIER, 0); // stop generating the carrier + noTone(CW_TONE); // stop generating the sidetone + if (space == 0) { + space = millis(); + } + if (millis() - space > gap * element) { + if (dit < dah) { + if (ditlatch || (u.cap_sens == 0 && !digitalRead(*paddleDIT)) || (u.cap_sens != 0 && capaKEY)) { + dit = millis(); + keyeron = true; + ditlatch = false; + keyDown = true; + gap = 1; //standard gap between elements + space = 0; + released = 0; + } + else { + if (dahlatch || (u.cap_sens == 0 && !digitalRead(*paddleDAH)) || (u.cap_sens != 0 && capaDAH)) { + dah = millis(); + keyeron = true; + dahlatch = false; + keyDown = true; + gap = 1; //standard gap between elements + space = 0; + released = 0; + } + else { + if (u.autospace) + gap = 3; // autospace - character gap is 3 elements + keyeron = true; + keyDown = true; + + if (millis() - space > gap * element) { + keyeron = false; + keyDown = false; + gap = 1; //standard gap between elements + space = 0; + released = 0; + } + } + } + } + else { + if (dahlatch || (u.cap_sens == 0 && !digitalRead(*paddleDAH)) || (u.cap_sens != 0 && capaDAH)) { + dah = millis(); + keyeron = true; + dahlatch = false; + keyDown = true; + gap = 1; //standard gap between elements + space = 0; + released = 0; + } + else { + if (ditlatch || (u.cap_sens == 0 && !digitalRead(*paddleDIT)) || (u.cap_sens != 0 && capaKEY)) { + dit = millis(); + keyeron = true; + ditlatch = false; + keyDown = true; + gap = 1; //standard gap between elements + space = 0; + released = 0; + } + else { + if (u.autospace) + gap = 3; // autospace - character gap is 3 elements + keyeron = true; + keyDown = true; + if (millis() - space > gap * element) { + keyeron = false; + keyDown = false; + gap = 1; //standard gap between elements + space = 0; + released = 0; + } + } + } + } + } + } + + if (released == 0) { + if (space == 0 && millis() - dit < element && ((u.cap_sens == 0 && digitalRead(*paddleDIT)) || (u.cap_sens != 0 && !capaKEY))) + released = millis(); + if (space == 0 && millis() - dah < 3 * element && ((u.cap_sens == 0 && digitalRead(*paddleDAH)) || (u.cap_sens != 0 && !capaDAH))) + released = millis(); + if (space > 0 && ((u.cap_sens == 0 && digitalRead(*paddleDIT)) || (u.cap_sens != 0 && !capaKEY)) && ((u.cap_sens == 0 && digitalRead(*paddleDAH)) || (u.cap_sens != 0 && !capaDAH))) + released = millis(); + } + + if (u.cap_sens == 0) { // if standard paddle is used + if (released > 0 && millis() - released > DIT_DELAY && !digitalRead(*paddleDIT)) { // DIT_DELAY optimized timing characteristics - tks Hidehiko, JA9MAT + ditlatch = true; + dahlatch = false; + } + else if (space > 0 && released > 0 && millis() - released > DAH_DELAY && !digitalRead(*paddleDAH)) { // DAH_DELAY optimized timing characteristics - tks Hidehiko, JA9MAT + dahlatch = true; + ditlatch = false; + } + } + else { // if touch keyer is used + if (released > 0 && capaKEY) { + ditlatch = true; + dahlatch = false; + } + else if (released > 0 && capaDAH) { + dahlatch = true; + ditlatch = false; + } + } + + if (!outofband) { + if (keyeron) { + itoa(u.wpm, b, DEC); + strcpy(c, "CW-speed "); + strcat(c, b); + strcat(c, " WPM"); + printLine(1, c); + } + else if (locked) + printLine(1, "dial is locked"); + else if (clicks >= 10) + printLine(1, "--- SETTINGS ---"); + else { + RIT_old = 0; + printLine(1, MY_CALLSIGN); + } + } +} + + +/** + The Function Button is used for several functions + NORMAL menu (normal operation): + 1 short press: swap VFO A/B + 2 short presses: toggle RIT on/off + 3 short presses: toggle SPLIT on/off + 4 short presses: toggle mode LSB-USB-CWL-CWU + 5 short presses: start freq scan mode + 5 short presses: start A/B monitor mode + long press (>1 Sec): VFO A=B + VERY long press (>3 sec): go to SETTINGS menu + + SETTINGS menu: + 1 short press: Set the 4 scan parameters (lower limit, upper limit, step size, step delay) + 2 short presses: Set the 6 CW parameters (key type, auto-space, touch sensitivity, semi-QSK on/off, QSK delay, sidetone pitch) + 3 short presses: LSB calibration + 4 short presses: USB calibration + 5 short presses: Set VFO drive level in LSB mode + 6 short presses: Set VFO drive level in USB mode + 7 short presses: Set tuning range parameters (minimum dial frequency, max dial freq, tuning pot span) + long press: exit SETTINGS menu - go back to NORMAL menu +*/ + +void checkButton() { + + static byte action; + static long t1, t2; + static bool pressed = false; + + if (digitalRead(FBUTTON)) { + t2 = millis() - t1; //time elapsed since last button press + if (pressed) + if (clicks < 10 && t2 > 600 && t2 < 3000) { //detect long press to reset the VFO's + resetVFOs(); + delay(700); + clicks = 0; + } + + if (t2 > CLICK_DELAY) { // max time between button clicks (ms) + action = clicks; + if (clicks >= 10) + clicks = 10; + else + clicks = 0; + } + pressed = false; + } + else { + delay(10); + if (!digitalRead(FBUTTON)) { + // button was really pressed, not just some noise + + if (locked && digitalRead(SPOT)) { + bleep(600, 50, 1); + printLine(1, MY_CALLSIGN); + delay(500); + locked = false; + shiftBase(); + clicks = 0; + pressed = false; + return; + } + + else { + if (!digitalRead(SPOT)) { + bleep(1200, 50, 1); + locked = true; + updateDisplay(); + printLine(1, "dial is locked"); + delay(500); + clicks = 0; + pressed = false; + return; + } + } + + if (ritOn) { + toggleRIT(); // disable the RIT when it was on and the FB is pressed again + old_knob = knob_position(); + bleep(600, 50, 1); + delay(100); + return; + } + if (!pressed) { + pressed = true; + t1 = millis(); + bleep(1200, 50, 1); + action = 0; + clicks++; + if (clicks > 17) + clicks = 11; + if (clicks > 7 && clicks < 10) + clicks = 1; + if (!PTTsense_installed) { + if (clicks == 2) // No RIT, no PLIT when PTTsense is not installed + clicks = 4; + if (clicks == 12) // No CW parameters when PTTsense is not installed + clicks++; + } + if ((!PTTsense_installed || !TXRX_installed) && clicks == 7) // No Roger Beep when either PTTsense or TXRX mod is not installed + clicks = 1; + switch (clicks) { + //Normal menu options + case 1: + printLine(1, "Swap VFOs"); + break; + case 2: + printLine(1, "RIT ON"); + break; + case 3: + printLine(1, "SPLIT ON/OFF"); + break; + case 4: + printLine(1, "Switch mode"); + break; + case 5: + printLine(1, "Start freq scan"); + break; + case 6: + printLine(1, "Monitor VFO A/B"); + break; + case 7: + printLine(1, "RogerBeep ON/OFF"); + break; + + //SETTINGS menu options + case 11: + printLine(1, "Set scan params"); + break; + case 12: + printLine(1, "Set CW params"); + break; + case 13: + printLine(1, "LSB calibration"); + break; + case 14: + printLine(1, "USB calibration"); + break; + case 15: + printLine(1, "VFO drive - LSB"); + break; + case 16: + printLine(1, "VFO drive - USB"); + break; + case 17: + printLine(1, "Set tuning range"); + break; + } + } + else if ((millis() - t1) > 600 && (millis() - t1) < 800 && clicks < 10) // long press: reset the VFOs + printLine(1, "Reset VFOs"); + + if ((millis() - t1) > 3000 && clicks < 10) { // VERY long press: go to the SETTINGS menu + bleep(1200, 150, 3); + printLine(1, "--- SETTINGS ---"); + clicks = 10; + } + + else if ((millis() - t1) > 1500 && clicks > 10) { // long press: return to the NORMAL menu + bleep(1200, 150, 3); + clicks = -1; + pressed = false; + printLine(1, " --- NORMAL ---"); + delay(700); + } + } + } + if (action != 0 && action != 10) { + bleep(600, 50, 1); + } + switch (action) { + // NORMAL menu + + case 1: // swap the VFOs + swapVFOs(); + break; + + case 2: // toggle the RIT on/off + toggleRIT(); + break; + + case 3: // toggle SPLIT on/off + toggleSPLIT(); + break; + + case 4: // toggle the mode LSB/USB + toggleMode(); + break; + + case 5: // start scan mode + RUNmode = RUN_SCAN; + TimeOut = millis() + u.scan_step_delay; + frequency = u.scan_start_freq * 1000L; + printLine(1, "freq scanning"); + break; + + case 6: // Monitor mode + RUNmode = RUN_MONITOR; + TimeOut = millis() + u.scan_step_delay; + printLine(1, "A/B monitoring"); + break; + + case 7: // toggle Roger Beep ON/OFF + u.RogerBeep = !u.RogerBeep; + if (u.RogerBeep) + printLine(1, "Roger Beep ON"); + else + printLine(1, "Roger Beep OFF"); + delay(1000); + break; + + // SETTINGS MENU + + case 11: // set the 4 scan parameters + param = 1; + scan_params(); + break; + + case 12: // set CW parameters (sidetone pitch, QSKdelay, key type, capacitive touch key, auto space) + param = 1; + set_CWparams(); + break; + + case 13: // calibrate the dial frequency in LSB + RXshift = 0; + mode = LSB; + setFrequency(); + SetSideBand(u.LSBdrive); + calibrate(); + break; + + case 14: // calibrate the dial frequency in USB + RXshift = 0; + mode = USB; + setFrequency(); + SetSideBand(u.USBdrive); + calibrate(); + break; + + case 15: // set the VFO drive level in LSB + mode = LSB; + SetSideBand(u.LSBdrive); + VFOdrive(); + break; + + case 16: // set the VFO drive level in USB + mode = USB; + SetSideBand(u.USBdrive); + VFOdrive(); + break; + + case 17: // set the tuning pot range + param = 1; + set_tune_range(); + break; + } +} + +void swapVFOs() { + if (u.vfoActive) { // if VFO B is active + u.vfoActive = false; // switch to VFO A + vfoB = frequency; + frequency = vfoA; + if (!u.splitOn) + mode = u.mode_A; // don't change the mode when SPLIT is on + } + + else { //if VFO A is active + u.vfoActive = true; // switch to VFO B + vfoA = frequency; + frequency = vfoB; + if (!u.splitOn) + mode = u.mode_B; // don't change the mode when SPLIT is on + } + + if (mode & 1) // if we are in UPPER side band mode + SetSideBand(u.USBdrive); + else // if we are in LOWER side band mode + SetSideBand(u.LSBdrive); + + if (!inTx && mode > 1) + RXshift = u.CW_OFFSET; // add RX shift when we are receiving in CW mode + else + RXshift = 0; // no RX shift when we are receiving in SSB mode + + shiftBase(); //align the current knob position with the current frequency +} + +void toggleRIT() { + + ritOn = !ritOn; // toggle RIT + if (!ritOn) + RIT = RIT_old = 0; + shiftBase(); //align the current knob position with the current frequency + firstrun = true; + if (u.splitOn) { + u.splitOn = false; // disable SPLIT when RIT is on + } + updateDisplay(); +} + +void toggleSPLIT() { + + u.splitOn = !u.splitOn; // toggle SPLIT + if (ritOn) { + ritOn = false; + RIT = RIT_old = 0; + shiftBase(); + } + updateDisplay(); +} + +void toggleMode() { + if (PTTsense_installed && !u.semiQSK) + mode = (mode + 1) & 3; // if not semiQSK: rotate through LSB-USB-CWL-CWU + else + mode = mode xor 1; // if semiQSK: toggle between LSB and USB (or between CWL and CWU, tks Michael VE3WMB) + + if (mode & 2) // if we are in CW mode + RXshift = u.CW_OFFSET; + else // if we are in SSB mode + RXshift = 0; + + if (mode & 1) // if we are in UPPER side band mode + SetSideBand(u.USBdrive); + else // if we are in LOWER side band mode + SetSideBand(u.LSBdrive); +} + +void SetSideBand(byte drivelevel) { + + set_drive_level(drivelevel); + setFrequency(); + if (!u.vfoActive) // if VFO A is active + u.mode_A = mode; + else // if VFO B is active + u.mode_B = mode; +} + +// resetting the VFO's will set both VFO's to the current frequency and mode +void resetVFOs() { + printLine(1, "VFO A=B !"); + vfoA = vfoB = frequency; + u.mode_A = u.mode_B = mode; + updateDisplay(); + bleep(600, 50, 2); +} + +void VFOdrive() { + static byte drive; + int knob = analogRead(ANALOG_TUNING); // get the current tuning knob position + + if (RUNmode != RUN_DRIVELEVEL) { + + if (mode & 1) // if UPPER side band mode + current_setting = u.USBdrive / 2 - 1; + else // if LOWER side band mode + current_setting = u.LSBdrive / 2 - 1; + + shift = knob; + } + + //generate drive level values 2,4,6,8 from tuning pot + drive = 2 * ((((knob - shift) / 50 + current_setting) & 3) + 1); + + // if Fbutton is pressed again, we save the setting + + if (!digitalRead(FBUTTON)) { + RUNmode = RUN_NORMAL; + + if (mode & 1) // if UPPER side band mode + u.USBdrive = drive; + else // if LOWER side band mode + u.LSBdrive = drive; + + bleep(600, 50, 2); + printLine(1, "--- SETTINGS ---"); + shiftBase(); //align the current knob position with the current frequency + } + else { + // while the drive level adjustment is in progress, keep tweaking the + // drive level as read out by the knob and display it in the second line + RUNmode = RUN_DRIVELEVEL; + set_drive_level(drive); + + itoa(drive, b, DEC); + strcpy(c, "drive level "); + strcat(c, b); + strcat(c, "mA"); + printLine(1, c); + } +} + +/* + this routine allows the user to set the tuning range depending on the type of potentiometer + for a standard 1-turn pot, a span of 50 kHz is recommended + for a 10-turn pot, a span of 200 kHz is recommended +*/ + +void set_tune_range() { + int knob = analogRead(ANALOG_TUNING); // get the current tuning knob position + + if (firstrun) { + switch (param) { + case 1: + current_setting = u.LOWEST_FREQ / 1000; + break; + case 2: + current_setting = u.HIGHEST_FREQ / 1000; + break; + case 3: + current_setting = u.POT_SPAN; + break; + } + shift = current_setting - 10 * knob / 20; + } + switch (param) { + case 1: + //generate values 7000-7500 from the tuning pot + u.LOWEST_FREQ = constrain(10 * knob / 20 + shift, 1000UL, 30000UL); + if (knob < 5 && u.LOWEST_FREQ > 1000UL) + shift = shift - 10; + else if (knob > 1020 && u.LOWEST_FREQ < 30000UL) + shift = shift + 10; + break; + case 2: + //generate values 7000-7500 from the tuning pot + u.HIGHEST_FREQ = constrain(10 * knob / 20 + shift, (u.LOWEST_FREQ / 1000 + u.POT_SPAN), 30000UL); + if (knob < 5 && u.HIGHEST_FREQ > (u.LOWEST_FREQ / 1000 + u.POT_SPAN)) + shift = shift - 10; + else if (knob > 1020 && u.HIGHEST_FREQ < 30000UL) + shift = shift + 10; + break; + case 3: + //generate values 10-500 from the tuning pot + u.POT_SPAN = constrain(10 * knob / 20 + shift, 10, 500); + if (knob < 5 && u.POT_SPAN > 10) + shift = shift - 10; + else if (knob > 1020 && u.POT_SPAN < 500) + shift = shift + 10; + break; + } + // if Fbutton is pressed again, we save the setting + if (!digitalRead(FBUTTON)) { + switch (param) { + case 1: + u.LOWEST_FREQ = u.LOWEST_FREQ * 1000UL; + bleep(600, 50, 1); + delay(200); + break; + case 2: + u.HIGHEST_FREQ = u.HIGHEST_FREQ * 1000UL; + bleep(600, 50, 1); + delay(200); + break; + case 3: + RUNmode = RUN_NORMAL; + bleep(600, 50, 2); + printLine(1, "--- SETTINGS ---"); + shiftBase(); //align the current knob position with the current frequency + break; + } + param ++; + firstrun = true; + } + + else { + RUNmode = RUN_TUNERANGE; + firstrun = false; + switch (param) { + case 1: + ultoa(u.LOWEST_FREQ, b, DEC); + strcpy(c, "min "); + strcat(c, b); + strcat(c, " kHz"); + printLine(1, c); + break; + case 2: + ultoa(u.HIGHEST_FREQ, b, DEC); + strcpy(c, "max "); + strcat(c, b); + strcat(c, " kHz"); + printLine(1, c); + break; + case 3: + itoa(u.POT_SPAN, b, DEC); + strcpy(c, "pot span "); + strcat(c, b); + strcat(c, " kHz"); + printLine(1, c); + break; + } + } +} + +// this routine allows the user to set the six CW parameters + +void set_CWparams() { + int knob = analogRead(ANALOG_TUNING); // get the current tuning knob position + + if (firstrun) { + switch (param) { + case 1: + mode = mode | 2; //switch to CW mode + updateDisplay(); + current_setting = u.key_type; + shift = knob; + break; + case 2: + current_setting = u.autospace; + shift = knob; + break; + case 3: + current_setting = u.cap_sens; + shift = knob; + break; + case 4: + current_setting = u.semiQSK; + shift = knob; + break; + case 5: + current_setting = u.QSK_DELAY; + shift = current_setting - 10 * (knob / 10); + break; + case 6: + RXshift = u.CW_OFFSET; + current_setting = u.CW_OFFSET; + shift = current_setting - knob - 200; + break; + } + } + + switch (param) { + case 1: + //generate values 0-1-2-3-4 from the tuning pot + u.key_type = ((((knob - shift) + 4 + current_setting * 26) & 127) / 26); + break; + case 2: + //generate values 0-1-0-1 from the tuning pot + u.autospace = ((((knob - shift + 4) & 64) / 64) + current_setting) & 1; + break; + case 3: + //generate values 0-25 from the tuning pot + u.cap_sens = constrain((knob - shift) / 40 + current_setting, 0, 25); + if (knob < 5 && u.cap_sens > 0) + shift = shift + 10; + else if (knob > 1020 && u.cap_sens < 25) + shift = shift - 10; + + //configure the morse keyer inputs + if (u.cap_sens == 0) { // enable the internal pull-ups if capacitive touch keys are NOT used + pinMode(KEY, INPUT_PULLUP); + pinMode(DAH, INPUT_PULLUP); + } + else { // disable the internal pull-ups if capacitive touch keys are used + pinMode(KEY, INPUT); + pinMode(DAH, INPUT); + } + break; + case 4: + //generate values 0-1-0-1 from the tuning pot + u.semiQSK = ((((knob - shift + 4) & 64) / 64) + current_setting) & 1; + break; + case 5: + //generate values 10-1000 from the tuning pot + u.QSK_DELAY = constrain(10 * (knob / 10) + shift, 10, 1000); + if (knob < 5 && u.QSK_DELAY >= 20) + shift = shift - 10; + else if (knob > 1020 && u.QSK_DELAY < 1000) + shift = shift + 10; + break; + case 6: + //generate values 500-1000 from the tuning pot + u.CW_OFFSET = constrain(knob + 200 + shift, 200, 1200); + if (knob < 5 && u.CW_OFFSET > 200) + shift = shift - 10; + else if (knob > 1020 && u.CW_OFFSET < 1200) + shift = shift + 10; + break; + } + + // if Fbutton is pressed again, we save the setting + if (!digitalRead(FBUTTON)) { + switch (param) { + case 1: // key type + bleep(600, 50, 1); + delay(200); + if (u.key_type == 0) { + param++; // no auto-space when straight key was selected + if (!CapTouch_installed) + param++; // no touch sensitivity setting when touch sensors are not installed + } + break; + case 2: // autospace + bleep(600, 50, 1); + delay(200); + if (!CapTouch_installed) + param++; // no touch sensitivity setting when touch sensors are not installed + break; + case 3: // touch keyer sensitivity + calibrate_touch_pads(); // measure the base capacitance of the touch pads while they're not being touched + bleep(600, 50, 1); + delay(200); + if (!TXRX_installed) + param = param + 2; // no Semi-QSK, QSK-delay settings when RX-TX line is not installed + break; + case 4: // semiQSK on/off + if (!u.semiQSK) { + u.QSK_DELAY = 10; // set QSKdelay to minimum when manual PTT is selected + param++; // skip the QSKdelay setting when manual PTT is selected + } + bleep(600, 50, 1); + delay(200); + break; + case 5: + bleep(600, 50, 1); + delay(200); + break; + case 6: + RUNmode = RUN_NORMAL; + bleep(600, 50, 2); + printLine(1, "--- SETTINGS ---"); + shiftBase(); //align the current knob position with the current frequency + break; + } + param ++; + firstrun = true; + } + + else { + RUNmode = RUN_CWOFFSET; + firstrun = false; + switch (param) { + case 1: + strcpy(c, "Key: "); + switch (u.key_type) { + case 0: + strcat(c, "straight"); + break; + case 1: + strcat(c, "paddle"); + break; + case 2: + strcat(c, "rev. paddle"); + break; + case 3: + strcat(c, "bug"); + break; + case 4: + strcat(c, "rev. bug"); + break; + } + break; + case 2: + strcpy(c, "Auto-space: "); + if (u.autospace) + strcat(c, "ON"); + else + strcat(c, "OFF"); + break; + case 3: + if (u.cap_sens == 0) + strcpy(c, "touch keyer OFF"); + else { + printLine(0, "Touch sensor"); + itoa((u.cap_sens), b, DEC); + strcpy(c, "sensitivity "); + strcat(c, b); + } + break; + case 4: + strcpy(c, "Semi-QSK: "); + if (u.semiQSK) + strcat(c, "ON"); + else + strcat(c, "OFF"); + break; + case 5: + itoa(u.QSK_DELAY, b, DEC); + strcpy(c, "QSK delay "); + strcat(c, b); + strcat(c, "ms"); + break; + case 6: + itoa(u.CW_OFFSET, b, DEC); + strcpy(c, "sidetone "); + strcat(c, b); + strcat(c, " Hz"); + break; + } + printLine(1, c); + } +} + +/* this routine allows the user to set the 4 scan parameters: lower limit, upper limit, step size and step delay +*/ + +void scan_params() { + + int knob = analogRead(ANALOG_TUNING); // get the current tuning knob position + if (firstrun) { + switch (param) { + + case 1: // set the lower scan limit + + current_setting = u.scan_start_freq; + shift = current_setting - knob / 2 - u.LOWEST_FREQ / 1000; + break; + + case 2: // set the upper scan limit + + current_setting = u.scan_stop_freq; + shift = current_setting - map(knob, 0, 1024, u.scan_start_freq, u.HIGHEST_FREQ / 1000); + break; + + case 3: // set the scan step size + + current_setting = u.scan_step_freq; + shift = current_setting - 50 * (knob / 5); + break; + + case 4: // set the scan step delay + + current_setting = u.scan_step_delay; + shift = current_setting - 50 * (knob / 25); + break; + } + } + + switch (param) { + + case 1: // set the lower scan limit + + //generate values 7000-7500 from the tuning pot + u.scan_start_freq = constrain(knob / 2 + 7000 + shift, u.LOWEST_FREQ / 1000, u.HIGHEST_FREQ / 1000); + if (knob < 5 && u.scan_start_freq > u.LOWEST_FREQ / 1000) + shift = shift - 1; + else if (knob > 1020 && u.scan_start_freq < u.HIGHEST_FREQ / 1000) + shift = shift + 1; + break; + + case 2: // set the upper scan limit + + //generate values 7000-7500 from the tuning pot + u.scan_stop_freq = constrain(map(knob, 0, 1024, u.scan_start_freq, u.HIGHEST_FREQ / 1000) + shift, u.scan_start_freq, u.HIGHEST_FREQ / 1000); + if (knob < 5 && u.scan_stop_freq > u.scan_start_freq) + shift = shift - 1; + else if (knob > 1020 && u.scan_stop_freq < u.HIGHEST_FREQ / 1000) + shift = shift + 1; + break; + + case 3: // set the scan step size + + //generate values 50-10000 from the tuning pot + u.scan_step_freq = constrain(50 * (knob / 5) + shift, 50, 10000); + if (knob < 5 && u.scan_step_freq > 50) + shift = shift - 50; + else if (knob > 1020 && u.scan_step_freq < 10000) + shift = shift + 50; + break; + + case 4: // set the scan step delay + + //generate values 0-2500 from the tuning pot + u.scan_step_delay = constrain(50 * (knob / 25) + shift, 0, 2000); + if (knob < 5 && u.scan_step_delay > 0) + shift = shift - 50; + else if (knob > 1020 && u.scan_step_delay < 2000) + shift = shift + 50; + break; + } + + // if Fbutton is pressed, we save the setting + + if (!digitalRead(FBUTTON)) { + switch (param) { + + case 1: // save the lower scan limit + case 2: // save the upper scan limit + case 3: // save the scan step size + bleep(600, 50, 1); + break; + + case 4: // save the scan step delay + RUNmode = RUN_NORMAL; + bleep(600, 50, 2); + printLine(1, "--- SETTINGS ---"); + shiftBase(); //align the current knob position with the current frequency + break; + } + param ++; + firstrun = true; + } + + else { + RUNmode = RUN_SCAN_PARAMS; + firstrun = false; + switch (param) { + + case 1: // display the lower scan limit + + itoa(u.scan_start_freq, b, DEC); + strcpy(c, "lower "); + strcat(c, b); + strcat(c, " kHz"); + break; + + case 2: // display the upper scan limit + + itoa(u.scan_stop_freq, b, DEC); + strcpy(c, "upper "); + strcat(c, b); + strcat(c, " kHz"); + break; + + case 3: // display the scan step size + + itoa(u.scan_step_freq, b, DEC); + strcpy(c, "step "); + strcat(c, b); + strcat(c, " Hz"); + break; + + case 4: // display the scan step delay + + itoa(u.scan_step_delay, b, DEC); + strcpy(c, "delay "); + strcat(c, b); + strcat(c, " ms"); + break; + } + printLine(1, c); + } +} + +/* interrupt service routine + When the PTT is keyed (either manually or by the semiQSK function), normal program execution + will be interrupted and the interrupt service routine (ISR) will be executed. After that, normal + program execution will continue from the point it was interrupted. + The only thing this ISR does, is setting the TXstart flag. This flag is frequently polled by the + knob_position routine. + The knob_position routine takes 100 readings from the tuning pot for extra precision. However + this takes more time (about 10ms). Normally this is not a problem, however when we key the PTT + we need to quickly turn off the VFO in order to prevent spurious emission. In that case 10ms is + too much delay. + In order to prevent this delay, the knob-position routine keeps checking the TXstart flag + and when it is set it will terminate immediately. +*/ + +void ISRptt(void) { + TXstart = true; // the TXstart flag will be set as soon as the PTT is keyed +} + +// routine to read the position of the tuning knob at high precision (Allard, PE1NWL) +int knob_position() { + unsigned long knob = 0; + // the knob value normally ranges from 0 through 1023 (10 bit ADC) + // in order to increase the precision by a factor 10, we need 10^2 = 100x oversampling + for (byte i = 0; i < 100; i++) { // it takes about 10 ms to run this loop 100 times + if (TXstart) { // when the PTT was keyed, in order to prevent 10 ms delay: + TXstart = false; // reset the TXstart flag that was set by the interrupt service routine + return old_knob; // then exit this routine immediately and return the old knob value + } + else + knob = knob + analogRead(ANALOG_TUNING); // take 100 readings from the ADC + } + knob = (knob + 5L) / 10L; // take the average of the 100 readings and multiply the result by 10 + //now the knob value ranges from 0 through 10,230 (10x more precision) + knob = knob * 10000L / 10230L; // scale the knob range down to 0-10,000 + return (int)knob; +} + +/* + Many BITX40's suffer from a strong birdie at 7199 kHz (LSB). + This birdie may be eliminated by using a different VFO drive level in LSB mode. + In USB mode, a high drive level may be needed to compensate for the attenuation of + higher VFO frequencies. + The drive level for each mode can be set in the SETTINGS menu +*/ + +void set_drive_level(byte level) { + si5351bx_drive[2] = level / 2 - 1; + setFrequency(); +} + +void doRIT() { + if (millis() - max(dit, dah) < 1000) + return; + int knob = knob_position(); // get the current tuning knob position + + if (firstrun) { + current_setting = RIToffset; + shift = current_setting - ((knob - 5000) / 2); + firstrun = false; + } + + //generate values -2500 ~ +2500 from the tuning pot + RIToffset = (knob - 5000) / 2 + shift; + if (knob < 5 && RIToffset > -2500) + shift = shift - 50; + else if (knob > 10220 && RIToffset < 2500) + shift = shift + 50; + + RIT = RIToffset; + if (RIT != RIT_old) { + setFrequency(); + itoa(RIToffset, b, DEC); + strcpy(c, "RIT "); + strcat(c, b); + strcat(c, " Hz"); + printLine(1, c); + + RIT_old = RIT; + old_knob = knob_position(); + delay(30); + } +} + +/* + routine to align the current knob position with the current frequency + If we switch between VFO's A and B, the frequency will change but the tuning knob + is still in the same position. We need to apply some offset so that the new frequency + corresponds with the current knob position. + This routine reads the current knob position, then it shifts the baseTune value up or down + so that the new frequency matches again with the current knob position. +*/ + +void shiftBase() { + setFrequency(); + unsigned long knob = knob_position(); // get the current tuning knob position + baseTune = frequency - (knob * (unsigned long)u.POT_SPAN / 10UL); +} + +/* + The Tuning mechansim of the Raduino works in a very innovative way. It uses a tuning potentiometer. + The tuning potentiometer that a voltage between 0 and 5 volts at ANALOG_TUNING pin of the control connector. + This is read as a value between 0 and 1023. By 100x oversampling this range is expanded by a factor 10. + Hence, the tuning pot gives you 10,000 steps from one end to the other end of its rotation. Each step is 50 Hz, + thus giving maximum 500 Khz of tuning range. The tuning range is scaled down depending on the POT_SPAN value. + The standard tuning range (for the standard 1-turn pot) is 50 Khz. But it is also possible to use a 10-turn pot + to tune accross the entire 40m band. The desired POT_SPAN can be set via the Function Button in the SETTINGS menu. + When the potentiometer is moved to either end of the range, the frequency starts automatically moving + up or down in 10 Khz increments, so it is still possible to tune beyond the range set by POT_SPAN. +*/ + +void doTuning() { + int knob = analogRead(ANALOG_TUNING) * 100000 / 10230; // get the current tuning knob position + + // tuning is disabled during TX (only when PTT sense line is installed) + if (inTx && clicks < 10 && abs(knob - old_knob) > 50 && !locked) { + printLine(1, "dial is locked"); + shiftBase(); + firstrun = true; + return; + } + else if (inTx || locked) // no tuning in TX or when dial is locked + return; + else if (abs(knob - old_knob) < 20) + save_frequency(); // only save VFO frequencies to EEPROM when tuning pot is not being turned + + knob = knob_position(); // get the precise tuning knob position + // the knob is fully on the low end, do fast tune: move down by 10 Khz and wait for 300 msec + // if the POT_SPAN is very small (less than 25 kHz) then use 1 kHz steps instead + + if (knob == 0 && frequency != u.LOWEST_FREQ) { + if (frequency > u.LOWEST_FREQ) { + if (u.POT_SPAN < 25) + baseTune = baseTune - 1000UL; // fast tune down in 1 kHz steps + else + baseTune = baseTune - 10000UL; // fast tune down in 10 kHz steps + frequency = baseTune + (unsigned long)knob * (unsigned long)u.POT_SPAN / 10UL; + if (clicks < 10) + printLine(1, "<<<<<<<"); // tks Paul KC8WBK + delay(FAST_TUNE_DELAY); + } + if (frequency < u.LOWEST_FREQ) + baseTune = frequency = u.LOWEST_FREQ; + setFrequency(); + old_knob = 0; + } + + // the knob is full on the high end, do fast tune: move up by 10 Khz and wait for 300 msec + // if the POT_SPAN is very small (less than 25 kHz) then use 1 kHz steps instead + + else if (knob == 10000 && frequency != u.HIGHEST_FREQ) { + if (frequency < u.HIGHEST_FREQ) { + if (u.POT_SPAN < 25) + baseTune = baseTune + 1000UL; // fast tune up in 1 kHz steps + else + baseTune = baseTune + 10000UL; // fast tune up in 10 kHz steps + frequency = baseTune + (unsigned long)knob * (unsigned long)u.POT_SPAN / 10UL; + if (clicks < 10) + printLine(1, " >>>>>>>"); // tks Paul KC8WBK + delay(FAST_TUNE_DELAY); + } + if (frequency > u.HIGHEST_FREQ) { + baseTune = u.HIGHEST_FREQ - (u.POT_SPAN * 1000UL); + frequency = u.HIGHEST_FREQ; + } + setFrequency(); + old_knob = 10000; + } + + // the tuning knob is at neither extremities, tune the signals as usual + else { + if (abs(knob - old_knob) > 4) { // improved "flutter fix": only change frequency when the current knob position is more than 4 steps away from the previous position + knob = (knob + old_knob) / 2; // tune to the midpoint between current and previous knob reading + old_knob = knob; + frequency = constrain(baseTune + (unsigned long)knob * (unsigned long)u.POT_SPAN / 10UL, u.LOWEST_FREQ, u.HIGHEST_FREQ); + setFrequency(); + delay(10); + } + } + + if (!u.vfoActive) // VFO A is active + vfoA = frequency; + else + vfoB = frequency; +} + +/* + "CW SPOT" function: When operating CW it is important that both stations transmit their carriers on the same frequency. + When the SPOT button is pressed while the radio is in CW mode, the RIT will be turned off and the sidetone will be generated (but no carrier will be transmitted). + The FINETUNE mode is then also enabled (fine tuning +/- 1000Hz). Fine tune the VFO so that the pitch of the received CW signal is equal to the pitch of the CW Spot tone. + By aligning the CW Spot tone to match the pitch of an incoming station's signal, you will cause your signal and the other station's signal to be exactly on the same frequency. + + When the SPOT button is pressed while the radio is in SSB mode, the radio will only be put in FINETUNE mode (no sidetone will be generated). +*/ + +void checkSPOT() { + if (digitalRead(SPOT) == LOW && !inTx) { + RUNmode = RUN_FINETUNING; + TimeOut = millis() - 1000UL; + if (ritOn) { + toggleRIT(); // disable the RIT if it was on + old_knob = knob_position(); + } + } +} + +void finetune() { + + // for SPOT tuning: in CW mode only, generate short side tone pulses every one second + if ((mode & 2) && millis() - TimeOut > 1000UL) { + tone(CW_TONE, u.CW_OFFSET); + if (millis() - TimeOut > 1150UL) { + noTone(CW_TONE); + TimeOut = millis(); + } + } + + int knob = knob_position(); // get the current tuning knob position + static int fine_old; + + if (digitalRead(SPOT) == LOW && !inTx && !locked) { + if (firstrun) { + firstrun = false; + fine = 0; + fine_old = 9999; + shift = (5000 - knob) / 5; + } + + //generate values -1000 ~ +1000 from the tuning pot + fine = (knob - 5000) / 5 + shift; + if (knob < 10 && fine > -1000 && frequency + fine > u.LOWEST_FREQ) + shift = shift - 10; + else if (knob > 10220 && fine < 1000 && frequency + fine < u.HIGHEST_FREQ) + shift = shift + 10; + + if (fine != fine_old) { + setFrequency(); // apply the finetuning offset + updateDisplay(); + } + + if (mode & 2) + printLine(1, "SPOT + FINE TUNE"); + else + printLine(1, "FINE TUNE"); + + fine_old = fine; + } + + else { // return to normal mode when SPOT button is released + firstrun = true; + TimeOut = 0; + noTone(CW_TONE); + RUNmode = RUN_NORMAL; + frequency = frequency + fine; // apply the finetuning offset + fine = 0; + shiftBase(); + old_knob = knob_position(); + if (clicks == 10) + printLine(1, "--- SETTINGS ---"); + } +} + +void factory_settings() { + + printLine(0, "loading standard"); + printLine(1, "settings..."); + + EEPROM.put(0, u); // save all user parameters to EEPROM + delay(1000); +} + +// routine to save both VFO frequencies when they haven't changed for more than 500Hz in the past 30 seconds + +void save_frequency() { + static unsigned long t3; + if (abs(vfoA - u.vfoA) > 500UL || abs(vfoB - u.vfoB) > 500UL) { + if (millis() - t3 > 30000UL) { + u.vfoA = vfoA; + u.vfoB = vfoB; + t3 = millis(); + } + } + else + t3 = millis(); +} + +void scan() { + + int knob = knob_position(); + + if (abs(knob - old_knob) > 8 || (digitalRead(PTT_SENSE) && PTTsense_installed) || !digitalRead(FBUTTON) || (u.cap_sens == 0 && !digitalRead(KEY)) || (u.cap_sens != 0 && capaKEY) || !digitalRead(SPOT)) { + //stop scanning + TimeOut = 0; // reset the timeout counter + RUNmode = RUN_NORMAL; + shiftBase(); + delay(400); + } + + else if (TimeOut < millis()) { + if (RUNmode == RUN_SCAN) { + frequency = frequency + u.scan_step_freq; // change frequency + // test for upper limit of scan + if (frequency > u.scan_stop_freq * 1000UL) + frequency = u.scan_start_freq * 1000UL; + setFrequency(); + } + else // monitor mode + swapVFOs(); + + TimeOut = millis() + u.scan_step_delay; + } +} + +/* + Routine to detect whether or not the touch pads are being touched. + This routine is only executed when the related touch keyer mod is installed. + We need two 470K resistors connected from the PULSE output to either KEY and DAH inputs. + We send LOW to the PULSE output and as a result the internal capacitance on the KEY and DAH + inputs will start to discharge. After a short "threshold" delay, we test whether the + KEY and DAH inputs are still HIGH or already LOW. + When the KEY or DAH input is already LOW, it means the internal base capacitance has rapidly + discharged, so we can conclude the touch pad wasn't touched. + When the KEY or DAH input is still HIGH, it means that the pad was touched. + When the pad is touched, the human body adds extra capacitance to the internal base capacity, + and he increased capacity will not rapidly discharge within the given threshold delay time. + Finally we send HIGH to the pulse output allowing the capacitance to recharge for the next cycle. + We can control the touch sensitivity by varying the threshold delay time. + The minimum delay time is the base_sens value as determined by the calibration routine which + is executed automatically during power on. + With minimum delay time we will have maximum touch sensitivity. If we want to reduce the sensitivity + we add some extra delay time (cap_sens). The user can set the desired touch sensisitivy value in the + SETTINGS menu (1-25 µs extra delay). +*/ + +void touch_key() { + static unsigned long KEYup = 0; + static unsigned long KEYdown = 0; + static unsigned long DAHup = 0; + static unsigned long DAHdown = 0; + + digitalWrite(PULSE, LOW); // send LOW to the PULSE output + delayMicroseconds(base_sens_KEY + 25 - u.cap_sens); // wait few microseconds for the KEY capacitance to discharge + if (digitalRead(KEY)) { // test if KEY input is still HIGH + KEYdown = millis(); // KEY touch pad was touched + if (!capaKEY && millis() - KEYup > 1) { + capaKEY = true; + released = 0; + } + } + else { // KEY touch pad was not touched + KEYup = millis(); + if (capaKEY && millis() - KEYdown > 10) + capaKEY = false; + } + + digitalWrite(PULSE, HIGH); // send HIGH to the PULSE output + delayMicroseconds(50); // allow the capacitance to recharge + + digitalWrite(PULSE, LOW); // send LOW to the PULSE output + delayMicroseconds(base_sens_DAH + 25 - u.cap_sens); // wait few microseconds for the DAH capacitance to discharge + + if (digitalRead(DAH)) { // test if DAH input is still HIGH + DAHdown = millis(); // DAH touch pad was touched + if (!capaDAH && millis() - DAHup > 1) { + capaDAH = true; + released = 0; + } + } + else { // DAH touch pad was not touched + DAHup = millis(); + if (capaDAH && millis() - DAHdown > 10) + capaDAH = false; + } + + digitalWrite(PULSE, HIGH); // send HIGH to the PULSE output + delayMicroseconds(50); // allow the capacitance to recharge + + // put the touch sensors in the correct position + if (u.key_type == 2 || u.key_type == 4) { + // swap capaKEY and capaDAH if paddle is reversed + // a nerdy way to swap two variables without having to use an extra temp variable + capaKEY = capaKEY xor capaDAH; + capaDAH = capaKEY xor capaDAH; + capaKEY = capaKEY xor capaDAH; + } +} + +/* + Routine to calibrate the touch key pads + (measure the time it takes the capacitance to discharge while the touch pads are NOT touched) + Even when the touch pads are NOT touched, there is some internal capacitance + The internal capacitance may vary depending on the length and routing of the wiring + We measure the base capacitance so that we can use it as a baseline reference + (threshold delay when the pads are not touched). +*/ + +void calibrate_touch_pads() { + // disable the internal pullups + pinMode(KEY, INPUT); + pinMode(DAH, INPUT); + + bool triggered; + // first we calibrate the KEY (DIT) touch pad + base_sens_KEY = 0; // base capacity of the KEY (DIT) touch pad + do { + base_sens_KEY++; // increment the delay time until the KEY touch pad is no longer triggered by the base capacitance + triggered = false; + for (int i = 0; i < 100; i++) { + digitalWrite(PULSE, HIGH); // bring the KEY input to a digital HIGH level + delayMicroseconds(50); // wait a short wile to allow voltage on the KEY input to stabalize + digitalWrite(PULSE, LOW); // now bring the KEY input to a digital LOW value + delayMicroseconds(base_sens_KEY); // wait few microseconds + if (digitalRead(KEY)) { // check 100 times if KEY input is still HIGH + triggered = true; // if KEY is still high, then it was triggered by the base capacitance + i = 100; + } + } + } while (triggered && base_sens_KEY != 255); // keep trying until KEY input is no longer triggered + + // Next we calibrate the DAH pad + base_sens_DAH = 0; // base capacity of the DAH touch pad + do { + base_sens_DAH++; // increment the delay time until the DAH touch pad is no longer triggered by the base capacitance + triggered = false; + for (int i = 0; i < 100; i++) { + digitalWrite(PULSE, HIGH); // bring the KEY input to a digital HIGH level + delayMicroseconds(50); // wait a short wile to allow voltage on the DAH input to stabalize + digitalWrite(PULSE, LOW); // now bring the DAH input to a digital LOW value + delayMicroseconds(base_sens_DAH); // wait few microseconds + if (digitalRead(DAH)) { // check 100 times if DAH input is still HIGH + triggered = true; // if KEY is still high, then it was triggered by the base capacitance + i = 100; + } + } + } while (triggered && base_sens_DAH != 255); // keep trying until KEY input is no longer triggered + + if (base_sens_KEY == 255 || base_sens_DAH == 255 || base_sens_KEY == 1 || base_sens_DAH == 1) { // if inputs are still triggered even with max delay (255 us) + CapTouch_installed = false; // then the base capacitance is too high (or the mod is not installed) so we can't use the touch keyer + u.cap_sens = 0; // turn capacitive touch keyer OFF + printLine(0, "touch sensors"); + printLine(1, "not detected"); + } + else if (u.cap_sens > 0) { + printLine(0, "touch key calibr"); + strcpy(c, "DIT "); + itoa(base_sens_KEY, b, DEC); + strcat(c, b); + strcat(c, ", DAH "); + itoa(base_sens_DAH, b, DEC); + strcat(c, b); + strcat(c, " us"); + printLine(1, c); + } + else + printLine(1, "touch keyer OFF"); + + delay(2000); + updateDisplay(); + + //configure the morse keyer inputs + if (u.cap_sens == 0) { // enable the internal pull-ups if touch keyer is disabled + pinMode(KEY, INPUT_PULLUP); + pinMode(DAH, INPUT_PULLUP); + } +} + +/* + setup is called on boot up + It setups up the modes for various pins as inputs or outputs + initiliaizes the Si5351 and sets various variables to initial state + + Just in case the LCD display doesn't work well, the debug log is dumped on the serial monitor + Choose Serial Monitor from Arduino IDE's Tools menu to see the Serial.print messages +*/ + +void setup() { + u.raduino_version = 29; + strcpy (c, "Raduino v1.29"); + + lcd.begin(16, 2); + + //Serial.begin(9600); //start the serial monitor port + analogReference(DEFAULT); + //Serial.println("*Raduino booting up"); + + //configure the function button to use the internal pull-up + pinMode(FBUTTON, INPUT_PULLUP); + //configure the CAL button to use the internal pull-up + pinMode(CAL_BUTTON, INPUT_PULLUP); + //configure the SPOT button to use the internal pull-up + pinMode(SPOT, INPUT_PULLUP); + //configure the PTT SENSE to use the internal pull-up + pinMode(PTT_SENSE, INPUT_PULLUP); + //configure the TX_RX to use the internal pull-up + pinMode(TX_RX, INPUT_PULLUP); + + delay(100); // allow the voltages on the Arduino inputs to settle for some milliseconds after power on + + if (digitalRead(TX_RX)) { // Test if TX_RX line is installed + TXRX_installed = false; + u.semiQSK = false; + u.QSK_DELAY = 10; + } + + pinMode(TX_RX, OUTPUT); + digitalWrite(TX_RX, 0); + pinMode(CW_CARRIER, OUTPUT); + digitalWrite(CW_CARRIER, 0); + pinMode(CW_TONE, OUTPUT); + digitalWrite(CW_TONE, 0); + pinMode(PULSE, OUTPUT); + digitalWrite(PULSE, 0); + + // when Fbutton or CALbutton is kept pressed during power up, + // or after a version update, + // then all settings will be restored to the standard "factory" values + byte old_version; + EEPROM.get(0, old_version); // previous sketch version + if (!digitalRead(CAL_BUTTON) || !digitalRead(FBUTTON) || (old_version != u.raduino_version)) { + factory_settings(); + } + delay(100); + + // check if PTT sense line is installed + PTTsense_installed = !digitalRead(PTT_SENSE); + + printLine(0, c); + delay(1000); + + //retrieve user settings from EEPROM + EEPROM.get(0, u); + + if (PTTsense_installed) { + pinMode(PTT_SENSE, INPUT); //disable the internal pull-up + + // attach interrupt to the PTT_SENSE input + // when PTT_SENSE goes from LOW to HIGH (so when PTT is keyed), execute the ISRptt routine + attachPCINT(digitalPinToPCINT(PTT_SENSE), ISRptt, RISING); + + calibrate_touch_pads(); // measure the base capacitance of the touch pads while they're not being touched + } + + //initialize the SI5351 + si5351bx_init(); + si5351bx_vcoa = (SI5351BX_XTAL * SI5351BX_MSA) + u.cal * 100L; // apply the calibration correction factor + + vfoA = u.vfoA; + vfoB = u.vfoB; + + if (!u.vfoActive) { // VFO A is active + frequency = vfoA; + mode = u.mode_A; + } + else { // VFO B is active + frequency = vfoB; + mode = u.mode_B; + } + + if (mode & 1) // if UPPER side band + SetSideBand(u.USBdrive); + else // if LOWER side band + SetSideBand(u.LSBdrive); + + if (mode > 1) // if in CW mode + RXshift = u.CW_OFFSET; + + shiftBase(); //align the current knob position with the current frequency + + //display warning message when VFO calibration data was erased + if ((u.cal == CAL_VALUE) && (u.USB_OFFSET == OFFSET_USB)) { + printLine(1, "VFO uncalibrated"); + delay(1000); + } + + bleep(u.CW_OFFSET, 60, 3); + bleep(u.CW_OFFSET, 180, 1); +} + +void loop() { + switch (RUNmode) { + case 0: // for backward compatibility: execute calibration when CAL button is pressed + if (!digitalRead(CAL_BUTTON)) { + RUNmode = RUN_CALIBRATE; + calbutton = true; + factory_settings(); + printLine(0, "Calibrating: Set"); + printLine(1, "to zerobeat"); + delay(2000); + } + else { + if (u.cap_sens != 0) + touch_key(); + if (!inTx && millis() - max(dit, dah) > 1000) { + checkButton(); + checkSPOT(); + if (clicks == 0 || clicks == 10) + EEPROM.put(0, u); // save all user parameters to EEPROM + if (clicks == 0 && !ritOn && !locked) + printLine(1, MY_CALLSIGN); + } + if (PTTsense_installed) { + if (inTx && outofband) { // if PTTsense is installed: + si5351bx_setfreq(2, 0); // disable CLK2 => no TX outside 40 ham band limits + if (clicks == 0) + printLine(1, "out of band!!"); + bleep(1000, 60, 1); + bleep(500, 120, 1); + delay(300); + } + checkCW(); + checkTX(); + if (keyeron) + keyer(); + } + else { + if (outofband) { // if PTTsense is NOT installed: + si5351bx_setfreq(2, 0); // disable CLK2 => no RX and no TX outside 40 ham band limits + if (clicks == 0) + printLine(1, "out of band!!"); + } + } + if (ritOn && !inTx) + doRIT(); + else if (millis() - max(dit, dah) > u.QSK_DELAY) + doTuning(); + } + return; + case 1: //calibration + calibrate(); + break; + case 2: //set VFO drive level + VFOdrive(); + break; + case 3: // set tuning range + set_tune_range(); + break; + case 4: // set CW parameters + set_CWparams(); + if (u.cap_sens != 0) + touch_key(); + checkCW(); + checkTX(); + if (keyeron) + keyer(); + return; + case 5: // scan mode + scan(); + break; + case 6: // set scan paramaters + scan_params(); + break; + case 7: // A/B monitor mode + scan(); + break; + case 8: // Fine tuning mode + finetune(); + break; + } + delay(100); +} diff --git a/raduino_v1.29.ino.with_bootloader.eightanaloginputs.hex b/raduino_v1.29.ino.with_bootloader.eightanaloginputs.hex new file mode 100644 index 0000000..6f2cb1e --- /dev/null +++ b/raduino_v1.29.ino.with_bootloader.eightanaloginputs.hex @@ -0,0 +1,1534 @@ +:100000000C9463000C948B000C948B000C948B006C +:100010000C94C21C0C948B000C948B000C94661CEA +:100020000C948B000C948B000C948B000C948B0024 +:100030000C948B000C948B000C948B000C948B0014 +:100040000C941C1C0C948B000C948B000C948B0057 +:100050000C948B000C948B000C948B000C948B00F4 +:100060000C941A1D0C948B00000000002400270043 +:100070002A000000000023002600290000000008DC +:10008000000201000003040700000000000000005F +:10009000010204081020408001020408102001021F +:1000A0000408102000000000250028002B00040494 +:1000B0000404040404040202020202020303030310 +:1000C00003030200391E11241FBECFEFD8E0DEBFAC +:1000D000CDBF14E0A0E0B1E0E8EAF4E502C005908D +:1000E0000D92A835B107D9F726E0A8E5B4E001C024 +:1000F0001D92A232B207E1F710E0C3E6D0E004C0DF +:100100002197FE010E94062AC236D107C9F70E9434 +:10011000B81E0C94522A0C9400008230910538F0DD +:10012000880F991F880F991F05970197F1F7089578 +:100130008091220120E4829FC00111248760809376 +:100140007C0080917A00806480937A0080917A00AC +:1001500086FDFCCF809178009091790008952091E0 +:10016000BD0581E0213208F044C0809199058111DC +:10017000FCCF8091BC0592E09093990591E090931B +:1001800098059FEF909397051092960520939505FB +:10019000ACE9B5E0E5E7F5E090E0291331C0109255 +:1001A000740590917405880F892B80937405809154 +:1001B0007305813049F51092730580917405809321 +:1001C000BB008091BC0083FDF8CF85EC8093BC0020 +:1001D000809199058230E1F3809197058F3FB1F0CE +:1001E000809197058032A1F080919705803391F03E +:1001F00084E010929B051092BD0510929A05089517 +:100200003D9131939F5FC9CF85EEE0CF80E0F1CF84 +:1002100082E0EFCF83E0EDCF833081F028F48130AE +:1002200099F08230A9F008958730A9F08830C9F09C +:100230008430B1F4809180008F7D03C08091800074 +:100240008F7780938000089584B58F7784BD08955B +:1002500084B58F7DFBCF8091B0008F778093B00005 +:1002600008958091B0008F7DF9CFCF93DF93282F31 +:1002700030E0F901E458FF4F8491F901E057FF4F56 +:10028000D491F901E255FF4FC491CC23A1F0811123 +:100290000E940C01EC2FF0E0EE0FFF1FEE58FF4F15 +:1002A000A591B491EC91ED2381E090E009F480E018 +:1002B000DF91CF91089580E090E0FACF1F93CF9324 +:1002C000DF93282F30E0F901E458FF4F8491F901C2 +:1002D000E057FF4FD491F901E255FF4FC491CC2371 +:1002E000A9F0162F81110E940C01EC2FF0E0EE0F07 +:1002F000FF1FEC55FF4FA591B4918FB7F894EC9187 +:10030000111108C0D095DE23DC938FBFDF91CF9110 +:100310001F910895DE2BF8CFCF93DF9390E0FC017F +:10032000E057FF4F249182559F4FFC018491882311 +:10033000C9F090E0880F991FFC01E859FF4FA59183 +:10034000B491FC01EC55FF4FC591D49161110DC0E2 +:100350009FB7F8948C91209582238C9388812823D1 +:1003600028839FBFDF91CF910895623051F49FB7EA +:10037000F8943C91822F809583238C93E8812E2BD7 +:10038000EFCF8FB7F894EC912E2B2C938FBFEACF41 +:100390008F929F92AF92BF92CF92DF92EF92FF9295 +:1003A000CF936C0180912301853069F08F3F09F470 +:1003B00070C0CF91FF90EF90DF90CF90BF90AF9043 +:1003C0009F908F900895E2ECF0E0C491C7FDF1CFCB +:1003D00061E085E00E948C01CC2319F0C23009F065 +:1003E00022C1F12CE12C60E072E18AE790E0A701E4 +:1003F00096010E948F2949015A01DA01C90101972A +:10040000A109B1098F3F9105A105B10509F00CF4CF +:10041000EEC060E472E48FE090E0A70196010E94D4 +:100420008F292150310941095109C23009F0A0C07A +:1004300082E02F3F31054105510509F00CF488C1D8 +:1004400060E970ED83E090E0A70196010E948F299A +:10045000215031094109510983E02F3F3105410500 +:10046000510509F00CF474C168E478EE81E090E085 +:10047000A70196010E948F29215031094109510994 +:100480002F3F31054105510511F00CF08FC084E07C +:100490005FC185E080932301E2ECF0E0C491CF3F9F +:1004A00009F487CFC13021F150F0C23009F43EC0C9 +:1004B000C7FD7FCF61E085E00E948C018FCF14BC27 +:1004C00015BC84B5826084BD85B5816085BDE3EBD4 +:1004D000F0E0E491F0E0EE0FFF1FEC55FF4F859147 +:1004E0009491E5E9F0E0E491E093C205C0E070CFBB +:1004F000109280001092810080918100886080932A +:10050000810080918100816080938100E3EBF0E0C5 +:10051000E491F0E0EE0FFF1FEC55FF4F85919491B1 +:10052000E5E9F0E0E491E093C10552CF1092B0000C +:100530001092B1008091B00082608093B0008091F1 +:10054000B10081608093B100E3EBF0E0E491F0E072 +:10055000EE0FFF1FEC55FF4F859194919093C005CE +:100560008093BF05E5E9F0E0E491E093BE0530CF6C +:100570002F3F31054105510509F00CF452C068E4E4 +:1005800078EE81E090E0A70196010E948F2921502A +:10059000310941095109CC2309F4AFC02F3F31057E +:1005A0004105510511F00CF0B0C071CF64E274EF59 +:1005B00080E090E0A70196010E948F292150310927 +:1005C0004109510985E02F3F31054105510509F0E9 +:1005D00008F4BEC062E17AE780E090E0A7019601EE +:1005E0000E948F2921503109410951099CC09C0169 +:1005F00081E0C111ADC095B5987F892B85BD27BD20 +:100600008FEF9FEFDC018093CB059093CC05A093F7 +:10061000CD05B093CE0580916E00826080936E0010 +:10062000C8CE82E0E6CFF12CE12C60E072E18AE7EF +:1006300090E0A70196010E948F29215031094109BC +:10064000510991E02115310581E0480751056CF011 +:1006500068E478EE81E090E0A70196010E948F297E +:10066000215031094109510993E0C13009F076C0A8 +:1006700080918100887F892B80938100309389004D +:10068000209388008FEF9FEFDC018093C705909344 +:10069000C805A093C905B093CA0580916F00826018 +:1006A00080936F0086CE2093B3008FEF9FEFDC0125 +:1006B0008093C3059093C405A093C505B093C60568 +:1006C0008091700082608093700073CE84E02F3F31 +:1006D00031054105510509F008F48DCF9AE0B59434 +:1006E000A794979487949A95D1F7A50194012150E6 +:1006F00031094109510985E07ECF83E02F3F310563 +:100700004105510509F00CF476CF62E17AE780E00B +:1007100090E0A70196010E948F29215031094109DB +:100720005109CC2399F286E02F3F3105410551054F +:1007300079F070F08AE0B594A794979487948A959D +:10074000D1F7A5019401215031094109510987E0F0 +:100750009091B100987F892B8093B100C13009F44A +:100760008DCFC23009F49FCFC11123CE48CF3FB700 +:10077000F89480916F0590917005A0917105B091EA +:10078000720526B5A89B05C02F3F19F00196A11D43 +:10079000B11D3FBFBA2FA92F982F8827BC01CD01CB +:1007A000620F711D811D911D42E0660F771F881F2A +:1007B000991F4A95D1F708958F929F92AF92BF9259 +:1007C000CF92DF92EF92FF926B017C010E94B70300 +:1007D0004B015C01C114D104E104F104B9F00E94A1 +:1007E000B703681979098A099B09683E7340810536 +:1007F000910580F321E0C21AD108E108F10888EEE2 +:10080000880E83E0981EA11CB11CE4CFFF90EF90EE +:10081000DF90CF90BF90AF909F908F9008952FB7AB +:10082000F89460916B0570916C0580916D059091C5 +:100830006E052FBF089581E0809384040895089584 +:10084000089508959091230189130BC0E2ECF0E024 +:10085000E4919FEF90932301E13049F028F0E230DA +:1008600061F060E00C945E0110926E00FACF9091FE +:100870006F009D7F90936F00F4CF909170009D7FEB +:100880009093700091E09093B0009091B100987FA8 +:1008900094609093B1001092B300E3CFAF92BF92F7 +:1008A000CF92DF92EF92FF920F931F93CF93DF933C +:1008B0006C017B018B01040F151FEB015E01AE186B +:1008C000BF08C017D10759F06991D601ED91FC918D +:1008D0000190F081E02DC6010995892B79F7C501BA +:1008E000DF91CF911F910F91FF90EF90DF90CF900C +:1008F000BF90AF900895089585ED8093BC008091DE +:10090000BC0084FDFCCF1092990508952091DD0470 +:10091000260F3327331F21323105ECF4209199053E +:10092000FC0190E080E0243069F082E00895A0911D +:10093000DD042191AC0143545B4FA40FB52FB11DD1 +:100940002C930196861798F38091DD04680F6093CD +:10095000DD0480E0089581E008950895E0910805A0 +:1009600080910705E81730F4F0E0E356FB4F8081F3 +:1009700090E008958FEF9FEF089590910805809182 +:1009800007052FEF3FEF981748F4E92FF0E0E35603 +:10099000FB4F208130E09F5F90930805C9010895C7 +:1009A0008091070590910805891B990B0895CF92B6 +:1009B000DF92EF92FF920F931F93CF93DF937C010F +:1009C000CB018A0120919A05222389F0EB016B016A +:1009D000C40ED51ECC15DD0569F06991D701ED91E6 +:1009E000FC910190F081E02DC7010995F3CF642FB0 +:1009F0000E948604C801DF91CF911F910F91FF9053 +:100A0000EF90DF90CF900895CF93DF931F92CDB7F3 +:100A1000DEB7698320919A052223F9F02091BD0564 +:100A2000203258F021E030E0FC013383228390E053 +:100A300080E00F90DF91CF91089580919B05E82F82 +:100A4000F0E0E456FA4F998190838F5F80939B0585 +:100A50008093BD0581E090E0ECCF61E0CE0101968E +:100A60000E948604F7CF0F931F93CF93DF938B01E0 +:100A700091E090939A0590E69093BC0510929B05A7 +:100A80001092BD05682F8FEC95E00E940405E801E7 +:100A900019E0115031F069918FEC95E00E94040546 +:100AA000F8CFDF91CF911F910F910C94AF0090E0A0 +:100AB00080E008950F931F93CF9360E082E00E943F +:100AC0005E0180919C0490E0499620912006821B53 +:100AD00091090E948D008FE00E943501C09196041B +:100AE000892B09F48CC00E940F04609397047093C3 +:100AF00098048093990490939A04C1111EC00E9497 +:100B00000F04009192041091930420919404309169 +:100B10009504601B710B820B930B6230710581058C +:100B2000910558F081E08093960410928E04109203 +:100B30008F04109290041092910461E082E00E9470 +:100B40005E0183EC90E00197F1F760E082E00E94A3 +:100B50005E0180919B0490E0499620912006821BC3 +:100B600091090E948D0083E00E943501C0918904A3 +:100B7000892B09F468C00E940F0460938A04709363 +:100B80008B0480938C0490938D04C1111EC00E942D +:100B90000F04009185041091860420918704309100 +:100BA0008804601B710B820B930B62307105810509 +:100BB000910558F081E08093890410928E04109280 +:100BC0008F04109290041092910461E082E00E94E0 +:100BD0005E0183EC90E00197F1F780911606823078 +:100BE00011F0843041F4809189049091960490939F +:100BF000890480939604CF911F910F9108950E94CC +:100C00000F046093920470939304809394049093E0 +:100C10009504CC2309F491CF0E940F04009197040E +:100C2000109198042091990430919A04601B710BE3 +:100C3000820B930B6B3071058105910508F47DCF14 +:100C4000109296047ACF0E940F04609385047093EB +:100C500086048093870490938804CC2309F4B5CF4D +:100C60000E940F0400918A0410918B0420918C043F +:100C700030918D04601B710B820B930B6B307105EF +:100C80008105910508F4A1CF109289049ECFCF92DF +:100C9000DF92EF92FF92CF93C4E6C12CD12C760164 +:100CA00080918404882361F01092840480914C0523 +:100CB00090914D05CF91FF90EF90DF90CF900895E8 +:100CC0000E949800092E000CAA0BBB0BC80ED91E5F +:100CD000EA1EFB1EC15021F7C701B6016B5F7F4FB3 +:100CE0008F4F9F4F2AE030E040E050E00E94C12942 +:100CF000A0E1B7E20E94AE2926EF37E240E050E0E3 +:100D00000E94C129C901D6CFCF92DF92EF92FF9204 +:100D10000F931F93CF93DF938C01D42FC0E06B010F +:100D2000770FEE08FF08C8010E94C801C701B6018D +:100D30000E94DC0385E00E942204C701B6010E94E4 +:100D4000DC03CF5FDC13EFCFDF91CF911F910F91C9 +:100D5000FF90EF90DF90CF900895CF93C62F91E052 +:100D600090939A0590E69093BC0510929B05109283 +:100D7000BD05682F8FEC95E00E9404056C2F8FEC69 +:100D800095E00E940405CF910C94AF004F925F92C2 +:100D90006F927F928F929F92AF92BF92CF92DF928B +:100DA000EF92FF921F93CF93DF93CDB7DEB72897D3 +:100DB0000FB6F894DEBF0FBECDBF6B017C01109162 +:100DC0002101DC01CB018052914AA740B109813257 +:100DD0009449A747B640F0F0146010932101609148 +:100DE000210183E00E94AD0628960FB6F894DEBF7D +:100DF0000FBECDBFDF91CF911F91FF90EF90DF909D +:100E0000CF90BF90AF909F908F907F906F905F90AA +:100E10004F90089560911D0170911E0180911F01F6 +:100E200090912001A70196010E94C12949015A0110 +:100E30002601370144245524E0EF6E224528462838 +:100E4000472849F09695879577956795F694E794A6 +:100E5000D794C794EDCF27E0660F771F881F991F9F +:100E60002A95D1F7A70196010E94C129E7E0880CD5 +:100E7000991CAA1CBB1CEA95D1F7E2E09E1AA108B6 +:100E8000B108820E931EA41EB51ED982CA82AB82FF +:100E90009C828D82F4E0CC0CDD1CEE1CFF1CFA95CC +:100EA000D1F7CC24DD2420EFE222FF24E82AEE82D1 +:100EB0007F836887BE016F5F7F4F8AE30E9433059F +:100EC00060911C016C6082E10E94AD061B7F85CFA2 +:100ED000FF920F931F93CF93DF9303EF15E0D0E0C2 +:100EE000C0E0F801F1908F01CE010E947929F81637 +:100EF00021F06F2DCE010E9481292196CF32D1059C +:100F000081F783EF95E0DF91CF911F910F91FF90D3 +:100F10000895CF93DF93EC0160E08E810E945E0123 +:100F200061E08E810E945E0160E08E810E945E0120 +:100F30008BE891E00197F1F7DF91CF910895CF927F +:100F4000DF92EF92FF920F931F93CF93DF936C0189 +:100F50007C0187E0E80EF11CD0E0C0E0062F10E035 +:100F6000B8010C2E02C0759567950A94E2F761707E +:100F7000F70181917F010E945E012196C430D10565 +:100F800079F7C601DF91CF911F910F91FF90EF90FC +:100F9000DF90CF900C948907CF92DF92EF92FF926F +:100FA0000F931F93CF93DF93EC01F62E642F8C8168 +:100FB0000E945E018D818F3F19F060E00E945E010A +:100FC0000F2D10E08F8584FF25C06E0187E0C80ECD +:100FD000D11CF12CE12CB8010E2C02C0759567953F +:100FE0000A94E2F76170F60181916F010E945E013F +:100FF000FFEFEF1AFF0A88E0E816F10461F7CE016F +:10100000DF91CF911F910F91FF90EF90DF90CF90E4 +:101010000C948907B80184E0759567958A95E1F786 +:10102000CE010E949F076F2DCE01DF91CF911F91BE +:101030000F91FF90EF90DF90CF900C949F0740E0CE +:10104000682F8BED95E00C94CC07CF93DF93823023 +:1010500029F49091EA0598609093EA058093EE0553 +:101060001092EF0580E48093F00580E18093F10514 +:1010700080E58093F20561E08091DF050E948C019C +:101080008091E0058F3F19F061E00E948C0161E0E2 +:101090008091E1050E948C01CBEDD5E09E012B5D96 +:1010A00035408091EA0584FD0BC084E090E02196F4 +:1010B0002817390744F461E08E810E948C01EECF3D +:1010C00088E090E0F4CF8BE39DE00197F1F760E0DA +:1010D0008091DF050E945E0160E08091E1050E9441 +:1010E0005E018091E0058F3F19F060E00E945E0193 +:1010F0008091EA0584FD39C063E08BED95E00E94A4 +:101100009F07CBE4D6E4CE010197F1F763E08BEDC6 +:1011100095E00E949F072197F1F763E08BED95E042 +:101120000E949F0783E592E00197F1F762E08BED63 +:1011300095E00E949F078091EA0580620E941F0847 +:1011400084E08093EB058CE00E941F0881E00E9400 +:101150001F088BE39FE10197F1F782E08093EC0594 +:1011600086E0DF91CF910C941F0880620E941F08D7 +:101170008BE496E40197F1F78091EA0580620E9482 +:101180001F0883E592E00197F1F78091EA058062FC +:101190000E941F08D0CF41E00E94CC0781E090E080 +:1011A0000895EF92FF920F931F93CF93DF93C82F71 +:1011B0008B0121E18202C00111249C01285A3B4F7E +:1011C0007901B901C8010E94322A892B79F1EC2FEB +:1011D0008091EE05C81710F0EFEFE80FF0E0E55250 +:1011E000FA4F848980680E941F08F801019000204E +:1011F000E9F73197AF01401B510BB8018BED95E03A +:101200000E944E04B801C7010E943B2AE8010990E0 +:101210000020E9F72197C01BC03140F441E060E2B3 +:101220008BED95E00E94CC07CF5FF6CFDF91CF9199 +:101230001F910F91FF90EF90089562E471E080E0BC +:101240000E94D10863E571E081E00E94D1080E940C +:10125000680768EE73E080E090E00C94DC034F9246 +:101260005F926F927F928F929F92AF92BF92CF9236 +:10127000DF92EF92FF9281E10E943501892B19F4F0 +:1012800081E08093820480918204882371F081E15F +:101290000E943501892B49F08091F405823328F4AE +:1012A000109282048F5F8093F40584E00E943501E0 +:1012B000892B19F481E08093810480918104882333 +:1012C00071F084E00E943501892B49F08091F4058A +:1012D000823028F01092810481508093F40580912F +:1012E0001606833020F180912006882369F08091D2 +:1012F0002006811107C110925F05109260051092BF +:1013000061051092620513C0E0910501F09106019C +:1013100080810E943501892B51F70E940F04609350 +:101320005F0570936005809361059093620520913D +:10133000F40530E050E040E060EB74E080E090E0E5 +:101340000E94C12969017A0140907D0450907E0479 +:1013500060907F0470908004411451046104710412 +:1013600009F0F3C00E940F0480906305909064051B +:10137000A0906505B0906605681979098A099B09EE +:101380006C157D058E059F0508F0C1C061E086E003 +:101390000E945E018091FE059091FF050E94C801A8 +:1013A00081E080937C0480918E0490918F04A091C1 +:1013B0009004B0919104892B8A2B8B2B09F04FC09C +:1013C00080917D0490917E04A0917F04B09180046F +:1013D000892B8A2B8B2B09F0DEC10E940F04809091 +:1013E000630590906405A0906505B0906605681946 +:1013F00079098A099B096C157D058E059F0548F4BE +:1014000080912006882309F4B2C1809120068111C1 +:10141000E0C10E940F0480905F0590906005A0904D +:101420006105B09062059B01AC01281939094A0990 +:101430005B0949015A01A3E0B0E0A70196010E94AF +:10144000AE2986169706A806B90648F480912006AC +:10145000882309F4C3C1809120068111E1C1C090A5 +:101460008E04D0908F04E0909004F09091048091CD +:101470002006811108C2C114D104E104F10409F06D +:101480001AC280917D0490917E04A0917F04B09156 +:101490008004892B8A2B8B2B09F029C280915A0555 +:1014A00081115BC280915905882309F442C2809161 +:1014B000F4054AE06AE275E090E00E9413296FE5C6 +:1014C00071E089E195E00E943B2A6AE275E089E1DA +:1014D00095E00E94272A69E671E089E195E00E9483 +:1014E000272A69E175E081E0FF90EF90DF90CF90CF +:1014F000BF90AF909F908F907F906F905F904F9034 +:101500000C94D10880918904811107CFF4CE0E94F8 +:101510000F0480905F0590906005A0906105B090E9 +:1015200062059B01AC01281939094A095B09490187 +:101530005A01A3E0B0E0A70196010E94AE298616E9 +:101540009706A806B90608F421CF60E086E00E945D +:101550005E0185E00E94220445284628472851F470 +:101560000E940F0460937D0470937E0480937F0437 +:10157000909380040E940F0480907D0490907E04DC +:10158000A0907F04B09080042B013C0148185908BA +:101590006A087B08A0910401B0E0A70196010E94AF +:1015A000AE29641575058605970508F0FCCE4091B7 +:1015B00063055091640560916505709166058091A1 +:1015C0005F0590916005A0916105B0916205481793 +:1015D00059076A077B0708F093C080917B04882332 +:1015E00081F00E940F0460936305709364058093FB +:1015F00065059093660581E08093590510927B0400 +:101600001BC0809120068823D1F080912006811193 +:1016100020C090917A04992309F10E940F046093ED +:101620005F0570936005809361059093620581E08A +:101630008093590510927A0480937C043EC0E09117 +:101640000201F091030180810E943501892B49F24A +:10165000DCCF909196049111C4CFDBCF882309F49D +:101660003FC080912006811146C080911F068823CB +:1016700019F083E08093040181E080935905809301 +:101680007C040E940F049B01AC01281939094A0906 +:101690005B0949015A01A0910401B0E0A70196013C +:1016A0000E94AE29681579058A059B0508F07BCE56 +:1016B0001092590510927C0481E0809304011092ED +:1016C0007D0410927E0410927F0410928004109288 +:1016D0008E0410928F04109290041092910463CEA5 +:1016E000E0910501F091060180810E943501892B6E +:1016F00009F493CFB6CF8091890481118ECFB5CFF5 +:1017000080917A04811189CF809120068823A1F0ED +:101710008091200681111BC090917B04911161CFB3 +:101720008823D1F080912006882309F49ECF8091F0 +:101730009604811156CF99CFE0910501F0910601F1 +:1017400080810E943501892B09F467CFE1CF909108 +:101750008904911162CFE0CFE0910201F091030181 +:1017600080810E943501892B09F43BCFDBCFE091CA +:101770000201F091030180810E943501892B09F457 +:1017800044CE0E940F0460938E0470938F04809364 +:1017900090049093910480917D0490917E04A09197 +:1017A0007F04B0918004892B8A2B8B2B09F431CED6 +:1017B000809120068823C9F180912006811157C0AD +:1017C000C0908E04D0908F04E0909004F09091042B +:1017D00052CE8091960481111CCED3CFE0910501A9 +:1017E000F091060180810E943501892B09F433CEE6 +:1017F0000E940F0460938E0470938F048093900472 +:101800009093910480917D0490917E04A0917F0437 +:10181000B0918004892B8A2B8B2B09F420CEC8CF62 +:101820008091890481111BCEE3CFE0910201F091F8 +:10183000030180810E943501892B09F4BDCF80917D +:10184000200681112EC0E0910501F09106018081F2 +:101850000E943501892B29F10E940F0460938E04A8 +:1018600070938F048093900490939104F8CD8091AD +:101870009604882321F3C0908E04D0908F04E090CA +:101880009004F0909104CD28CE28CF2809F406CEFC +:1018900080919604811125C080918904811143C0F3 +:1018A000FDCD80912006882309F48ACF8091890498 +:1018B0008111E1CFD1CF0E940F046C197D098E09EF +:1018C0009F09663071058105910508F4DACDE09134 +:1018D0000201F091030180810E943501892B09F0FA +:1018E000D0CD81E080937B0410927A04D7CDC114CF +:1018F000D104E104F10409F4D1CD0E940F046C1964 +:101900007D098E099F09603171058105910508F4F3 +:10191000C5CDE0910501F091060180810E9435015D +:10192000892B09F0BBCD81E080937A0410927B046F +:10193000B5CD80915C056EE671E08111D4CD8091CA +:101940005E056DE771E08A300CF0CDCD1092350563 +:101950001092340568E672E0C6CDFF90EF90DF90FC +:10196000CF90BF90AF909F908F907F906F905F903F +:101970004F9008950F931F93CF93DF93E9E1F5E024 +:1019800081E1DF011D928A95E9F7EAE2F5E08AE05C +:10199000DF011D928A95E9F700914E0510914F05E0 +:1019A000209150053091510580915C05811105C051 +:1019B00080916905883009F07EC080915205909130 +:1019C0005305092E000CAA0BBB0BBC01CD01600F07 +:1019D000711F821F931F2AE04AE255E00E942D29C1 +:1019E00068E672E089E195E00E943B2ACAE2D5E010 +:1019F00009900020E9F7CB52D540DD278E01065033 +:101A00001109A8016AE275E089E195E00E94422A85 +:101A10006DE571E089E195E00E94272AB801665DD5 +:101A20007A4F43E050E089E195E00E94422A6DE55B +:101A300071E089E195E00E94272ABE01695D7A4F35 +:101A400080915C05811106C08091690541E050E0FC +:101A5000883011F443E050E089E195E00E94422A89 +:101A600080915805813009F442C0D0F1823009F4E8 +:101A700041C0833009F441C080916705882331F06B +:101A800080915A056EE971E0882331F080910B0650 +:101A9000882331F062EA71E089E195E00E94272A0B +:101AA0001092290569E175E080E0DF91CF911F91E7 +:101AB0000F910C94D108C901B8016E5C7F4F8F4F14 +:101AC0009F4F2AE04AE255E00E942D2980910A06A4 +:101AD0006EE871E0882309F485CF61E971E082CF77 +:101AE0006FE973E089E195E00E94272AC5CF6FEA8C +:101AF00073E0F8CF64E971E0F5CF69E971E0F2CF06 +:101B0000AF92BF92CF92DF92FF920F931F93CF932A +:101B1000DF9360E08FE00E948C0160E083E00E9430 +:101B20008C0110929C0493ECC92ED12CD0919C0472 +:101B3000DF5FD0939C0410E000E0C0E0AD2EB12C3C +:101B400061E082E00E945E01C6010197F1F760E06A +:101B500082E00E945E01C5010E948D008FE00E941C +:101B60003501892B19F004E610E0C1E00F5F1F4F2B +:101B7000043611052CF3CC2311F0DF3FB9F6109297 +:101B80009B0483ECC82ED12CC0919B04CF5FC093E3 +:101B90009B0410E000E0F12CAC2EB12C61E082E05F +:101BA0000E945E01C6010197F1F760E082E00E94A9 +:101BB0005E01C5010E948D0083E00E943501892BE2 +:101BC00021F004E610E0FF24F3940F5F1F4F04366A +:101BD000110524F3FF2069F1CF3FB1F61092070100 +:101BE0001092200666EA71E080E00E94D10864EB62 +:101BF00071E081E00E94D10860ED77E080E090E044 +:101C00000E94DC030E94BA0C80912006811155C00D +:101C100062E08FE00E948C0162E083E0DF91CF916F +:101C20001F910F91FF90DF90CF90BF90AF900C94D9 +:101C30008C01DF3F99F2CF3F89F2D13079F2C13088 +:101C400069F28091200662EE71E0882391F261ECE6 +:101C500071E080E00E94D10862ED71E089E195E0D9 +:101C60000E943B2A80919C044AE06AE275E090E081 +:101C70000E9413296AE275E089E195E00E94272A13 +:101C800067ED71E089E195E00E94272A80919B042D +:101C90004AE06AE275E090E00E9413296AE275E08A +:101CA00089E195E00E94272A6EED71E089E195E0D7 +:101CB0000E94272A69E175E09CCFDF91CF911F91A7 +:101CC0000F91FF90DF90CF90BF90AF9008958F92CB +:101CD0009F92AF92BF92CF92DF92EF92FF920F93BB +:101CE0001F9310925A0580914E0590914F05A09137 +:101CF0005005B0915105AC01BD01405C5F4C6A4696 +:101D00007109413E53496440710518F021E0209368 +:101D10005A0520915B05211104C020915A0521111B +:101D20003CC0C0900C05D0900D050D2C000CEE08A9 +:101D3000FF080091360510913705012E000C220B8B +:101D4000330B8090520590905305092C000CAA0883 +:101D5000BB084091580540FF2CC084519A4EA844BE +:101D6000BF4FBC01CD01600F711F821F931F680D13 +:101D7000791D8A1D9B1D6C197D098E099F09009193 +:101D8000F5051091F605012E000C220B330B601B9C +:101D9000710B820B930B0E94C6061F910F91FF904F +:101DA000EF90DF90CF90BF90AF909F908F900C946A +:101DB000BA0C4CEE55E167EB70E0481959096A0915 +:101DC0007B090C0D1D1D2E1D3F1D800F911FA21F95 +:101DD000B31F8A019B01081B190B2A0B3B0BC9017E +:101DE000B801D9CF0E94670E0E9447062091FC05DA +:101DF0003091FD05DC010E940C2A2AE030E040E031 +:101E000050E00E94C12980914E0590914F05A0910C +:101E10005005B0915105821B930BA40BB50B809319 +:101E2000150190931601A0931701B093180108951E +:101E30003F924F925F926F927F928F929F92AF925A +:101E4000BF92CF92DF92EF92FF920F931F93CF93A7 +:101E5000DF930E949800EC0130900E0580911901EB +:101E600042E0882309F4C3C1341609F4F2C04315D3 +:101E700078F181E0381609F470C081E10E943501E3 +:101E8000892B09F028C281E0381640F043E0431561 +:101E900008F008C284E0381609F40CC280910E05DF +:101EA0008F5F80930E0581E080931901DF91CF91C0 +:101EB0001F910F91FF90EF90DF90CF90BF90AF9068 +:101EC0009F908F907F906F905F904F903F9008957C +:101ED00043E0341609F445C184E03812CECF209196 +:101EE0001206309113063093390520933805CE0140 +:101EF00067EE7FEF0E94F22942E3469FC001479FB1 +:101F0000900D1124820F931F9093490580934805EB +:101F10002091480530914905CE0169E170E00E94A9 +:101F2000F22942E3469FC001479F900D1124820F82 +:101F3000931F97FDABC1813D47E0940714F080EDFE +:101F400097E09093130680931206C530D1050CF0EC +:101F5000A0C1892B09F491CF43C100910C061091C7 +:101F60000D061093390500933805CE01D7FD019673 +:101F700095958795081B190B6091170670911806A7 +:101F80008091190690911A0628EE33E040E050E067 +:101F90000E94C129021B130B1093490500934805A9 +:101FA00000914805109149056E01D7FF03C04FEF1E +:101FB000C41AD40AD594C79488EAC81A84EED80AF9 +:101FC000C00ED11E0D2C000CEE08FF086091170604 +:101FD000709118068091190690911A0628EE422EEB +:101FE00023E0522E612C712CA30192010E94C12981 +:101FF00049015A01C814D904EA04FB0490F0609125 +:102000001B0670911C0680911D0690911E06A3016F +:1020100092010E94C129C216D306E406F50608F40F +:10202000960130930D0620930C06C530D1050CF0B7 +:10203000F0C06901F12CE12C015011098C149D04B0 +:10204000AE04BF0408F019CF10934905009348056A +:1020500014CF00910E0610910F06109339050093CE +:102060003805C0900C06D0900D06F12CE12C609143 +:102070001B0670911C0680911D0690911E0628EE8D +:1020800033E040E050E00E94C1292C193D094E097F +:102090005F09DE010E94B92920E034E040E050E011 +:1020A0000E948F29C20ED31EE41EF51E0C191D09B5 +:1020B000109349050093480560911B0670911C061A +:1020C00080911D0690911E0628EE33E040E050E01E +:1020D0000E94C12969017A0100910C0610910D0638 +:1020E0004801B12CA12C281939094A095B09DE01E4 +:1020F0000E94B92920E034E040E050E00E948F299E +:10210000280D391D4A1D5B1D809148059091490598 +:102110002C01092E000C66087708420E531E641E1F +:10212000751E481459046A047B040CF490C0280FEF +:10213000391FC414D504E604F70408F4960130935B +:102140000F0620930E06C530D1050CF082C0019712 +:102150000217130708F091CE909349058093480524 +:102160008CCE2091100630911106309339052093C2 +:102170003805CE016BEF7FEF0E94F22942E3469FC4 +:10218000C001479F900D1124820F931F9093490522 +:10219000809348052091480530914905CE0165E0BE +:1021A00070E00E94F22942E3469FC001479F900DD4 +:1021B0001124820F931F823391050CF459C0813191 +:1021C00047E2940714F080E197E290931106809320 +:1021D0001006C530D1050CF04EC0C29709F44DCEA3 +:1021E00022533109309349052093480546CE3416D1 +:1021F00009F462CF431528F081E0381609F4D0CEF7 +:102200003CCE43E0341631F284E0381609F480CE37 +:1022100034CECD3FD3400CF430CE6901F12CE12C0B +:1022200060911B0670911C0680911D0690911E0600 +:1022300028EE33E040E050E00E94C129C216D306E8 +:10224000E406F50608F019CE0F5F1F4FFDCE98018A +:1022500076CFCD3FD3400CF410CEA90170E060E002 +:102260004C155D056E057F0508F007CE019674CF0D +:1022700082E390E0AACFCD3FD3400CF4FECD803175 +:10228000974209F4FACD2E5C3F4FACCF90E080E04E +:1022900058CECD3FD3400CF4F0CD803D974009F4AB +:1022A000ECCDF1CF41E062E370E088E592E00E947E +:1022B0008406F4CD1092690542E062E370E088E59F +:1022C00092E00E9484066DE771E081E00E94D108EF +:1022D0000E94F20EE3CD86E0809369051092190109 +:1022E00042E0341609F456C04315C8F081E03816B0 +:1022F000A9F169E175E081E0DF91CF911F910F9124 +:10230000FF90EF90DF90CF90BF90AF909F908F9015 +:102310007F906F905F904F903F900C94D10843E076 +:10232000341609F443C084E03812E3CF4AE06AE28D +:1023300075E080911206909113060E9413296DE6B4 +:1023400072E089E195E00E943B2A6AE275E089E14A +:1023500095E00E94272A6FE072E017C04AE06AE227 +:1023600075E080910C0690910D060E94132962EF92 +:1023700071E089E195E00E943B2A6AE275E089E11B +:1023800095E00E94272A69EF71E089E195E00E94BB +:10239000272AAFCF4AE06AE275E080910E0690915D +:1023A0000F060E9413296EEF71E0E3CF4AE06AE264 +:1023B00075E080911006909111060E94132965E046 +:1023C00072E089E195E00E943B2A6AE275E089E1CA +:1023D00095E00E94272A6BE072E0D7CFCF92DF9280 +:1023E000EF92FF921F93CF93DF930E949800EC012E +:1023F00080911901882361F080910E05833009F4E2 +:1024000061C0A0F5813009F447C0823009F457C09B +:1024100010910E05133009F4D1C008F073C01130CB +:1024200009F498C0123009F4B3C081E10E9435016B +:10243000892B09F0D7C1133009F4A2C108F05DC19E +:10244000113009F473C1123009F48CC180910E056A +:102450008F5F80930E0581E080931901DF91CF910A +:102460001F91FF90EF90DF90CF900895853081F11C +:1024700060F1863069F68091FE059091FF0590939A +:102480000D0580930C0590933905809338058C1BBE +:102490009D0B885C910931C08091580582608093C2 +:1024A00058050E94BA0C8091160690E09093390569 +:1024B00080933805D0934905C0934805A9CF8091F2 +:1024C0001F06F3CF80912006F0CF8091FB05EDCF62 +:1024D0002091140630911506309339052093380564 +:1024E000CE0166EF7FEF0E94F2294AE0469FC001CD +:1024F000479F900D1124820F931F9093490580935D +:10250000480586CF153009F4B3C008F49BC01630D7 +:1025100009F08BCF2091480530914905CE018853B1 +:102520009F4F820F931F883C91050CF4DAC0813BCA +:1025300044E0940714F080EB94E09093FF058093BF +:10254000FE05C530D1050CF0CFC0883C910509F4DB +:102550006CCFB2C080914805909149059E01281B1F +:10256000390BC901049620913805309139054AE1AB +:10257000429FE001439FD00D11248C0F9D1F8F7748 +:1025800099276AE170E00E94F229609316064DCF08 +:102590008091480590914905C81BD90B2496C6FB2C +:1025A000CC24C0F8D12C8091380590913905C80E03 +:1025B000D91E4C2D417040931F0637CF20914805FE +:1025C00030914905CE01821B930B68E270E00E94B6 +:1025D000F2298091380590913905680F791F77FDB0 +:1025E00015C06A31710514F069E170E0609320064E +:1025F000C530D105CCF461110BC062E08FE00E94C0 +:102600008C0162E083E00E948C010FCF60E0EECF8E +:10261000265F3F4F309349052093480560E08FE0E7 +:102620000E948C0160E0EECFCD3FD34044F0693191 +:10263000A9F32A50310930934905209348056623B0 +:10264000E1F2ECCF8091480590914905C81BD90B68 +:102650002496C6FBEE24E0F8F12C80913805909189 +:102660003905E80EF91E3E2D31703093FB05DDCEA5 +:102670002091480530914905CE016AE070E00E9442 +:10268000F2294AE0469FC001479F900D1124820F16 +:10269000931F8A309105BCF0893E43E0940714F003 +:1026A00088EE93E09093150680931406C530D1050B +:1026B0006CF4449708F4B9CE2A5031093093490597 +:1026C00020934805B2CE8AE090E0ECCFCD3FD340D6 +:1026D0000CF4ABCE883E934009F4A7CE265F3F4F63 +:1026E000EDCF88EC90E029CFCD3FD3400CF49DCEC8 +:1026F000803B944009F499CEF1CF153009F464C0C1 +:1027000008F455C0163009F0A1CE1092690542E0D8 +:1027100062E370E088E592E00E9484066DE771E074 +:1027200081E00E94D1080E94F20E90CE41E062E367 +:1027300070E088E592E00E94840668EC70E080E03A +:1027400090E00E94DC038091160681117FCE82E02A +:1027500080930E0580910701811178CE83E08093EC +:102760000E0574CE41E062E370E088E592E00E94DD +:10277000840668EC70E080E090E00E94DC03EACF21 +:102780000E94800D41E062E370E088E592E00E94E3 +:10279000840668EC70E080E090E00E94DC038091A9 +:1027A0001401811153CE80910E058E5FD8CF809198 +:1027B000FB05811109C08AE090E090931506809393 +:1027C000140685E080930E0541E062E370E088E541 +:1027D00092E00E94840668EC70E080E090E00E9445 +:1027E000DC0334CE84E08093690510921901133024 +:1027F00009F466C088F4113071F1123009F451C047 +:1028000069E175E081E0DF91CF911F91FF90EF903A +:10281000DF90CF900C94D108153009F47BC008F4F8 +:1028200070C0163069F74AE06AE275E08091FE05F3 +:102830009091FF050E94132964E772E089E195E019 +:102840000E943B2A6AE275E089E195E00E94272A0E +:102850006BE072E011C063E172E089E195E00E94F3 +:102860003B2A809116068230B1F058F4882381F01B +:1028700067E272E0813021F689E195E00E94272A23 +:10288000BFCF833059F06EE272E08430A9F3B8CF45 +:1028900069E172E0F1CF62E272E0EECF63E372E0F1 +:1028A000EBCF67E372E089E195E00E943B2A8091DB +:1028B0001F0669E073E08111DFCF66E174E0DCCFD1 +:1028C00080912006811107C062EE71E089E195E0F8 +:1028D0000E943B2A95CF64E472E080E00E94D10818 +:1028E000809120064AE06AE275E090E00E94132998 +:1028F00061E572E089E195E00E943B2A6AE275E0B9 +:10290000BBCF6EE572E089E195E00E943B2A8091A1 +:10291000FB05CFCF4AE06AE275E080911406909102 +:1029200015060E94132969E672E089E195E00E948C +:102930003B2A6AE275E089E195E00E94272A60E17E +:1029400072E09ACF4F925F926F927F928F929F9296 +:10295000AF92BF92EF92FF920F931F93CF93DF93AB +:102960000E949800EC0110910E05809119018823B6 +:1029700049F1123009F473C0133009F479C01130F1 +:1029800091F4609117067091180680911906909144 +:102990001A0628EE33E040E050E00E94C12930934F +:1029A000390520933805CE01D7FD01969595879579 +:1029B0002091380530913905281B390B3093490592 +:1029C00020934805123009F469C0133009F414C18A +:1029D000113009F0B4C02091480530914905CE016D +:1029E000D7FD019695958795820F931F092E000CB0 +:1029F000AA0BBB0BF82E092FFA2FEB2F813345E7DB +:102A00009407A105B10528F070E3F72E05E7F0E083 +:102A1000E0E04F2D502F6F2F7E2F483EE3E05E0702 +:102A20006105710520F448EE53E060E070E04093EA +:102A30001706509318066093190670931A06C5304E +:102A4000D105FCF4893E9340A105B10508F477C097 +:102A50002A503109309349052093480570C0609190 +:102A60001B0670911C0680911D0690911E0691CF49 +:102A70008091FC059091FD05909339058093380570 +:102A800092CFCD3FD3400CF45AC080339547A10577 +:102A9000B10508F054C0265F3F4FDCCFE0904805F9 +:102AA000F0904905CE01D7FD0196959587958E0D3D +:102AB0009F1D4C01990FAA08BB08609117067091E1 +:102AC00018068091190690911A0628EE33E040E02E +:102AD00050E00E94C1298091FC059091FD052901DB +:102AE0003A01480E591E611C711CD301C20188169F +:102AF0009906AA06BB0608F468C021E3821625E7FA +:102B00009206A104B10430F050E3852E55E7952ECE +:102B1000A12CB12C80921B0690921C06A0921D063F +:102B2000B0921E06C530D1050CF052C04AE0E41A3E +:102B3000F10888159905AA05BB0508F458C081E17C +:102B40000E943501892B09F0A8C0123009F47FC01A +:102B5000133009F491C0113009F520911706309116 +:102B600018064091190650911A06A8EEB3E00E948B +:102B7000AE296093170670931806809319069093F8 +:102B80001A0641E062E370E088E592E00E94840664 +:102B900068EC70E080E090E00E94DC0380910E051C +:102BA0008F5F80930E0581E080931901DF91CF91B3 +:102BB0001F910F91FF90EF90BF90AF909F908F90DB +:102BC0007F906F905F904F90089542015301A2CF84 +:102BD000CD3FD3400CF4B3CFE0E38E16E5E79E067D +:102BE000A104B10408F0ABCF2AE0E20EF11CF09290 +:102BF0004905E0924805A3CF209148053091490549 +:102C0000CE01D7FD019695958795820F931F8A3047 +:102C100091058CF0853F41E0940714F084EF91E03A +:102C20009093FD058093FC05C530D1053CF40A97CF +:102C300009F485CF0DCF8AE090E0F2CFCD3FD340AD +:102C40000CF47DCF843F914009F479CF24CF2091BB +:102C50001B0630911C0640911D0650911E06A8EEE1 +:102C6000B3E00E94AE2960931B0670931C0680930C +:102C70001D0690931E0685CF1092690542E062E31F +:102C800070E088E592E00E9484066DE771E081E0E3 +:102C90000E94D1080E94F20E81CF83E080936905E3 +:102CA000109219011230C1F1133009F445C01130EE +:102CB00009F07CCF60911706709118068091190673 +:102CC00090911A062AE04AE255E00E942D296EE70B +:102CD00072E089E195E00E943B2A6AE275E089E1B1 +:102CE00095E00E94272A69EF71E089E195E00E9452 +:102CF000272A69E175E081E0DF91CF911F910F9163 +:102D0000FF90EF90BF90AF909F908F907F906F90CB +:102D10005F904F900C94D10860911B0670911C0637 +:102D200080911D0690911E062AE04AE255E00E941D +:102D30002D2963E872E0CDCF4AE06AE275E0809128 +:102D4000FC059091FD050E94132968E872E0C1CF4F +:102D500080915D0591E0892780935D05811108C010 +:102D600010923505109234051092370510923605F1 +:102D70000E94F20E81E08093190180910B0681116F +:102D800010920B060C94BA0C1F93CF93DF930E9402 +:102D9000980020916905223089F02091580520FF84 +:102DA00042C02091FA0526952150330B3093390506 +:102DB0002093380590934905809348052091480554 +:102DC00030914905821B930B62E370E00E94F22967 +:102DD000C0913805D0913905C60FD71FC370DD27C4 +:102DE00021961C2F110F1093830481E10E9435015D +:102DF000892BF9F4109269058091580580FF16C05F +:102E00001093FA0542E062E370E088E592E00E94E8 +:102E100084066DE771E081E00E94D108DF91CF91D7 +:102E20001F910C94F20E2091F905BDCF1093F90576 +:102E3000E9CF82E080936905C150C0931C010E94D4 +:102E4000670E809183044AE06AE275E090E00E9498 +:102E5000132962E972E089E195E00E943B2A6AE267 +:102E600075E089E195E00E94272A6FE972E089E127 +:102E700095E00E94272A69E175E081E0DF91CF911A +:102E80001F910C94D1088695815080931C010E945B +:102E9000670E0E94670E90910A0680915805911165 +:102EA00003C0809308060895809309060895409111 +:102EB0004E0550914F0560915005709151058091DC +:102EC0000B0690910A069923D1F110920A064093BD +:102ED000400550934105609342057093430540912E +:102EE0004405509145056091460570914705409312 +:102EF0004E0550934F056093500570935105811115 +:102F000004C080910806809358058091580580FF81 +:102F100036C08091FA050E944317809167058111A0 +:102F200031C080915805823068F18091FE05909102 +:102F3000FF0590930D0580930C050C94F20E91E023 +:102F400090930A06409344055093450560934605C7 +:102F500070934705409140055091410560914205AD +:102F60007091430540934E0550934F056093500573 +:102F7000709351058111C9CF80910906C4CF80910A +:102F8000F905C9CF10920D0510920C05D6CFCF923E +:102F9000DF92EF92FF920E94470620914C053091FC +:102FA0004D05821B930B97FF03C091958195910965 +:102FB0000997B4F0109254051092550510925605D9 +:102FC00010925705109269050E94F20E60E971E0B7 +:102FD00080E090E0FF90EF90DF90CF900C94DC03C6 +:102FE0008EE00E943501892B21F080915B058111D3 +:102FF000E1CF81E10E943501892BE1F28091200629 +:10300000882309F46AC08091200681116DC084E094 +:103010000E943501892B71F20E940F04C090540563 +:10302000D0905505E0905605F0905705C616D70686 +:10303000E806F90608F04CC080916905853009F072 +:1030400059C08091100690911106C0904E05D09005 +:103050004F05E0905005F0905105C80ED91EE11CB7 +:10306000F11CC0924E05D0924F05E0925005F092AF +:10307000510520910E0630910F06A8EEB3E00E9494 +:10308000E3296C157D058E059F0570F420910C06D3 +:1030900030910D060E94E32960934E0570934F0511 +:1030A00080935005909351050E94670E0E940F0473 +:1030B0002091120630911306620F731F811D911D1E +:1030C000609354057093550580935605909357056A +:1030D000FF90EF90DF90CF9008958FE00E94350130 +:1030E000892B09F090CF66CF80919604882309F44C +:1030F0008ECF60CF0E945717D9CFCF92DF92EF9239 +:10310000FF9280917C04811115C180912006882353 +:1031100009F46DC08091200681112EC19091160690 +:10312000992339F0882309F42CC18091200681115C +:103130002FC18091590581113FC0C0905405D09096 +:103140005505E0905605F0905705C114D104E104EF +:10315000F10491F180916705882371F10E940F04B9 +:10316000C616D706E806F90638F58091FB058823D0 +:1031700019F110926705109254051092550510929E +:103180005605109257058091FE059091FF0590938A +:103190000D0580930C050E94F20E80910B068111A3 +:1031A0000E94571760E087E00E945E016AE070E0CD +:1031B00080E090E00E94DC0380911606811161C1DD +:1031C00080917C04882309F44CC18091580581FFCB +:1031D00048C161E086E00E945E018091FE05909109 +:1031E000FF05FF90EF90DF90CF900C94C8018FE027 +:1031F0000E943501892B09F08DCF81E080937C04FA +:103200008091FB058111C9C080911606882379F150 +:103210009091580591FF2BC091E090935905109221 +:103220008E0410928F04109290041092910480FFEB +:10323000BAC081E091E0909306018093050180E09F +:1032400091E09093030180930201809120068823EE +:1032500009F4B2C0809120068111C3C0809120067C +:10326000882309F4C3C0809120068111D4C08091C5 +:10327000670581115AC08091FB05882309F455C068 +:1032800060E070E0CB010E94C60661E087E00E942A +:103290005E0181E0809367058091580590910A0650 +:1032A0009111BEC080930806109235051092340526 +:1032B000109237051092360510920D0510920C05EC +:1032C00061E470E080E090E00E94DC0380910B06F6 +:1032D000882309F4A8C00E94571780916305909134 +:1032E0006405A0916505B0916605885B9F4FAF4F5F +:1032F000BF4F8093630590936405A0936505B093D9 +:10330000660580915F0590916005A0916105B0917F +:103310006205885B9F4FAF4FBF4F80935F059093CF +:103320006005A0936105B093620580917C048823B9 +:1033300009F4FFCE0E940F04209114063091150667 +:10334000620F731F811D911D609354057093550585 +:10335000809356059093570580912006882309F4A1 +:1033600065C080912006882309F4E3CE80919604FD +:10337000882309F461C0DDCE9091960491113DCF70 +:10338000CDCE83E00E943501892B09F436CFCDCE16 +:1033900080918904811131CFCCCE80915805826013 +:1033A0008093580531CF80E091E09093060180939F +:1033B000050181E091E045CFE0910201F091030128 +:1033C00080810E943501892B09F044CF0E940F04AF +:1033D000609363057093640580936505909366051B +:1033E0003DCF809196048111F1CF38CFE091050156 +:1033F000F091060180810E943501892B09F033CFBD +:103400000E940F0460935F057093600580936105CF +:10341000909362052CCF809189048111F1CF27CF41 +:103420008093090641CF0E94F20E57CF8FE00E9491 +:103430003501892B09F495CF10927C040E940F046A +:103440002091140630911506620F731F811D911D86 +:1034500060935405709355058093560590935705D6 +:1034600068CE84E00E943501019759F460E086E05F +:103470000E945E0185E0FF90EF90DF90CF900C946A +:103480002204FF90EF90DF90CF9008950F931F9349 +:10349000CF938091540590915505A0915605B09118 +:1034A0005705892B8A2B8B2B21F08091FB058111ED +:1034B000CEC08EE00E943501892BB1F18091670565 +:1034C000811132C060E070E0CB010E94C60681E04D +:1034D00080936705109235051092340510923705D8 +:1034E0001092360510920D0510920C058091FB0587 +:1034F000882359F0809158058D7F809358059091CD +:103500000A06911196C08093080661E470E080E09D +:1035100090E00E94DC030E94F20E0E94BA0C80919F +:103520000B0681110E9457178EE00E943501892BEE +:1035300009F08DC080916705882309F488C062E393 +:1035400070E080E090E00E94DC038EE00E94350194 +:10355000892B09F07CC0C0915805C23008F04DC0DD +:1035600080912106882309F448C061E087E00E9429 +:103570005E0161E086E00E945E0182E999E00E94BE +:10358000C80180914E0590914F05A0915005B091D2 +:1035900051050091520510915305012E000C220B8C +:1035A000330BCC2309F448C08258904EA844BF4F37 +:1035B000BC01CD01600F711F821F931F0091F505A3 +:1035C0001091F605012E000C220B330B601B710BC2 +:1035D000820B930B0E94C6066AEF70E080E090E0D9 +:1035E0000E94DC0360E086E00E945E0185E00E94AC +:1035F000220460E087E00E945E01109267050E944D +:10360000670E0E94BA0C80910B0681110E94571719 +:103610008091580581FF1BC08091FE059091FF05A8 +:1036200090930D0580930C05CF911F910F910C94F1 +:10363000F20E8093090669CF4EE75FE167EB70E019 +:10364000481B590B6A0B7B0BCB01BA01BFCFCF9143 +:103650001F910F9108950F931F930E949800E0917E +:103660005805209169052130C1F02091F505309170 +:10367000F605E13021F02091F7053091F8053093FF +:103680003905209338052091380530913905281BDC +:10369000390B309349052093480520914805309116 +:1036A0004905AC01420F531FBA014131F7E25F07F0 +:1036B00014F060E177E2603FF8ED7F0714F460EF0B +:1036C00078EDE13089F47093F6056093F505853067 +:1036D00091055CF5413F584D8CF02A5031093093EB +:1036E0004905209348050AC07093F8056093F705D3 +:1036F00085309105D4F4413F584D7CF781E10E941B +:103700003501892BD9F41092690542E062E370E03B +:1037100088E592E00E9484066DE771E081E00E94F6 +:10372000D1081F910F910C94F20E8D3F934034F30A +:10373000403157421CF7265F3F4FD1CF809168053B +:10374000882329F080E10E943501892BE1F681E090 +:103750008093690580915805813021F50E94670E9C +:103760004AE06AE275E08091F5059091F6050E94C5 +:10377000132962EA72E089E195E00E943B2A6AE23D +:1037800075E089E195E00E94272A6BE072E089E10B +:1037900095E00E94272A69E175E081E01F910F9171 +:1037A0000C94D108A091F705B091F80524E630E01B +:1037B0000E940C2A60547F48884D9B4C60931D01E9 +:1037C00070931E0180931F019093200100914E057C +:1037D00010914F0520915005309151056CEE75E127 +:1037E00087EB90E0601B710B820B930B0E94C60667 +:1037F000A091F705B091F80524E630E00E940C2A6C +:103800002BE633E040E050E00E948F29CA01B90165 +:103810002AE04AE255E00E941E296AEA72E089E144 +:1038200095E00E943B2A6AE275E089E195E00E94FA +:10383000272A60EB72E0ABCF1F920F920FB60F9268 +:1038400011242F933F938F939F93AF93BF938091B6 +:103850006B0590916C05A0916D05B0916E0530914E +:103860006A0523E0230F2D3758F50196A11DB11DE0 +:1038700020936A0580936B0590936C05A0936D056A +:10388000B0936E0580916F0590917005A0917105C0 +:10389000B09172050196A11DB11D80936F059093A3 +:1038A0007005A0937105B0937205BF91AF919F9180 +:1038B0008F913F912F910F900FBE0F901F901895F1 +:1038C00026E8230F0296A11DB11DD2CF1F920F92A1 +:1038D0000FB60F9211242F933F934F935F936F93E3 +:1038E0007F938F939F93AF93BF93EF93FF938091B9 +:1038F000C3059091C405A091C505B091C605892B5B +:103900008A2B8B2BD1F19091BE05E091BF05F091F0 +:10391000C0058081892780838091C3059091C4056B +:10392000A091C505B091C605181619061A061B0602 +:103930009CF48091C3059091C405A091C505B091F8 +:10394000C6050197A109B1098093C3059093C405E9 +:10395000A093C505B093C605FF91EF91BF91AF91BC +:103960009F918F917F916F915F914F913F912F9197 +:103970000F900FBE0F901F901895809123010E9409 +:103980002204EACF1F920F920FB60F9211242F93A9 +:103990003F934F935F936F937F938F939F93AF93D7 +:1039A000BF93CF93EF93FF9396B1C0910105C0955C +:1039B00080910A058923C82390930105C0FF05C0A3 +:1039C000E0911201F09113010995C1FF05C0E0914A +:1039D0000801F09109010995C2FF05C0E0910A01B3 +:1039E000F0910B010995C3FF05C0E0910C01F09126 +:1039F0000D010995C4FF05C0E0910E01F0910F0182 +:103A00000995C5FF05C0E0911001F09111010995DC +:103A1000FF91EF91CF91BF91AF919F918F917F9146 +:103A20006F915F914F913F912F910F900FBE0F902B +:103A30001F9018951F920F920FB60F9211242F937B +:103A40003F934F935F936F937F938F939F93AF9326 +:103A5000BF93EF93FF938091B900887F803609F47C +:103A60004AC0F0F5883209F4A7C018F5803109F48E +:103A70009BC0B8F4882309F4F9C0883009F494C0D5 +:103A8000FF91EF91BF91AF919F918F917F916F9136 +:103A90005F914F913F912F910F900FBE0F901F900C +:103AA0001895883109F488C0803251F780939705C2 +:103AB00014C0803409F49DC040F48033B9F38833D6 +:103AC000F9F68093970585ECB0C0803509F485C080 +:103AD000883509F496C0883499F60E947C04D0CFCA +:103AE000883909F48CC038F5883729F050F48836C5 +:103AF00011F0803729F683E0809399051092FF0436 +:103B000057C0883809F47BC0803919F0803809F033 +:103B1000B7CF8091FF04803208F071C0E091FF04BC +:103B200081E08E0F8093FF048091BB00F0E0E152B2 +:103B3000FB4F80833DC0803B39F0E0F4803A09F4CC +:103B400079C0883A09F09CCF84E08093990510925F +:103B5000DE041092DD04E0910505F091060509955B +:103B60008091DD0481110FC081E08093DD0410920B +:103B7000BD0409C0803C09F4A6CF883C09F4A3CF5A +:103B8000883B09F07DCFE091DE0481E08E0F8093C9 +:103B9000DE04F0E0E354FB4F80818093BB00909102 +:103BA000DE048091DD0429C0809174058093BB0000 +:103BB00085EC8093BC0064CF90919605809195052B +:103BC000981758F5E091960581E08E0F8093960541 +:103BD000F0E0EB58FA4F8081E9CFE091960581E063 +:103BE0008E0F809396058091BB00F0E0EB58FA4F62 +:103BF000808390919605809195059817C8F285E885 +:103C0000D8CFE091960581E08E0F80939605809144 +:103C1000BB00F0E0EB58FA4F80838091980581114A +:103C20005CCF81E08093730584EA8093BC0010929E +:103C3000990526CF85EC8093BC0010929905809160 +:103C4000FF04803230F4E091FF04F0E0E152FB4FDA +:103C500010826091FF0470E0E0910305F09104058B +:103C60008FED94E009951092FF040ACF109297050A +:103C700034CFEFECF5E01382128288EE93E0A0E0FF +:103C8000B0E084839583A683B78388E291E0918333 +:103C90008083E3EFF5E080E181838CED95E0938311 +:103CA000828387E296E09583848384E0868388E03C +:103CB000878391E0908722E330E03287218720E2FA +:103CC00033E03487238748E058EB6CE670E04587A3 +:103CD00056876787708B418B528B638B748B158AE9 +:103CE000168A178A108E2CEB3BE1328F218F2EEE35 +:103CF0003BE1348F238F28EE33E0368F258F24EF7E +:103D000031E030A3278F2EE531E032A321A313A2A7 +:103D100040EC5FEC6AE670E044A355A366A377A38A +:103D200040EA53E66FE670E040A751A762A773A789 +:103D300014A615A616A6EBEDF5E0138212822AE36F +:103D400031E03183208384838FEF858389E086830C +:103D50008AE087838BE080878CE081878DE0828793 +:103D60001386148615861686178681E00C9425081E +:103D7000CF93DF93CDB7DEB728970FB6F894DEBFA9 +:103D80000FBECDBF789484B5826084BD84B5816058 +:103D900084BD85B5826085BD85B5816085BD809116 +:103DA0006E00816080936E0010928100809181008E +:103DB0008260809381008091810081608093810086 +:103DC000809180008160809380008091B100846048 +:103DD0008093B1008091B00081608093B0008091A9 +:103DE0007A00846080937A0080917A008260809368 +:103DF0007A0080917A00816080937A0080917A00C5 +:103E0000806880937A001092C1008DE18093F30561 +:103E100065EB72E089E195E00E943B2A82E00E9416 +:103E2000250881E08093220162E081E10E948C01FB +:103E300062E080E10E948C0162E084E00E948C01DB +:103E400062E08EE00E948C0162E087E00E948C01BB +:103E500064E670E080E090E00E94DC0387E00E946E +:103E60003501892B51F0109214011092FB058AE064 +:103E700090E0909315068093140661E087E00E941D +:103E80008C0160E087E00E945E0161E086E00E94B4 +:103E90008C0160E086E00E945E0161E085E00E94A6 +:103EA0008C0160E085E00E945E0161E082E00E949A +:103EB0008C0160E082E00E945E0190E080E00E9460 +:103EC0007929182F80E10E943501892B09F045C11D +:103ED0000E941D0964E670E080E090E00E94DC032F +:103EE0008EE00E94350121E0892B09F020E020932B +:103EF0005B0569E175E080E00E94D10868EE73E03F +:103F000080E090E00E94DC03B3EFEB2EB5E0FB2EE7 +:103F100010E000E0C8010E947929F70181937F0138 +:103F20000F5F1F4F0F321105A9F780915B058823A2 +:103F3000F1F060E08EE00E948C018BE194E09093C0 +:103F400013018093120180910A05816080930A0514 +:103F500086B18093010580916C00816080936C0034 +:103F6000809168008260809368000E94800D1092AA +:103F700008051092070510929B051092BD0510923E +:103F8000990511E0109398051092730561E082E1A4 +:103F90000E945E0161E083E10E945E018091B900B0 +:103FA0008E7F8093B9008091B9008D7F8093B90096 +:103FB00088E48093B80085E48093BC008FE194E0AE +:103FC000909306058093050580E294E090930405A4 +:103FD0008093030560E085E90E94AD0660912101B0 +:103FE00083E00E94AD0662E987EB0E94AD06BE0148 +:103FF0006F5F7F4F88E0FB0111928A95E9F71A8382 +:104000008FE08C8380E88D838AE10E94330560E233 +:1040100081EB0E94AD06A091F705B091F80524E66A +:1040200030E00E940C2A60547F48884D9B4C60937E +:104030001D0170931E0180931F01909320014091F8 +:1040400000065091010660910206709103064093AC +:10405000440550934505609346057093470580914C +:10406000040690910506A0910606B091070680937C +:10407000400590934105A0934205B09343052091DC +:104080000A06211175C040934E0550934F05609369 +:1040900050057093510580910806809358058091D2 +:1040A000580580FF70C08091FA050E9443178091E7 +:1040B0005805823040F08091FE059091FF05909365 +:1040C0000D0580930C050E94F20E8091F7059091EA +:1040D000F8058732964091F48091F5059091F605A8 +:1040E0008C3D954059F463EC72E081E00E94D10868 +:1040F00068EE73E080E090E00E94DC0343E06CE354 +:1041000070E08091FE059091FF050E94840641E0D9 +:1041100064EB70E08091FE059091FF050E9484069B +:10412000F5E02F2E312CAAE08A2E912CA12CB12C57 +:1041300011E00BE0E0916905E93008F024C7F0E0F8 +:10414000EC55FF4D0C94062AC620C127C827CB2763 +:10415000CE27E127E427E127E72781E10E94350107 +:10416000892B09F4B5CE8091F3058113B1CEB2CE7F +:1041700080934E0590934F05A0935005B093510541 +:10418000809109068ACF8091F9058FCF80E10E9446 +:104190003501892BB9F410936905109368050E94C5 +:1041A0001D0964ED72E080E00E94D10865EE72E0C6 +:1041B00081E00E94D10860ED77E080E090E00E940D +:1041C000DC039FC48091200681110E945A058091D2 +:1041D000670581113CC10E940F0440905F0550901B +:1041E00060056090610570906205C0906305D09095 +:1041F0006405E0906505F0906605C414D504E604F6 +:10420000F70410F4730162016C197D098E099F098E +:10421000693E73408105910508F419C181E10E944E +:104220003501892B09F4A3C00E940F04C090150525 +:10423000D0901605E0901705F09018056C197D09CF +:104240008E099F09609311057093120580931305E1 +:104250009093140520911005222309F441C0209168 +:104260005E052A30ECF569557240810991096F3578 +:10427000794081059105A0F561EF72E081E00E942F +:10428000D10880914E0590914F05A0915005B091B5 +:1042900051058093400590934105A0934205B0934A +:1042A00043058093440590934505A0934605B0933C +:1042B00047058091580580930906809308060E945F +:1042C000BA0C42E062E370E088E592E00E94840666 +:1042D0006CEB72E080E090E00E94DC0310925E05DF +:1042E0008091110590911205A0911305B0911405CC +:1042F0008D3D9540A105B10554F080915E058093F8 +:104300000F058A300CF430C08AE080935E0510926D +:104310001005F0900F05FF2009F45AC05AE0F51679 +:1043200009F456C041E062E370E088E592E00E9443 +:104330008406EFEFEF0DE13108F04AC0F0E0ED55F3 +:10434000FE4D0C94062A42244524482460248B24E4 +:10435000B624CE24E821E821E821E124E624EB2458 +:10436000FA2405250E25132510925E05D0CF6AE0AC +:1043700070E080E090E00E94DC0381E10E94350162 +:10438000892B09F0C6CF80915C05882309F43FC1D1 +:1043900084E00E943501892B09F439C141E062E3D0 +:1043A00070E088E592E00E94840668E672E081E0B1 +:1043B0000E94D10864EF71E080E090E00E94DC038D +:1043C00010925C050E94F20E10925E05109210058C +:1043D00084E00E943501892B09F5809167058111E0 +:1043E0001DC088E0809369050E940F04685E7340D9 +:1043F0008109910960935405709355058093560582 +:104400009093570580915D05882341F00E94A8167E +:104410000E94470690934D0580934C0580915E0560 +:10442000882311F08A3011F40E94680780915E059C +:1044300081110DC080915D05811109C080915C05DD +:10444000811105C068E672E081E00E94D108809188 +:104450005B05882309F4ECC280916705882331F15C +:1044600080915A05882311F160E070E0CB010E9431 +:10447000C60680915E05811105C06AE174E081E0A5 +:104480000E94D10841E06CE370E088EE93E00E9466 +:10449000840641E068E770E084EF91E00E948406C2 +:1044A0006CE271E080E090E00E94DC030E947D18E5 +:1044B0000E94461A8091590581110E942F0980910E +:1044C0005F0590916005A0916105B0916205C09073 +:1044D0006305D0906405E0906505F0906605C81608 +:1044E000D906EA06FB0610F46C017D0180915D059A +:1044F000882309F4C1C2809167058111BDC20E9461 +:104500000F046C197D098E099F09683E734081056F +:10451000910508F4F6C20E944706409119019C01DA +:10452000285833414423B9F040913A0550913B0556 +:104530005093390540933805B90137FF02C06F5FCA +:104540007F4F75956795461B570B509349054093D0 +:10455000480510921901409148055091490537FFCF +:1045600002C02F5F3F4F35952795240F351F30939D +:104570003B0520933A05853091050CF06DC24253FE +:1045800051092D33F6EF3F070CF071C23093370518 +:104590002093360580913405909135052817390709 +:1045A00009F4AFC20E94670E4AE06AE275E08091AA +:1045B0003A0590913B050E94132968E274E089E175 +:1045C00095E00E943B2A6AE275E089E195E00E944D +:1045D000272A6BE072E089E195E00E94272A69E1D1 +:1045E00075E081E00E94D1088091360590913705F1 +:1045F00090933505809334050E94470690934D05AE +:1046000080934C056EE170E080E090E0D8CD84E0CE +:104610000E943501892BB9F441E062E370E080EB40 +:1046200094E00E94840610935C050E94BA0C6EE62A +:1046300071E081E00E94D10864EF71E080E090E0D9 +:104640000E94DC03C1CE80915D058823B1F00E94F9 +:10465000A8160E94470690934D0580934C0541E0B3 +:1046600062E370E088E592E00E94840664E670E010 +:1046700080E090E00E94DC03ABCE809110058111B8 +:10468000C9C0109310050E940F0460931505709324 +:104690001605809317059093180541E062E370E0DA +:1046A00080EB94E00E94840610920F0580915E05D5 +:1046B0008F5F82310CF03EC080935E0580915E0575 +:1046C0008850823010F410935E0580915B05811153 +:1046D000A5C580915E05823019F484E080935E0563 +:1046E00080915E058C3019F46DE060935E058091D9 +:1046F0005E05873011F410935E05E0915E05E15090 +:10470000E13100F50E2E000CFF0BE757FC4D0C9429 +:10471000062A9D23E323E623E923EC23EF23F22358 +:10472000A223A223A223F523F823FB23FE230124A3 +:104730000424072400935E05C1CF6BEF72E081E093 +:104740000E94D1080E940F04C0901505D090160554 +:10475000E0901705F09018056C197D098E099F09E6 +:10476000693B7B408105910528F080915E058A3088 +:104770000CF478C00E940F046C197D098E099F0902 +:104780006D3D75408105910508F4C3CD80915E05AE +:104790008B300CF4BECD43E066E970E080EB94E032 +:1047A0000E9484064FEF40935E051092100560ED65 +:1047B00073E081E00E94D1086CEB72E080E090E051 +:1047C0000E94DC03A6CD65E073E0B9CF6CE073E036 +:1047D000B6CF69E173E0B3CF65E273E0B0CF65E3D4 +:1047E00073E0ADCF65E473E0AACF66E573E0A7CFD1 +:1047F00066E673E0A4CF64E773E0A1CF64E873E0FA +:104800009ECF64E973E09BCF64EA73E098CF64EBDA +:1048100073E095CF0E940F04C0901505D090160547 +:10482000E0901705F09018056C197D098E099F0915 +:10483000693572408105910508F484CF0E940F0408 +:104840006C197D098E099F096032734081059105BD +:1048500008F078CF80915E058A300CF073CF65EC5C +:1048600073E06DCF43E066E970E080EB94E00E9476 +:1048700084066DE771E081E00E94D1088AE08093B0 +:104880005E0547CD0E945717A3CD0E94A816A0CD64 +:1048900080910B06812780930B0680915D0588230C +:1048A00061F010925D05109235051092340510925A +:1048B0003705109236050E94F20E0E94BA0C88CD80 +:1048C0008091580590915B059923D9F09091FB0553 +:1048D000911117C08F5F83708093580580915805A0 +:1048E00081FF11C02091FE053091FF0530930D0529 +:1048F00020930C0580FF0CC08091FA050E9443179D +:1049000067CD8127E9CF10920D0510920C05F2CFEB +:104910008091F905F3CF85E0809369050E940F042B +:104920002091120630911306620F731F811D911D95 +:1049300060935405709355058093560590935705E1 +:1049400020910C0630910D06A8EEB3E00E94E329F9 +:1049500060934E0570934F058093500590935105D9 +:1049600060EE73E081E00E94D10832CD67E0609391 +:1049700069050E940F042091120630911306620F00 +:10498000731F811D911D609354057093550580938D +:104990005605909357056EEE73E0E4CF80912106A3 +:1049A0008127809321066DEF73E0811102C06BE0D7 +:1049B00074E081E00E94D10868EE73E080E090E04E +:1049C00059CE10930E050E94180F02CD10930E05BC +:1049D0000E94EE11FDCC10920D0510920C05109264 +:1049E00058050E94670E8091F9050E9443170E94A6 +:1049F0002B1BEECC10920D0510920C051093580550 +:104A00000E94670E8091FA05F0CF109258058091B0 +:104A1000F9050E9443170E94C416DACC109358057A +:104A20008091FA05F6CF10930E050E94A214D0CC07 +:104A300080915A05882309F442CD60E070E0CB01F3 +:104A40000E94C60680915E05811139CD6AE174E04D +:104A500081E00E94D10833CD8D3E97420CF496CD73 +:104A6000243C89E038070CF091CD4E5C5F4F5093A9 +:104A70004905409348058ACD0E940F04AB01BC0153 +:104A80004C195D096E097F098091140690911506F5 +:104A9000B0E0A0E084179507A607B70790F50E943D +:104AA0009800DC0120EA36E841E050E00E94B92994 +:104AB00026EF37E240E050E00E948F2980916705A1 +:104AC000882339F180915E058A30DCF480914C05B1 +:104AD00090914D05281B390B37FF03C03195219567 +:104AE0003109233331056CF080915C05811109C0D7 +:104AF0006EE671E081E00E94D1080E94F20E1093F0 +:104B00001901E0E0F0E0EF2B09F414CB0E94000063 +:104B10000FCB80915C058111F4CFE0904C05F090B3 +:104B20004D052E193F092D5E3F4F2732310508F004 +:104B300075C08091000690910106A0910206B09187 +:104B400003064090440550904505609046057090DE +:104B5000470584159505A605B70569F0A3019201DF +:104B6000281B390B4A0B5B0BDA01C901853F9140C9 +:104B7000A105B105F0F4409104065091050660913D +:104B80000606709107068091400590914105A0911D +:104B90004205B0914305481759076A077B07A1F101 +:104BA000841B950BA60BB70B853F9140A105B10562 +:104BB00058F10E940F0420913C0530913D05409131 +:104BC0003E0550913F05621B730B840B950B6133BF +:104BD00075478105910510F140920006509201063B +:104BE0006092020670920306809140059091410503 +:104BF000A0914205B0914305809304069093050669 +:104C0000A0930606B09307060E940F0460933C052C +:104C100070933D0580933E0590933F050E944706A3 +:104C2000009709F09BC040901706509018066090BE +:104C3000190670901A0640914E0550914F056091EB +:104C4000500570915105441655066606770609F41D +:104C500030C14416550666067706D8F580911501D1 +:104C600090911601A0911701B09118012091FC05B7 +:104C70003091FD052931310508F06BC0885E934005 +:104C8000A109B1098093150190931601A093170112 +:104C9000B09318018091150190911601A091170110 +:104CA000B091180180934E0590934F05A093500545 +:104CB000B093510580915E058A302CF46DE274E06A +:104CC00081E00E94D1086CE271E080E090E00E94F7 +:104CD000DC038091170690911806A0911906B091F7 +:104CE0001A0640914E0550914F0560915005709104 +:104CF0005105481759076A077B0780F480934E05D2 +:104D000090934F05A0935005B093510580931501E2 +:104D100090931601A0931701B09318010E94670E9B +:104D200010924D0510924C0580914E0590914F05C3 +:104D3000A0915005B091510520910A06211118C18A +:104D40008093440590934505A0934605B09347058D +:104D5000D8CE80519742A109B10994CF803167E242 +:104D6000960709F0A6C040901B0650901C06609064 +:104D70001D0670901E0640914E0550914F056091A2 +:104D8000500570915105441655066606770609F4DC +:104D900090C0441555056605770508F047C0209179 +:104DA000FC053091FD058091150190911601A091AF +:104DB0001701B09118012931310508F075C08851EB +:104DC0009C4FAF4FBF4F8093150190931601A09356 +:104DD0001701B0931801A0E1B7E20E94E329A501F1 +:104DE00094010E94C1298091150190911601A09112 +:104DF0001701B0911801820F931FA41FB51F809354 +:104E00004E0590934F05A0935005B09351058091A6 +:104E10005E058A302CF465E374E081E00E94D108DD +:104E20006CE271E080E090E00E94DC03C0901B0621 +:104E3000D0901C06E0901D06F0901E0680914E0555 +:104E400090914F05A0915005B0915105C816D90613 +:104E5000EA06FB0600F52091FC053091FD05A8EE61 +:104E6000B3E00E94E329A7019601261B370B480BEC +:104E7000590BDA01C9018093150190931601A09393 +:104E80001701B0931801C0924E05D0924F05E092E1 +:104E90005005F09251050E94670E40E157E2509391 +:104EA0004D0540934C0540CF805F984DAF4FBF4FAD +:104EB0008ACF9C012E193F0937FF03C031952195F8 +:104EC0003109253031050CF42FCFE80EF91EF7FE1D +:104ED00003C05FEFE51AF50AD701B595A795B09322 +:104EE0004D05A0934C052091FC053091FD050E94D5 +:104EF0000C2AA50194010E94C1298091150190916D +:104F00001601A0911701B0911801BA01A901480F2B +:104F1000591F6A1F7B1F8091170690911806A09158 +:104F20001906B0911A06481759076A077B0778F0E7 +:104F300080911B0690911C06A0911D06B0911E0643 +:104F4000481759076A077B0710F4DB01CA018093F1 +:104F50004E0590934F05A0935005B09351050E94C4 +:104F6000670E6AE070E080E090E00E94DC03DCCE37 +:104F70008093400590934105A0934205B09343056B +:104F8000C0CD0E942B1B64E670E080E090E017C962 +:104F90000E94C416F8CF0E94A214F5CF0E94EE1111 +:104FA0008091200681110E945A050E947D180E945E +:104FB000461A80915905882309F4A3CD0E942F0930 +:104FC000A0CD0E94C717DFCF0E94180FDCCF8091C1 +:104FD000580581FF31C00E940F04C0905405D09045 +:104FE0005505E0905605F09057056C197D098E091E +:104FF0009F09693E734081059105F0F08091FE059F +:105000009091FF050E94C8010E940F046C197D0950 +:105010008E099F096F3774408105910568F085E01E +:105020000E9422040E940F0460935405709355055A +:1050300080935605909357050E9447067C0184E0B3 +:105040000E943501892B49F480916705811105C0C3 +:1050500080915C05882309F442C0109319011092D5 +:10506000540510925505109256051092570585E08B +:105070000E94220410926905809152059091530577 +:10508000092E000CAA0BBB0B40914E0550914F0509 +:105090006091500570915105840F951FA61FB71F91 +:1050A00080934E0590934F05A0935005B093510502 +:1050B00010925305109252050E94F20E0E9447066C +:1050C00090934D0580934C0580915E058A3009F0E0 +:1050D0005ACF6DE771E081E00E94D10854CF8091F2 +:1050E0001901882399F0109219012FE037E23093CB +:1050F0004B0520934A0588E893E18E199F09B10179 +:105100000E94F22970934905609348052091480553 +:1051100030914905C70188589341B1010E94F22995 +:10512000620F731F70935305609352054AE0E416B3 +:10513000F1040CF044C069315CEF75070CF1C090CC +:105140004E05D0904F05E0905005F0905105CB01F1 +:10515000072E000CAA0BBB0B8C0D9D1DAE1DBF1D99 +:10516000C0901706D0901806E0901906F0901A0625 +:105170002A503109C816D906EA06FB0608F449C0C8 +:1051800080914A0590914B056817790721F00E949C +:10519000670E0E94BA0C8091580566E474E081FDA8 +:1051A00002C06DE474E081E00E94D1088091520554 +:1051B0009091530590934B0580934A05E4CE8DEE74 +:1051C000E81687E2F806E4F2683E93E07907C4F651 +:1051D000C0904E05D0904F05E0905005F0905105DD +:1051E000CB01072E000CAA0BBB0B8C0D9D1DAE1D19 +:1051F000BF1DC0901B06D0901C06E0901D06F090CD +:105200001E068C159D05AE05BF0508F0B9CF265FBB +:105210003F4F3093490520934805B2CF8091140148 +:1052200081116BCA64CABB274A3031F4992322F436 +:10523000BDE2909581959F4F0C945029BB272A3051 +:1052400051F4992342F4BDE2909580957095619553 +:105250007F4F8F4F9F4F0C942E29BB27FA01A62F0B +:105260006217710581059105330B30FB66F0AA27A3 +:10527000660F771F881F991FAA1FA21710F0A21B85 +:1052800063953850A9F7A05DAA3308F0A95DA193F2 +:1052900036F7B111B1931082CA010C946929BB276A +:1052A000FB015527AA27880F991FAA1FA41710F0E2 +:1052B000A41B83955051B9F7A05DAA3308F0A95DEE +:1052C000A193009779F7B111B1931192CB010C948E +:1052D0006929DC01FC01672F71917723E1F732978F +:1052E00004C07C916D9370836291AE17BF07C8F3C1 +:1052F0000895F999FECF92BD81BDF89A992780B59E +:105300000895262FF999FECF1FBA92BD81BD20BD09 +:105310000FB6F894FA9AF99A0FBE01960895052EE1 +:1053200097FB1EF400940E94A62957FD07D00E9407 +:10533000C12907FC03D04EF40C94A6295095409542 +:10534000309521953F4F4F4F5F4F08959095809531 +:10535000709561957F4F8F4F9F4F08950E94E3296D +:10536000A59F900DB49F900DA49F800D911D1124B9 +:105370000895B7FF0C94AE290E94AE29821B930BAF +:105380000895A1E21A2EAA1BBB1BFD010DC0AA1F86 +:10539000BB1FEE1FFF1FA217B307E407F50720F09E +:1053A000A21BB30BE40BF50B661F771F881F991F19 +:1053B0001A9469F760957095809590959B01AC0162 +:1053C000BD01CF010895A29FB001B39FC001A39F6B +:1053D000700D811D1124911DB29F700D811D11242E +:1053E000911D089597FB072E16F4009407D077FDC2 +:1053F00009D00E94132A07FC05D03EF490958195B0 +:105400009F4F0895709561957F4F0895EE0FFF1F90 +:105410000590F491E02D09940E94E329B7FF0895C7 +:10542000821B930B0895AA1BBB1B51E107C0AA1F47 +:10543000BB1FA617B70710F0A61BB70B881F991F35 +:105440005A95A9F780959095BC01CD010895FB016F +:10545000DC010D900020E9F7119701900D920020DA +:10546000E1F70895FB01DC018D9101908019011095 +:10547000D9F3990B0895FB01DC0101900D920020F6 +:10548000E1F70895FB01DC010D900020E9F7119789 +:105490004150504028F0019000200D92C9F7089526 +:0854A0001C920895F894FFCF5F +:1054A8000F03000101010101210421042104210449 +:1054B800210421040160566C0001010101C070271C +:1054C80034FF01FF000000000405D7045705AD04B0 +:1054D800D004BD04AE0400000000CB084E045705FC +:1054E8007B046C6F6164696E67207374616E6461BC +:1054F80072640073657474696E67732E2E2E004390 +:10550800572D737065656420002057504D006469FD +:10551800616C206973206C6F636B6564002D2D2DA1 +:105528002053455454494E4753202D2D2D004120DA +:10553800004220002043574C002043575500205478 +:10554800580020535000746F7563682073656E733C +:105558006F7273006E6F742064657465637465643C +:1055680000746F756368206B65792063616C69628C +:10557800720044495420002C2044414820002075E2 +:105588007300746F756368206B65796572204F4688 +:1055980046006C6F7765722000206B487A00757042 +:1055A800706572200073746570200020487A0020AE +:1055B8006D73004B65793A2000737472616967688E +:1055C80074007265762E20706164646C6500726583 +:1055D800762E20627567004175746F2D7370616354 +:1055E800653A2000546F7563682073656E736F7237 +:1055F8000073656E73697469766974792000536500 +:105608006D692D51534B3A200051534B2064656C02 +:105618006179200073696465746F6E6520006D6937 +:105628006E20006D61782000706F74207370616E59 +:1056380020006472697665206C6576656C20006D63 +:1056480041006F66667365742000636F7272200094 +:105658002070706D0052616475696E6F2076312E0E +:1056680032390056464F20756E63616C696272610B +:105678007465640043616C6962726174696E673A4B +:105688002053657400746F207A65726F6265617467 +:105698000056464F20413D4220210053776170203B +:1056A80056464F7300524954204F4E0053504C49B0 +:1056B80054204F4E2F4F4646005377697463682035 +:1056C8006D6F646500537461727420667265712031 +:1056D8007363616E004D6F6E69746F722056464F2A +:1056E80020412F4200526F67657242656570204FF6 +:1056F8004E2F4F464600536574207363616E2070C9 +:105708006172616D73005365742043572070617234 +:10571800616D73004C53422063616C6962726174FD +:10572800696F6E005553422063616C6962726174DF +:10573800696F6E0056464F206472697665202D2089 +:105748004C53420056464F206472697665202D20DE +:10575800555342005365742074756E696E672072E4 +:10576800616E67650052657365742056464F730015 +:10577800202D2D2D204E4F524D414C202D2D2D00EA +:1057880066726571207363616E6E696E6700412F82 +:1057980042206D6F6E69746F72696E6700526F6731 +:1057A80065722042656570204F4E00526F676572C2 +:1057B8002042656570204F4646006F7574206F66FD +:1057C8002062616E6421210052495420003C3C3C17 +:1057D8003C3C3C3C002020202020202020203E3E35 +:1057E8003E3E3E3E3E0053504F54202B2046494EED +:0857F800452054554E45000008 +:107800000C94343C0C94513C0C94513C0C94513CE1 +:107810000C94513C0C94513C0C94513C0C94513CB4 +:107820000C94513C0C94513C0C94513C0C94513CA4 +:107830000C94513C0C94513C0C94513C0C94513C94 +:107840000C94513C0C94513C0C94513C0C94513C84 +:107850000C94513C0C94513C0C94513C0C94513C74 +:107860000C94513C0C94513C11241FBECFEFD8E036 +:10787000DEBFCDBF11E0A0E0B1E0ECE9FFE702C060 +:1078800005900D92A230B107D9F712E0A2E0B1E065 +:1078900001C01D92AD30B107E1F70E942D3D0C945F +:1078A000CC3F0C94003C982F959595959595959582 +:1078B000905D8F708A307CF0282F295A8091C0000B +:1078C00085FFFCCF9093C6008091C00085FFFCCF60 +:1078D0002093C6000895282F205DF0CF982F809127 +:1078E000C00085FFFCCF9093C6000895EF92FF92F1 +:1078F0000F931F93EE24FF2487018091C00087FD22 +:1079000017C00894E11CF11C011D111D81E4E8164B +:1079100082E4F8068FE0080780E0180770F3E09132 +:107920000401F091050109958091C00087FFE9CF1E +:107930008091C6001F910F91FF90EF9008950E94D3 +:10794000763C982F8091C00085FFFCCF9093C600B5 +:1079500091362CF490330CF09053892F089597555D +:10796000892F08951F930E949F3C182F0E949F3CCF +:107970001295107F810F1F9108951F93182F882350 +:1079800021F00E94763C1150E1F71F9108951F935A +:10799000182F0E94763C803249F0809103018F5F5E +:1079A000809303018530C1F01F9108958091C0003C +:1079B00085FFFCCF84E18093C6008091C00085FFE5 +:1079C000FCCF1093C6008091C00085FFFCCF80E102 +:1079D0008093C6001F910895E0910401F091050184 +:1079E00009951F9108950E94763C803241F0809164 +:1079F00003018F5F80930301853081F008958091AA +:107A0000C00085FFFCCF84E18093C6008091C00058 +:107A100085FFFCCF80E18093C6000895E0910401CA +:107A2000F09105010995089540E951E08823A1F0FE +:107A30002D9A28EE33E0FA013197F1F721503040CA +:107A4000D1F72D9828EE33E0FA013197F1F7215064 +:107A50003040D1F7815061F708953F924F925F9285 +:107A60006F927F928F929F92AF92BF92CF92DF924E +:107A7000EF92FF920F931F93CF93DF93000080E16B +:107A80008093C4001092C50088E18093C10086E015 +:107A90008093C2005098589A259A81E00E94143D24 +:107AA00024E1F22E9EE1E92E85E9D82E0FE0C02ECA +:107AB00010E1B12EAA24A394B1E49B2EA6E58A2E50 +:107AC000F2E57F2EE0E26E2E79E4572E63E5462E36 +:107AD00050E5352E0E94763C8033B1F18133B9F107 +:107AE000803409F46FC0813409F476C0823409F41B +:107AF00085C0853409F488C0803531F1823521F1A3 +:107B0000813511F1853509F485C0863509F48DC0BC +:107B1000843609F496C0843709F403C1853709F423 +:107B200072C1863709F466C0809103018F5F80932C +:107B30000301853079F6E0910401F0910501099582 +:107B40000E94763C803351F60E94F33CC3CF0E94E2 +:107B5000763C803249F78091C00085FFFCCFF092DF +:107B6000C6008091C00085FFFCCF9092C600809136 +:107B7000C00085FFFCCF8092C6008091C00085FFC9 +:107B8000FCCF7092C6008091C00085FFFCCF609250 +:107B9000C6008091C00085FFFCCF5092C600809146 +:107BA000C00085FFFCCF4092C6008091C00085FFD9 +:107BB000FCCF3092C6008091C00085FFFCCFB09210 +:107BC000C60088CF0E94763C863808F4BDCF0E945C +:107BD000763C0E94F33C7ECF0E94763C803809F4CC +:107BE0009CC0813809F40BC1823809F43CC1883942 +:107BF00009F48FC080E00E94C73C6CCF84E10E94F2 +:107C0000BD3C0E94F33C66CF85E00E94BD3C0E94D3 +:107C1000F33C60CF0E94763C809306010E94763C44 +:107C2000809307010E94F33C55CF0E94763C80333D +:107C300009F41DC183E00E94BD3C80E00E94C73C66 +:107C400049CF0E94763C809309020E94763C809343 +:107C5000080280910C028E7F80930C020E94763C79 +:107C6000853409F415C18091080290910902892B8D +:107C700089F000E010E00E94763CF801E85FFE4FDA +:107C800080830F5F1F4F80910802909109020817AF +:107C9000190788F30E94763C803209F045CF809125 +:107CA0000C0280FF01C16091060170910701660F0F +:107CB000771F7093070160930601A0910802B091AD +:107CC00009021097C9F0E8E0F1E09B01AD014E0F09 +:107CD0005F1FF999FECF32BD21BD819180BDFA9A17 +:107CE000F99A2F5F3F4FE417F50799F76A0F7B1F4B +:107CF00070930701609306018091C00085FFFCCF5F +:107D0000F092C6008091C00085FFFCCFB092C60003 +:107D1000E1CE83E00E94C73CDDCE82E00E94C73CFA +:107D2000D9CE0E94763C809309020E94763C8093D3 +:107D300008028091060190910701880F991F909386 +:107D40000701809306010E94763C853409F4A6C0A1 +:107D500080910C028E7F80930C020E94763C8032D0 +:107D600009F0B8CE8091C00085FFFCCFF092C6002C +:107D7000609108027091090261157105B9F140E046 +:107D800050E080910C02A82FA170B82FB27011C0E2 +:107D9000BB2309F45CC0E0910601F0910701319624 +:107DA000F0930701E09306014F5F5F4F46175707B7 +:107DB000E8F4AA2369F3F999FECF209106013091E6 +:107DC000070132BD21BDF89A90B58091C00085FFB2 +:107DD000FCCF9093C6002F5F3F4F30930701209355 +:107DE00006014F5F5F4F4617570718F38091C00099 +:107DF00085FDE5CE8091C00085FFF8CFE0CE81E023 +:107E00000E94C73C67CE0E94763C803209F08CCE3F +:107E10008091C00085FFFCCFF092C6008091C00029 +:107E200085FFFCCFE092C6008091C00085FFFCCFAB +:107E3000D092C6008091C00085FFFCCFC092C600E2 +:107E40008091C00085FFFCCFB092C60043CEE09188 +:107E50000601F091070194918091C00085FFFCCF4D +:107E60009093C6009CCF80E10E94C73C33CE0E9415 +:107E7000763C0E94763C182F0E94763C112309F430 +:107E800083C0113009F484C08FE00E94C73C22CE29 +:107E900080910C02816080930C02E5CE80910C02EF +:107EA000816080930C0259CF809107018823880F4D +:107EB000880B8A2180930B02809106019091070123 +:107EC000880F991F90930701809306018091080203 +:107ED00080FF09C080910802909109020196909359 +:107EE000090280930802F894F999FECF1127E091D6 +:107EF0000601F0910701C8E0D1E08091080290915D +:107F00000902103091F40091570001700130D9F34B +:107F100003E000935700E89500915700017001308D +:107F2000D9F301E100935700E89509901990009169 +:107F3000570001700130D9F301E000935700E89534 +:107F40001395103498F011270091570001700130FB +:107F5000D9F305E000935700E895009157000170B0 +:107F60000130D9F301E100935700E895329602976A +:107F700009F0C7CF103011F00296E5CF112480919F +:107F8000C00085FFB9CEBCCE8EE10E94C73CA2CD19 +:0C7F900085E90E94C73C9ECDF894FFCF0D +:027F9C00800063 +:040000030000780081 +:00000001FF diff --git a/sidetone wiring.png b/sidetone wiring.png new file mode 100644 index 0000000..923380b Binary files /dev/null and b/sidetone wiring.png differ